LCOV - code coverage report
Current view: directory - content/base/src - nsWebSocket.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 806 2 0.2 %
Date: 2012-06-02 Functions: 82 2 2.4 %

       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                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is mozilla.org code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Wellington Fernando de Macedo.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2009
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *    Wellington Fernando de Macedo <wfernandom2004@gmail.com> (original author)
      25                 :  *    Patrick McManus <mcmanus@ducksong.com>
      26                 :  *    Jason Duell <jduell.mcbugs@gmail.com>
      27                 :  *
      28                 :  * Alternatively, the contents of this file may be used under the terms of
      29                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      30                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      31                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      32                 :  * of those above. If you wish to allow use of your version of this file only
      33                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      34                 :  * use your version of this file under the terms of the MPL, indicate your
      35                 :  * decision by deleting the provisions above and replace them with the notice
      36                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      37                 :  * the provisions above, a recipient may use your version of this file under
      38                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      39                 :  *
      40                 :  * ***** END LICENSE BLOCK ***** */
      41                 : 
      42                 : #include "mozilla/Util.h"
      43                 : 
      44                 : #include "nsWebSocket.h"
      45                 : 
      46                 : #include "nsIScriptGlobalObject.h"
      47                 : #include "nsIDOMWindow.h"
      48                 : #include "nsIDocument.h"
      49                 : #include "nsXPCOM.h"
      50                 : #include "nsIXPConnect.h"
      51                 : #include "nsContentUtils.h"
      52                 : #include "nsEventDispatcher.h"
      53                 : #include "nsDOMError.h"
      54                 : #include "nsIScriptObjectPrincipal.h"
      55                 : #include "nsDOMClassInfoID.h"
      56                 : #include "jsapi.h"
      57                 : #include "nsIURL.h"
      58                 : #include "nsIPrivateDOMEvent.h"
      59                 : #include "nsICharsetConverterManager.h"
      60                 : #include "nsIUnicodeEncoder.h"
      61                 : #include "nsThreadUtils.h"
      62                 : #include "nsIDOMMessageEvent.h"
      63                 : #include "nsIPromptFactory.h"
      64                 : #include "nsIWindowWatcher.h"
      65                 : #include "nsIPrompt.h"
      66                 : #include "nsIStringBundle.h"
      67                 : #include "nsIConsoleService.h"
      68                 : #include "nsLayoutStatics.h"
      69                 : #include "nsIDOMCloseEvent.h"
      70                 : #include "nsICryptoHash.h"
      71                 : #include "jsdbgapi.h"
      72                 : #include "nsIJSContextStack.h"
      73                 : #include "nsJSUtils.h"
      74                 : #include "nsIScriptError.h"
      75                 : #include "nsNetUtil.h"
      76                 : #include "nsILoadGroup.h"
      77                 : #include "mozilla/Preferences.h"
      78                 : #include "nsDOMLists.h"
      79                 : #include "xpcpublic.h"
      80                 : #include "nsContentPolicyUtils.h"
      81                 : #include "nsContentErrors.h"
      82                 : #include "jstypedarray.h"
      83                 : #include "prmem.h"
      84                 : #include "nsDOMFile.h"
      85                 : #include "nsWrapperCacheInlines.h"
      86                 : #include "nsDOMEventTargetHelper.h"
      87                 : #include "nsIObserverService.h"
      88                 : 
      89                 : using namespace mozilla;
      90                 : 
      91                 : #define UTF_8_REPLACEMENT_CHAR    static_cast<PRUnichar>(0xFFFD)
      92                 : 
      93                 : #define TRUE_OR_FAIL_WEBSOCKET(x, ret)                                    \
      94                 :   PR_BEGIN_MACRO                                                          \
      95                 :     if (NS_UNLIKELY(!(x))) {                                              \
      96                 :       NS_WARNING("TRUE_OR_FAIL_WEBSOCKET(" #x ") failed");                \
      97                 :       FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR);          \
      98                 :       return ret;                                                         \
      99                 :     }                                                                     \
     100                 :   PR_END_MACRO
     101                 : 
     102                 : #define SUCCESS_OR_FAIL_WEBSOCKET(res, ret)                               \
     103                 :   PR_BEGIN_MACRO                                                          \
     104                 :     nsresult __rv = res;                                                  \
     105                 :     if (NS_FAILED(__rv)) {                                                \
     106                 :       NS_ENSURE_SUCCESS_BODY(res, ret)                                    \
     107                 :       FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR);          \
     108                 :       return ret;                                                         \
     109                 :     }                                                                     \
     110                 :   PR_END_MACRO
     111                 : 
     112                 : nsresult
     113               0 : nsWebSocket::PrintErrorOnConsole(const char *aBundleURI,
     114                 :                                  const PRUnichar *aError,
     115                 :                                  const PRUnichar **aFormatStrings,
     116                 :                                  PRUint32 aFormatStringsLen)
     117                 : {
     118               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     119                 :   nsresult rv;
     120                 : 
     121                 :   nsCOMPtr<nsIStringBundleService> bundleService =
     122               0 :     do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
     123               0 :   NS_ENSURE_SUCCESS(rv, rv);
     124                 : 
     125               0 :   nsCOMPtr<nsIStringBundle> strBundle;
     126               0 :   rv = bundleService->CreateBundle(aBundleURI, getter_AddRefs(strBundle));
     127               0 :   NS_ENSURE_SUCCESS(rv, rv);
     128                 : 
     129                 :   nsCOMPtr<nsIConsoleService> console(
     130               0 :     do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
     131               0 :   NS_ENSURE_SUCCESS(rv, rv);
     132                 : 
     133                 :   nsCOMPtr<nsIScriptError> errorObject(
     134               0 :     do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv));
     135               0 :   NS_ENSURE_SUCCESS(rv, rv);
     136                 : 
     137                 :   // Localize the error message
     138               0 :   nsXPIDLString message;
     139               0 :   if (aFormatStrings) {
     140               0 :     rv = strBundle->FormatStringFromName(aError, aFormatStrings,
     141                 :                                          aFormatStringsLen,
     142               0 :                                          getter_Copies(message));
     143                 :   } else {
     144               0 :     rv = strBundle->GetStringFromName(aError, getter_Copies(message));
     145                 :   }
     146               0 :   NS_ENSURE_SUCCESS(rv, rv);
     147                 : 
     148               0 :   rv = errorObject->InitWithWindowID(message.get(),
     149               0 :                                      NS_ConvertUTF8toUTF16(mScriptFile).get(),
     150                 :                                      nsnull, mScriptLine, 0,
     151                 :                                      nsIScriptError::errorFlag, "Web Socket",
     152               0 :                                      mInnerWindowID);
     153               0 :   NS_ENSURE_SUCCESS(rv, rv);
     154                 : 
     155                 :   // print the error message directly to the JS console
     156               0 :   rv = console->LogMessage(errorObject);
     157               0 :   NS_ENSURE_SUCCESS(rv, rv);
     158                 : 
     159               0 :   return NS_OK;
     160                 : }
     161                 : 
     162                 : // when this is called the browser side wants no more part of it
     163                 : nsresult
     164               0 : nsWebSocket::CloseConnection(PRUint16 aReasonCode,
     165                 :                              const nsACString& aReasonString)
     166                 : {
     167               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     168               0 :   if (mDisconnected)
     169               0 :     return NS_OK;
     170                 : 
     171                 :   // Disconnect() can release this object, so we keep a
     172                 :   // reference until the end of the method
     173               0 :   nsRefPtr<nsWebSocket> kungfuDeathGrip = this;
     174                 : 
     175               0 :   if (mReadyState == nsIWebSocket::CONNECTING) {
     176               0 :     SetReadyState(nsIWebSocket::CLOSED);
     177               0 :     if (mChannel) {
     178               0 :       mChannel->Close(aReasonCode, aReasonString);
     179                 :     }
     180               0 :     Disconnect();
     181               0 :     return NS_OK;
     182                 :   }
     183                 : 
     184               0 :   SetReadyState(nsIWebSocket::CLOSING);
     185                 : 
     186               0 :   if (mDisconnected) {
     187               0 :     SetReadyState(nsIWebSocket::CLOSED);
     188               0 :     Disconnect();
     189               0 :     return NS_OK;
     190                 :   }
     191                 : 
     192               0 :   return mChannel->Close(aReasonCode, aReasonString);
     193                 : }
     194                 : 
     195                 : nsresult
     196               0 : nsWebSocket::ConsoleError()
     197                 : {
     198               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     199                 :   nsresult rv;
     200                 : 
     201               0 :   nsCAutoString targetSpec;
     202               0 :   rv = mURI->GetSpec(targetSpec);
     203               0 :   if (NS_FAILED(rv)) {
     204               0 :     NS_WARNING("Failed to get targetSpec");
     205                 :   } else {
     206               0 :     NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
     207               0 :     const PRUnichar *formatStrings[] = { specUTF16.get() };
     208                 : 
     209               0 :     if (mReadyState < nsIWebSocket::OPEN) {
     210                 :       PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
     211               0 :                           NS_LITERAL_STRING("connectionFailure").get(),
     212               0 :                           formatStrings, ArrayLength(formatStrings));
     213                 :     } else {
     214                 :       PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
     215               0 :                           NS_LITERAL_STRING("netInterrupt").get(),
     216               0 :                           formatStrings, ArrayLength(formatStrings));
     217                 :     }
     218                 :   }
     219                 :   /// todo some specific errors - like for message too large
     220               0 :   return rv;
     221                 : }
     222                 : 
     223                 : 
     224                 : nsresult
     225               0 : nsWebSocket::FailConnection(PRUint16 aReasonCode,
     226                 :                             const nsACString& aReasonString)
     227                 : {
     228               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     229               0 :   ConsoleError();
     230                 : 
     231               0 :   CloseConnection(aReasonCode, aReasonString);
     232                 : 
     233               0 :   nsresult rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
     234               0 :   if (NS_FAILED(rv))
     235               0 :     NS_WARNING("Failed to dispatch the error event");
     236                 : 
     237               0 :   return NS_OK;
     238                 : }
     239                 : 
     240                 : nsresult
     241               0 : nsWebSocket::Disconnect()
     242                 : {
     243               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     244                 : 
     245               0 :   if (mDisconnected)
     246               0 :     return NS_OK;
     247                 : 
     248               0 :   nsCOMPtr<nsILoadGroup> loadGroup;
     249               0 :   GetLoadGroup(getter_AddRefs(loadGroup));
     250               0 :   if (loadGroup)
     251               0 :     loadGroup->RemoveRequest(this, nsnull, NS_OK);
     252                 : 
     253               0 :   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     254               0 :   if (os) {
     255               0 :     os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
     256               0 :     os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
     257                 :   }
     258                 : 
     259                 :   // DontKeepAliveAnyMore() can release the object. So hold a reference to this
     260                 :   // until the end of the method.
     261               0 :   nsRefPtr<nsWebSocket> kungfuDeathGrip = this;
     262                 : 
     263               0 :   DontKeepAliveAnyMore();
     264               0 :   mChannel = nsnull;
     265               0 :   mDisconnected = true;
     266                 : 
     267               0 :   return NS_OK;
     268                 : }
     269                 : 
     270                 : //-----------------------------------------------------------------------------
     271                 : // nsWebSocket::nsIWebSocketListener methods:
     272                 : //-----------------------------------------------------------------------------
     273                 : 
     274                 : nsresult
     275               0 : nsWebSocket::DoOnMessageAvailable(const nsACString & aMsg, bool isBinary)
     276                 : {
     277               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     278               0 :   NS_ABORT_IF_FALSE(!mDisconnected, "Received message after disconnecting");
     279                 : 
     280               0 :   if (mReadyState == nsIWebSocket::OPEN) {
     281                 :     // Dispatch New Message
     282               0 :     nsresult rv = CreateAndDispatchMessageEvent(aMsg, isBinary);
     283               0 :     if (NS_FAILED(rv)) {
     284               0 :       NS_WARNING("Failed to dispatch the message event");
     285                 :     }
     286                 :   } else {
     287                 :     // CLOSING should be the only other state where it's possible to get msgs
     288                 :     // from channel: Spec says to drop them.
     289               0 :     NS_ASSERTION(mReadyState == nsIWebSocket::CLOSING,
     290                 :                  "Received message while CONNECTING or CLOSED");
     291                 :   }
     292                 : 
     293               0 :   return NS_OK;
     294                 : }
     295                 : 
     296                 : NS_IMETHODIMP
     297               0 : nsWebSocket::OnMessageAvailable(nsISupports *aContext, const nsACString & aMsg)
     298                 : {
     299               0 :   return DoOnMessageAvailable(aMsg, false);
     300                 : }
     301                 : 
     302                 : NS_IMETHODIMP
     303               0 : nsWebSocket::OnBinaryMessageAvailable(nsISupports *aContext,
     304                 :                                       const nsACString & aMsg)
     305                 : {
     306               0 :   return DoOnMessageAvailable(aMsg, true);
     307                 : }
     308                 : 
     309                 : NS_IMETHODIMP
     310               0 : nsWebSocket::OnStart(nsISupports *aContext)
     311                 : {
     312               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     313               0 :   if (mDisconnected)
     314               0 :     return NS_OK;
     315                 : 
     316                 :   // Attempt to kill "ghost" websocket: but usually too early for check to fail
     317               0 :   nsresult rv = CheckInnerWindowCorrectness();
     318               0 :   if (NS_FAILED(rv)) {
     319               0 :     FailConnectionQuietly();
     320               0 :     return rv;
     321                 :   }
     322                 : 
     323               0 :   if (!mRequestedProtocolList.IsEmpty()) {
     324               0 :     mChannel->GetProtocol(mEstablishedProtocol);
     325                 :   }
     326                 : 
     327               0 :   mChannel->GetExtensions(mEstablishedExtensions);
     328               0 :   UpdateURI();
     329                 : 
     330               0 :   SetReadyState(nsIWebSocket::OPEN);
     331               0 :   return NS_OK;
     332                 : }
     333                 : 
     334                 : NS_IMETHODIMP
     335               0 : nsWebSocket::OnStop(nsISupports *aContext, nsresult aStatusCode)
     336                 : {
     337               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     338               0 :   if (mDisconnected)
     339               0 :     return NS_OK;
     340                 : 
     341               0 :   mCloseEventWasClean = NS_SUCCEEDED(aStatusCode);
     342                 : 
     343               0 :   if (aStatusCode == NS_BASE_STREAM_CLOSED &&
     344                 :       mReadyState >= nsIWebSocket::CLOSING) {
     345                 :     // don't generate an error event just because of an unclean close
     346               0 :     aStatusCode = NS_OK;
     347                 :   }
     348                 : 
     349               0 :   if (NS_FAILED(aStatusCode)) {
     350               0 :     ConsoleError();
     351               0 :     nsresult rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
     352               0 :     if (NS_FAILED(rv))
     353               0 :       NS_WARNING("Failed to dispatch the error event");
     354                 :   }
     355                 : 
     356               0 :   SetReadyState(nsIWebSocket::CLOSED);
     357               0 :   Disconnect();
     358                 : 
     359               0 :   return NS_OK;
     360                 : }
     361                 : 
     362                 : NS_IMETHODIMP
     363               0 : nsWebSocket::OnAcknowledge(nsISupports *aContext, PRUint32 aSize)
     364                 : {
     365               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     366                 : 
     367               0 :   if (aSize > mOutgoingBufferedAmount)
     368               0 :     return NS_ERROR_UNEXPECTED;
     369                 : 
     370               0 :   mOutgoingBufferedAmount -= aSize;
     371               0 :   return NS_OK;
     372                 : }
     373                 : 
     374                 : NS_IMETHODIMP
     375               0 : nsWebSocket::OnServerClose(nsISupports *aContext, PRUint16 aCode,
     376                 :                            const nsACString &aReason)
     377                 : {
     378               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     379                 : 
     380               0 :   NS_ABORT_IF_FALSE(mReadyState != nsIWebSocket::CONNECTING,
     381                 :                     "Received server close before connected?");
     382                 : 
     383               0 :   if (mReadyState == nsIWebSocket::CLOSED) {
     384               0 :     NS_WARNING("Received server close after already closed!");
     385               0 :     return NS_ERROR_UNEXPECTED;
     386                 :   }
     387                 : 
     388                 :   // store code/string for onclose DOM event
     389               0 :   mCloseEventCode = aCode;
     390               0 :   CopyUTF8toUTF16(aReason, mCloseEventReason);
     391                 : 
     392               0 :   if (mReadyState == nsIWebSocket::OPEN) {
     393                 :     // Send reciprocal Close frame.
     394                 :     // 5.5.1: "When sending a Close frame in response, the endpoint typically
     395                 :     // echos the status code it received"
     396               0 :     CloseConnection(aCode, aReason);
     397                 :   } else {
     398                 :     // Nothing else to do: OnStop does the rest of the work.
     399               0 :     NS_ASSERTION (mReadyState == nsIWebSocket::CLOSING, "unknown state");
     400               0 :     NS_ASSERTION(!mDisconnected, "should not be disconnected during CLOSING");
     401                 :   }
     402                 : 
     403               0 :   return NS_OK;
     404                 : }
     405                 : 
     406                 : //-----------------------------------------------------------------------------
     407                 : // nsWebSocket::nsIInterfaceRequestor
     408                 : //-----------------------------------------------------------------------------
     409                 : 
     410                 : NS_IMETHODIMP
     411               0 : nsWebSocket::GetInterface(const nsIID &aIID, void **aResult)
     412                 : {
     413               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     414                 : 
     415               0 :   if (mDisconnected)
     416               0 :     return NS_ERROR_FAILURE;
     417                 : 
     418               0 :   if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
     419               0 :       aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
     420                 :     nsresult rv;
     421                 : 
     422               0 :     nsIScriptContext* sc = GetContextForEventHandlers(&rv);
     423                 :     nsCOMPtr<nsIDocument> doc =
     424               0 :       nsContentUtils::GetDocumentFromScriptContext(sc);
     425               0 :     if (!doc)
     426               0 :       return NS_ERROR_NOT_AVAILABLE;
     427                 : 
     428                 :     nsCOMPtr<nsIPromptFactory> wwatch =
     429               0 :       do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
     430               0 :     NS_ENSURE_SUCCESS(rv, rv);
     431                 : 
     432               0 :     nsCOMPtr<nsPIDOMWindow> outerWindow = doc->GetWindow();
     433               0 :     return wwatch->GetPrompt(outerWindow, aIID, aResult);
     434                 :   }
     435                 : 
     436               0 :   return QueryInterface(aIID, aResult);
     437                 : }
     438                 : 
     439                 : ////////////////////////////////////////////////////////////////////////////////
     440                 : // nsWebSocket
     441                 : ////////////////////////////////////////////////////////////////////////////////
     442                 : 
     443               0 : nsWebSocket::nsWebSocket() : mKeepingAlive(false),
     444                 :                              mCheckMustKeepAlive(true),
     445                 :                              mTriggeredCloseEvent(false),
     446                 :                              mDisconnected(false),
     447                 :                              mCloseEventWasClean(false),
     448                 :                              mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL),
     449                 :                              mReadyState(nsIWebSocket::CONNECTING),
     450                 :                              mOutgoingBufferedAmount(0),
     451                 :                              mBinaryType(WS_BINARY_TYPE_BLOB),
     452                 :                              mScriptLine(0),
     453               0 :                              mInnerWindowID(0)
     454                 : {
     455               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     456               0 :   nsLayoutStatics::AddRef();
     457               0 : }
     458                 : 
     459               0 : nsWebSocket::~nsWebSocket()
     460                 : {
     461               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     462                 : 
     463               0 :   Disconnect();
     464               0 :   nsLayoutStatics::Release();
     465               0 : }
     466                 : 
     467            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsWebSocket)
     468                 : 
     469               0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsWebSocket)
     470               0 :   bool isBlack = tmp->IsBlack();
     471               0 :   if (isBlack|| tmp->mKeepingAlive) {
     472               0 :     if (tmp->mListenerManager) {
     473               0 :       tmp->mListenerManager->UnmarkGrayJSListeners();
     474               0 :       NS_UNMARK_LISTENER_WRAPPER(Open)
     475               0 :       NS_UNMARK_LISTENER_WRAPPER(Error)
     476               0 :       NS_UNMARK_LISTENER_WRAPPER(Message)
     477               0 :       NS_UNMARK_LISTENER_WRAPPER(Close)
     478                 :     }
     479               0 :     if (!isBlack) {
     480               0 :       xpc_UnmarkGrayObject(tmp->PreservingWrapper() ? 
     481               0 :                            tmp->GetWrapperPreserveColor() :
     482               0 :                            tmp->GetExpandoObjectPreserveColor());
     483                 :     }
     484               0 :     return true;
     485                 :   }
     486               0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
     487                 : 
     488               0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsWebSocket)
     489               0 :   return tmp->IsBlack();
     490                 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
     491                 : 
     492               0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsWebSocket)
     493               0 :   return tmp->IsBlack();
     494                 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
     495                 : 
     496               0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsWebSocket,
     497                 :                                                nsDOMEventTargetHelper)
     498               0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
     499                 : 
     500               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsWebSocket,
     501                 :                                                   nsDOMEventTargetHelper)
     502               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnOpenListener)
     503               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnMessageListener)
     504               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnCloseListener)
     505               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
     506               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPrincipal)
     507               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mURI)
     508               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
     509               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     510                 : 
     511               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsWebSocket,
     512                 :                                                 nsDOMEventTargetHelper)
     513               0 :   tmp->Disconnect();
     514               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnOpenListener)
     515               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnMessageListener)
     516               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnCloseListener)
     517               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
     518               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPrincipal)
     519               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mURI)
     520               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannel)
     521               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     522                 : 
     523                 : DOMCI_DATA(WebSocket, nsWebSocket)
     524                 : 
     525               0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsWebSocket)
     526               0 :   NS_INTERFACE_MAP_ENTRY(nsIWebSocket)
     527               0 :   NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
     528               0 :   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
     529               0 :   NS_INTERFACE_MAP_ENTRY(nsIWebSocketListener)
     530               0 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
     531               0 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     532               0 :   NS_INTERFACE_MAP_ENTRY(nsIRequest)
     533               0 :   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WebSocket)
     534               0 : NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
     535                 : 
     536               0 : NS_IMPL_ADDREF_INHERITED(nsWebSocket, nsDOMEventTargetHelper)
     537               0 : NS_IMPL_RELEASE_INHERITED(nsWebSocket, nsDOMEventTargetHelper)
     538                 : 
     539                 : void
     540               0 : nsWebSocket::DisconnectFromOwner()
     541                 : {
     542               0 :   nsDOMEventTargetHelper::DisconnectFromOwner();
     543               0 :   NS_DISCONNECT_EVENT_HANDLER(Open)
     544               0 :   NS_DISCONNECT_EVENT_HANDLER(Message)
     545               0 :   NS_DISCONNECT_EVENT_HANDLER(Close)
     546               0 :   NS_DISCONNECT_EVENT_HANDLER(Error)
     547               0 :   FailConnectionQuietly();
     548               0 :   DontKeepAliveAnyMore();
     549               0 : }
     550                 : 
     551                 : //-----------------------------------------------------------------------------
     552                 : // nsWebSocket::nsIJSNativeInitializer methods:
     553                 : //-----------------------------------------------------------------------------
     554                 : 
     555                 : /**
     556                 :  * This Initialize method is called from XPConnect via nsIJSNativeInitializer.
     557                 :  * It is used for constructing our nsWebSocket from JavaScript. It expects a URL
     558                 :  * string parameter and an optional protocol parameter which may be a string or
     559                 :  * an array of strings. It also initializes the principal, the script context and
     560                 :  * the window owner.
     561                 :  */
     562                 : NS_IMETHODIMP
     563               0 : nsWebSocket::Initialize(nsISupports* aOwner,
     564                 :                         JSContext* aContext,
     565                 :                         JSObject* aObject,
     566                 :                         PRUint32 aArgc,
     567                 :                         jsval* aArgv)
     568                 : {
     569               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     570               0 :   nsAutoString urlParam;
     571                 : 
     572               0 :   if (!PrefEnabled()) {
     573               0 :     return NS_ERROR_DOM_SECURITY_ERR;
     574                 :   }
     575                 : 
     576               0 :   if (aArgc != 1 && aArgc != 2) {
     577               0 :     return NS_ERROR_DOM_SYNTAX_ERR;
     578                 :   }
     579                 : 
     580               0 :   JSAutoRequest ar(aContext);
     581                 : 
     582               0 :   JSString* jsstr = JS_ValueToString(aContext, aArgv[0]);
     583               0 :   if (!jsstr) {
     584               0 :     return NS_ERROR_DOM_SYNTAX_ERR;
     585                 :   }
     586                 : 
     587               0 :   JS::Anchor<JSString *> deleteProtector(jsstr);
     588                 :   size_t length;
     589               0 :   const jschar *chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
     590               0 :   if (!chars) {
     591               0 :     return NS_ERROR_OUT_OF_MEMORY;
     592                 :   }
     593                 : 
     594               0 :   urlParam.Assign(chars, length);
     595               0 :   deleteProtector.clear();
     596                 : 
     597               0 :   nsCOMPtr<nsPIDOMWindow> ownerWindow = do_QueryInterface(aOwner);
     598               0 :   NS_ENSURE_STATE(ownerWindow);
     599                 : 
     600               0 :   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
     601               0 :   NS_ENSURE_STATE(sgo);
     602               0 :   nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
     603               0 :   NS_ENSURE_STATE(scriptContext);
     604                 : 
     605               0 :   nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal(do_QueryInterface(aOwner));
     606               0 :   NS_ENSURE_STATE(scriptPrincipal);
     607               0 :   nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
     608               0 :   NS_ENSURE_STATE(principal);
     609                 : 
     610               0 :   nsTArray<nsString> protocolArray;
     611                 : 
     612               0 :   if (aArgc == 2) {
     613                 :     JSObject *jsobj;
     614                 : 
     615               0 :     if (JSVAL_IS_OBJECT(aArgv[1]) &&
     616               0 :         (jsobj = JSVAL_TO_OBJECT(aArgv[1])) &&
     617               0 :         JS_IsArrayObject(aContext, jsobj)) {
     618                 :       uint32_t len;
     619               0 :       JS_GetArrayLength(aContext, jsobj, &len);
     620                 :       
     621               0 :       for (PRUint32 index = 0; index < len; ++index) {
     622                 :         jsval value;
     623                 : 
     624               0 :         if (!JS_GetElement(aContext, jsobj, index, &value))
     625               0 :           return NS_ERROR_DOM_SYNTAX_ERR;
     626                 : 
     627               0 :         jsstr = JS_ValueToString(aContext, value);
     628               0 :         if (!jsstr)
     629               0 :           return NS_ERROR_DOM_SYNTAX_ERR;
     630                 : 
     631               0 :         deleteProtector.set(jsstr);
     632               0 :         chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
     633               0 :         if (!chars)
     634               0 :           return NS_ERROR_OUT_OF_MEMORY;
     635                 : 
     636               0 :         nsDependentString protocolElement(chars, length);
     637               0 :         if (protocolElement.IsEmpty())
     638               0 :           return NS_ERROR_DOM_SYNTAX_ERR;
     639               0 :         if (protocolArray.Contains(protocolElement))
     640               0 :           return NS_ERROR_DOM_SYNTAX_ERR;
     641               0 :         if (protocolElement.FindChar(',') != -1)  /* interferes w/list */
     642               0 :           return NS_ERROR_DOM_SYNTAX_ERR;
     643               0 :         protocolArray.AppendElement(protocolElement);
     644               0 :         deleteProtector.clear();
     645                 :       }
     646                 :     } else {
     647               0 :       jsstr = JS_ValueToString(aContext, aArgv[1]);
     648               0 :       if (!jsstr)
     649               0 :         return NS_ERROR_DOM_SYNTAX_ERR;
     650                 :       
     651               0 :       deleteProtector.set(jsstr);
     652               0 :       chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
     653               0 :       if (!chars)
     654               0 :         return NS_ERROR_OUT_OF_MEMORY;
     655                 :       
     656               0 :       nsDependentString protocolElement(chars, length);
     657               0 :       if (protocolElement.IsEmpty())
     658               0 :         return NS_ERROR_DOM_SYNTAX_ERR;
     659               0 :       if (protocolElement.FindChar(',') != -1)  /* interferes w/list */
     660               0 :         return NS_ERROR_DOM_SYNTAX_ERR;
     661               0 :       protocolArray.AppendElement(protocolElement);
     662                 :     }
     663                 :   }
     664                 : 
     665               0 :   return Init(principal, scriptContext, ownerWindow, urlParam, protocolArray);
     666                 : }
     667                 : 
     668                 : //-----------------------------------------------------------------------------
     669                 : // nsWebSocket methods:
     670                 : //-----------------------------------------------------------------------------
     671                 : 
     672                 : class nsAutoCloseWS
     673                 : {
     674                 : public:
     675               0 :   nsAutoCloseWS(nsWebSocket *aWebSocket)
     676               0 :     : mWebSocket(aWebSocket)
     677               0 :   {}
     678                 : 
     679               0 :   ~nsAutoCloseWS()
     680               0 :   {
     681               0 :     if (!mWebSocket->mChannel) {
     682               0 :       mWebSocket->CloseConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR);
     683                 :     }
     684               0 :   }
     685                 : private:
     686                 :   nsRefPtr<nsWebSocket> mWebSocket;
     687                 : };
     688                 : 
     689                 : nsresult
     690               0 : nsWebSocket::EstablishConnection()
     691                 : {
     692               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     693               0 :   NS_ABORT_IF_FALSE(!mChannel, "mChannel should be null");
     694                 : 
     695                 :   nsresult rv;
     696                 : 
     697               0 :   nsCOMPtr<nsIWebSocketChannel> wsChannel;
     698               0 :   nsAutoCloseWS autoClose(this);
     699                 : 
     700               0 :   if (mSecure) {
     701                 :     wsChannel =
     702               0 :       do_CreateInstance("@mozilla.org/network/protocol;1?name=wss", &rv);
     703                 :   } else {
     704                 :     wsChannel =
     705               0 :       do_CreateInstance("@mozilla.org/network/protocol;1?name=ws", &rv);
     706                 :   }
     707               0 :   NS_ENSURE_SUCCESS(rv, rv);
     708                 : 
     709               0 :   rv = wsChannel->SetNotificationCallbacks(this);
     710               0 :   NS_ENSURE_SUCCESS(rv, rv);
     711                 : 
     712                 :   // add ourselves to the document's load group and
     713                 :   // provide the http stack the loadgroup info too
     714               0 :   nsCOMPtr<nsILoadGroup> loadGroup;
     715               0 :   rv = GetLoadGroup(getter_AddRefs(loadGroup));
     716               0 :   if (loadGroup) {
     717               0 :     rv = wsChannel->SetLoadGroup(loadGroup);
     718               0 :     NS_ENSURE_SUCCESS(rv, rv);
     719               0 :     rv = loadGroup->AddRequest(this, nsnull);
     720               0 :     NS_ENSURE_SUCCESS(rv, rv);
     721                 :   }
     722                 : 
     723               0 :   if (!mRequestedProtocolList.IsEmpty()) {
     724               0 :     rv = wsChannel->SetProtocol(mRequestedProtocolList);
     725               0 :     NS_ENSURE_SUCCESS(rv, rv);
     726                 :   }
     727                 : 
     728               0 :   nsCString asciiOrigin;
     729               0 :   rv = nsContentUtils::GetASCIIOrigin(mPrincipal, asciiOrigin);
     730               0 :   NS_ENSURE_SUCCESS(rv, rv);
     731                 : 
     732               0 :   ToLowerCase(asciiOrigin);
     733                 : 
     734               0 :   rv = wsChannel->AsyncOpen(mURI, asciiOrigin, this, nsnull);
     735               0 :   NS_ENSURE_SUCCESS(rv, rv);
     736                 : 
     737               0 :   mChannel = wsChannel;
     738                 : 
     739               0 :   return NS_OK;
     740                 : }
     741                 : 
     742                 : class nsWSCloseEvent : public nsRunnable
     743               0 : {
     744                 : public:
     745               0 : nsWSCloseEvent(nsWebSocket *aWebSocket, bool aWasClean, 
     746                 :                PRUint16 aCode, const nsString &aReason)
     747                 :     : mWebSocket(aWebSocket),
     748                 :       mWasClean(aWasClean),
     749                 :       mCode(aCode),
     750               0 :       mReason(aReason)
     751               0 :   {}
     752                 : 
     753               0 :   NS_IMETHOD Run()
     754                 :   {
     755                 :     nsresult rv = mWebSocket->CreateAndDispatchCloseEvent(mWasClean,
     756               0 :                                                           mCode, mReason);
     757               0 :     mWebSocket->UpdateMustKeepAlive();
     758               0 :     return rv;
     759                 :   }
     760                 : 
     761                 : private:
     762                 :   nsRefPtr<nsWebSocket> mWebSocket;
     763                 :   bool mWasClean;
     764                 :   PRUint16 mCode;
     765                 :   nsString mReason;
     766                 : };
     767                 : 
     768                 : nsresult
     769               0 : nsWebSocket::CreateAndDispatchSimpleEvent(const nsString& aName)
     770                 : {
     771               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     772                 :   nsresult rv;
     773                 : 
     774               0 :   rv = CheckInnerWindowCorrectness();
     775               0 :   if (NS_FAILED(rv)) {
     776               0 :     return NS_OK;
     777                 :   }
     778                 : 
     779               0 :   nsCOMPtr<nsIDOMEvent> event;
     780               0 :   rv = NS_NewDOMEvent(getter_AddRefs(event), nsnull, nsnull);
     781               0 :   NS_ENSURE_SUCCESS(rv, rv);
     782                 : 
     783                 :   // it doesn't bubble, and it isn't cancelable
     784               0 :   rv = event->InitEvent(aName, false, false);
     785               0 :   NS_ENSURE_SUCCESS(rv, rv);
     786                 : 
     787               0 :   nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
     788               0 :   rv = privateEvent->SetTrusted(true);
     789               0 :   NS_ENSURE_SUCCESS(rv, rv);
     790                 : 
     791               0 :   return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
     792                 : }
     793                 : 
     794                 : nsresult
     795               0 : nsWebSocket::CreateAndDispatchMessageEvent(const nsACString& aData,
     796                 :                                            bool isBinary)
     797                 : {
     798               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     799                 :   nsresult rv;
     800                 : 
     801               0 :   rv = CheckInnerWindowCorrectness();
     802               0 :   if (NS_FAILED(rv))
     803               0 :     return NS_OK;
     804                 : 
     805                 :   // Get the JSContext
     806               0 :   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(GetOwner());
     807               0 :   NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
     808                 : 
     809               0 :   nsIScriptContext* scriptContext = sgo->GetContext();
     810               0 :   NS_ENSURE_TRUE(scriptContext, NS_ERROR_FAILURE);
     811                 : 
     812               0 :   JSContext* cx = scriptContext->GetNativeContext();
     813               0 :   NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
     814                 : 
     815                 :   // Create appropriate JS object for message
     816                 :   jsval jsData;
     817                 :   {
     818               0 :     JSAutoRequest ar(cx);
     819               0 :     if (isBinary) {
     820               0 :       if (mBinaryType == WS_BINARY_TYPE_BLOB) {
     821               0 :         rv = CreateResponseBlob(aData, cx, jsData);
     822               0 :         NS_ENSURE_SUCCESS(rv, rv);
     823               0 :       } else if (mBinaryType == WS_BINARY_TYPE_ARRAYBUFFER) {
     824                 :         JSObject *arrayBuf;
     825               0 :         rv = nsContentUtils::CreateArrayBuffer(cx, aData, &arrayBuf);
     826               0 :         NS_ENSURE_SUCCESS(rv, rv);
     827               0 :         jsData = OBJECT_TO_JSVAL(arrayBuf);
     828                 :       } else {
     829               0 :         NS_RUNTIMEABORT("Unknown binary type!");
     830               0 :         return NS_ERROR_UNEXPECTED;
     831                 :       }
     832                 :     } else {
     833                 :       // JS string
     834               0 :       NS_ConvertUTF8toUTF16 utf16Data(aData);
     835                 :       JSString* jsString;
     836               0 :       jsString = JS_NewUCStringCopyN(cx, utf16Data.get(), utf16Data.Length());
     837               0 :       NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE);
     838                 : 
     839               0 :       jsData = STRING_TO_JSVAL(jsString);
     840                 :     }
     841                 :   }
     842                 : 
     843                 :   // create an event that uses the MessageEvent interface,
     844                 :   // which does not bubble, is not cancelable, and has no default action
     845                 : 
     846               0 :   nsCOMPtr<nsIDOMEvent> event;
     847               0 :   rv = NS_NewDOMMessageEvent(getter_AddRefs(event), nsnull, nsnull);
     848               0 :   NS_ENSURE_SUCCESS(rv, rv);
     849                 : 
     850               0 :   nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
     851               0 :   rv = messageEvent->InitMessageEvent(NS_LITERAL_STRING("message"),
     852                 :                                       false, false,
     853                 :                                       jsData,
     854                 :                                       mUTF16Origin,
     855               0 :                                       EmptyString(), nsnull);
     856               0 :   NS_ENSURE_SUCCESS(rv, rv);
     857                 : 
     858               0 :   nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
     859               0 :   rv = privateEvent->SetTrusted(true);
     860               0 :   NS_ENSURE_SUCCESS(rv, rv);
     861                 : 
     862               0 :   return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
     863                 : }
     864                 : 
     865                 : // Initial implementation: only stores to RAM, not file
     866                 : // TODO: bug 704447: large file support
     867                 : nsresult
     868               0 : nsWebSocket::CreateResponseBlob(const nsACString& aData, JSContext *aCx,
     869                 :                                 jsval &jsData)
     870                 : {
     871               0 :   PRUint32 blobLen = aData.Length();
     872               0 :   void *blobData = PR_Malloc(blobLen);
     873               0 :   nsCOMPtr<nsIDOMBlob> blob;
     874               0 :   if (blobData) {
     875               0 :     memcpy(blobData, aData.BeginReading(), blobLen);
     876               0 :     blob = new nsDOMMemoryFile(blobData, blobLen, EmptyString());
     877                 :   } else {
     878               0 :     return NS_ERROR_OUT_OF_MEMORY;
     879                 :   }
     880               0 :   JSObject* scope = JS_GetGlobalForScopeChain(aCx);
     881               0 :   return nsContentUtils::WrapNative(aCx, scope, blob, &jsData, nsnull, true);
     882                 : }
     883                 : 
     884                 : nsresult
     885               0 : nsWebSocket::CreateAndDispatchCloseEvent(bool aWasClean,
     886                 :                                          PRUint16 aCode,
     887                 :                                          const nsString &aReason)
     888                 : {
     889               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     890                 :   nsresult rv;
     891                 : 
     892               0 :   mTriggeredCloseEvent = true;
     893                 : 
     894               0 :   rv = CheckInnerWindowCorrectness();
     895               0 :   if (NS_FAILED(rv)) {
     896               0 :     return NS_OK;
     897                 :   }
     898                 : 
     899                 :   // create an event that uses the CloseEvent interface,
     900                 :   // which does not bubble, is not cancelable, and has no default action
     901                 : 
     902               0 :   nsCOMPtr<nsIDOMEvent> event;
     903               0 :   rv = NS_NewDOMCloseEvent(getter_AddRefs(event), nsnull, nsnull);
     904               0 :   NS_ENSURE_SUCCESS(rv, rv);
     905                 : 
     906               0 :   nsCOMPtr<nsIDOMCloseEvent> closeEvent = do_QueryInterface(event);
     907               0 :   rv = closeEvent->InitCloseEvent(NS_LITERAL_STRING("close"),
     908                 :                                   false, false,
     909               0 :                                   aWasClean, aCode, aReason);
     910               0 :   NS_ENSURE_SUCCESS(rv, rv);
     911                 : 
     912               0 :   nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
     913               0 :   rv = privateEvent->SetTrusted(true);
     914               0 :   NS_ENSURE_SUCCESS(rv, rv);
     915                 : 
     916               0 :   return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
     917                 : }
     918                 : 
     919                 : bool
     920               0 : nsWebSocket::PrefEnabled()
     921                 : {
     922               0 :   return Preferences::GetBool("network.websocket.enabled", true);
     923                 : }
     924                 : 
     925                 : void
     926               0 : nsWebSocket::SetReadyState(PRUint16 aNewReadyState)
     927                 : {
     928               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
     929                 :   nsresult rv;
     930                 : 
     931               0 :   if (mReadyState == aNewReadyState) {
     932               0 :     return;
     933                 :   }
     934                 : 
     935               0 :   NS_ABORT_IF_FALSE((aNewReadyState == nsIWebSocket::OPEN)    ||
     936                 :                     (aNewReadyState == nsIWebSocket::CLOSING) ||
     937                 :                     (aNewReadyState == nsIWebSocket::CLOSED),
     938                 :                     "unexpected readyState");
     939                 : 
     940               0 :   if (aNewReadyState == nsIWebSocket::OPEN) {
     941               0 :     NS_ABORT_IF_FALSE(mReadyState == nsIWebSocket::CONNECTING,
     942                 :                       "unexpected readyState transition");
     943               0 :     mReadyState = aNewReadyState;
     944                 : 
     945               0 :     rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
     946               0 :     if (NS_FAILED(rv)) {
     947               0 :       NS_WARNING("Failed to dispatch the open event");
     948                 :     }
     949               0 :     UpdateMustKeepAlive();
     950               0 :     return;
     951                 :   }
     952                 : 
     953               0 :   if (aNewReadyState == nsIWebSocket::CLOSING) {
     954               0 :     NS_ABORT_IF_FALSE((mReadyState == nsIWebSocket::CONNECTING) ||
     955                 :                       (mReadyState == nsIWebSocket::OPEN),
     956                 :                       "unexpected readyState transition");
     957               0 :     mReadyState = aNewReadyState;
     958               0 :     return;
     959                 :   }
     960                 : 
     961               0 :   if (aNewReadyState == nsIWebSocket::CLOSED) {
     962               0 :     mReadyState = aNewReadyState;
     963                 : 
     964                 :     // The close event must be dispatched asynchronously.
     965                 :     rv = NS_DispatchToMainThread(new nsWSCloseEvent(this, mCloseEventWasClean,
     966                 :                                                     mCloseEventCode,
     967               0 :                                                     mCloseEventReason),
     968               0 :                                  NS_DISPATCH_NORMAL);
     969               0 :     if (NS_FAILED(rv)) {
     970               0 :       NS_WARNING("Failed to dispatch the close event");
     971               0 :       mTriggeredCloseEvent = true;
     972               0 :       UpdateMustKeepAlive();
     973                 :     }
     974                 :   }
     975                 : }
     976                 : 
     977                 : nsresult
     978               0 : nsWebSocket::ParseURL(const nsString& aURL)
     979                 : {
     980                 :   nsresult rv;
     981                 : 
     982               0 :   NS_ENSURE_TRUE(!aURL.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
     983                 : 
     984               0 :   nsCOMPtr<nsIURI> uri;
     985               0 :   rv = NS_NewURI(getter_AddRefs(uri), aURL);
     986               0 :   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
     987                 : 
     988               0 :   nsCOMPtr<nsIURL> parsedURL(do_QueryInterface(uri, &rv));
     989               0 :   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
     990                 : 
     991               0 :   nsCAutoString fragment;
     992               0 :   rv = parsedURL->GetRef(fragment);
     993               0 :   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && fragment.IsEmpty(),
     994                 :                  NS_ERROR_DOM_SYNTAX_ERR);
     995                 : 
     996               0 :   nsCAutoString scheme;
     997               0 :   rv = parsedURL->GetScheme(scheme);
     998               0 :   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !scheme.IsEmpty(),
     999                 :                  NS_ERROR_DOM_SYNTAX_ERR);
    1000                 : 
    1001               0 :   nsCAutoString host;
    1002               0 :   rv = parsedURL->GetAsciiHost(host);
    1003               0 :   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !host.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
    1004                 : 
    1005                 :   PRInt32 port;
    1006               0 :   rv = parsedURL->GetPort(&port);
    1007               0 :   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
    1008                 : 
    1009               0 :   rv = NS_CheckPortSafety(port, scheme.get());
    1010               0 :   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
    1011                 : 
    1012               0 :   nsCAutoString filePath;
    1013               0 :   rv = parsedURL->GetFilePath(filePath);
    1014               0 :   if (filePath.IsEmpty()) {
    1015               0 :     filePath.AssignLiteral("/");
    1016                 :   }
    1017               0 :   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
    1018                 : 
    1019               0 :   nsCAutoString query;
    1020               0 :   rv = parsedURL->GetQuery(query);
    1021               0 :   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
    1022                 : 
    1023               0 :   if (scheme.LowerCaseEqualsLiteral("ws")) {
    1024               0 :      mSecure = false;
    1025               0 :      mPort = (port == -1) ? DEFAULT_WS_SCHEME_PORT : port;
    1026               0 :   } else if (scheme.LowerCaseEqualsLiteral("wss")) {
    1027               0 :     mSecure = true;
    1028               0 :     mPort = (port == -1) ? DEFAULT_WSS_SCHEME_PORT : port;
    1029                 :   } else {
    1030               0 :     return NS_ERROR_DOM_SYNTAX_ERR;
    1031                 :   }
    1032                 : 
    1033               0 :   rv = nsContentUtils::GetUTFOrigin(parsedURL, mUTF16Origin);
    1034               0 :   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
    1035                 : 
    1036               0 :   mAsciiHost = host;
    1037               0 :   ToLowerCase(mAsciiHost);
    1038                 : 
    1039               0 :   mResource = filePath;
    1040               0 :   if (!query.IsEmpty()) {
    1041               0 :     mResource.AppendLiteral("?");
    1042               0 :     mResource.Append(query);
    1043                 :   }
    1044               0 :   PRUint32 length = mResource.Length();
    1045                 :   PRUint32 i;
    1046               0 :   for (i = 0; i < length; ++i) {
    1047               0 :     if (mResource[i] < static_cast<PRUnichar>(0x0021) ||
    1048               0 :         mResource[i] > static_cast<PRUnichar>(0x007E)) {
    1049               0 :       return NS_ERROR_DOM_SYNTAX_ERR;
    1050                 :     }
    1051                 :   }
    1052                 : 
    1053               0 :   mOriginalURL = aURL;
    1054               0 :   mURI = parsedURL;
    1055               0 :   return NS_OK;
    1056                 : }
    1057                 : 
    1058                 : //-----------------------------------------------------------------------------
    1059                 : // Methods that keep alive the WebSocket object when:
    1060                 : //   1. the object has registered event listeners that can be triggered
    1061                 : //      ("strong event listeners");
    1062                 : //   2. there are outgoing not sent messages.
    1063                 : //-----------------------------------------------------------------------------
    1064                 : 
    1065                 : void
    1066               0 : nsWebSocket::UpdateMustKeepAlive()
    1067                 : {
    1068               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
    1069               0 :   if (!mCheckMustKeepAlive) {
    1070               0 :     return;
    1071                 :   }
    1072                 : 
    1073               0 :   bool shouldKeepAlive = false;
    1074                 : 
    1075               0 :   if (mListenerManager) {
    1076               0 :     switch (mReadyState)
    1077                 :     {
    1078                 :       case nsIWebSocket::CONNECTING:
    1079                 :       {
    1080               0 :         if (mListenerManager->HasListenersFor(NS_LITERAL_STRING("open")) ||
    1081               0 :             mListenerManager->HasListenersFor(NS_LITERAL_STRING("message")) ||
    1082               0 :             mListenerManager->HasListenersFor(NS_LITERAL_STRING("error")) ||
    1083               0 :             mListenerManager->HasListenersFor(NS_LITERAL_STRING("close"))) {
    1084               0 :           shouldKeepAlive = true;
    1085                 :         }
    1086                 :       }
    1087               0 :       break;
    1088                 : 
    1089                 :       case nsIWebSocket::OPEN:
    1090                 :       case nsIWebSocket::CLOSING:
    1091                 :       {
    1092               0 :         if (mListenerManager->HasListenersFor(NS_LITERAL_STRING("message")) ||
    1093               0 :             mListenerManager->HasListenersFor(NS_LITERAL_STRING("error")) ||
    1094               0 :             mListenerManager->HasListenersFor(NS_LITERAL_STRING("close")) ||
    1095                 :             mOutgoingBufferedAmount != 0) {
    1096               0 :           shouldKeepAlive = true;
    1097                 :         }
    1098                 :       }
    1099               0 :       break;
    1100                 : 
    1101                 :       case nsIWebSocket::CLOSED:
    1102                 :       {
    1103                 :         shouldKeepAlive =
    1104               0 :           (!mTriggeredCloseEvent &&
    1105               0 :            mListenerManager->HasListenersFor(NS_LITERAL_STRING("close")));
    1106                 :       }
    1107                 :     }
    1108                 :   }
    1109                 : 
    1110               0 :   if (mKeepingAlive && !shouldKeepAlive) {
    1111               0 :     mKeepingAlive = false;
    1112               0 :     static_cast<nsIDOMEventTarget*>(this)->Release();
    1113               0 :   } else if (!mKeepingAlive && shouldKeepAlive) {
    1114               0 :     mKeepingAlive = true;
    1115               0 :     static_cast<nsIDOMEventTarget*>(this)->AddRef();
    1116                 :   }
    1117                 : }
    1118                 : 
    1119                 : void
    1120               0 : nsWebSocket::DontKeepAliveAnyMore()
    1121                 : {
    1122               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
    1123               0 :   if (mKeepingAlive) {
    1124               0 :     mKeepingAlive = false;
    1125               0 :     static_cast<nsIDOMEventTarget*>(this)->Release();
    1126                 :   }
    1127               0 :   mCheckMustKeepAlive = false;
    1128               0 : }
    1129                 : 
    1130                 : void
    1131               0 : nsWebSocket::FailConnectionQuietly()
    1132                 : {
    1133                 :   // Fail without console error or JS onerror message: onmessage/onclose will
    1134                 :   // also be blocked so long as CheckInnerWindowCorrectness is failing.
    1135               0 :   CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
    1136               0 : }
    1137                 : 
    1138                 : nsresult
    1139               0 : nsWebSocket::UpdateURI()
    1140                 : {
    1141                 :   // Check for Redirections
    1142               0 :   nsCOMPtr<nsIURI> uri;
    1143               0 :   nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
    1144               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1145                 : 
    1146               0 :   nsCAutoString spec;
    1147               0 :   rv = uri->GetSpec(spec);
    1148               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1149               0 :   CopyUTF8toUTF16(spec, mEffectiveURL);
    1150                 : 
    1151               0 :   bool isWSS = false;
    1152               0 :   rv = uri->SchemeIs("wss", &isWSS);
    1153               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1154               0 :   mSecure = isWSS ? true : false;
    1155                 : 
    1156               0 :   return NS_OK;
    1157                 : }
    1158                 : 
    1159                 : NS_IMETHODIMP
    1160               0 : nsWebSocket::RemoveEventListener(const nsAString& aType,
    1161                 :                                  nsIDOMEventListener* aListener,
    1162                 :                                  bool aUseCapture)
    1163                 : {
    1164               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
    1165                 :   nsresult rv = nsDOMEventTargetHelper::RemoveEventListener(aType,
    1166                 :                                                             aListener,
    1167               0 :                                                             aUseCapture);
    1168               0 :   if (NS_SUCCEEDED(rv)) {
    1169               0 :     UpdateMustKeepAlive();
    1170                 :   }
    1171               0 :   return rv;
    1172                 : }
    1173                 : 
    1174                 : NS_IMETHODIMP
    1175               0 : nsWebSocket::AddEventListener(const nsAString& aType,
    1176                 :                               nsIDOMEventListener *aListener,
    1177                 :                               bool aUseCapture,
    1178                 :                               bool aWantsUntrusted,
    1179                 :                               PRUint8 optional_argc)
    1180                 : {
    1181               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
    1182                 :   nsresult rv = nsDOMEventTargetHelper::AddEventListener(aType,
    1183                 :                                                          aListener,
    1184                 :                                                          aUseCapture,
    1185                 :                                                          aWantsUntrusted,
    1186               0 :                                                          optional_argc);
    1187               0 :   if (NS_SUCCEEDED(rv)) {
    1188               0 :     UpdateMustKeepAlive();
    1189                 :   }
    1190               0 :   return rv;
    1191                 : }
    1192                 : 
    1193                 : //-----------------------------------------------------------------------------
    1194                 : // nsWebSocket::nsIWebSocket methods:
    1195                 : //-----------------------------------------------------------------------------
    1196                 : 
    1197                 : NS_IMETHODIMP
    1198               0 : nsWebSocket::GetUrl(nsAString& aURL)
    1199                 : {
    1200               0 :   if (mEffectiveURL.IsEmpty()) {
    1201               0 :     aURL = mOriginalURL;
    1202                 :   } else {
    1203               0 :     aURL = mEffectiveURL;
    1204                 :   }
    1205               0 :   return NS_OK;
    1206                 : }
    1207                 : 
    1208                 : NS_IMETHODIMP
    1209               0 : nsWebSocket::GetExtensions(nsAString& aExtensions)
    1210                 : {
    1211               0 :   CopyUTF8toUTF16(mEstablishedExtensions, aExtensions);
    1212               0 :   return NS_OK;
    1213                 : }
    1214                 : 
    1215                 : NS_IMETHODIMP
    1216               0 : nsWebSocket::GetProtocol(nsAString& aProtocol)
    1217                 : {
    1218               0 :   CopyUTF8toUTF16(mEstablishedProtocol, aProtocol);
    1219               0 :   return NS_OK;
    1220                 : }
    1221                 : 
    1222                 : NS_IMETHODIMP
    1223               0 : nsWebSocket::GetReadyState(PRUint16 *aReadyState)
    1224                 : {
    1225               0 :   *aReadyState = mReadyState;
    1226               0 :   return NS_OK;
    1227                 : }
    1228                 : 
    1229                 : NS_IMETHODIMP
    1230               0 : nsWebSocket::GetBufferedAmount(PRUint32 *aBufferedAmount)
    1231                 : {
    1232               0 :   *aBufferedAmount = mOutgoingBufferedAmount;
    1233               0 :   return NS_OK;
    1234                 : }
    1235                 : 
    1236                 : NS_IMETHODIMP
    1237               0 : nsWebSocket::GetBinaryType(nsAString& aBinaryType)
    1238                 : {
    1239               0 :   switch (mBinaryType) {
    1240                 :   case WS_BINARY_TYPE_ARRAYBUFFER:
    1241               0 :     aBinaryType.AssignLiteral("arraybuffer");
    1242               0 :     break;
    1243                 :   case WS_BINARY_TYPE_BLOB:
    1244               0 :     aBinaryType.AssignLiteral("blob");
    1245               0 :     break;
    1246                 :   default:
    1247               0 :     NS_ERROR("Should not happen");
    1248                 :   }
    1249               0 :   return NS_OK;
    1250                 : }
    1251                 : 
    1252                 : NS_IMETHODIMP
    1253               0 : nsWebSocket::SetBinaryType(const nsAString& aBinaryType)
    1254                 : {
    1255               0 :   if (aBinaryType.EqualsLiteral("arraybuffer")) {
    1256               0 :     mBinaryType = WS_BINARY_TYPE_ARRAYBUFFER;
    1257               0 :   } else if (aBinaryType.EqualsLiteral("blob")) {
    1258               0 :     mBinaryType = WS_BINARY_TYPE_BLOB;
    1259                 :   } else  {
    1260               0 :     return NS_ERROR_INVALID_ARG;
    1261                 :   }
    1262                 : 
    1263               0 :   return NS_OK;
    1264                 : }
    1265                 : 
    1266                 : #define NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(_eventlistenername, _eventlistener) \
    1267                 :   NS_IMETHODIMP                                                                \
    1268                 :   nsWebSocket::GetOn##_eventlistenername(nsIDOMEventListener * *aEventListener)\
    1269                 :   {                                                                            \
    1270                 :     return GetInnerEventListener(_eventlistener, aEventListener);              \
    1271                 :   }                                                                            \
    1272                 :                                                                                \
    1273                 :   NS_IMETHODIMP                                                                \
    1274                 :   nsWebSocket::SetOn##_eventlistenername(nsIDOMEventListener * aEventListener) \
    1275                 :   {                                                                            \
    1276                 :     return RemoveAddEventListener(NS_LITERAL_STRING(#_eventlistenername),      \
    1277                 :                                   _eventlistener, aEventListener);             \
    1278                 :   }
    1279                 : 
    1280               0 : NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(open, mOnOpenListener)
    1281               0 : NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(error, mOnErrorListener)
    1282               0 : NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(message, mOnMessageListener)
    1283               0 : NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(close, mOnCloseListener)
    1284                 : 
    1285                 : static bool
    1286               0 : ContainsUnpairedSurrogates(const nsAString& aData)
    1287                 : {
    1288                 :   // Check for unpaired surrogates.
    1289               0 :   PRUint32 i, length = aData.Length();
    1290               0 :   for (i = 0; i < length; ++i) {
    1291               0 :     if (NS_IS_LOW_SURROGATE(aData[i])) {
    1292               0 :       return true;
    1293                 :     }
    1294               0 :     if (NS_IS_HIGH_SURROGATE(aData[i])) {
    1295               0 :       ++i;
    1296               0 :       if (i == length || !NS_IS_LOW_SURROGATE(aData[i])) {
    1297               0 :         return true;
    1298                 :       }
    1299               0 :       continue;
    1300                 :     }
    1301                 :   }
    1302               0 :   return false;
    1303                 : }
    1304                 : 
    1305                 : NS_IMETHODIMP
    1306               0 : nsWebSocket::Send(nsIVariant *aData)
    1307                 : {
    1308               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
    1309                 : 
    1310               0 :   if (mReadyState == nsIWebSocket::CONNECTING) {
    1311               0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    1312                 :   }
    1313                 : 
    1314               0 :   nsCString msgString;
    1315               0 :   nsCOMPtr<nsIInputStream> msgStream;
    1316                 :   bool isBinary;
    1317                 :   PRUint32 msgLen;
    1318               0 :   nsresult rv = GetSendParams(aData, msgString, msgStream, isBinary, msgLen);
    1319               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1320                 : 
    1321                 :   // Always increment outgoing buffer len, even if closed
    1322               0 :   mOutgoingBufferedAmount += msgLen;
    1323                 : 
    1324               0 :   if (mReadyState == nsIWebSocket::CLOSING ||
    1325                 :       mReadyState == nsIWebSocket::CLOSED) {
    1326               0 :     return NS_OK;
    1327                 :   }
    1328                 : 
    1329               0 :   NS_ASSERTION(mReadyState == nsIWebSocket::OPEN,
    1330                 :                "Unknown state in nsWebSocket::Send");
    1331                 : 
    1332               0 :   if (msgStream) {
    1333               0 :     rv = mChannel->SendBinaryStream(msgStream, msgLen);
    1334                 :   } else {
    1335               0 :     if (isBinary) {
    1336               0 :       rv = mChannel->SendBinaryMsg(msgString);
    1337                 :     } else {
    1338               0 :       rv = mChannel->SendMsg(msgString);
    1339                 :     }
    1340                 :   }
    1341               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1342                 : 
    1343               0 :   UpdateMustKeepAlive();
    1344                 : 
    1345               0 :   return NS_OK;
    1346                 : }
    1347                 : 
    1348                 : nsresult
    1349               0 : nsWebSocket::GetSendParams(nsIVariant *aData, nsCString &aStringOut,
    1350                 :                            nsCOMPtr<nsIInputStream> &aStreamOut,
    1351                 :                            bool &aIsBinary, PRUint32 &aOutgoingLength)
    1352                 : {
    1353                 :   // Get type of data (arraybuffer, blob, or string)
    1354                 :   PRUint16 dataType;
    1355               0 :   nsresult rv = aData->GetDataType(&dataType);
    1356               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1357                 : 
    1358               0 :   if (dataType == nsIDataType::VTYPE_INTERFACE ||
    1359                 :       dataType == nsIDataType::VTYPE_INTERFACE_IS) {
    1360               0 :     nsCOMPtr<nsISupports> supports;
    1361                 :     nsID *iid;
    1362               0 :     rv = aData->GetAsInterface(&iid, getter_AddRefs(supports));
    1363               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1364                 : 
    1365               0 :     nsMemory::Free(iid);
    1366                 : 
    1367                 :     // ArrayBuffer?
    1368                 :     jsval realVal;
    1369                 :     JSObject* obj;
    1370               0 :     nsresult rv = aData->GetAsJSVal(&realVal);
    1371               0 :     if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(realVal) &&
    1372                 :         (obj = JSVAL_TO_OBJECT(realVal)) &&
    1373               0 :         (js_IsArrayBuffer(obj))) {
    1374               0 :       PRInt32 len = JS_GetArrayBufferByteLength(obj);
    1375               0 :       char* data = (char*)JS_GetArrayBufferData(obj);
    1376                 : 
    1377               0 :       aStringOut.Assign(data, len);
    1378               0 :       aIsBinary = true;
    1379               0 :       aOutgoingLength = len;
    1380               0 :       return NS_OK;
    1381                 :     }
    1382                 : 
    1383                 :     // Blob?
    1384               0 :     nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
    1385               0 :     if (blob) {
    1386               0 :       rv = blob->GetInternalStream(getter_AddRefs(aStreamOut));
    1387               0 :       NS_ENSURE_SUCCESS(rv, rv);
    1388                 : 
    1389                 :       // GetSize() should not perform blocking I/O (unlike Available())
    1390                 :       PRUint64 blobLen;
    1391               0 :       rv = blob->GetSize(&blobLen);
    1392               0 :       NS_ENSURE_SUCCESS(rv, rv);
    1393               0 :       if (blobLen > PR_UINT32_MAX) {
    1394               0 :         return NS_ERROR_FILE_TOO_BIG;
    1395                 :       }
    1396               0 :       aOutgoingLength = static_cast<PRUint32>(blobLen);
    1397                 : 
    1398               0 :       aIsBinary = true;
    1399               0 :       return NS_OK;
    1400                 :     }
    1401                 :   }
    1402                 : 
    1403                 :   // Text message: if not already a string, turn it into one.
    1404                 :   // TODO: bug 704444: Correctly coerce any JS type to string
    1405                 :   //
    1406               0 :   PRUnichar* data = nsnull;
    1407               0 :   PRUint32 len = 0;
    1408               0 :   rv = aData->GetAsWStringWithSize(&len, &data);
    1409               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1410                 : 
    1411               0 :   nsString text;
    1412               0 :   text.Adopt(data, len);
    1413                 : 
    1414               0 :   if (ContainsUnpairedSurrogates(text)) {
    1415               0 :     return NS_ERROR_DOM_SYNTAX_ERR;
    1416                 :   }
    1417                 : 
    1418               0 :   rv = ConvertTextToUTF8(text, aStringOut);
    1419               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1420                 : 
    1421               0 :   aIsBinary = false;
    1422               0 :   aOutgoingLength = aStringOut.Length();
    1423               0 :   return NS_OK;
    1424                 : }
    1425                 : 
    1426                 : nsresult
    1427               0 : nsWebSocket::ConvertTextToUTF8(const nsString& aMessage, nsCString& buf)
    1428                 : {
    1429               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
    1430                 :   nsresult rv;
    1431                 : 
    1432                 :   nsCOMPtr<nsICharsetConverterManager> ccm =
    1433               0 :     do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
    1434               0 :   SUCCESS_OR_FAIL_WEBSOCKET(rv, rv);
    1435                 : 
    1436               0 :   nsCOMPtr<nsIUnicodeEncoder> converter;
    1437               0 :   rv = ccm->GetUnicodeEncoder("UTF-8", getter_AddRefs(converter));
    1438               0 :   SUCCESS_OR_FAIL_WEBSOCKET(rv, rv);
    1439                 : 
    1440               0 :   rv = converter->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace,
    1441               0 :                                          nsnull, UTF_8_REPLACEMENT_CHAR);
    1442               0 :   SUCCESS_OR_FAIL_WEBSOCKET(rv, rv);
    1443                 : 
    1444               0 :   PRInt32 inLen = aMessage.Length();
    1445                 :   PRInt32 maxLen;
    1446               0 :   rv = converter->GetMaxLength(aMessage.BeginReading(), inLen, &maxLen);
    1447               0 :   SUCCESS_OR_FAIL_WEBSOCKET(rv, rv);
    1448                 : 
    1449               0 :   buf.SetLength(maxLen);
    1450               0 :   TRUE_OR_FAIL_WEBSOCKET(buf.Length() == static_cast<PRUint32>(maxLen),
    1451                 :                          NS_ERROR_OUT_OF_MEMORY);
    1452                 : 
    1453               0 :   char* start = buf.BeginWriting();
    1454                 : 
    1455               0 :   PRInt32 outLen = maxLen;
    1456               0 :   rv = converter->Convert(aMessage.BeginReading(), &inLen, start, &outLen);
    1457               0 :   if (NS_SUCCEEDED(rv)) {
    1458               0 :     PRInt32 outLen2 = maxLen - outLen;
    1459               0 :     rv = converter->Finish(start + outLen, &outLen2);
    1460               0 :     outLen += outLen2;
    1461                 :   }
    1462               0 :   if (NS_FAILED(rv) || rv == NS_ERROR_UENC_NOMAPPING) {
    1463                 :     // Yes, NS_ERROR_UENC_NOMAPPING is a success code
    1464               0 :     return NS_ERROR_DOM_SYNTAX_ERR;
    1465                 :   }
    1466                 : 
    1467               0 :   buf.SetLength(outLen);
    1468               0 :   TRUE_OR_FAIL_WEBSOCKET(buf.Length() == static_cast<PRUint32>(outLen),
    1469                 :                          NS_ERROR_UNEXPECTED);
    1470                 : 
    1471               0 :   return NS_OK;
    1472                 : }
    1473                 : 
    1474                 : NS_IMETHODIMP
    1475               0 : nsWebSocket::Close(PRUint16 code, const nsAString & reason, PRUint8 argc)
    1476                 : {
    1477               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
    1478                 : 
    1479                 :   // the reason code is optional, but if provided it must be in a specific range
    1480               0 :   PRUint16 closeCode = 0;
    1481               0 :   if (argc >= 1) {
    1482               0 :     if (code != 1000 && (code < 3000 || code > 4999)) {
    1483               0 :       return NS_ERROR_DOM_INVALID_ACCESS_ERR;
    1484                 :     }
    1485               0 :     closeCode = code;
    1486                 :   }
    1487                 : 
    1488               0 :   nsCAutoString closeReason;
    1489               0 :   if (argc >= 2) {
    1490               0 :     if (ContainsUnpairedSurrogates(reason)) {
    1491               0 :       return NS_ERROR_DOM_SYNTAX_ERR;
    1492                 :     }
    1493               0 :     CopyUTF16toUTF8(reason, closeReason);
    1494                 : 
    1495                 :     // The API requires the UTF-8 string to be 123 or less bytes
    1496               0 :     if (closeReason.Length() > 123) {
    1497               0 :       return NS_ERROR_DOM_SYNTAX_ERR;
    1498                 :     }
    1499                 :   }
    1500                 : 
    1501               0 :   if (mReadyState == nsIWebSocket::CLOSING ||
    1502                 :       mReadyState == nsIWebSocket::CLOSED) {
    1503               0 :     return NS_OK;
    1504                 :   }
    1505                 : 
    1506               0 :   if (mReadyState == nsIWebSocket::CONNECTING) {
    1507                 :     // FailConnection() can release the object, so we keep a reference
    1508                 :     // before calling it
    1509               0 :     nsRefPtr<nsWebSocket> kungfuDeathGrip = this;
    1510                 : 
    1511               0 :     FailConnection(closeCode, closeReason);
    1512               0 :     return NS_OK;
    1513                 :   }
    1514                 : 
    1515                 :   // mReadyState == nsIWebSocket::OPEN
    1516               0 :   CloseConnection(closeCode, closeReason);
    1517                 : 
    1518               0 :   return NS_OK;
    1519                 : }
    1520                 : 
    1521                 : /**
    1522                 :  * This Init method should only be called by C++ consumers.
    1523                 :  */
    1524                 : NS_IMETHODIMP
    1525               0 : nsWebSocket::Init(nsIPrincipal* aPrincipal,
    1526                 :                   nsIScriptContext* aScriptContext,
    1527                 :                   nsPIDOMWindow* aOwnerWindow,
    1528                 :                   const nsAString& aURL,
    1529                 :                   nsTArray<nsString> & protocolArray)
    1530                 : {
    1531               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
    1532                 :   nsresult rv;
    1533                 : 
    1534               0 :   NS_ENSURE_ARG(aPrincipal);
    1535                 : 
    1536               0 :   if (!PrefEnabled()) {
    1537               0 :     return NS_ERROR_DOM_SECURITY_ERR;
    1538                 :   }
    1539                 : 
    1540               0 :   mPrincipal = aPrincipal;
    1541               0 :   if (aOwnerWindow) {
    1542               0 :     BindToOwner(aOwnerWindow->IsOuterWindow() ?
    1543               0 :                 aOwnerWindow->GetCurrentInnerWindow() : aOwnerWindow);
    1544                 :   } else {
    1545               0 :     BindToOwner(aOwnerWindow);
    1546                 :   }
    1547                 : 
    1548                 :   // Attempt to kill "ghost" websocket: but usually too early for check to fail
    1549               0 :   rv = CheckInnerWindowCorrectness();
    1550               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1551                 : 
    1552                 :   // Shut down websocket if window is frozen or destroyed (only needed for
    1553                 :   // "ghost" websockets--see bug 696085)
    1554               0 :   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
    1555               0 :   NS_ENSURE_STATE(os);
    1556               0 :   rv = os->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true);
    1557               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1558               0 :   rv = os->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true);
    1559               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1560                 : 
    1561                 :   nsCOMPtr<nsIJSContextStack> stack =
    1562               0 :     do_GetService("@mozilla.org/js/xpc/ContextStack;1");
    1563               0 :   JSContext* cx = nsnull;
    1564               0 :   if (stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx) {
    1565                 :     unsigned lineno;
    1566                 :     JSScript *script;
    1567                 : 
    1568               0 :     if (JS_DescribeScriptedCaller(cx, &script, &lineno)) {
    1569               0 :         mScriptFile = JS_GetScriptFilename(cx, script);
    1570               0 :         mScriptLine = lineno;
    1571                 :     }
    1572                 : 
    1573               0 :     mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx);
    1574                 :   }
    1575                 : 
    1576                 :   // parses the url
    1577               0 :   rv = ParseURL(PromiseFlatString(aURL));
    1578               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1579                 : 
    1580               0 :   nsIScriptContext* sc = GetContextForEventHandlers(&rv);
    1581                 :   nsCOMPtr<nsIDocument> originDoc =
    1582               0 :     nsContentUtils::GetDocumentFromScriptContext(sc);
    1583                 : 
    1584                 :   // Don't allow https:// to open ws://
    1585               0 :   if (!mSecure &&
    1586                 :       !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS",
    1587               0 :                             false)) {
    1588                 :     // Confirmed we are opening plain ws:// and want to prevent this from a
    1589                 :     // secure context (e.g. https). Check the security context of the document
    1590                 :     // associated with this script, which is the same as associated with mOwner.
    1591               0 :     if (originDoc && originDoc->GetSecurityInfo())
    1592               0 :       return NS_ERROR_DOM_SECURITY_ERR;
    1593                 :   }
    1594                 : 
    1595                 :   // Assign the sub protocol list and scan it for illegal values
    1596               0 :   for (PRUint32 index = 0; index < protocolArray.Length(); ++index) {
    1597               0 :     for (PRUint32 i = 0; i < protocolArray[index].Length(); ++i) {
    1598               0 :       if (protocolArray[index][i] < static_cast<PRUnichar>(0x0021) ||
    1599               0 :           protocolArray[index][i] > static_cast<PRUnichar>(0x007E))
    1600               0 :         return NS_ERROR_DOM_SYNTAX_ERR;
    1601                 :     }
    1602                 : 
    1603               0 :     if (!mRequestedProtocolList.IsEmpty())
    1604               0 :       mRequestedProtocolList.Append(NS_LITERAL_CSTRING(", "));
    1605               0 :     AppendUTF16toUTF8(protocolArray[index], mRequestedProtocolList);
    1606                 :   }
    1607                 : 
    1608                 :   // Check content policy.
    1609               0 :   PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
    1610                 :   rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET,
    1611                 :                                  mURI,
    1612                 :                                  mPrincipal,
    1613                 :                                  originDoc,
    1614               0 :                                  EmptyCString(),
    1615                 :                                  nsnull,
    1616                 :                                  &shouldLoad,
    1617                 :                                  nsContentUtils::GetContentPolicy(),
    1618               0 :                                  nsContentUtils::GetSecurityManager());
    1619               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1620               0 :   if (NS_CP_REJECTED(shouldLoad)) {
    1621                 :     // Disallowed by content policy.
    1622               0 :     return NS_ERROR_CONTENT_BLOCKED;
    1623                 :   }
    1624                 : 
    1625                 :   // the constructor should throw a SYNTAX_ERROR only if it fails to parse the
    1626                 :   // url parameter, so we don't care about the EstablishConnection result.
    1627               0 :   EstablishConnection();
    1628                 : 
    1629               0 :   return NS_OK;
    1630                 : }
    1631                 : 
    1632                 : //-----------------------------------------------------------------------------
    1633                 : // nsWebSocket::nsIObserver
    1634                 : //-----------------------------------------------------------------------------
    1635                 : 
    1636                 : NS_IMETHODIMP
    1637               0 : nsWebSocket::Observe(nsISupports* aSubject,
    1638                 :                      const char* aTopic,
    1639                 :                      const PRUnichar* aData)
    1640                 : {
    1641               0 :   if ((mReadyState == nsIWebSocket::CLOSING) ||
    1642                 :       (mReadyState == nsIWebSocket::CLOSED)) {
    1643               0 :     return NS_OK;
    1644                 :   }
    1645                 : 
    1646               0 :   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSubject);
    1647               0 :   if (!GetOwner() || window != GetOwner()) {
    1648               0 :     return NS_OK;
    1649                 :   }
    1650                 : 
    1651               0 :   if ((strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC) == 0) ||
    1652               0 :       (strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0))
    1653                 :   {
    1654               0 :     FailConnectionQuietly();
    1655                 :   }
    1656                 : 
    1657               0 :   return NS_OK;
    1658                 : }
    1659                 : 
    1660                 : 
    1661                 : //-----------------------------------------------------------------------------
    1662                 : // nsWebSocket::nsIRequest
    1663                 : //-----------------------------------------------------------------------------
    1664                 : 
    1665                 : NS_IMETHODIMP
    1666               0 : nsWebSocket::GetName(nsACString &aName)
    1667                 : {
    1668               0 :   CopyUTF16toUTF8(mOriginalURL, aName);
    1669               0 :   return NS_OK;
    1670                 : }
    1671                 : 
    1672                 : NS_IMETHODIMP
    1673               0 : nsWebSocket::IsPending(bool *aValue)
    1674                 : {
    1675               0 :   *aValue = !mDisconnected;
    1676               0 :   return NS_OK;
    1677                 : }
    1678                 : 
    1679                 : NS_IMETHODIMP
    1680               0 : nsWebSocket::GetStatus(nsresult *aStatus)
    1681                 : {
    1682               0 :   *aStatus = NS_OK;
    1683               0 :   return NS_OK;
    1684                 : }
    1685                 : 
    1686                 : // Window closed, stop/reload button pressed, user navigated away from page, etc.
    1687                 : NS_IMETHODIMP
    1688               0 : nsWebSocket::Cancel(nsresult aStatus)
    1689                 : {
    1690               0 :   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
    1691                 : 
    1692               0 :   if (mDisconnected)
    1693               0 :     return NS_OK;
    1694                 : 
    1695               0 :   ConsoleError();
    1696               0 :   return CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
    1697                 : }
    1698                 : 
    1699                 : NS_IMETHODIMP
    1700               0 : nsWebSocket::Suspend()
    1701                 : {
    1702               0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1703                 : }
    1704                 : 
    1705                 : NS_IMETHODIMP
    1706               0 : nsWebSocket::Resume()
    1707                 : {
    1708               0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1709                 : }
    1710                 : 
    1711                 : NS_IMETHODIMP
    1712               0 : nsWebSocket::GetLoadGroup(nsILoadGroup **aLoadGroup)
    1713                 : {
    1714               0 :   *aLoadGroup = nsnull;
    1715                 : 
    1716                 :   nsresult rv;
    1717               0 :   nsIScriptContext* sc = GetContextForEventHandlers(&rv);
    1718                 :   nsCOMPtr<nsIDocument> doc =
    1719               0 :     nsContentUtils::GetDocumentFromScriptContext(sc);
    1720                 : 
    1721               0 :   if (doc) {
    1722               0 :     *aLoadGroup = doc->GetDocumentLoadGroup().get();  // already_AddRefed
    1723                 :   }
    1724                 : 
    1725               0 :   return NS_OK;
    1726                 : }
    1727                 : 
    1728                 : NS_IMETHODIMP
    1729               0 : nsWebSocket::SetLoadGroup(nsILoadGroup *aLoadGroup)
    1730                 : {
    1731               0 :   return NS_ERROR_UNEXPECTED;
    1732                 : }
    1733                 : 
    1734                 : NS_IMETHODIMP
    1735               0 : nsWebSocket::GetLoadFlags(nsLoadFlags *aLoadFlags)
    1736                 : {
    1737               0 :   *aLoadFlags = nsIRequest::LOAD_BACKGROUND;
    1738               0 :   return NS_OK;
    1739                 : }
    1740                 : 
    1741                 : NS_IMETHODIMP
    1742               0 : nsWebSocket::SetLoadFlags(nsLoadFlags aLoadFlags)
    1743                 : {
    1744                 :   // we won't change the load flags at all.
    1745               0 :   return NS_OK;
    1746            4392 : }

Generated by: LCOV version 1.7