bugzilla 640003 websockets ietf-07 r=smaug r=biesi sr=bz
authorPatrick McManus <mcmanus@ducksong.com>
Sat, 21 May 2011 21:27:52 -0400
changeset 69862 1a2f85fcf598098d15645d6204994b9575a1377c
parent 69861 edfde66134a66c36412aafea42df8537ed3316d8
child 69863 9c8537aa965a8e7cc1e7c0ff4519627e6eb72c42
push id20124
push usermcmanus@ducksong.com
push dateSun, 22 May 2011 02:11:09 +0000
treeherdermozilla-central@9c8537aa965a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, biesi, bz
bugs640003
milestone6.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bugzilla 640003 websockets ietf-07 r=smaug r=biesi sr=bz
configure.in
content/base/public/nsIWebSocket.idl
content/base/src/nsWebSocket.cpp
content/base/src/nsWebSocket.h
layout/build/nsLayoutModule.cpp
layout/build/nsLayoutStatics.cpp
modules/libpref/src/init/all.js
netwerk/build/nsNetCID.h
netwerk/build/nsNetModule.cpp
netwerk/necko-config.h.in
netwerk/protocol/Makefile.in
netwerk/protocol/websocket/Makefile.in
netwerk/protocol/websocket/nsIWebSocketProtocol.idl
netwerk/protocol/websocket/nsWebSocketHandler.cpp
netwerk/protocol/websocket/nsWebSocketHandler.h
netwerk/streamconv/converters/nsHTTPCompressConv.cpp
--- a/configure.in
+++ b/configure.in
@@ -4856,17 +4856,17 @@ MOZ_XUL=1
 MOZ_ZIPWRITER=1
 NS_PRINTING=1
 MOZ_PDF_PRINTING=
 MOZ_DISABLE_DOMCRYPTO=
 NSS_DISABLE_DBM=
 NECKO_WIFI=1
 NECKO_COOKIES=1
 NECKO_DISK_CACHE=1
-NECKO_PROTOCOLS_DEFAULT="about data file ftp http res viewsource wyciwyg"
+NECKO_PROTOCOLS_DEFAULT="about data file ftp http res viewsource websocket wyciwyg"
 USE_ARM_KUSER=
 BUILD_CTYPES=1
 XPC_IDISPATCH_SUPPORT=
 
 
 case "${target}" in
 *android*|*darwin*)
     ACCESSIBILITY=
--- a/content/base/public/nsIWebSocket.idl
+++ b/content/base/public/nsIWebSocket.idl
@@ -46,20 +46,21 @@ interface nsPIDOMWindow;
 
 /**
  * The nsIWebSocket interface enables Web applications to maintain
  * bidirectional communications with server-side processes as described in:
  *
  * http://dev.w3.org/html5/websockets/
  *
  */
-[scriptable, uuid(4403cd57-07fc-477f-a062-d6ba7dd0781b)]
+[scriptable, uuid(431aea4c-568a-470e-b876-c57a29ff0fc6)]
 interface nsIWebSocket : nsISupports
 {
   readonly attribute DOMString url;
+  readonly attribute DOMString protocol;
 
   //ready state
   const unsigned short CONNECTING = 0;
   const unsigned short OPEN = 1;
   const unsigned short CLOSING = 2;
   const unsigned short CLOSED = 3;
   readonly attribute unsigned short readyState;
 
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is
  * Wellington Fernando de Macedo.
  * Portions created by the Initial Developer are Copyright (C) 2009
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *    Wellington Fernando de Macedo <wfernandom2004@gmail.com> (original author)
+ *    Patrick McManus <mcmanus@ducksong.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -45,74 +46,49 @@
 #include "nsIXPConnect.h"
 #include "nsContentUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsDOMError.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIDOMClassInfo.h"
 #include "nsDOMClassInfo.h"
 #include "jsapi.h"
-#include "nsNetUtil.h"
-#include "nsIStandardURL.h"
 #include "nsIURL.h"
 #include "nsIPrivateDOMEvent.h"
-#include "nsISocketTransportService.h"
-#include "nsIProtocolProxyCallback.h"
-#include "nsISocketTransport.h"
-#include "nsIAsyncInputStream.h"
-#include "nsIAsyncOutputStream.h"
-#include "nsICancelable.h"
 #include "nsIInterfaceRequestor.h"
-#include "nsISSLSocketControl.h"
-#include "nsISocketProviderService.h"
-#include "nsIProtocolProxyService2.h"
-#include "nsISocketProvider.h"
-#include "nsDeque.h"
-#include "nsICookieService.h"
 #include "nsICharsetConverterManager.h"
 #include "nsIUnicodeEncoder.h"
 #include "nsThreadUtils.h"
 #include "nsIDOMDocumentEvent.h"
 #include "nsIDOMMessageEvent.h"
-#include "nsIStandardURL.h"
 #include "nsIPromptFactory.h"
 #include "nsIWindowWatcher.h"
 #include "nsIPrompt.h"
 #include "nsIStringBundle.h"
 #include "nsIConsoleService.h"
-#include "nsITimer.h"
-#include "nsIDNSListener.h"
-#include "nsIDNSRecord.h"
-#include "nsIDNSService.h"
 #include "nsLayoutStatics.h"
-#include "nsIHttpAuthenticableChannel.h"
-#include "nsIHttpChannelAuthProvider.h"
-#include "mozilla/Mutex.h"
 #include "nsIDOMCloseEvent.h"
 #include "nsICryptoHash.h"
 #include "jsdbgapi.h"
 #include "nsIJSContextStack.h"
 #include "nsJSUtils.h"
 #include "nsIScriptError.h"
+#include "nsNetUtil.h"
+#include "nsIWebSocketProtocol.h"
+#include "nsILoadGroup.h"
+#include "nsIRequest.h"
 
 using namespace mozilla;
 
-static nsIThread *gWebSocketThread = nsnull;
-
 ////////////////////////////////////////////////////////////////////////////////
 // nsWebSocketEstablishedConnection
 ////////////////////////////////////////////////////////////////////////////////
 
-#define DEFAULT_BUFFER_SIZE 2048
 #define UTF_8_REPLACEMENT_CHAR    static_cast<PRUnichar>(0xFFFD)
 
-#define TIMEOUT_TRY_CONNECT_AGAIN         1000
-#define TIMEOUT_WAIT_FOR_SERVER_RESPONSE  20000
-#define TIMEOUT_WAIT_FOR_CLOSING          20000
-
 #define ENSURE_TRUE_AND_FAIL_IF_FAILED(x, ret)                            \
   PR_BEGIN_MACRO                                                          \
     if (NS_UNLIKELY(!(x))) {                                              \
        NS_WARNING("ENSURE_TRUE_AND_FAIL_IF_FAILED(" #x ") failed");       \
        FailConnection();                                                  \
        return ret;                                                        \
     }                                                                     \
   PR_END_MACRO
@@ -122,615 +98,106 @@ static nsIThread *gWebSocketThread = nsn
     nsresult __rv = res;                                                  \
     if (NS_FAILED(__rv)) {                                                \
       NS_ENSURE_SUCCESS_BODY(res, ret)                                    \
       FailConnection();                                                   \
       return ret;                                                         \
     }                                                                     \
   PR_END_MACRO
 
-#define CHECK_TRUE_AND_FAIL_IF_FAILED(x)                                  \
-  PR_BEGIN_MACRO                                                          \
-    if (NS_UNLIKELY(!(x))) {                                              \
-       NS_WARNING("CHECK_TRUE_AND_FAIL_IF_FAILED(" #x ") failed");        \
-       FailConnection();                                                  \
-       return;                                                            \
-    }                                                                     \
-  PR_END_MACRO
-
-#define CHECK_SUCCESS_AND_FAIL_IF_FAILED(res)                             \
-  PR_BEGIN_MACRO                                                          \
-    nsresult __rv = res;                                                  \
-    if (NS_FAILED(__rv)) {                                                \
-      NS_ENSURE_SUCCESS_BODY(res, ret)                                    \
-      FailConnection();                                                   \
-      return;                                                             \
-    }                                                                     \
-  PR_END_MACRO
-
-#define CHECK_SUCCESS_AND_FAIL_IF_FAILED2(res)                            \
-  PR_BEGIN_MACRO                                                          \
-    nsresult __rv = res;                                                  \
-    if (NS_FAILED(__rv)) {                                                \
-      NS_ENSURE_SUCCESS_BODY(res, ret)                                    \
-      thisObject->FailConnection();                                       \
-      return;                                                             \
-    }                                                                     \
-  PR_END_MACRO
-
-#define WARN_IF_FALSE_AND_RETURN(_expr, _msg)                             \
-  if (!(_expr)) {                                                         \
-    NS_WARNING(_msg);                                                     \
-    return;                                                               \
-  }
-
-#define DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(_method)                      \
-  nsresult _method();                                                     \
-  void MainRunnable##_method();
-
-#define IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(_method)                \
-  nsresult                                                                \
-  nsWebSocketEstablishedConnection::_method()                             \
-  {                                                                       \
-    if (!NS_IsMainThread()) {                                             \
-      nsCOMPtr<nsIRunnable> event =                                       \
-        NS_NewRunnableMethod(this, &nsWebSocketEstablishedConnection::    \
-                                    MainRunnable##_method);               \
-      if (!event) {                                                       \
-        return NS_ERROR_OUT_OF_MEMORY;                                    \
-      }                                                                   \
-      return NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);          \
-    }                                                                     \
-    MainRunnable##_method();                                              \
-    return NS_OK;                                                         \
-  }                                                                       \
-                                                                          \
-  void                                                                    \
-  nsWebSocketEstablishedConnection::MainRunnable##_method()               \
-  {                                                                       \
-    if (!mOwner) {                                                        \
-      return;                                                             \
-    }
-
-#define IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END                           \
-  }
-
-// protocol specific
-#define IS_HIGH_BIT_OF_FRAME_TYPE_SET(_v) ((_v & 0x80) == 0x80)
-#define IS_HIGH_BIT_OF_BYTE_SET(_v) ((_v & 0x80) == 0x80)
-#define START_BYTE_OF_MESSAGE 0x00
-#define END_BYTE_OF_MESSAGE 0xff
-#define START_BYTE_OF_CLOSE_FRAME 0xff
-#define END_BYTE_OF_CLOSE_FRAME 0x00
-
 // nsIInterfaceRequestor will be the unambiguous class for this class
 class nsWebSocketEstablishedConnection: public nsIInterfaceRequestor,
-                                        public nsIDNSListener,
-                                        public nsIProtocolProxyCallback,
-                                        public nsIInputStreamCallback,
-                                        public nsIOutputStreamCallback,
-                                        public nsIChannel,
-                                        public nsIHttpAuthenticableChannel
+                                        public nsIWebSocketListener,
+                                        public nsIRequest
 {
-friend class nsWSNetAddressComparator;
-friend class nsWSAutoClose;
-
 public:
   nsWebSocketEstablishedConnection();
   virtual ~nsWebSocketEstablishedConnection();
 
   NS_DECL_ISUPPORTS
-  NS_DECL_NSIDNSLISTENER
-  NS_DECL_NSIPROTOCOLPROXYCALLBACK
-  NS_DECL_NSIINPUTSTREAMCALLBACK
-  NS_DECL_NSIOUTPUTSTREAMCALLBACK
   NS_DECL_NSIINTERFACEREQUESTOR
+  NS_DECL_NSIWEBSOCKETLISTENER
   NS_DECL_NSIREQUEST
-  NS_DECL_NSICHANNEL
-  NS_DECL_NSIPROXIEDCHANNEL
-
-  // nsIHttpAuthenticableChannel.
-  // We can't use NS_DECL_NSIHTTPAUTHENTICABLECHANNEL because it duplicates
-  // cancel() and others.
-  NS_IMETHOD GetRequestMethod(nsACString &method);
-  NS_IMETHOD GetIsSSL(PRBool *aIsSSL);
-  NS_IMETHOD GetProxyMethodIsConnect(PRBool *aProxyMethodIsConnect);
-  NS_IMETHOD GetServerResponseHeader(nsACString & aServerResponseHeader);
-  NS_IMETHOD GetProxyChallenges(nsACString & aChallenges);
-  NS_IMETHOD GetWWWChallenges(nsACString & aChallenges);
-  NS_IMETHOD SetProxyCredentials(const nsACString & aCredentials);
-  NS_IMETHOD SetWWWCredentials(const nsACString & aCredentials);
-  NS_IMETHOD OnAuthAvailable();
-  NS_IMETHOD OnAuthCancelled(PRBool userCancel);
 
   nsresult Init(nsWebSocket *aOwner);
   nsresult Disconnect();
 
-  // These are called always on the main thread (they dispatch themselves).
-  // ATTENTION, these method when called can release both the WebSocket object
+  // these method when called can release both the WebSocket object
   // (i.e. mOwner) and its connection (i.e. *this*).
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(Close)
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(FailConnection)
+  nsresult Close();
+  nsresult FailConnection();
+  nsresult ConsoleError();
 
   PRBool HasOutgoingMessages()
-  { return mOutgoingMessages.GetSize() != 0; }
+  { return mOutgoingBufferedAmount != 0; }
 
   PRBool ClosedCleanly() { return mClosedCleanly; }
 
   nsresult PostMessage(const nsString& aMessage);
   PRUint32 GetOutgoingBufferedAmount() { return mOutgoingBufferedAmount; }
 
-  // prevent more than one instance from connecting at a time per IP
-  static nsTArray<nsRefPtr<nsWebSocketEstablishedConnection> >* sWSsConnecting;
-
 private:
-  enum WSFrameType {
-    eConnectFrame,
-    eUTF8MessageFrame,
-    eCloseFrame
-  };
-
-  struct nsWSFrame {
-    WSFrameType mType;
-    nsAutoPtr<nsCString> mData;
-  };
-
-  // We can only establish one connection at a time per IP address.
-  // TryConnect ensures this by checking sWSsConnecting.
-  // If there is a IP address entry there it tries again after
-  // TIMEOUT_TRY_CONNECT_AGAIN milliseconds.
-  static void TryConnect(nsITimer *aTimer,
-                         void     *aClosure);
-
-  // We wait for the initial server response for
-  // TIMEOUT_WAIT_FOR_SERVER_RESPONSE milliseconds
-  static void TimerInitialServerResponseCallback(nsITimer *aTimer,
-                                                 void     *aClosure);
 
-  // We wait TIMEOUT_WAIT_FOR_CLOSING milliseconds to the connection be closed
-  // after setting the readyState to CLOSING.
-  static void TimerForceCloseCallback(nsITimer *aTimer,
-                                      void     *aClosure);
-
-  // Similar to the Close method, but this one neither sends the close frame
-  // nor expects the connection to be closed (mStatus == CONN_CLOSED) to
-  // disconnect.
-  void ForceClose();
-
-  nsresult DoConnect();
-  nsresult HandleNewInputString(PRUint32 aStart);
-  nsresult AddAuthorizationHeaders(nsCString& aAuthHeaderStr,
-                                   PRBool     aIsProxyAuth);
-  nsresult AddCookiesToRequest(nsCString& aAuthHeaderStr);
-
-  // Returns the related number of the generated key.
-  PRUint32 GenerateSecKey(nsCString& aKey);
-  nsresult GenerateRequestKeys(nsCString& aKey1,
-                               nsCString& aKey2,
-                               nsCString& aKey3);
-
-  PRBool UsingHttpProxy();
-  nsresult Reset();
-  void RemoveFromLoadGroup();
-  nsresult ProcessHeaders();
-  nsresult PostData(nsCString  *aBuffer,
-                    WSFrameType aWSFrameType);
   nsresult PrintErrorOnConsole(const char       *aBundleURI,
                                const PRUnichar  *aError,
                                const PRUnichar **aFormatStrings,
                                PRUint32          aFormatStringsLen);
-
-  // auth specific methods
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(ProcessAuthentication)
-
-  // these are called always on the main thread (they dispatch themselves)
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(AddWSConnecting)
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(RemoveWSConnecting)
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(HandleSetCookieHeader)
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(DoInitialRequest)
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(Connected)
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(FrameError)
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(DispatchNewMessage)
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(Retry)
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(ResolveNextProxyAndConnect)
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(UpdateMustKeepAlive)
-
-  // called to cause the underlying socket to start speaking SSL
-  nsresult ProxyStartSSL();
-
-  // shared by both threads (the main and the socket ones)
-  Mutex   mLockDisconnect;
-  Mutex   mLockOutgoingMessages;
-  Mutex   mLockReceivedMessages;
-  nsCOMPtr<nsISocketTransport>    mSocketTransport;
-  nsCOMPtr<nsIAsyncInputStream>   mSocketInput;
-  nsCOMPtr<nsIAsyncOutputStream>  mSocketOutput;
-  nsCOMPtr<nsIProxyInfo>          mProxyInfo;
-  nsDeque mOutgoingMessages; // has nsWSFrame* which need to be sent
-  PRUint32 mBytesAlreadySentOfFirstOutString;
-  PRUint32 mOutgoingBufferedAmount; // not really necessary, but it is
-                                    // here for fast access.
-  nsDeque mReceivedMessages; // has nsCString* messages that need
-                             // to be dispatched as message events
-  nsCString mExpectedMD5Challenge;
-  nsWebSocket* mOwner; // WEAK
-
-  // used in mHeaders
-  enum {
-    kUpgradePos = 0,
-    kConnectionPos,
-    kSecWebSocketOriginPos,
-    kSecWebSocketLocationPos,
-    kSecWebSocketProtocolPos,
-    kSetCookiePos,
-    kProxyAuthenticatePos,
-    kServerPos,  // for digest auth
-    kHeadersLen
-  };
-
-  // used only by the socket thread
-  nsCString mBuffer;
-  PRUint32 mBytesInBuffer; // it is needed because mBuffer.SetLength() does
-                           // change also the buffer's Capacity.
-  nsCString mHeaders[kHeadersLen];
-  PRUint32 mLengthToDiscard;
-  PRPackedBool mReadingProxyConnectResponse;
-
-  // WebSockets should resolve proxy in this order:
-  //    (1) socks, (2) https, (3) http
-  enum ProxyConfig {
-    eNotResolvingProxy,
-    eResolvingSOCKSProxy,
-    eResolvingHTTPSProxy,
-    eResolvingHTTPProxy,
-    eResolvingProxyFailed
-  };
-  ProxyConfig mCurrentProxyConfig;
-
-  nsresult mProxyFailureReason;
-  nsCOMPtr<nsICancelable> mProxyResolveCancelable;
-  nsCOMPtr<nsICancelable> mDNSRequest;
-  PRNetAddr mPRNetAddr;
-
-  nsCOMPtr<nsITimer> mTryConnectTimer;
-  nsCOMPtr<nsITimer> mInitialServerResponseTimer;
-  nsCOMPtr<nsITimer> mCloseFrameServerResponseTimer;
-
-  // for nsIRequest implementation
-  nsCString mRequestName;
-  nsresult mFailureStatus;
+  nsresult UpdateMustKeepAlive();
+  
+  // Frames that have been sent to websockethandler but not placed on wire
+  PRUint32 mOutgoingBufferedAmount;
 
-  // auth specific data
-  nsCString mProxyCredentials;
-  nsCString mCredentials;
-  nsCOMPtr<nsIHttpChannelAuthProvider> mAuthProvider;
-  PRPackedBool mAuthenticating;
-
-  PRPackedBool mPostedCloseFrame;
-  PRPackedBool mSentCloseFrame;
-  PRPackedBool mClosedCleanly;
+  nsWebSocket* mOwner; // weak reference
+  nsCOMPtr<nsIWebSocketProtocol> mWebSocketProtocol;
 
-  /**
-   * A simple state machine used to manage the flow status of the input/output
-   * streams of the connection.
-   *
-   * CONN_NOT_CONNECTED (initial state)              ->
-   *                          CONN_CONNECTING |
-   *                          CONN_CLOSED
-   *
-   * CONN_CONNECTING                                 ->
-   *                          CONN_CONNECTING_TO_HTTP_PROXY |
-   *                          CONN_SENDING_INITIAL_REQUEST |
-   *                          CONN_CLOSED
-   *
-   * CONN_RETRYING_TO_AUTHENTICATE                   ->
-   *                          CONN_CONNECTING_TO_HTTP_PROXY |
-   *                          CONN_SENDING_INITIAL_REQUEST |
-   *                          CONN_CLOSED
-   *
-   * CONN_CONNECTING_TO_HTTP_PROXY                    ->
-   *                          CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME |
-   *                          CONN_CLOSED
-   *
-   * CONN_SENDING_INITIAL_REQUEST                    ->
-   *                          CONN_WAITING_RESPONSE_FOR_INITIAL_REQUEST |
-   *                          CONN_CLOSED
-   *
-   * CONN_WAITING_RESPONSE_FOR_INITIAL_REQUEST        ->
-   *                          CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME |
-   *                          CONN_CLOSED
-   *
-   * CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME  ->
-   *                          CONN_READING_RESPONSE_HEADER_NAME |
-   *                          CONN_WAITING_LF_CHAR_TO_CONNECTING |
-   *                          CONN_CLOSED
-   *
-   * CONN_READING_RESPONSE_HEADER_NAME                ->
-   *                          CONN_READING_RESPONSE_HEADER_VALUE |
-   *                          CONN_CLOSED
-   *
-   * CONN_READING_RESPONSE_HEADER_VALUE               ->
-   *                          CONN_WAITING_LF_CHAR_OF_RESPONSE_HEADER |
-   *                          CONN_CLOSED
-   *
-   * CONN_WAITING_LF_CHAR_OF_RESPONSE_HEADER          ->
-   *                          CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME |
-   *                          CONN_CLOSED
-   *
-   * CONN_WAITING_LF_CHAR_TO_CONNECTING               ->
-   *                          CONN_SENDING_INITIAL_REQUEST |
-   *                          CONN_READING_CHALLENGE_RESPONSE |
-   *                          CONN_RETRYING_TO_AUTHENTICATE |
-   *                          CONN_CLOSED
-   *
-   * CONN_READING_CHALLENGE_RESPONSE                  ->
-   *                          CONN_CONNECTED_AND_READY |
-   *                          CONN_CLOSED
-   *
-   * CONN_CONNECTED_AND_READY                         ->
-   *                          CONN_HIGH_BIT_OF_FRAME_TYPE_SET |
-   *                          CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET |
-   *                          CONN_CLOSED
-   *
-   * CONN_HIGH_BIT_OF_FRAME_TYPE_SET                  ->
-   *                          CONN_READING_AND_DISCARDING_LENGTH_BYTES |
-   *                          CONN_SENDING_ACK_CLOSE_FRAME |
-   *                          CONN_CLOSED
-   *
-   * CONN_READING_AND_DISCARDING_LENGTH_BYTES         ->
-   *                          CONN_CONNECTED_AND_READY |
-   *                          CONN_CLOSED
-   *
-   * CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET              ->
-   *                          CONN_CONNECTED_AND_READY |
-   *                          CONN_CLOSED
-   *
-   * CONN_SENDING_ACK_CLOSE_FRAME ->
-   *                          CONN_CLOSED
-   *
-   * CONN_CLOSED (final state)
-   *
-   */
+  PRPackedBool mClosedCleanly;
 
   enum ConnectionStatus {
     CONN_NOT_CONNECTED,
-    CONN_CONNECTING,
-    // connecting to the http proxy
-    CONN_RETRYING_TO_AUTHENTICATE,
-    CONN_CONNECTING_TO_HTTP_PROXY,
-    // doing the websocket handshake
-    CONN_SENDING_INITIAL_REQUEST,
-    CONN_WAITING_RESPONSE_FOR_INITIAL_REQUEST,
-    CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME,
-    CONN_READING_RESPONSE_HEADER_NAME,
-    CONN_READING_RESPONSE_HEADER_VALUE,
-    CONN_WAITING_LF_CHAR_OF_RESPONSE_HEADER,
-    CONN_WAITING_LF_CHAR_TO_CONNECTING,
-    CONN_READING_CHALLENGE_RESPONSE,
-    // the websocket connection is established
     CONN_CONNECTED_AND_READY,
-    CONN_HIGH_BIT_OF_FRAME_TYPE_SET,
-    CONN_READING_AND_DISCARDING_LENGTH_BYTES,
-    CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET,
-    // the websocket server requested to close the connection and because of
-    // this the close frame in acknowledgement is expected to be sent
-    CONN_SENDING_ACK_CLOSE_FRAME,
-    // the websocket connection is closed (or it is going to)
     CONN_CLOSED
   };
-  // Shared by both threads (the main and the socket ones).
-  // The initial states (CONN_NOT_CONNECTED, CONN_CONNECTING,
-  // CONN_CONNECTING_TO_HTTP_PROXY and CONN_SENDING_INITIAL_REQUEST) are set by
-  // the main thread. The other ones are set by the socket thread.
+
   ConnectionStatus mStatus;
 };
 
-nsTArray<nsRefPtr<nsWebSocketEstablishedConnection> >*
-  nsWebSocketEstablishedConnection::sWSsConnecting = nsnull;
-
-//------------------------------------------------------------------------------
-// Helper classes
-//------------------------------------------------------------------------------
-
-class nsWSNetAddressComparator
-{
-public:
-  // when comparing, if the connection is under a proxy it'll use its hostname,
-  // otherwise it'll use its mPRNetAddr.
-  PRBool Equals(nsWebSocketEstablishedConnection* a,
-                nsWebSocketEstablishedConnection* b) const;
-  PRBool LessThan(nsWebSocketEstablishedConnection* a,
-                  nsWebSocketEstablishedConnection* b) const;
-};
-
-class nsWSAutoClose
-{
-public:
-  nsWSAutoClose(nsWebSocketEstablishedConnection* conn) : mConnection(conn)
-  {}
-
-  ~nsWSAutoClose() { mConnection->Close(); }
-
-private:
-  nsWebSocketEstablishedConnection* mConnection;
-};
-
-PRBool
-nsWSNetAddressComparator::Equals(nsWebSocketEstablishedConnection* a,
-                                 nsWebSocketEstablishedConnection* b) const
-{
-  NS_ASSERTION(a->mOwner && b->mOwner, "Unexpected disconnected connection");
-
-  if ((a->mProxyInfo && !b->mProxyInfo) ||
-      (!a->mProxyInfo && b->mProxyInfo)) {
-    return PR_FALSE;
-  }
-
-  if (a->mProxyInfo) {
-    return a->mOwner->mAsciiHost.Equals(b->mOwner->mAsciiHost);
-  }
-
-  if (a->mPRNetAddr.raw.family != b->mPRNetAddr.raw.family) {
-    return PR_FALSE;
-  }
-
-  if (a->mPRNetAddr.raw.family == PR_AF_INET) {
-    return a->mPRNetAddr.inet.ip == b->mPRNetAddr.inet.ip;
-  }
-
-  NS_ASSERTION(a->mPRNetAddr.raw.family == PR_AF_INET6,
-               "Invalid net raw family");
-
-  return a->mPRNetAddr.ipv6.ip.pr_s6_addr64[0] ==
-           b->mPRNetAddr.ipv6.ip.pr_s6_addr64[0] &&
-         a->mPRNetAddr.ipv6.ip.pr_s6_addr64[1] ==
-           b->mPRNetAddr.ipv6.ip.pr_s6_addr64[1];
-}
-
-PRBool
-nsWSNetAddressComparator::LessThan(nsWebSocketEstablishedConnection* a,
-                                   nsWebSocketEstablishedConnection* b) const
-{
-  NS_ASSERTION(a->mOwner && b->mOwner, "Unexpected disconnected connection");
-
-  if (a->mProxyInfo && !b->mProxyInfo) {
-    return PR_FALSE;
-  }
-
-  if (!a->mProxyInfo && b->mProxyInfo) {
-    return PR_TRUE;
-  }
-
-  if (a->mProxyInfo) {
-    return (a->mOwner->mAsciiHost < b->mOwner->mAsciiHost);
-  }
-
-  if (a->mPRNetAddr.raw.family == PR_AF_INET &&
-      b->mPRNetAddr.raw.family == PR_AF_INET6) {
-    return PR_TRUE;
-  }
-
-  if (a->mPRNetAddr.raw.family == PR_AF_INET6 &&
-      b->mPRNetAddr.raw.family == PR_AF_INET) {
-    return PR_FALSE;
-  }
-
-  if (a->mPRNetAddr.raw.family == PR_AF_INET &&
-      b->mPRNetAddr.raw.family == PR_AF_INET) {
-    return a->mPRNetAddr.inet.ip < b->mPRNetAddr.inet.ip;
-  }
-
-  NS_ASSERTION(a->mPRNetAddr.raw.family == PR_AF_INET6 &&
-               b->mPRNetAddr.raw.family == PR_AF_INET6,
-               "Invalid net raw family");
-
-  return a->mPRNetAddr.ipv6.ip.pr_s6_addr64[0] <
-           b->mPRNetAddr.ipv6.ip.pr_s6_addr64[0] ||
-         (a->mPRNetAddr.ipv6.ip.pr_s6_addr64[0] ==
-           b->mPRNetAddr.ipv6.ip.pr_s6_addr64[0] &&
-          a->mPRNetAddr.ipv6.ip.pr_s6_addr64[1] <
-           b->mPRNetAddr.ipv6.ip.pr_s6_addr64[1]);
-}
-
 //-----------------------------------------------------------------------------
 // nsWebSocketEstablishedConnection::nsISupports
 //-----------------------------------------------------------------------------
 
-NS_IMPL_THREADSAFE_ISUPPORTS9(nsWebSocketEstablishedConnection,
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsWebSocketEstablishedConnection,
                               nsIInterfaceRequestor,
-                              nsIDNSListener,
-                              nsIProtocolProxyCallback,
-                              nsIInputStreamCallback,
-                              nsIOutputStreamCallback,
-                              nsIRequest,
-                              nsIChannel,
-                              nsIProxiedChannel,
-                              nsIHttpAuthenticableChannel)
+                              nsIWebSocketListener,
+                              nsIRequest)
 
 //-----------------------------------------------------------------------------
 // nsWebSocketEstablishedConnection methods:
 //-----------------------------------------------------------------------------
 
 nsWebSocketEstablishedConnection::nsWebSocketEstablishedConnection() :
-  mLockDisconnect("WebSocket's disconnect lock"),
-  mLockOutgoingMessages("WebSocket's outgoing messages lock"),
-  mLockReceivedMessages("WebSocket's received messages lock"),
-  mBytesAlreadySentOfFirstOutString(0),
   mOutgoingBufferedAmount(0),
   mOwner(nsnull),
-  mBytesInBuffer(0),
-  mLengthToDiscard(0),
-  mReadingProxyConnectResponse(PR_FALSE),
-  mCurrentProxyConfig(eNotResolvingProxy),
-  mProxyFailureReason(NS_OK),
-  mFailureStatus(NS_OK),
-  mAuthenticating(PR_FALSE),
-  mPostedCloseFrame(PR_FALSE),
-  mSentCloseFrame(PR_FALSE),
   mClosedCleanly(PR_FALSE),
   mStatus(CONN_NOT_CONNECTED)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsLayoutStatics::AddRef();
 }
 
 nsWebSocketEstablishedConnection::~nsWebSocketEstablishedConnection()
 {
-  NS_ASSERTION(!mOwner, "Disconnect wasn't called!");
-}
-
-nsresult
-nsWebSocketEstablishedConnection::PostData(nsCString *aBuffer,
-                                           WSFrameType aWSFrameType)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
-
-  nsAutoPtr<nsCString> data(aBuffer);
-
-  if (mStatus == CONN_CLOSED) {
-    NS_ASSERTION(mOwner, "Posting data after disconnecting the websocket!");
-    // the tcp connection has been closed, but the main thread hasn't received
-    // the event for disconnecting the object yet.
-    return NS_BASE_STREAM_CLOSED;
-  }
-
-  MutexAutoLock lockOut(mLockOutgoingMessages);
-
-  nsAutoPtr<nsWSFrame> frame(new nsWSFrame());
-  NS_ENSURE_TRUE(frame.get(), NS_ERROR_OUT_OF_MEMORY);
-  frame->mType = aWSFrameType;
-  frame->mData = data.forget();
-
-  nsresult rv;
-  PRInt32 sizeBefore = mOutgoingMessages.GetSize();
-  mOutgoingMessages.Push(frame.forget());
-  NS_ENSURE_TRUE(mOutgoingMessages.GetSize() == sizeBefore + 1,
-                 NS_ERROR_OUT_OF_MEMORY);
-  if (aWSFrameType == eUTF8MessageFrame) {
-    // without the START_BYTE_OF_MESSAGE and END_BYTE_OF_MESSAGE bytes
-    mOutgoingBufferedAmount += aBuffer->Length() - 2;
-  } else if (aWSFrameType == eCloseFrame) {
-    mPostedCloseFrame = PR_TRUE;
-  }
-
-  if (sizeBefore == 0) {
-    mBytesAlreadySentOfFirstOutString = 0;
-    rv = mSocketOutput->AsyncWait(this, 0, 0, gWebSocketThread);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  UpdateMustKeepAlive();
-
-  return NS_OK;
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
+  NS_ABORT_IF_FALSE(!mOwner, "Disconnect wasn't called!");
+  NS_ABORT_IF_FALSE(!mWebSocketProtocol, "Disconnect wasn't called!");
 }
 
 nsresult
 nsWebSocketEstablishedConnection::PostMessage(const nsString& aMessage)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
   if (!mOwner) {
     return NS_OK;
   }
 
   // only send messages when connected
   NS_ENSURE_STATE(mStatus >= CONN_CONNECTED_AND_READY);
 
@@ -747,1208 +214,110 @@ nsWebSocketEstablishedConnection::PostMe
   rv = converter->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace,
                                          nsnull, UTF_8_REPLACEMENT_CHAR);
   ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
 
   PRInt32 inLen = aMessage.Length();
   PRInt32 maxLen;
   rv = converter->GetMaxLength(aMessage.BeginReading(), inLen, &maxLen);
   ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-  maxLen += 2;   // 2 bytes for START_BYTE_OF_MESSAGE and END_BYTE_OF_MESSAGE
 
-  nsAutoPtr<nsCString> buf(new nsCString());
-  ENSURE_TRUE_AND_FAIL_IF_FAILED(buf.get(), NS_ERROR_OUT_OF_MEMORY);
-
-  buf->SetLength(maxLen);
-  ENSURE_TRUE_AND_FAIL_IF_FAILED(buf->Length() == static_cast<PRUint32>(maxLen),
+  nsCString buf;
+  buf.SetLength(maxLen);
+  ENSURE_TRUE_AND_FAIL_IF_FAILED(buf.Length() == static_cast<PRUint32>(maxLen),
                                  NS_ERROR_OUT_OF_MEMORY);
 
-  char* start = buf->BeginWriting();
-  *start = static_cast<char>(START_BYTE_OF_MESSAGE);
-  ++start;
+  char* start = buf.BeginWriting();
 
   PRInt32 outLen = maxLen;
   rv = converter->Convert(aMessage.BeginReading(), &inLen, start, &outLen);
   if (NS_SUCCEEDED(rv)) {
     PRInt32 outLen2 = maxLen - outLen;
     rv = converter->Finish(start + outLen, &outLen2);
     outLen += outLen2;
   }
   if (NS_FAILED(rv) || rv == NS_ERROR_UENC_NOMAPPING) {
     // Yes, NS_ERROR_UENC_NOMAPPING is a success code
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
-  char* end = buf->BeginWriting() + outLen + 1;
-  *end = static_cast<char>(END_BYTE_OF_MESSAGE);
-
-  outLen += 2;
-
-  buf->SetLength(outLen);
-  ENSURE_TRUE_AND_FAIL_IF_FAILED(buf->Length() == static_cast<PRUint32>(outLen),
+  buf.SetLength(outLen);
+  ENSURE_TRUE_AND_FAIL_IF_FAILED(buf.Length() == static_cast<PRUint32>(outLen),
                                  NS_ERROR_UNEXPECTED);
 
-  rv = PostData(buf.forget(), eUTF8MessageFrame);
-  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-
-  return NS_OK;
-}
-
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(DoInitialRequest)
-{
-  nsresult rv;
-  nsString strRequestTmp;
-
-  nsAutoPtr<nsCString> buf(new nsCString());
-  CHECK_TRUE_AND_FAIL_IF_FAILED(buf.get());
-
-  // GET resource HTTP/1.1
-  buf->AppendLiteral("GET ");
-  buf->Append(mOwner->mResource);
-  buf->AppendLiteral(" HTTP/1.1\r\n");
-
-  nsCAutoString key_1, key_2, key_3;
-  rv = GenerateRequestKeys(key_1, key_2, key_3);
-  CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
-
-  // the headers should be sent in a random order
-
-  enum eRequestHeader { upgradeHeader = 0, connectionHeader, hostHeader,
-                        originHeader, secWebSocketProtocolHeader,
-                        authorizationHeaders, cookieHeaders,
-                        secWebSocketKey1Header, secWebSocketKey2Header,
-                        numberRequestHeaders };
-  nsAutoTArray<PRUint32, numberRequestHeaders> headersToSend;
-  for (PRUint32 i = 0; i < numberRequestHeaders; ++i) {
-    headersToSend.AppendElement(i);
-  }
-
-  while (!headersToSend.IsEmpty()) {
-    PRUint8 headerPosToSendNow = rand() % headersToSend.Length();
-    eRequestHeader headerToSendNow =
-      static_cast<eRequestHeader>(headersToSend[headerPosToSendNow]);
-
-    switch (headerToSendNow)
-    {
-      case upgradeHeader:
-      {
-        buf->AppendLiteral("Upgrade: WebSocket\r\n");
-      }
-      break;
-
-      case connectionHeader:
-      {
-        buf->AppendLiteral("Connection: Upgrade\r\n");
-      }
-      break;
-
-      case hostHeader:
-      {
-        buf->AppendLiteral("Host: ");
-        buf->Append(mOwner->mAsciiHost);
-        buf->AppendLiteral(":");
-        buf->AppendInt(mOwner->mPort);
-        buf->AppendLiteral("\r\n");
-      }
-      break;
-
-      case originHeader:
-      {
-        buf->AppendLiteral("Origin: ");
-        buf->Append(mOwner->mOrigin);
-        buf->AppendLiteral("\r\n");
-      }
-      break;
-
-      case secWebSocketProtocolHeader:
-      {
-        if (!mOwner->mProtocol.IsEmpty()) {
-          buf->AppendLiteral("Sec-WebSocket-Protocol: ");
-          buf->Append(mOwner->mProtocol);
-          buf->AppendLiteral("\r\n");
-        }
-      }
-      break;
-
-      case authorizationHeaders:
-      {
-        rv = AddAuthorizationHeaders(*buf, PR_FALSE);
-        CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
-      }
-      break;
-
-      case cookieHeaders:
-      {
-        rv = AddCookiesToRequest(*buf);
-        CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
-      }
-      break;
-
-      case secWebSocketKey1Header:
-      {
-        buf->AppendLiteral("Sec-WebSocket-Key1: ");
-        buf->Append(key_1);
-        buf->AppendLiteral("\r\n");
-      }
-      break;
-
-      case secWebSocketKey2Header:
-      {
-        buf->AppendLiteral("Sec-WebSocket-Key2: ");
-        buf->Append(key_2);
-        buf->AppendLiteral("\r\n");
-      }
-      break;
-
-      case numberRequestHeaders:
-      break;
-    }
-
-    headersToSend.RemoveElementAt(headerPosToSendNow);
-  }
-
-  buf->AppendLiteral("\r\n");
-  buf->Append(key_3);
-
-  mStatus = CONN_SENDING_INITIAL_REQUEST;
-
-  rv = PostData(buf.forget(), eConnectFrame);
-  CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
-}
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
-
-static
-nsresult
-GetHttpResponseCode(const nsCString& aLine, PRUint32 aLen,
-                    PRUint32 *aStatusCode, PRUint32 *aLineLen)
-{
-  // make sure we have HTTP at the beginning
-  if (aLen < 4) {
-    return NS_ERROR_IN_PROGRESS;
-  }
-  if (!StringBeginsWith(aLine, NS_LITERAL_CSTRING("HTTP"))) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  // get the response code
-  PRUint32 responseCode = 0;
-  PRUint8 responseCodeReadingState = 0; // 0:not reading, 1:reading,
-                                        // 2:done reading
-  char last2Chrs[2] = {'\0', '\0'};
-  PRUint32 i = 4;  // just after the HTTP
-  for (; i < aLen; ++i) {
-    if (responseCodeReadingState == 0 && aLine[i] == ' ') {
-      responseCodeReadingState = 1;
-    } else if (responseCodeReadingState == 1) {
-      if (aLine[i] == ' ') {
-        responseCodeReadingState = 2;
-      } else if (aLine[i] >= '0' && aLine[i] <= '9') {
-        responseCode = 10 * responseCode + (aLine[i] - '0');
-        if (responseCode > 999) { // the response code must be three digits long
-          return NS_ERROR_UNEXPECTED;
-        }
-      } else {
-        return NS_ERROR_UNEXPECTED;
-      }
-    }
-
-    last2Chrs[0] = last2Chrs[1];
-    last2Chrs[1] = aLine[i];
-    if (last2Chrs[0] == '\r' && last2Chrs[1] == '\n') { // CR LF
-      *aStatusCode = responseCode;
-      *aLineLen = i + 1;
-      return NS_OK;
-    }
-  }
-
-  return NS_ERROR_IN_PROGRESS;
-}
-
-nsresult
-nsWebSocketEstablishedConnection::HandleNewInputString(PRUint32 aStart)
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
-
-  if (mBytesInBuffer == 0 || aStart == mBytesInBuffer) {
-    return NS_OK;
+  if (mStatus == CONN_CLOSED) {
+    NS_ABORT_IF_FALSE(mOwner, "Posting data after disconnecting the websocket");
+    // the tcp connection has been closed, but the main thread hasn't received
+    // the event for disconnecting the object yet.
+    rv = NS_BASE_STREAM_CLOSED;
+  } else {
+    mOutgoingBufferedAmount += buf.Length();
+    mWebSocketProtocol->SendMsg(buf);
+    rv = NS_OK;
   }
 
-  NS_ENSURE_STATE(aStart < mBytesInBuffer);
-
-  nsresult rv;
-
-  switch (mStatus)
-  {
-    case CONN_CONNECTING_TO_HTTP_PROXY:
-    {
-      PRUint32 statusCode, lengthStr;
-
-      rv = GetHttpResponseCode(mBuffer, mBytesInBuffer, &statusCode,
-                               &lengthStr);
-      if (rv != NS_ERROR_IN_PROGRESS) {
-        NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
-
-        if (statusCode == 200) {
-          mReadingProxyConnectResponse = PR_TRUE;
-          mAuthenticating = PR_FALSE;
-        } else if (statusCode == 407) {
-          mReadingProxyConnectResponse = PR_TRUE;
-          mAuthenticating = PR_TRUE;
-        } else {
-          return NS_ERROR_UNEXPECTED;
-        }
-
-        mStatus = CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME;
-        mBuffer.Cut(0, lengthStr);
-        mBytesInBuffer -= lengthStr;
-
-        return HandleNewInputString(0);
-      }
-    }
-    break;
-
-    case CONN_SENDING_ACK_CLOSE_FRAME:
-    case CONN_CLOSED:
-    {
-      mBytesInBuffer = 0;
-    }
-    break;
-
-    case CONN_WAITING_RESPONSE_FOR_INITIAL_REQUEST:
-    {
-      PRUint32 statusCode, lengthStr;
-
-      rv = GetHttpResponseCode(mBuffer, mBytesInBuffer, &statusCode,
-                               &lengthStr);
-      if (rv != NS_ERROR_IN_PROGRESS) {
-        NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
-
-        if (statusCode != 101) {
-          return NS_ERROR_UNEXPECTED;
-        }
-
-        mReadingProxyConnectResponse = PR_FALSE;
-        mAuthenticating = PR_FALSE;
-
-        mStatus = CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME;
-        mBuffer.Cut(0, lengthStr);
-        mBytesInBuffer -= lengthStr;
-
-        // we have just received the server response. We must cancel this timer
-        // or it will fail the connection.
-        mInitialServerResponseTimer->Cancel();
-
-        return HandleNewInputString(0);
-      }
-    }
-    break;
-
-    case CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME:
-    {
-      if (mBuffer[aStart] == '\r') {
-        mStatus = CONN_WAITING_LF_CHAR_TO_CONNECTING;
-        return HandleNewInputString(aStart + 1);
-      }
-
-      NS_ENSURE_STATE(mBuffer[aStart] != '\n');
-
-      mStatus = CONN_READING_RESPONSE_HEADER_NAME;
-      return HandleNewInputString(aStart);
-    }
-    break;
-
-    case CONN_READING_RESPONSE_HEADER_NAME:
-    {
-      PRUint32 i;
-      for (i = aStart; i < mBytesInBuffer; ++i) {
-        NS_ENSURE_STATE(mBuffer[i] != '\r' && mBuffer[i] != '\n');
-
-        if (mBuffer[i] == ':') {
-          mStatus = CONN_READING_RESPONSE_HEADER_VALUE;
-          return HandleNewInputString(i + 1);
-        }
-      }
-    }
-    break;
-
-    case CONN_READING_RESPONSE_HEADER_VALUE:
-    {
-      PRUint32 i;
-      for (i = aStart; i < mBytesInBuffer; ++i) {
-        if (mBuffer[i] == '\r') {
-          mStatus = CONN_WAITING_LF_CHAR_OF_RESPONSE_HEADER;
-          return HandleNewInputString(i + 1);
-        }
-
-        NS_ENSURE_STATE(mBuffer[i] != '\n');
-      }
-    }
-    break;
-
-    case CONN_WAITING_LF_CHAR_OF_RESPONSE_HEADER:
-    {
-      NS_ENSURE_STATE(mBuffer[aStart] == '\n');
-
-      PRUint32 posColon = mBuffer.FindChar(':');
-      PRUint32 posCR = mBuffer.FindChar('\r');
-
-      const nsCSubstring& headerName = Substring(mBuffer, 0, posColon);
-
-      nsCString headerValue;
-      if (mBuffer[posColon + 1] == 0x20 && posColon + 2 != posCR) {
-        headerValue = Substring(mBuffer, posColon + 2, posCR - posColon - 2);
-      } else if (posColon + 1 != posCR) {
-        headerValue = Substring(mBuffer, posColon + 1, posCR - posColon - 1);
-      } else {
-        ; // No header value
-      }
-
-      NS_ENSURE_STATE(!headerName.IsEmpty());
-
-      PRInt32 headerPos = -1;
-      if (mReadingProxyConnectResponse) {
-        if (headerName.LowerCaseEqualsLiteral("proxy-authenticate")) {
-          headerPos = kProxyAuthenticatePos;
-        }
-      } else {
-        if (headerName.LowerCaseEqualsLiteral("upgrade")) {
-          headerPos = kUpgradePos;
-        } else if (headerName.LowerCaseEqualsLiteral("connection")) {
-          headerPos = kConnectionPos;
-        } else if (headerName.LowerCaseEqualsLiteral("sec-websocket-origin")) {
-          headerPos = kSecWebSocketOriginPos;
-        } else if (headerName.LowerCaseEqualsLiteral("sec-websocket-location")) {
-          headerPos = kSecWebSocketLocationPos;
-        } else if (headerName.LowerCaseEqualsLiteral("sec-websocket-protocol")) {
-          headerPos = kSecWebSocketProtocolPos;
-        } else if (headerName.LowerCaseEqualsLiteral("set-cookie")) {
-          headerPos = kSetCookiePos;
-        }
-      }
-      if (headerPos == -1 && headerName.LowerCaseEqualsLiteral("server")) {
-        headerPos = kServerPos;
-      }
-
-      if (headerPos != -1) {
-        NS_ENSURE_STATE(mHeaders[headerPos].IsEmpty());
-        mHeaders[headerPos] = headerValue;
-      }
-
-      mStatus = CONN_READING_FIRST_CHAR_OF_RESPONSE_HEADER_NAME;
-      mBuffer.Cut(0, aStart + 1);
-      mBytesInBuffer -= aStart + 1;
-
-      return HandleNewInputString(0);
-    }
-    break;
-
-    case CONN_WAITING_LF_CHAR_TO_CONNECTING:
-    {
-      NS_ENSURE_STATE(mBuffer[aStart] == '\n');
-
-      rv = ProcessHeaders();
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
-
-      if (mAuthenticating) {
-        Reset();
-        mStatus = CONN_RETRYING_TO_AUTHENTICATE;
-        return ProcessAuthentication();
-      }
-
-      if (mReadingProxyConnectResponse) {
-        if (mOwner->mSecure) {
-          rv = ProxyStartSSL();
-          NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
-        }
-
-        mBytesInBuffer = 0;
-        return DoInitialRequest();
-      }
-
-      mStatus = CONN_READING_CHALLENGE_RESPONSE;
-
-      mBuffer.Cut(0, aStart + 1);
-      mBytesInBuffer -= aStart + 1;
-      return HandleNewInputString(0);
-    }
-
-    case CONN_READING_CHALLENGE_RESPONSE:
-    {
-      NS_ENSURE_STATE(aStart == 0);
-
-      if (mBytesInBuffer < 16) {
-        return NS_OK;
-      }
-
-      const nsCSubstring& receivedMD5Challenge = Substring(mBuffer, 0, 16);
-      if (!mExpectedMD5Challenge.Equals(receivedMD5Challenge)) {
-        return NS_ERROR_UNEXPECTED;
-      }
-
-      mStatus = CONN_CONNECTED_AND_READY;
-      rv = Connected();
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
-
-      mBuffer.Cut(0, aStart + 16);
-      mBytesInBuffer -= aStart + 16;
-      return HandleNewInputString(0);
-    }
-
-    case CONN_CONNECTED_AND_READY:
-    {
-      NS_ENSURE_STATE(aStart == 0);
-      PRUint8 frameType = mBuffer[0];
-
-      if (IS_HIGH_BIT_OF_FRAME_TYPE_SET(frameType)) {
-        mStatus = CONN_HIGH_BIT_OF_FRAME_TYPE_SET;
-        mLengthToDiscard = 0;
-      } else {
-        mStatus = CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET;
-      }
-
-      return HandleNewInputString(1);
-    }
-    break;
-
-    case CONN_HIGH_BIT_OF_FRAME_TYPE_SET:
-    {
-      PRUint32 i;
-      PRUint8 frameType = mBuffer[0];
-      for (i = aStart; i < mBytesInBuffer; ++i) {
-        PRUint8 b, bv;
-        b = mBuffer[i];
-        bv = (b & 0x7f);
-
-        // prevent overflow
-        NS_ENSURE_STATE(mLengthToDiscard <= ((PR_UINT32_MAX - bv) / 128));
-
-        mLengthToDiscard = mLengthToDiscard * 128 + bv;
-
-        if (!IS_HIGH_BIT_OF_BYTE_SET(b)) {
-          // check if it is the close frame
-          if (mLengthToDiscard == 0 && frameType == START_BYTE_OF_CLOSE_FRAME) {
-            mBytesInBuffer = 0;
-            if (mSentCloseFrame) {
-              mClosedCleanly = PR_TRUE;
-              mStatus = CONN_CLOSED;
-            } else {
-              mStatus = CONN_SENDING_ACK_CLOSE_FRAME;
-            }
-            return Close();
-          }
-          FrameError();
-          mStatus = CONN_READING_AND_DISCARDING_LENGTH_BYTES;
-          return HandleNewInputString(i + 1);
-        }
-      }
-      mBytesInBuffer = 0;
-    }
-    break;
-
-    case CONN_READING_AND_DISCARDING_LENGTH_BYTES:
-    {
-      if (mBytesInBuffer - aStart >= mLengthToDiscard) {
-        mBuffer.Cut(0, aStart + mLengthToDiscard);
-        mBytesInBuffer -= aStart + mLengthToDiscard;
-
-        mStatus = CONN_CONNECTED_AND_READY;
-        return HandleNewInputString(0);
-      }
-
-      mLengthToDiscard -= mBytesInBuffer - aStart;
-      mBytesInBuffer = 0;
-    }
-    break;
-
-    case CONN_HIGH_BIT_OF_FRAME_TYPE_NOT_SET:
-    {
-      PRUint32 i;
-      for (i = aStart; i < mBytesInBuffer; ++i) {
-        PRUint8 b;
-        b = mBuffer[i];
-        if (b == END_BYTE_OF_MESSAGE) {
-          PRUint8 frameType = mBuffer[0];
-          if (frameType == START_BYTE_OF_MESSAGE) {
-            // get the message, without the START_BYTE_OF_MESSAGE and
-            // END_BYTE_OF_MESSAGE bytes
-            nsAutoPtr<nsCString> dataMessage(new nsCString());
-            NS_ENSURE_TRUE(dataMessage.get(), NS_ERROR_OUT_OF_MEMORY);
-            dataMessage->Assign(Substring(mBuffer, 1, i - 1));
-
-            // push the new message onto our stack
-            {
-              MutexAutoLock lockIn(mLockReceivedMessages);
-
-              PRInt32 sizeBefore = mReceivedMessages.GetSize();
-              mReceivedMessages.Push(dataMessage.forget());
-              NS_ENSURE_TRUE(mReceivedMessages.GetSize() == sizeBefore + 1,
-                             NS_ERROR_OUT_OF_MEMORY);
-            }
-
-            // and dispatch it
-            rv = DispatchNewMessage();
-            NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
-          } else {
-            FrameError();
-          }
-
-          mBuffer.Cut(0, i + 1);
-          mBytesInBuffer -= i + 1;
-
-          mStatus = CONN_CONNECTED_AND_READY;
-          return HandleNewInputString(0);
-        }
-      }
-    }
-    break;
-
-    default:
-      NS_ASSERTION(PR_FALSE, "Invalid state.");
-  }
+  UpdateMustKeepAlive();
+  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
-nsWebSocketEstablishedConnection::AddAuthorizationHeaders(nsCString& aStr,
-                                                          PRBool aIsProxyAuth)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
-
-  mAuthProvider->AddAuthorizationHeaders();
-
-  if (UsingHttpProxy() && !mProxyCredentials.IsEmpty()) {
-    aStr.AppendLiteral("Proxy-Authorization: ");
-    aStr.Append(mProxyCredentials);
-    aStr.AppendLiteral("\r\n");
-  }
-
-  if (!aIsProxyAuth && !mCredentials.IsEmpty()) {
-    aStr.AppendLiteral("Authorization: ");
-    aStr.Append(mCredentials);
-    aStr.AppendLiteral("\r\n");
-  }
-  return NS_OK;
-}
-
-nsresult
-nsWebSocketEstablishedConnection::AddCookiesToRequest(nsCString& aStr)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
-
-  // get the content of the cookie request header
-  nsCOMPtr<nsICookieService> cookieService =
-    do_GetService(NS_COOKIESERVICE_CONTRACTID);
-  nsCOMPtr<nsIDocument> doc =
-    nsContentUtils::GetDocumentFromScriptContext(mOwner->mScriptContext);
-
-  if (!cookieService || !doc) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIURI> documentURI = doc->GetDocumentURI();
-  if (!documentURI) {
-    return NS_OK;
-  }
-
-  nsXPIDLCString cookieValue;
-  cookieService->GetCookieStringFromHttp(documentURI,
-                                         documentURI,
-                                         nsnull,
-                                         getter_Copies(cookieValue));
-  if (!cookieValue.IsEmpty()) {
-    aStr.AppendLiteral("Cookie: ");
-    aStr.Append(cookieValue);
-    aStr.AppendLiteral("\r\n");
-  }
-
-  return NS_OK;
-}
-
-PRUint32
-nsWebSocketEstablishedConnection::GenerateSecKey(nsCString& aKey)
-{
-  PRUint32 i;
-
-  PRUint32 spaces = rand() % 12 + 1;
-  PRUint32 max = PR_UINT32_MAX / spaces;
-  PRUint32 number = rand() % max;
-  PRUint32 product = number * spaces;
-
-  nsCAutoString key;
-
-  key.AppendInt(product);
-
-  // Insert between one and twelve random characters from the ranges
-  // U+0021 to U+002F and U+003A to U+007E into the key at random
-  // positions.
-  PRUint32 numberOfCharsToInsert = rand() % 12 + 1;
-  for (i = 0; i < numberOfCharsToInsert; ++i) {
-    PRUint32 posToInsert = rand() % key.Length();
-    char charToInsert =
-      rand() % 2 == 0 ?
-        static_cast<char>(0x21 + (rand() % (0x2F - 0x21 + 1))) :
-        static_cast<char>(0x3A + (rand() % (0x7E - 0x3A + 1)));
-
-    key.Insert(charToInsert, posToInsert);
-  }
-
-  // Insert /spaces/ U+0020 SPACE characters into the key at random
-  // positions other than the start or end of the string.
-  for (i = 0; i < spaces; ++i) {
-    PRUint32 posToInsert = rand() % (key.Length() - 1) + 1;
-    key.Insert(static_cast<char>(0x20), posToInsert);
-  }
-
-  aKey = key;
-  return number;
-}
-
-nsresult
-nsWebSocketEstablishedConnection::GenerateRequestKeys(nsCString& aKey1,
-                                                      nsCString& aKey2,
-                                                      nsCString& aKey3)
-{
-  nsresult rv;
-  PRUint32 i;
-
-  nsCAutoString key_1;
-  PRUint32 number_1;
-
-  nsCAutoString key_2;
-  PRUint32 number_2;
-
-  // generate the sec-keys headers values
-  number_1 = GenerateSecKey(key_1);
-  number_2 = GenerateSecKey(key_2);
-
-  // key3 must be a string consisting of eight random bytes
-  nsCAutoString key_3;
-  for (i = 0; i < 8; ++i) {
-    // get a byte between 1 and 255. 0x00 was discarted to prevent possible
-    // issues in ws servers.
-    key_3 += static_cast<char>(rand() % 0xff + 1);
-  }
-
-  // since we have the keys, we calculate the server md5 challenge response,
-  // which is the md5 string of the concatenation of /number_1/, expressed as
-  // a big-endian 32 bit integer, /number_2/, expressed as a big-endian
-  // 32 bit integer, and the eight bytes of /key_3/
-
-  nsCOMPtr<nsICryptoHash> md5CryptoHash =
-    do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = md5CryptoHash->Init(nsICryptoHash::MD5);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  PRUint8 data[16];
-  for (i = 1; i <= 4; ++i) {
-    data[i - 1] = static_cast<PRUint8>(number_1 >> (32 - i * 8));
-  }
-  for (i = 1; i <= 4; ++i) {
-    data[i + 3] = static_cast<PRUint8>(number_2 >> (32 - i * 8));
-  }
-  for (i = 0; i < 8; ++i) {
-    data[i + 8] = static_cast<PRUint8>(key_3[i]);
-  }
-
-  rv = md5CryptoHash->Update(data, 16);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = md5CryptoHash->Finish(PR_FALSE, mExpectedMD5Challenge);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  aKey1 = key_1;
-  aKey2 = key_2;
-  aKey3 = key_3;
-
-  return NS_OK;
-}
-
-PRBool
-nsWebSocketEstablishedConnection::UsingHttpProxy()
-{
-  if (!mProxyInfo) {
-    return PR_FALSE;
-  }
-
-  nsCAutoString proxyType;
-  mProxyInfo->GetType(proxyType);
-  return proxyType.EqualsLiteral("http");
-}
-
-// it is called by both main and socket threads under the mLockDisconnect
-nsresult
-nsWebSocketEstablishedConnection::Reset()
-{
-  RemoveWSConnecting();
-
-  mStatus = CONN_NOT_CONNECTED;
-
-  if (mSocketTransport) {
-    mSocketTransport->Close(NS_OK);
-    mSocketTransport = nsnull;
-  }
-  mSocketInput = nsnull;
-  mSocketOutput = nsnull;
-
-  while (mOutgoingMessages.GetSize() != 0) {
-    delete static_cast<nsWSFrame*>(mOutgoingMessages.PopFront());
-  }
-
-  while (mReceivedMessages.GetSize() != 0) {
-    delete static_cast<nsCString*>(mReceivedMessages.PopFront());
-  }
-
-  mBytesAlreadySentOfFirstOutString = 0;
-  mBytesInBuffer = 0;
-
-  return NS_OK;
-}
-
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(Connected)
-{
-  RemoveWSConnecting();
-
-  if (mAuthProvider) {
-    mAuthProvider->Disconnect(NS_ERROR_ABORT);
-    mAuthProvider = nsnull;
-  }
-
-  mOwner->SetReadyState(nsIWebSocket::OPEN);
-}
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
-
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(FrameError)
-{
-  nsresult rv =
-    mOwner->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Failed to dispatch the error event");
-  }
-}
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
-
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(DispatchNewMessage)
-{
-  nsresult rv;
-
-  while (PR_TRUE) {
-    nsAutoPtr<nsCString> data;
-
-    {
-      MutexAutoLock lockIn(mLockReceivedMessages);
-
-      if (mReceivedMessages.GetSize() == 0) {
-        return;
-      }
-
-      data = static_cast<nsCString*>(mReceivedMessages.PopFront());
-    }
-
-    rv = mOwner->CreateAndDispatchMessageEvent(data);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to dispatch the message event");
-    }
-  }
-}
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
-
-nsresult
-nsWebSocketEstablishedConnection::ProxyStartSSL()
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
-
-  nsCOMPtr<nsISupports> securityInfo;
-  nsresult rv = mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return ssl->ProxyStartSSL();
-}
-
-nsresult
 nsWebSocketEstablishedConnection::Init(nsWebSocket *aOwner)
 {
-  // test if it has been alredy initialized
-  NS_ASSERTION(!mOwner, "WebSocket's connection is already initialized");
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
+  NS_ABORT_IF_FALSE(!mOwner, "WebSocket's connection is already initialized");
 
   nsresult rv;
-
   mOwner = aOwner;
 
   if (mOwner->mSecure) {
-    // HACK: make sure PSM gets initialized on the main thread.
-    nsCOMPtr<nsISocketProviderService> spserv =
-      do_GetService(NS_SOCKETPROVIDERSERVICE_CONTRACTID);
-    NS_ENSURE_STATE(spserv);
-
-    nsCOMPtr<nsISocketProvider> provider;
-    rv = spserv->GetSocketProvider("ssl", getter_AddRefs(provider));
-    NS_ENSURE_SUCCESS(rv, rv);
+    mWebSocketProtocol =
+      do_CreateInstance("@mozilla.org/network/protocol;1?name=wss", &rv);
   }
-
-  mTryConnectTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+  else {
+    mWebSocketProtocol =
+      do_CreateInstance("@mozilla.org/network/protocol;1?name=ws", &rv);
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  rv = mWebSocketProtocol->SetNotificationCallbacks(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mInitialServerResponseTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // add ourselves to the document's load group
+  // add ourselves to the document's load group and
+  // provide the http stack the loadgroup info too
   nsCOMPtr<nsILoadGroup> loadGroup;
   rv = GetLoadGroup(getter_AddRefs(loadGroup));
-  NS_ENSURE_SUCCESS(rv, rv);
   if (loadGroup) {
+    rv = mWebSocketProtocol->SetLoadGroup(loadGroup);
+    NS_ENSURE_SUCCESS(rv, rv);
     rv = loadGroup->AddRequest(this, nsnull);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  mAuthProvider =
-    do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1",
-                      &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = mAuthProvider->Init(this);
+  if (!mOwner->mProtocol.IsEmpty())
+    rv = mWebSocketProtocol->SetProtocol(mOwner->mProtocol);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  CopyUTF16toUTF8(mOwner->mOriginalURL, mRequestName);
-
-  if (!sWSsConnecting) {
-    sWSsConnecting =
-      new nsTArray<nsRefPtr<nsWebSocketEstablishedConnection> >();
-    ENSURE_TRUE_AND_FAIL_IF_FAILED(sWSsConnecting, NS_ERROR_OUT_OF_MEMORY);
-  }
-
-  if (!gWebSocketThread) {
-    rv = NS_NewThread(&gWebSocketThread);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  rv = ResolveNextProxyAndConnect();
+  nsCString utf8Origin;
+  CopyUTF16toUTF8(mOwner->mUTF16Origin, utf8Origin);
+  rv = mWebSocketProtocol->AsyncOpen(mOwner->mURI,
+                                     utf8Origin, this, nsnull);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
-nsWebSocketEstablishedConnection::DoConnect()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
-
-  nsresult rv;
-
-  rv = AddWSConnecting();
-  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-
-  mStatus = CONN_CONNECTING;
-
-  nsCOMPtr<nsISocketTransportService> sts =
-    do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
-  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-
-  // configure the socket type based on the connection type requested.
-  const char* types[1];
-  nsAdoptingCString value =
-    nsContentUtils::GetCharPref("network.http.default-socket-type");
-
-  if (mOwner->mSecure) {
-    types[0] = "ssl";
-  } else {
-    if (value.IsEmpty()) {
-      types[0] = nsnull;
-    } else {
-      types[0] = value.get();
-    }
-  }
-
-  nsCOMPtr<nsISocketTransport> strans;
-  PRUint32 typeCount = (types[0] != nsnull ? 1 : 0);
-
-  rv = sts->CreateTransport(types, typeCount, mOwner->mAsciiHost, mOwner->mPort,
-                            mProxyInfo, getter_AddRefs(strans));
-  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-
-  rv = strans->SetSecurityCallbacks(this);
-  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-
-  nsCOMPtr<nsIOutputStream> outStream;
-  rv = strans->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
-                                getter_AddRefs(outStream));
-  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-
-  nsCOMPtr<nsIInputStream> inStream;
-  rv = strans->OpenInputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
-                               getter_AddRefs(inStream));
-  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-
-  mSocketTransport = strans;
-  mSocketInput = do_QueryInterface(inStream);
-  mSocketOutput = do_QueryInterface(outStream);
-  mProxyResolveCancelable = nsnull;
-
-  if (!UsingHttpProxy()) {
-    rv = DoInitialRequest();
-    ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-    return NS_OK;
-  }
-
-  nsAutoPtr<nsCString> buf(new nsCString());
-  ENSURE_TRUE_AND_FAIL_IF_FAILED(buf.get(), NS_ERROR_OUT_OF_MEMORY);
-
-  nsString strRequestTmp;
-
-  // CONNECT host:port HTTP/1.1
-  buf->AppendLiteral("CONNECT ");
-  buf->Append(mOwner->mAsciiHost);
-  buf->AppendLiteral(":");
-  buf->AppendInt(mOwner->mPort);
-  buf->AppendLiteral(" HTTP/1.1\r\n");
-
-  // Host
-  // all HTTP/1.1 requests must include a Host header (even though it
-  // may seem redundant in this case; see bug 82388).
-  buf->AppendLiteral("Host: ");
-  buf->Append(mOwner->mAsciiHost);
-  buf->AppendLiteral(":");
-  buf->AppendInt(mOwner->mPort);
-  buf->AppendLiteral("\r\n");
-
-  // Proxy Authorization
-  rv = AddAuthorizationHeaders(*buf, PR_TRUE);
-  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-
-  buf->AppendLiteral("\r\n");
-
-  mStatus = CONN_CONNECTING_TO_HTTP_PROXY;
-
-  rv = PostData(buf.forget(), eConnectFrame);
-  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-
-  return NS_OK;
-}
-
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(AddWSConnecting)
-{
-#ifdef DEBUG
-  PRUint32 index =
-    sWSsConnecting->BinaryIndexOf(this, nsWSNetAddressComparator());
-  NS_ASSERTION(index == nsTArray<PRNetAddr>::NoIndex,
-               "The ws connection shouldn't be already added in the "
-               "serialization list.");
-  bool inserted = !!
-#endif
-  sWSsConnecting->InsertElementSorted(this, nsWSNetAddressComparator());
-  NS_ASSERTION(inserted, "Couldn't insert the ws connection into the "
-                         "serialization list.");
-}
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
-
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(RemoveWSConnecting)
-{
-  if (mStatus == CONN_NOT_CONNECTED) {
-    return;
-  }
-  PRUint32 index =
-    sWSsConnecting->BinaryIndexOf(this, nsWSNetAddressComparator());
-  if (index != nsTArray<PRNetAddr>::NoIndex) {
-    sWSsConnecting->RemoveElementAt(index);
-  }
-}
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
-
-// static
-void
-nsWebSocketEstablishedConnection::TryConnect(nsITimer* aTimer,
-                                             void* aClosure)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
-
-  nsresult rv;
-  nsRefPtr<nsWebSocketEstablishedConnection> thisObject =
-    static_cast<nsWebSocketEstablishedConnection*>(aClosure);
-
-  if (!thisObject->mOwner) { // we have been disconnected
-    return;
-  }
-
-  PRUint32 index = sWSsConnecting->BinaryIndexOf(thisObject,
-                                                 nsWSNetAddressComparator());
-  if (index != nsTArray<PRNetAddr>::NoIndex) {
-    // try again after TIMEOUT_TRY_CONNECT_AGAIN second
-    rv = thisObject->mTryConnectTimer->
-      InitWithFuncCallback(TryConnect, thisObject,
-                           TIMEOUT_TRY_CONNECT_AGAIN, nsITimer::TYPE_ONE_SHOT);
-    CHECK_SUCCESS_AND_FAIL_IF_FAILED2(rv);
-  } else {
-    rv = thisObject->DoConnect();
-    CHECK_SUCCESS_AND_FAIL_IF_FAILED2(rv);
-  }
-}
-
-// static
-void
-nsWebSocketEstablishedConnection::
-  TimerInitialServerResponseCallback(nsITimer* aTimer,
-                                     void* aClosure)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
-
-  nsRefPtr<nsWebSocketEstablishedConnection> thisObject =
-    static_cast<nsWebSocketEstablishedConnection*>(aClosure);
-
-  if (!thisObject->mOwner) { // we have been disconnected
-    return;
-  }
-
-  thisObject->FailConnection();
-}
-
-// static
-void
-nsWebSocketEstablishedConnection::TimerForceCloseCallback(nsITimer* aTimer,
-                                                          void* aClosure)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
-
-  nsRefPtr<nsWebSocketEstablishedConnection> thisObject =
-    static_cast<nsWebSocketEstablishedConnection*>(aClosure);
-
-  if (!thisObject->mOwner) { // we have been disconnected
-    return;
-  }
-
-  thisObject->ForceClose();
-}
-
-nsresult
-nsWebSocketEstablishedConnection::ProcessHeaders()
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
-
-  nsresult rv;
-
-  if (mAuthenticating) {
-    if (mHeaders[kProxyAuthenticatePos].IsEmpty())
-      return NS_ERROR_UNEXPECTED;
-    return NS_OK;
-  }
-
-  if (mReadingProxyConnectResponse) {
-    return NS_OK;
-  }
-
-  // test the upgrade header
-
-  if (!mHeaders[kUpgradePos].EqualsLiteral("WebSocket")) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  // test the connection header
-
-  if (!mHeaders[kConnectionPos].LowerCaseEqualsLiteral("upgrade")) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  // test the sec-websocket-origin header
-
-  nsCString responseOriginHeader = mHeaders[kSecWebSocketOriginPos];
-  ToLowerCase(responseOriginHeader);
-
-  if (!responseOriginHeader.Equals(mOwner->mOrigin)) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  // test the sec-websocket-location header
-
-  nsCString validWebSocketLocation1, validWebSocketLocation2;
-  validWebSocketLocation1.Append(mOwner->mSecure ? "wss://" : "ws://");
-  validWebSocketLocation1.Append(mOwner->mAsciiHost);
-  validWebSocketLocation1.Append(":");
-  validWebSocketLocation1.AppendInt(mOwner->mPort);
-  validWebSocketLocation1.Append(mOwner->mResource);
-
-  if ((mOwner->mSecure && mOwner->mPort != DEFAULT_WSS_SCHEME_PORT) ||
-      (!mOwner->mSecure && mOwner->mPort != DEFAULT_WS_SCHEME_PORT)) {
-    validWebSocketLocation2 = validWebSocketLocation1;
-  } else {
-    validWebSocketLocation2.Append(mOwner->mSecure ? "wss://" : "ws://");
-    validWebSocketLocation2.Append(mOwner->mAsciiHost);
-    validWebSocketLocation2.Append(mOwner->mResource);
-  }
-
-  if (!mHeaders[kSecWebSocketLocationPos].Equals(validWebSocketLocation1) &&
-      !mHeaders[kSecWebSocketLocationPos].Equals(validWebSocketLocation2)) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  // handle the sec-websocket-protocol header
-  if (!mOwner->mProtocol.IsEmpty() &&
-      !mHeaders[kSecWebSocketProtocolPos].
-        Equals(mOwner->mProtocol)) {
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  // handle the set-cookie header
-
-  if (!mHeaders[kSetCookiePos].IsEmpty()) {
-    rv = HandleSetCookieHeader();
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  return NS_OK;
-}
-
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(HandleSetCookieHeader)
-{
-  nsresult rv;
-
-  nsCOMPtr<nsICookieService> cookieService =
-    do_GetService(NS_COOKIESERVICE_CONTRACTID);
-  nsCOMPtr<nsIDocument> doc =
-    nsContentUtils::GetDocumentFromScriptContext(mOwner->mScriptContext);
-
-  if (!cookieService || !doc) {
-    return;
-  }
-
-  nsCOMPtr<nsIURI> documentURI = doc->GetDocumentURI();
-  if (!documentURI) {
-    return;
-  }
-
-  nsCOMPtr<nsIPromptFactory> wwatch =
-    do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
-  CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
-
-  nsCOMPtr<nsIPrompt> prompt;
-  nsCOMPtr<nsPIDOMWindow> outerWindow = doc->GetWindow();
-  rv = wwatch->GetPrompt(outerWindow, NS_GET_IID(nsIPrompt),
-                         getter_AddRefs(prompt));
-  CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
-
-  rv = cookieService->SetCookieStringFromHttp(documentURI,
-                                              documentURI,
-                                              prompt,
-                                              mHeaders[kSetCookiePos].get(),
-                                              nsnull,
-                                              nsnull);
-  CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
-}
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
-
-nsresult
 nsWebSocketEstablishedConnection::PrintErrorOnConsole(const char *aBundleURI,
                                                       const PRUnichar *aError,
                                                       const PRUnichar **aFormatStrings,
                                                       PRUint32 aFormatStringsLen)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
   nsresult rv;
 
   nsCOMPtr<nsIStringBundleService> bundleService =
     do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIStringBundle> strBundle;
@@ -1985,864 +354,227 @@ nsWebSocketEstablishedConnection::PrintE
   // print the error message directly to the JS console
   nsCOMPtr<nsIScriptError> logError(do_QueryInterface(errorObject));
   rv = console->LogMessage(logError);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(Close)
+// when this is called the browser side wants no more part of it
+nsresult
+nsWebSocketEstablishedConnection::Close()
 {
-  nsresult rv;
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
   // Disconnect() can release this object, so we keep a
   // reference until the end of the method
   nsRefPtr<nsWebSocketEstablishedConnection> kungfuDeathGrip = this;
 
   if (mOwner->mReadyState == nsIWebSocket::CONNECTING) {
     mOwner->SetReadyState(nsIWebSocket::CLOSING);
     mOwner->SetReadyState(nsIWebSocket::CLOSED);
     Disconnect();
-    return;
+    return NS_OK;
   }
 
   mOwner->SetReadyState(nsIWebSocket::CLOSING);
-  if (!mCloseFrameServerResponseTimer) {
-    mCloseFrameServerResponseTimer =
-      do_CreateInstance("@mozilla.org/timer;1", &rv);
-
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to create mCloseFrameServerResponseTimer.");
-    } else {
-      rv = mCloseFrameServerResponseTimer->
-        InitWithFuncCallback(TimerForceCloseCallback, this,
-                             TIMEOUT_WAIT_FOR_CLOSING, nsITimer::TYPE_ONE_SHOT);
-      if (NS_FAILED(rv)) {
-        NS_WARNING("Failed to start the ForceClose timeout.");
-      }
-    }
-  }
 
   if (mStatus == CONN_CLOSED) {
     mOwner->SetReadyState(nsIWebSocket::CLOSED);
     Disconnect();
-  } else if (!mPostedCloseFrame) {
-    nsAutoPtr<nsCString> closeFrame(new nsCString());
-    if (!closeFrame.get()) {
-      return;
-    }
-
-    closeFrame->SetLength(2);
-    if (closeFrame->Length() != 2) {
-      return;
-    }
-
-    closeFrame->SetCharAt(START_BYTE_OF_CLOSE_FRAME, 0);
-    closeFrame->SetCharAt(END_BYTE_OF_CLOSE_FRAME, 1);
+    return NS_OK;
+  }
 
-    rv = PostData(closeFrame.forget(), eCloseFrame);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to post the close frame");
-      return;
-    }
-  } else {
-    // Probably failed to send the close frame. Just disconnect.
-    Disconnect();
-  }
-}
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
-
-void
-nsWebSocketEstablishedConnection::ForceClose()
-{
-  // Disconnect() can release this object, so we keep a
-  // reference until the end of the method
-  nsRefPtr<nsWebSocketEstablishedConnection> kungfuDeathGrip = this;
-
-  mOwner->SetReadyState(nsIWebSocket::CLOSING);
-  mOwner->SetReadyState(nsIWebSocket::CLOSED);
-  Disconnect();
+  return mWebSocketProtocol->Close();
 }
 
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(FailConnection)
+nsresult
+nsWebSocketEstablishedConnection::ConsoleError()
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv;
-  nsWSAutoClose autoClose(this);
-
-  if (mFailureStatus == NS_OK) {
-    mFailureStatus = NS_ERROR_UNEXPECTED;
-  }
-
+  if (!mOwner) return NS_OK;
+  
   nsCAutoString targetSpec;
   rv = mOwner->mURI->GetSpec(targetSpec);
-  WARN_IF_FALSE_AND_RETURN(NS_SUCCEEDED(rv), "Failed to get targetSpec");
-
-  NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
-  const PRUnichar *formatStrings[] = { specUTF16.get() };
-
-  if (mStatus < CONN_CONNECTED_AND_READY) {
-    if (mCurrentProxyConfig == eResolvingProxyFailed) {
-      PrintErrorOnConsole("chrome://browser/locale/appstrings.properties",
-                          NS_LITERAL_STRING("proxyConnectFailure").get(),
-                          nsnull, 0);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to get targetSpec");
+  } else {
+    NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
+    const PRUnichar *formatStrings[] = { specUTF16.get() };
+    
+    if (mStatus < CONN_CONNECTED_AND_READY) {
+      PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
+                          NS_LITERAL_STRING("connectionFailure").get(),
+                          formatStrings, NS_ARRAY_LENGTH(formatStrings));
+    } else {
+      PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
+                          NS_LITERAL_STRING("netInterrupt").get(),
+                          formatStrings, NS_ARRAY_LENGTH(formatStrings));
     }
-    PrintErrorOnConsole("chrome://browser/locale/appstrings.properties",
-                        NS_LITERAL_STRING("connectionFailure").get(),
-                        formatStrings, NS_ARRAY_LENGTH(formatStrings));
-  } else {
-    PrintErrorOnConsole("chrome://browser/locale/appstrings.properties",
-                        NS_LITERAL_STRING("netInterrupt").get(),
-                        formatStrings, NS_ARRAY_LENGTH(formatStrings));
   }
+  /// todo some sepcific errors - like for message too large
+  return rv;
 }
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
+
+
+nsresult
+nsWebSocketEstablishedConnection::FailConnection()
+{
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
+  nsresult rv = ConsoleError();
+  Close();
+  return rv;
+}
 
 nsresult
 nsWebSocketEstablishedConnection::Disconnect()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
-
-  {
-    if (!mOwner) {
-      return NS_OK;
-    }
- 
-    MutexAutoLock lockDisconnect(mLockDisconnect);
-
-    // If mOwner is deleted when calling mOwner->DontKeepAliveAnyMore()
-    // then this method can be called again, and we will get a deadlock.
-    nsRefPtr<nsWebSocket> kungfuDeathGrip = mOwner;
-
-    mOwner->DontKeepAliveAnyMore();
-
-    RemoveWSConnecting();
-
-    mStatus = CONN_CLOSED;
-    mOwner = nsnull;
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
-    if (mAuthProvider) {
-      mAuthProvider->Disconnect(NS_ERROR_ABORT);
-      mAuthProvider = nsnull;
-    }
-
-    if (mTryConnectTimer) {
-      mTryConnectTimer->Cancel();
-      mTryConnectTimer = nsnull;
-    }
-
-    if (mInitialServerResponseTimer) {
-      mInitialServerResponseTimer->Cancel();
-      mInitialServerResponseTimer = nsnull;
-    }
-
-    if (mCloseFrameServerResponseTimer) {
-      mCloseFrameServerResponseTimer->Cancel();
-      mCloseFrameServerResponseTimer = nsnull;
-    }
+  if (!mOwner) {
+    return NS_OK;
+  }
+  
+  nsCOMPtr<nsILoadGroup> loadGroup;
+  GetLoadGroup(getter_AddRefs(loadGroup));
+  if (loadGroup)
+    loadGroup->RemoveRequest(this, nsnull, NS_OK);
 
-    if (mProxyResolveCancelable) {
-      mProxyResolveCancelable->Cancel(NS_ERROR_ABORT);
-      mProxyResolveCancelable = nsnull;
-    }
-
-    if (mDNSRequest) {
-      mDNSRequest->Cancel(NS_ERROR_ABORT);
-      mDNSRequest = nsnull;
-    }
+  // If mOwner is deleted when calling mOwner->DontKeepAliveAnyMore()
+  // then this method can be called again, and we will get a deadlock.
+  nsRefPtr<nsWebSocket> kungfuDeathGrip = mOwner;
+  
+  mOwner->DontKeepAliveAnyMore();
+  mStatus = CONN_CLOSED;
+  mOwner = nsnull;
+  mWebSocketProtocol = nsnull;
 
-    if (mSocketInput) {
-      mSocketInput->Close();
-      mSocketInput = nsnull;
-    }
-    if (mSocketOutput) {
-      mSocketOutput->Close();
-      mSocketOutput = nsnull;
-    }
-    if (mSocketTransport) {
-      mSocketTransport->Close(NS_OK);
-      mSocketTransport = nsnull;
-    }
-    mProxyInfo = nsnull;
-
-    while (mOutgoingMessages.GetSize() != 0) {
-      delete static_cast<nsWSFrame*>(mOutgoingMessages.PopFront());
-    }
-
-    while (mReceivedMessages.GetSize() != 0) {
-      delete static_cast<nsCString*>(mReceivedMessages.PopFront());
-    }
-
-    // Remove ourselves from the document's load group. nsIRequest expects
-    // this be done asynchronously.
-    nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(this, &nsWebSocketEstablishedConnection::
-                                  RemoveFromLoadGroup);
-    if (event) {
-      NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
-    }
-
-    nsLayoutStatics::Release();
-  }
-
+  nsLayoutStatics::Release();
   return NS_OK;
 }
 
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(Retry)
-{
-  nsresult rv;
-
-  for (PRUint32 i = 0; i < kHeadersLen; ++i) {
-    mHeaders[i].Truncate();
-  }
-
-  rv = OnProxyAvailable(nsnull, mOwner->mURI, mProxyInfo, NS_OK);
-  CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
-}
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
-
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(ResolveNextProxyAndConnect)
-{
-  nsresult rv;
-
-  if (mCurrentProxyConfig == eResolvingProxyFailed) {
-    return;
-  }
-
-  nsCOMPtr<nsIProtocolProxyService2> proxyService =
-    do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Failed getting proxyService");
-    mCurrentProxyConfig = eResolvingProxyFailed;
-    mFailureStatus = NS_ERROR_UNKNOWN_PROXY_HOST;
-    FailConnection();
-    return;
-  }
-
-  if (mProxyInfo) {
-    // If there was already a proxy info it means we tried to connect to its
-    // proxy, but we couldn't. We have to remove the connection from the
-    // serialization list, reset it, and try to get a failover proxy.
-
-    {
-      MutexAutoLock lockDisconnect(mLockDisconnect);
-      rv = Reset();
-      CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
-    }
-
-    nsCOMPtr<nsIProxyInfo> pi;
-    rv = proxyService->GetFailoverForProxy(mProxyInfo, mOwner->mURI,
-                                           mProxyFailureReason,
-                                           getter_AddRefs(pi));
-    if (NS_FAILED(rv)) {
-      mProxyInfo = nsnull;
-      ResolveNextProxyAndConnect();
-      return;
-    }
-
-    OnProxyAvailable(nsnull, mOwner->mURI, pi, NS_OK);
-    return;
-  }
-
-  // if (!mProxyInfo)
-
-  PRUint32 flags = nsIProtocolProxyService::RESOLVE_IGNORE_URI_SCHEME;
-
-  if (mCurrentProxyConfig == eNotResolvingProxy) {
-    mCurrentProxyConfig = eResolvingSOCKSProxy;
-    flags |= nsIProtocolProxyService::RESOLVE_PREFER_SOCKS_PROXY;
-  } else if (mCurrentProxyConfig == eResolvingSOCKSProxy) {
-    flags |= nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY;
-    mCurrentProxyConfig = eResolvingHTTPSProxy;
-  } else if (mCurrentProxyConfig == eResolvingHTTPSProxy) {
-    mCurrentProxyConfig = eResolvingHTTPProxy;
-  } else if (mCurrentProxyConfig == eResolvingHTTPProxy) {
-    mCurrentProxyConfig = eResolvingProxyFailed;
-    mFailureStatus = NS_ERROR_UNKNOWN_PROXY_HOST;
-    FailConnection();
-    return;
-  }
-
-  rv = proxyService->AsyncResolve(mOwner->mURI,
-                                  flags, this,
-                                  getter_AddRefs(mProxyResolveCancelable));
-  if (NS_FAILED(rv)) {
-    ResolveNextProxyAndConnect();
-    return;
-  }
-}
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
-
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(UpdateMustKeepAlive)
-{
-  mOwner->UpdateMustKeepAlive();
-}
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
-
-void
-nsWebSocketEstablishedConnection::RemoveFromLoadGroup()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
-  nsCOMPtr<nsILoadGroup> loadGroup;
-  GetLoadGroup(getter_AddRefs(loadGroup));
-  if (loadGroup) {
-    loadGroup->RemoveRequest(this, nsnull, NS_OK);
-  }
-}
-
-//-----------------------------------------------------------------------------
-// Authentication
-//-----------------------------------------------------------------------------
-
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(ProcessAuthentication)
+nsresult
+nsWebSocketEstablishedConnection::UpdateMustKeepAlive()
 {
-  nsresult rv = mAuthProvider->ProcessAuthentication(407, PR_FALSE);
-
-  if (rv == NS_ERROR_IN_PROGRESS) {
-    return;
-  }
-
-  if (NS_FAILED(rv)) {
-    NS_WARNING("ProcessAuthentication failed");
-    FailConnection();
-    return;
-  }
-
-  Retry();
-}
-IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-
-#define NOT_IMPLEMENTED_IF_FUNC_BEGIN(_func)                                   \
-  NS_IMETHODIMP                                                                \
-  nsWebSocketEstablishedConnection::_func
-
-#define NOT_IMPLEMENTED_IF_FUNC_END(_func)                                     \
-  {                                                                            \
-    return NS_ERROR_NOT_IMPLEMENTED;                                           \
-  }
-
-#define NOT_IMPLEMENTED_IF_FUNC_0(_func)                                       \
-  NOT_IMPLEMENTED_IF_FUNC_BEGIN(_func) ()                                      \
-  NOT_IMPLEMENTED_IF_FUNC_END(_func)
-
-#define NOT_IMPLEMENTED_IF_FUNC_1(_func, _arg)                                 \
-  NOT_IMPLEMENTED_IF_FUNC_BEGIN(_func) (_arg)                                  \
-  NOT_IMPLEMENTED_IF_FUNC_END(_func)
-
-#define NOT_IMPLEMENTED_IF_FUNC_2(_func, _arg1, arg2)                          \
-  NOT_IMPLEMENTED_IF_FUNC_BEGIN(_func) (_arg1, arg2)                           \
-  NOT_IMPLEMENTED_IF_FUNC_END(_func)
-
-//-----------------------------------------------------------------------------
-// nsWebSocketEstablishedConnection::nsIRequest
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::GetName(nsACString &aName)
-{
-  aName = mRequestName;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::IsPending(PRBool *aValue)
-{
-  *aValue = !!(mOwner);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::GetStatus(nsresult *aStatus)
-{
-  *aStatus = mFailureStatus;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::Cancel(nsresult aStatus)
-{
-  if (!mOwner) {
-    return NS_OK;
-  }
-
-  mFailureStatus = aStatus;
-
-  return Close();
-}
-
-NOT_IMPLEMENTED_IF_FUNC_0(Suspend)
-NOT_IMPLEMENTED_IF_FUNC_0(Resume)
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::GetLoadGroup(nsILoadGroup **aLoadGroup)
-{
-  *aLoadGroup = nsnull;
-  if (!mOwner)
-    return NS_OK;
-
-  nsCOMPtr<nsIDocument> doc =
-    nsContentUtils::GetDocumentFromScriptContext(mOwner->mScriptContext);
-
-  if (doc) {
-    *aLoadGroup = doc->GetDocumentLoadGroup().get();  // already_AddRefed
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::SetLoadGroup(nsILoadGroup *aLoadGroup)
-{
-  return NS_ERROR_UNEXPECTED;
-}
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::GetLoadFlags(nsLoadFlags *aLoadFlags)
-{
-  *aLoadFlags = nsIRequest::LOAD_BACKGROUND;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::SetLoadFlags(nsLoadFlags aLoadFlags)
-{
-  // we won't change the load flags at all.
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
+  mOwner->UpdateMustKeepAlive();
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
-// nsWebSocketEstablishedConnection::nsIChannel
-//-----------------------------------------------------------------------------
-
-NOT_IMPLEMENTED_IF_FUNC_1(GetOriginalURI, nsIURI **originalURI)
-NOT_IMPLEMENTED_IF_FUNC_1(SetOriginalURI, nsIURI *originalURI)
-NOT_IMPLEMENTED_IF_FUNC_1(GetOwner, nsISupports **owner)
-NOT_IMPLEMENTED_IF_FUNC_1(SetOwner, nsISupports *owner)
-NOT_IMPLEMENTED_IF_FUNC_1(SetNotificationCallbacks,
-                          nsIInterfaceRequestor *callbacks)
-NOT_IMPLEMENTED_IF_FUNC_1(GetSecurityInfo, nsISupports **securityInfo)
-NOT_IMPLEMENTED_IF_FUNC_1(GetContentType, nsACString &value)
-NOT_IMPLEMENTED_IF_FUNC_1(SetContentType, const nsACString &value)
-NOT_IMPLEMENTED_IF_FUNC_1(GetContentCharset, nsACString &value)
-NOT_IMPLEMENTED_IF_FUNC_1(SetContentCharset, const nsACString &value)
-NOT_IMPLEMENTED_IF_FUNC_1(GetContentLength, PRInt32 *value)
-NOT_IMPLEMENTED_IF_FUNC_1(SetContentLength, PRInt32 value)
-NOT_IMPLEMENTED_IF_FUNC_1(Open, nsIInputStream **_retval)
-NOT_IMPLEMENTED_IF_FUNC_2(AsyncOpen, nsIStreamListener *listener,
-                          nsISupports *context)
-
-//-----------------------------------------------------------------------------
-// nsWebSocketEstablishedConnection::nsIHttpAuthenticableChannel
+// nsWebSocketEstablishedConnection::nsIWebSocketListener methods:
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
-nsWebSocketEstablishedConnection::GetProxyInfo(nsIProxyInfo **result)
-{
-  NS_IF_ADDREF(*result = mProxyInfo);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::GetIsSSL(PRBool *aIsSSL)
-{
-  *aIsSSL = mOwner->mSecure;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::GetProxyMethodIsConnect(PRBool *aProxyMethodIsConnect)
+nsWebSocketEstablishedConnection::OnMessageAvailable(nsISupports *aContext,
+                                                     const nsACString & aMsg)
 {
-  *aProxyMethodIsConnect = UsingHttpProxy();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::GetURI(nsIURI **aURI)
-{
-  NS_IF_ADDREF(*aURI = mOwner->mURI);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::GetNotificationCallbacks(nsIInterfaceRequestor **callbacks)
-{
-  NS_ADDREF(*callbacks = this);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::GetRequestMethod(nsACString &method)
-{
-  if (mAuthenticating) {
-    method.AssignLiteral("CONNECT");
-  } else {
-    method.AssignLiteral("GET");
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
+  if (!mOwner)
+    return NS_ERROR_NOT_AVAILABLE;
+  
+  // Dispatch New Message
+  nsresult rv = mOwner->CreateAndDispatchMessageEvent(aMsg);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch the message event");
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWebSocketEstablishedConnection::GetServerResponseHeader(nsACString &value)
+nsWebSocketEstablishedConnection::OnBinaryMessageAvailable(
+  nsISupports *aContext,
+  const nsACString & aMsg)
 {
-  if (mHeaders[kServerPos].IsEmpty()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  value.Assign(mHeaders[kServerPos]);
-  return NS_OK;
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
+  return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-nsWebSocketEstablishedConnection::GetProxyChallenges(nsACString &value)
-{
-  if (mHeaders[kProxyAuthenticatePos].IsEmpty()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-  value.Assign(mHeaders[kProxyAuthenticatePos]);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::GetWWWChallenges(nsACString &value)
+nsWebSocketEstablishedConnection::OnStart(nsISupports *aContext)
 {
-  return NS_ERROR_NOT_AVAILABLE;
-}
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
+  if (!mOwner)
+    return NS_OK;
 
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::SetProxyCredentials(const nsACString &value)
-{
-  mProxyCredentials.Assign(value);
-  return NS_OK;
-}
+  if (!mOwner->mProtocol.IsEmpty())
+    mWebSocketProtocol->GetProtocol(mOwner->mProtocol);
 
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::SetWWWCredentials(const nsACString &value)
-{
-  mCredentials.Assign(value);
+  mStatus = CONN_CONNECTED_AND_READY;
+  mOwner->SetReadyState(nsIWebSocket::OPEN);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWebSocketEstablishedConnection::OnAuthAvailable()
+nsWebSocketEstablishedConnection::OnStop(nsISupports *aContext,
+                                         nsresult aStatusCode)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
-
-  if (!mOwner) {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
+  if (!mOwner)
     return NS_OK;
-  }
-
-  return Retry();
-}
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::OnAuthCancelled(PRBool userCancel)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
-
-  if (!mOwner) {
-    return NS_OK;
-  }
 
-  if (!userCancel) {
-    return FailConnection();
-  }
-
-  return Close();
-}
+  mClosedCleanly = NS_SUCCEEDED(aStatusCode);
 
-//-----------------------------------------------------------------------------
-// nsWebSocketEstablishedConnection::nsIDNSListener
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::OnLookupComplete(nsICancelable *aRequest,
-                                                   nsIDNSRecord  *aRec,
-                                                   nsresult       aStatus)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
-
-  if (!mOwner) {
-    return NS_ERROR_ABORT;
+  if (aStatusCode == NS_BASE_STREAM_CLOSED && 
+      mOwner->mReadyState >= nsIWebSocket::CLOSING) {
+    // don't generate an error event just because of an unclean close
+    aStatusCode = NS_OK;
   }
 
-  mDNSRequest = nsnull;
-  mFailureStatus = aStatus;
-  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(aStatus, aStatus);
-
-  nsresult rv;
-
-  rv = aRec->GetNextAddr(mOwner->mPort, &mPRNetAddr);
-  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-
-  TryConnect(nsnull, this);
-
-  return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsWebSocketEstablishedConnection::nsIProtocolProxyCallback
-//-----------------------------------------------------------------------------
-
-NS_IMETHODIMP
-nsWebSocketEstablishedConnection::OnProxyAvailable(nsICancelable *aRequest,
-                                                   nsIURI *aUri,
-                                                   nsIProxyInfo *aProxyInfo,
-                                                   nsresult aStatus)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
-
-  nsresult rv;
-
-  if (!mOwner) {
-    return NS_ERROR_ABORT;
+  if (NS_FAILED(aStatusCode)) {
+    ConsoleError();
+    if (mOwner && mOwner->mReadyState != nsIWebSocket::CONNECTING) {
+      nsresult rv =
+        mOwner->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
+      if (NS_FAILED(rv))
+        NS_WARNING("Failed to dispatch the error event");
+    }
   }
 
-  if (NS_FAILED(aStatus)) {
-    return ResolveNextProxyAndConnect();
+  mStatus = CONN_CLOSED;
+  if (mOwner) {
+    mOwner->SetReadyState(nsIWebSocket::CLOSED);
+    Disconnect();
   }
-
-  mProxyInfo = aProxyInfo;
-
-  if (mProxyInfo) {
-    TryConnect(nsnull, this);
-  } else {
-    // we need the server IP address because it must connect only one instance
-    // per IP address at a time.
-
-    nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
-    ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-
-    nsCOMPtr<nsIThread> thread = do_GetMainThread();
-    rv = dns->AsyncResolve(mOwner->mAsciiHost,
-                           0, this, thread, getter_AddRefs(mDNSRequest));
-    ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-  }
-
   return NS_OK;
 }
 
-//-----------------------------------------------------------------------------
-// nsWebSocketEstablishedConnection::nsIInputStreamCallback methods:
-//-----------------------------------------------------------------------------
-
-nsresult
-nsWebSocketEstablishedConnection::OnInputStreamReady(nsIAsyncInputStream *aStream)
+NS_IMETHODIMP
+nsWebSocketEstablishedConnection::OnAcknowledge(nsISupports *aContext,
+                                                PRUint32 aSize)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
-
-  nsresult rv;
-
-  {
-    MutexAutoLock lockDisconnect(mLockDisconnect);
-
-    if (!mOwner) {
-      return NS_ERROR_ABORT;
-    }
-
-    NS_ASSERTION(aStream == mSocketInput, "unexpected stream");
-
-    while (PR_TRUE) {
-      if (mBuffer.Length() - mBytesInBuffer < DEFAULT_BUFFER_SIZE) {
-        PRUint32 newLen = mBuffer.Length() + DEFAULT_BUFFER_SIZE;
-        mBuffer.SetLength(newLen);
-        ENSURE_TRUE_AND_FAIL_IF_FAILED(mBuffer.Length() == newLen,
-                                       NS_ERROR_OUT_OF_MEMORY);
-      }
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
-      PRUint32 read;
-      rv = aStream->Read(mBuffer.BeginWriting() + mBytesInBuffer,
-                         DEFAULT_BUFFER_SIZE, &read);
-      if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
-        break;
-      }
-      mFailureStatus = rv;
-      ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-
-      // check if the stream has been closed
-      if (read == 0) {
-        // If we are asking for credentials then the old connection has been
-        // closed. In this case we have to reset the WebSocket, not Close it.
-        if (mStatus != CONN_RETRYING_TO_AUTHENTICATE) {
-          mStatus = CONN_CLOSED;
-          mFailureStatus = NS_BASE_STREAM_CLOSED;
-          if (mStatus < CONN_CONNECTED_AND_READY) {
-            FailConnection();
-          } else {
-            Close();
-          }
-        }
-        return NS_BASE_STREAM_CLOSED;
-      }
-
-      PRUint32 start = mBytesInBuffer;
-      mBytesInBuffer += read;
-      rv = HandleNewInputString(start);
-      ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-    }
-
-    rv = mSocketInput->AsyncWait(this, 0, 0, gWebSocketThread);
-    ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-  }
-
+  if (aSize > mOutgoingBufferedAmount)
+    return NS_ERROR_UNEXPECTED;
+  
+  mOutgoingBufferedAmount -= aSize;
   return NS_OK;
 }
 
-//-----------------------------------------------------------------------------
-// nsWebSocketEstablishedConnection::nsIOutputStreamCallback methods:
-//-----------------------------------------------------------------------------
-
-nsresult
-nsWebSocketEstablishedConnection::OnOutputStreamReady(nsIAsyncOutputStream *aStream)
+NS_IMETHODIMP
+nsWebSocketEstablishedConnection::OnServerClose(nsISupports *aContext)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Not running on socket thread");
-
-  nsresult rv;
-
-  {
-    MutexAutoLock lockDisconnect(mLockDisconnect);
-
-    if (!mOwner) {
-      return NS_ERROR_ABORT;
-    }
-
-    NS_ASSERTION(aStream == mSocketOutput, "unexpected stream");
-
-    {
-      MutexAutoLock lockOut(mLockOutgoingMessages);
-
-      while (PR_TRUE) {
-        if (mOutgoingMessages.GetSize() == 0) {
-          break;
-        }
-
-        // send what we can of the 1st string
-
-        nsWSFrame *frameToSend =
-          static_cast<nsWSFrame*>(mOutgoingMessages.PeekFront());
-        nsCString *strToSend = frameToSend->mData;
-        PRUint32 sizeToSend =
-          strToSend->Length() - mBytesAlreadySentOfFirstOutString;
-        PRBool currentStrHasStartFrameByte =
-          (mBytesAlreadySentOfFirstOutString == 0);
-
-        if (sizeToSend != 0) {
-          PRUint32 written;
-          rv = aStream->Write(strToSend->get() + mBytesAlreadySentOfFirstOutString,
-                              sizeToSend, &written);
-          if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
-            break;
-          }
-
-          // on proxy errors when connecting, try to failover
-          if ((mStatus == CONN_CONNECTING_TO_HTTP_PROXY ||
-               (mStatus == CONN_SENDING_INITIAL_REQUEST && mProxyInfo)) &&
-              (rv == NS_ERROR_PROXY_CONNECTION_REFUSED ||
-               rv == NS_ERROR_UNKNOWN_PROXY_HOST ||
-               rv == NS_ERROR_NET_TIMEOUT ||
-               rv == NS_ERROR_NET_RESET)) {
-            mProxyFailureReason = rv;
-            return ResolveNextProxyAndConnect();
-          }
-
-          mFailureStatus = rv;
-          ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
-          if (written == 0) {
-            mStatus = CONN_CLOSED;
-            mFailureStatus = NS_BASE_STREAM_CLOSED;
-            if (mStatus < CONN_CONNECTED_AND_READY) {
-              FailConnection();
-            } else {
-              Close();
-            }
-            return NS_BASE_STREAM_CLOSED;
-          }
-
-          if (frameToSend->mType == eUTF8MessageFrame) {
-            PRBool currentStrHasEndFrameByte =
-              (mBytesAlreadySentOfFirstOutString + written ==
-               strToSend->Length());
-
-            // START_BYTE_OF_MESSAGE and END_BYTE_OF_MESSAGE bytes don't count
-            if (currentStrHasStartFrameByte) {
-              if (currentStrHasEndFrameByte) {
-                mOutgoingBufferedAmount -= written - 2;
-              } else {
-                mOutgoingBufferedAmount -= written - 1;
-              }
-            } else {
-              if (currentStrHasEndFrameByte) {
-                mOutgoingBufferedAmount -= written - 1;
-              } else {
-                mOutgoingBufferedAmount -= written;
-              }
-            }
-          }
-
-          mBytesAlreadySentOfFirstOutString += written;
-        }
-
-        sizeToSend = strToSend->Length() - mBytesAlreadySentOfFirstOutString;
-        if (sizeToSend != 0) { // if different, we try sending what remain after
-          break;
-        }
-
-        // ok, send the next string
-        if (frameToSend->mType == eCloseFrame) {
-          mSentCloseFrame = PR_TRUE;
-        }
-        mOutgoingMessages.PopFront();
-        delete frameToSend;
-        mBytesAlreadySentOfFirstOutString = 0;
-        UpdateMustKeepAlive();
-      }
-
-      if (mOutgoingMessages.GetSize() != 0) {
-        rv = mSocketOutput->AsyncWait(this, 0, 0, gWebSocketThread);
-        ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-      } else {
-        if (mStatus == CONN_SENDING_ACK_CLOSE_FRAME && mSentCloseFrame) {
-          mClosedCleanly = PR_TRUE;
-          mStatus = CONN_CLOSED;
-          return Close();
-        }
-
-        if (mStatus == CONN_SENDING_INITIAL_REQUEST ||
-            mStatus == CONN_CONNECTING_TO_HTTP_PROXY) {
-          if (mStatus == CONN_SENDING_INITIAL_REQUEST) {
-            mStatus = CONN_WAITING_RESPONSE_FOR_INITIAL_REQUEST;
-
-            rv = mInitialServerResponseTimer->
-              InitWithFuncCallback(TimerInitialServerResponseCallback, this,
-                                   TIMEOUT_WAIT_FOR_SERVER_RESPONSE,
-                                   nsITimer::TYPE_ONE_SHOT);
-            ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-          }
-          rv = mSocketInput->AsyncWait(this, 0, 0, gWebSocketThread);
-          ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
-        }
-      }
-    }
-  }
-
+  Close();                                        /* reciprocate! */
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsWebSocketEstablishedConnection::nsIInterfaceRequestor
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsWebSocketEstablishedConnection::GetInterface(const nsIID &aIID,
                                                void **aResult)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
   if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
       aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
     nsresult rv;
 
     nsCOMPtr<nsIDocument> doc =
       nsContentUtils::GetDocumentFromScriptContext(mOwner->mScriptContext);
 
@@ -2868,20 +600,22 @@ nsWebSocketEstablishedConnection::GetInt
 nsWebSocket::nsWebSocket() : mKeepingAlive(PR_FALSE),
                              mCheckMustKeepAlive(PR_TRUE),
                              mTriggeredCloseEvent(PR_FALSE),
                              mReadyState(nsIWebSocket::CONNECTING),
                              mOutgoingBufferedAmount(0),
                              mScriptLine(0),
                              mWindowID(0)
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 }
 
 nsWebSocket::~nsWebSocket()
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   if (mConnection) {
     mConnection->Disconnect();
     mConnection = nsnull;
   }
   if (mListenerManager) {
     mListenerManager->Disconnect();
     mListenerManager = nsnull;
   }
@@ -2938,16 +672,17 @@ NS_IMPL_RELEASE_INHERITED(nsWebSocket, n
  */
 NS_IMETHODIMP
 nsWebSocket::Initialize(nsISupports* aOwner,
                         JSContext* aContext,
                         JSObject* aObject,
                         PRUint32 aArgc,
                         jsval* aArgv)
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsAutoString urlParam, protocolParam;
 
   if (!PrefEnabled()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   if (aArgc != 1 && aArgc != 2) {
     return NS_ERROR_DOM_SYNTAX_ERR;
@@ -3006,23 +741,23 @@ nsWebSocket::Initialize(nsISupports* aOw
 
 //-----------------------------------------------------------------------------
 // nsWebSocket methods:
 //-----------------------------------------------------------------------------
 
 nsresult
 nsWebSocket::EstablishConnection()
 {
-  NS_ASSERTION(!mConnection, "mConnection should be null");
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
+  NS_ABORT_IF_FALSE(!mConnection, "mConnection should be null");
 
   nsresult rv;
 
   nsRefPtr<nsWebSocketEstablishedConnection> conn =
     new nsWebSocketEstablishedConnection();
-  NS_ENSURE_TRUE(conn, NS_ERROR_OUT_OF_MEMORY);
 
   rv = conn->Init(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mConnection = conn;
 
   return NS_OK;
 }
@@ -3045,16 +780,17 @@ public:
 private:
   nsRefPtr<nsWebSocket> mWebSocket;
   PRBool mWasClean;
 };
 
 nsresult
 nsWebSocket::CreateAndDispatchSimpleEvent(const nsString& aName)
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv;
 
   rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMEvent> event;
@@ -3068,50 +804,52 @@ nsWebSocket::CreateAndDispatchSimpleEven
   nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
   rv = privateEvent->SetTrusted(PR_TRUE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
 }
 
 nsresult
-nsWebSocket::CreateAndDispatchMessageEvent(nsCString *aData)
+nsWebSocket::CreateAndDispatchMessageEvent(const nsACString& aData)
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv;
-
+  
   rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     return NS_OK;
   }
 
   // create an event that uses the MessageEvent interface,
   // which does not bubble, is not cancelable, and has no default action
 
   nsCOMPtr<nsIDOMEvent> event;
   rv = NS_NewDOMMessageEvent(getter_AddRefs(event), nsnull, nsnull);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
   rv = messageEvent->InitMessageEvent(NS_LITERAL_STRING("message"),
                                       PR_FALSE, PR_FALSE,
-                                      NS_ConvertUTF8toUTF16(*aData),
-                                      NS_ConvertUTF8toUTF16(mOrigin),
+                                      NS_ConvertUTF8toUTF16(aData),
+                                      mUTF16Origin,
                                       EmptyString(), nsnull);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
   rv = privateEvent->SetTrusted(PR_TRUE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
 }
 
 nsresult
 nsWebSocket::CreateAndDispatchCloseEvent(PRBool aWasClean)
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv;
 
   mTriggeredCloseEvent = PR_TRUE;
 
   rv = CheckInnerWindowCorrectness();
   if (NS_FAILED(rv)) {
     return NS_OK;
   }
@@ -3134,73 +872,71 @@ nsWebSocket::CreateAndDispatchCloseEvent
   NS_ENSURE_SUCCESS(rv, rv);
 
   return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
 }
 
 PRBool
 nsWebSocket::PrefEnabled()
 {
-  return nsContentUtils::GetBoolPref("network.websocket.enabled", PR_TRUE) &&
-    nsContentUtils::GetBoolPref("network.websocket.override-security-block",
-                                PR_FALSE);
+  return nsContentUtils::GetBoolPref("network.websocket.enabled", PR_TRUE);
 }
 
 void
 nsWebSocket::SetReadyState(PRUint16 aNewReadyState)
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv;
 
   if (mReadyState == aNewReadyState) {
     return;
   }
 
-  NS_ASSERTION((aNewReadyState == nsIWebSocket::OPEN) ||
-               (aNewReadyState == nsIWebSocket::CLOSING) ||
-               (aNewReadyState == nsIWebSocket::CLOSED),
-               "unexpected readyState");
+  NS_ABORT_IF_FALSE((aNewReadyState == nsIWebSocket::OPEN)    ||
+                    (aNewReadyState == nsIWebSocket::CLOSING) ||
+                    (aNewReadyState == nsIWebSocket::CLOSED),
+                    "unexpected readyState");
 
   if (aNewReadyState == nsIWebSocket::OPEN) {
-    NS_ASSERTION(mReadyState == nsIWebSocket::CONNECTING,
-                 "unexpected readyState transition");
+    NS_ABORT_IF_FALSE(mReadyState == nsIWebSocket::CONNECTING,
+                      "unexpected readyState transition");
     mReadyState = aNewReadyState;
 
     rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to dispatch the open event");
     }
     UpdateMustKeepAlive();
     return;
   }
 
   if (aNewReadyState == nsIWebSocket::CLOSING) {
-    NS_ASSERTION((mReadyState == nsIWebSocket::CONNECTING) ||
-                 (mReadyState == nsIWebSocket::OPEN),
-                 "unexpected readyState transition");
+    NS_ABORT_IF_FALSE((mReadyState == nsIWebSocket::CONNECTING) ||
+                      (mReadyState == nsIWebSocket::OPEN),
+                      "unexpected readyState transition");
     mReadyState = aNewReadyState;
     return;
   }
 
   if (aNewReadyState == nsIWebSocket::CLOSED) {
-    NS_ASSERTION(mReadyState == nsIWebSocket::CLOSING,
-                 "unexpected readyState transition");
     mReadyState = aNewReadyState;
 
-    // The close event must be dispatched asynchronously.
-    nsCOMPtr<nsIRunnable> event =
-      new nsWSCloseEvent(this, mConnection->ClosedCleanly());
+    if (mConnection) {
+      // The close event must be dispatched asynchronously.
+      nsCOMPtr<nsIRunnable> event =
+        new nsWSCloseEvent(this, mConnection->ClosedCleanly());
+      mOutgoingBufferedAmount += mConnection->GetOutgoingBufferedAmount();
+      mConnection = nsnull; // this is no longer necessary
 
-    mOutgoingBufferedAmount += mConnection->GetOutgoingBufferedAmount();
-    mConnection = nsnull; // this is no longer necessary
-
-    rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to dispatch the close event");
-      mTriggeredCloseEvent = PR_TRUE;
-      UpdateMustKeepAlive();
+      rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to dispatch the close event");
+        mTriggeredCloseEvent = PR_TRUE;
+        UpdateMustKeepAlive();
+      }
     }
   }
 }
 
 nsresult
 nsWebSocket::ParseURL(const nsString& aURL)
 {
   nsresult rv;
@@ -3255,19 +991,19 @@ nsWebSocket::ParseURL(const nsString& aU
      mPort = (port == -1) ? DEFAULT_WS_SCHEME_PORT : port;
   } else if (scheme.LowerCaseEqualsLiteral("wss")) {
     mSecure = PR_TRUE;
     mPort = (port == -1) ? DEFAULT_WSS_SCHEME_PORT : port;
   } else {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
-  mOrigin = origin;
-  ToLowerCase(mOrigin);
-
+  ToLowerCase(origin);
+  CopyUTF8toUTF16(origin, mUTF16Origin);
+    
   mAsciiHost = host;
   ToLowerCase(mAsciiHost);
 
   mResource = filePath;
   if (!query.IsEmpty()) {
     mResource.AppendLiteral("?");
     mResource.Append(query);
   }
@@ -3276,19 +1012,17 @@ nsWebSocket::ParseURL(const nsString& aU
   for (i = 0; i < length; ++i) {
     if (mResource[i] < static_cast<PRUnichar>(0x0021) ||
         mResource[i] > static_cast<PRUnichar>(0x007E)) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
   }
 
   mOriginalURL = aURL;
-
   mURI = parsedURL;
-
   return NS_OK;
 }
 
 nsresult
 nsWebSocket::SetProtocol(const nsString& aProtocol)
 {
   if (aProtocol.IsEmpty()) {
     return NS_ERROR_DOM_SYNTAX_ERR;
@@ -3312,16 +1046,17 @@ nsWebSocket::SetProtocol(const nsString&
 //   1. the object has registered event listeners that can be triggered
 //      ("strong event listeners");
 //   2. there are outgoing not sent messages.
 //-----------------------------------------------------------------------------
 
 void
 nsWebSocket::UpdateMustKeepAlive()
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   if (!mCheckMustKeepAlive) {
     return;
   }
 
   PRBool shouldKeepAlive = PR_FALSE;
 
   if (mListenerManager) {
     switch (mReadyState)
@@ -3363,58 +1098,62 @@ nsWebSocket::UpdateMustKeepAlive()
     mKeepingAlive = PR_TRUE;
     static_cast<nsPIDOMEventTarget*>(this)->AddRef();
   }
 }
 
 void
 nsWebSocket::DontKeepAliveAnyMore()
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   if (mKeepingAlive) {
     mKeepingAlive = PR_FALSE;
     static_cast<nsPIDOMEventTarget*>(this)->Release();
   }
   mCheckMustKeepAlive = PR_FALSE;
 }
 
 NS_IMETHODIMP
 nsWebSocket::AddEventListener(const nsAString& aType,
                               nsIDOMEventListener* aListener,
                               PRBool aUseCapture)
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv = nsDOMEventTargetHelper::AddEventListener(aType,
                                                          aListener,
                                                          aUseCapture);
   if (NS_SUCCEEDED(rv)) {
     UpdateMustKeepAlive();
   }
   return rv;
 }
 
 NS_IMETHODIMP
 nsWebSocket::RemoveEventListener(const nsAString& aType,
                                  nsIDOMEventListener* aListener,
                                  PRBool aUseCapture)
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv = nsDOMEventTargetHelper::RemoveEventListener(aType,
                                                             aListener,
                                                             aUseCapture);
   if (NS_SUCCEEDED(rv)) {
     UpdateMustKeepAlive();
   }
   return rv;
 }
 
 NS_IMETHODIMP
 nsWebSocket::AddEventListener(const nsAString& aType,
                               nsIDOMEventListener *aListener,
                               PRBool aUseCapture,
                               PRBool aWantsUntrusted,
                               PRUint8 optional_argc)
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv = nsDOMEventTargetHelper::AddEventListener(aType,
                                                          aListener,
                                                          aUseCapture,
                                                          aWantsUntrusted,
                                                          optional_argc);
   if (NS_SUCCEEDED(rv)) {
     UpdateMustKeepAlive();
   }
@@ -3428,16 +1167,23 @@ nsWebSocket::AddEventListener(const nsAS
 NS_IMETHODIMP
 nsWebSocket::GetUrl(nsAString& aURL)
 {
   aURL = mOriginalURL;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsWebSocket::GetProtocol(nsAString& aProtocol)
+{
+  CopyUTF8toUTF16(mProtocol, aProtocol);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsWebSocket::GetReadyState(PRUint16 *aReadyState)
 {
   *aReadyState = mReadyState;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebSocket::GetBufferedAmount(PRUint32 *aBufferedAmount)
@@ -3467,16 +1213,17 @@ nsWebSocket::GetBufferedAmount(PRUint32 
 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(open, mOnOpenListener)
 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(error, mOnErrorListener)
 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(message, mOnMessageListener)
 NS_WEBSOCKET_IMPL_DOMEVENTLISTENER(close, mOnCloseListener)
 
 NS_IMETHODIMP
 nsWebSocket::Send(const nsAString& aData, PRBool *aRet)
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   *aRet = PR_FALSE;
 
   if (mReadyState == nsIWebSocket::CONNECTING) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   // We need to check if there isn't unpaired surrogates.
   PRUint32 i, length = aData.Length();
@@ -3503,16 +1250,17 @@ nsWebSocket::Send(const nsAString& aData
   *aRet = NS_SUCCEEDED(rv);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebSocket::Close()
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   if (mReadyState == nsIWebSocket::CLOSING ||
       mReadyState == nsIWebSocket::CLOSED) {
     return NS_OK;
   }
 
   if (mReadyState == nsIWebSocket::CONNECTING) {
     // FailConnection() can release the object, so we keep a reference
     // before calling it
@@ -3533,16 +1281,17 @@ nsWebSocket::Close()
  */
 NS_IMETHODIMP
 nsWebSocket::Init(nsIPrincipal* aPrincipal,
                   nsIScriptContext* aScriptContext,
                   nsPIDOMWindow* aOwnerWindow,
                   const nsAString& aURL,
                   const nsAString& aProtocol)
 {
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv;
 
   NS_ENSURE_ARG(aPrincipal);
 
   if (!PrefEnabled()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
@@ -3588,119 +1337,98 @@ nsWebSocket::Init(nsIPrincipal* aPrincip
 
   // the constructor should throw a SYNTAX_ERROR only if it fails to parse the
   // url parameter, so we don't care about the EstablishConnection result.
   EstablishConnection();
 
   return NS_OK;
 }
 
-// static
-void
-nsWebSocket::ReleaseGlobals()
+//-----------------------------------------------------------------------------
+// nsWebSocketEstablishedConnection::nsIRequest
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsWebSocketEstablishedConnection::GetName(nsACString &aName)
 {
-  if (nsWebSocketEstablishedConnection::sWSsConnecting) {
-    nsWebSocketEstablishedConnection::sWSsConnecting->Clear();
-    delete nsWebSocketEstablishedConnection::sWSsConnecting;
-    nsWebSocketEstablishedConnection::sWSsConnecting = nsnull;
-  }
-  if (gWebSocketThread) {
-    gWebSocketThread->Shutdown();
-    NS_RELEASE(gWebSocketThread);
-  }
+  if (!mOwner)
+    return NS_ERROR_UNEXPECTED;
+  
+  CopyUTF16toUTF8(mOwner->mOriginalURL, aName);
+  return NS_OK;
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// nsWSProtocolHandler
-////////////////////////////////////////////////////////////////////////////////
-
-NS_IMPL_ISUPPORTS2(nsWSProtocolHandler,
-                   nsIProtocolHandler, nsIProxiedProtocolHandler)
-
 NS_IMETHODIMP
-nsWSProtocolHandler::GetScheme(nsACString& aScheme)
+nsWebSocketEstablishedConnection::IsPending(PRBool *aValue)
 {
-  aScheme.AssignLiteral("ws");
+  *aValue = !!(mOwner);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWSProtocolHandler::GetDefaultPort(PRInt32 *aDefaultPort)
+nsWebSocketEstablishedConnection::GetStatus(nsresult *aStatus)
 {
-  *aDefaultPort = DEFAULT_WS_SCHEME_PORT;
+  *aStatus = NS_OK;
   return NS_OK;
 }
 
+// probably means window went away or stop button pressed
 NS_IMETHODIMP
-nsWSProtocolHandler::GetProtocolFlags(PRUint32 *aProtocolFlags)
+nsWebSocketEstablishedConnection::Cancel(nsresult aStatus)
 {
-  *aProtocolFlags = URI_STD | URI_NON_PERSISTABLE | URI_DOES_NOT_RETURN_DATA |
-                    ALLOWS_PROXY | ALLOWS_PROXY_HTTP | URI_DANGEROUS_TO_LOAD;
-  return NS_OK;
+  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
+
+  if (!mOwner) {
+    return NS_OK;
+  }
+
+  ConsoleError();
+  return Close();
 }
 
 NS_IMETHODIMP
-nsWSProtocolHandler::NewURI(const nsACString& aSpec,
-                            const char *aCharset,
-                            nsIURI *aBaseURI,
-                            nsIURI **aURI)
+nsWebSocketEstablishedConnection::Suspend()
 {
-  nsresult rv;
-  nsCOMPtr<nsIStandardURL> url(do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv));
-  NS_ENSURE_SUCCESS(rv, rv);
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsWebSocketEstablishedConnection::Resume()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
 
-  PRInt32 defaultPort;
-  GetDefaultPort(&defaultPort);
+NS_IMETHODIMP
+nsWebSocketEstablishedConnection::GetLoadGroup(nsILoadGroup **aLoadGroup)
+{
+  *aLoadGroup = nsnull;
+  if (!mOwner)
+    return NS_OK;
 
-  rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY,
-                 defaultPort, aSpec, aCharset, aBaseURI);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIDocument> doc =
+    nsContentUtils::GetDocumentFromScriptContext(mOwner->mScriptContext);
 
-  rv = CallQueryInterface(url, aURI);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (doc) {
+    *aLoadGroup = doc->GetDocumentLoadGroup().get();  // already_AddRefed
+  }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWSProtocolHandler::NewChannel(nsIURI *aURI,
-                                nsIChannel **aChannel)
+nsWebSocketEstablishedConnection::SetLoadGroup(nsILoadGroup *aLoadGroup)
 {
-  return NS_ERROR_NOT_AVAILABLE;
-}
-
-NS_IMETHODIMP
-nsWSProtocolHandler::NewProxiedChannel(nsIURI *aURI,
-                                       nsIProxyInfo* aProxyInfo,
-                                       nsIChannel **aChannel)
-{
-  return NS_ERROR_NOT_AVAILABLE;
+  return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
-nsWSProtocolHandler::AllowPort(PRInt32 aPort,
-                               const char *aScheme,
-                               PRBool *aAllowPort)
+nsWebSocketEstablishedConnection::GetLoadFlags(nsLoadFlags *aLoadFlags)
 {
-  PRInt32 defaultPort;
-  GetDefaultPort(&defaultPort);
-
-  *aAllowPort = (aPort == defaultPort);
-  return NS_OK;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// nsWSSProtocolHandler
-////////////////////////////////////////////////////////////////////////////////
-
-NS_IMETHODIMP
-nsWSSProtocolHandler::GetScheme(nsACString& aScheme)
-{
-  aScheme.AssignLiteral("wss");
+  *aLoadFlags = nsIRequest::LOAD_BACKGROUND;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWSSProtocolHandler::GetDefaultPort(PRInt32 *aDefaultPort)
+nsWebSocketEstablishedConnection::SetLoadFlags(nsLoadFlags aLoadFlags)
 {
-  *aDefaultPort = DEFAULT_WSS_SCHEME_PORT;
+  // we won't change the load flags at all.
   return NS_OK;
 }
--- a/content/base/src/nsWebSocket.h
+++ b/content/base/src/nsWebSocket.h
@@ -45,17 +45,16 @@
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsIJSNativeInitializer.h"
 #include "nsIPrincipal.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDOMEventListener.h"
 #include "nsDOMEventTargetWrapperCache.h"
 #include "nsAutoPtr.h"
-#include "nsIProxiedProtocolHandler.h"
 
 #define DEFAULT_WS_SCHEME_PORT  80
 #define DEFAULT_WSS_SCHEME_PORT 443
 
 #define NS_WEBSOCKET_CID                            \
  { /* 7ca25214-98dc-40a6-bc1f-41ddbe41f46c */       \
   0x7ca25214, 0x98dc, 0x40a6,                       \
  {0xbc, 0x1f, 0x41, 0xdd, 0xbe, 0x41, 0xf4, 0x6c} }
@@ -96,32 +95,30 @@ public:
 
   // nsIDOMNSEventTarget
   NS_IMETHOD AddEventListener(const nsAString& aType,
                               nsIDOMEventListener *aListener,
                               PRBool aUseCapture,
                               PRBool aWantsUntrusted,
                               PRUint8 optional_argc);
 
-  static void ReleaseGlobals();
-
   // Determine if preferences allow WebSocket
   static PRBool PrefEnabled();
 
   const PRUint64 WindowID() const { return mWindowID; }
   const nsCString& GetScriptFile() const { return mScriptFile; }
   const PRUint32 GetScriptLine() const { return mScriptLine; }
 
 protected:
   nsresult ParseURL(const nsString& aURL);
   nsresult SetProtocol(const nsString& aProtocol);
   nsresult EstablishConnection();
 
   nsresult CreateAndDispatchSimpleEvent(const nsString& aName);
-  nsresult CreateAndDispatchMessageEvent(nsCString *aData);
+  nsresult CreateAndDispatchMessageEvent(const nsACString& aData);
   nsresult CreateAndDispatchCloseEvent(PRBool aWasClean);
 
   // called from mConnection accordingly to the situation
   void SetReadyState(PRUint16 aNewReadyState);
 
   // if there are "strong event listeners" (see comment in nsWebSocket.cpp) or
   // outgoing not sent messages then this method keeps the object alive
   // when js doesn't have strong references to it.
@@ -142,17 +139,18 @@ protected:
 
   PRPackedBool mKeepingAlive;
   PRPackedBool mCheckMustKeepAlive;
   PRPackedBool mTriggeredCloseEvent;
 
   nsCString mAsciiHost;  // hostname
   PRUint32  mPort;
   nsCString mResource; // [filepath[?query]]
-  nsCString mOrigin;
+  nsString  mUTF16Origin;
+  
   nsCOMPtr<nsIURI> mURI;
   nsCString mProtocol;
 
   PRUint16 mReadyState;
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   nsRefPtr<nsWebSocketEstablishedConnection> mConnection;
@@ -170,49 +168,9 @@ protected:
   PRUint32 mScriptLine;
   PRUint64 mWindowID;
 
 private:
   nsWebSocket(const nsWebSocket& x);   // prevent bad usage
   nsWebSocket& operator=(const nsWebSocket& x);
 };
 
-#define NS_WSPROTOCOLHANDLER_CONTRACTID \
-    NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ws"
-
-#define NS_WSSPROTOCOLHANDLER_CONTRACTID \
-    NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "wss"
-
-#define NS_WSPROTOCOLHANDLER_CID                     \
-{ /* a4e6aa3b-b6db-4809-aa11-e292e074cbc4 */         \
-    0xa4e6aa3b,                                      \
-    0xb6db,                                          \
-    0x4809,                                          \
-    {0xaa, 0x11, 0xe2, 0x92, 0xe0, 0x74, 0xcb, 0xc4} \
-}
-
-#define NS_WSSPROTOCOLHANDLER_CID                    \
-{ /* c6531804-b5c8-4a53-80bf-e339b82d3161 */         \
-    0xc6531804,                                      \
-    0xb5c8,                                          \
-    0x4a53,                                          \
-    {0x80, 0xbf, 0xe3, 0x39, 0xb8, 0x2d, 0x31, 0x61} \
-}
-
-class nsWSProtocolHandler: public nsIProxiedProtocolHandler
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIPROTOCOLHANDLER
-  NS_DECL_NSIPROXIEDPROTOCOLHANDLER
-
-  nsWSProtocolHandler() {};
-};
-
-class nsWSSProtocolHandler: public nsWSProtocolHandler
-{
-public:
-  NS_IMETHOD GetScheme(nsACString & aScheme);
-  NS_IMETHOD GetDefaultPort(PRInt32 *aDefaultPort);
-  nsWSSProtocolHandler() {};
-};
-
 #endif
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -304,18 +304,16 @@ static void Shutdown();
 
 // Factory Constructor
 NS_GENERIC_FACTORY_CONSTRUCTOR(txMozillaXSLTProcessor)
 NS_GENERIC_AGGREGATED_CONSTRUCTOR_INIT(nsXPathEvaluator, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(txNodeSetAdaptor, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMSerializer)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsXMLHttpRequest, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebSocket)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsWSProtocolHandler)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsWSSProtocolHandler)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDOMFileReader, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFormData)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFileDataProtocolHandler)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMParser)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDOMStorageManager,
                                          nsDOMStorageManager::GetInstance)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsChannelPolicy)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(IndexedDatabaseManager,
@@ -837,18 +835,16 @@ NS_DEFINE_NAMED_CID(TRANSFORMIIX_XSLT_PR
 NS_DEFINE_NAMED_CID(TRANSFORMIIX_XPATH_EVALUATOR_CID);
 NS_DEFINE_NAMED_CID(TRANSFORMIIX_NODESET_CID);
 NS_DEFINE_NAMED_CID(NS_XMLSERIALIZER_CID);
 NS_DEFINE_NAMED_CID(NS_FILEREADER_CID);
 NS_DEFINE_NAMED_CID(NS_FORMDATA_CID);
 NS_DEFINE_NAMED_CID(NS_FILEDATAPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_XMLHTTPREQUEST_CID);
 NS_DEFINE_NAMED_CID(NS_WEBSOCKET_CID);
-NS_DEFINE_NAMED_CID(NS_WSPROTOCOLHANDLER_CID);
-NS_DEFINE_NAMED_CID(NS_WSSPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSTORAGE_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSTORAGE2_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSTORAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMJSON_CID);
 NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
 NS_DEFINE_NAMED_CID(INDEXEDDB_MANAGER_CID );
 #ifdef ENABLE_EDITOR_API_LOG
@@ -986,18 +982,16 @@ static const mozilla::Module::CIDEntry k
   { &kTRANSFORMIIX_XPATH_EVALUATOR_CID, false, NULL, nsXPathEvaluatorConstructor },
   { &kTRANSFORMIIX_NODESET_CID, false, NULL, txNodeSetAdaptorConstructor },
   { &kNS_XMLSERIALIZER_CID, false, NULL, nsDOMSerializerConstructor },
   { &kNS_FILEREADER_CID, false, NULL, nsDOMFileReaderConstructor },
   { &kNS_FORMDATA_CID, false, NULL, nsFormDataConstructor },
   { &kNS_FILEDATAPROTOCOLHANDLER_CID, false, NULL, nsFileDataProtocolHandlerConstructor },
   { &kNS_XMLHTTPREQUEST_CID, false, NULL, nsXMLHttpRequestConstructor },
   { &kNS_WEBSOCKET_CID, false, NULL, nsWebSocketConstructor },
-  { &kNS_WSPROTOCOLHANDLER_CID, false, NULL, nsWSProtocolHandlerConstructor },
-  { &kNS_WSSPROTOCOLHANDLER_CID, false, NULL, nsWSSProtocolHandlerConstructor },
   { &kNS_DOMPARSER_CID, false, NULL, nsDOMParserConstructor },
   { &kNS_DOMSTORAGE_CID, false, NULL, NS_NewDOMStorage },
   { &kNS_DOMSTORAGE2_CID, false, NULL, NS_NewDOMStorage2 },
   { &kNS_DOMSTORAGEMANAGER_CID, false, NULL, nsDOMStorageManagerConstructor },
   { &kNS_DOMJSON_CID, false, NULL, NS_NewJSON },
   { &kNS_TEXTEDITOR_CID, false, NULL, nsPlaintextEditorConstructor },
   { &kINDEXEDDB_MANAGER_CID, false, NULL, IndexedDatabaseManagerConstructor },
 #ifdef ENABLE_EDITOR_API_LOG
@@ -1130,18 +1124,16 @@ static const mozilla::Module::ContractID
   { NS_XPATH_EVALUATOR_CONTRACTID, &kTRANSFORMIIX_XPATH_EVALUATOR_CID },
   { TRANSFORMIIX_NODESET_CONTRACTID, &kTRANSFORMIIX_NODESET_CID },
   { NS_XMLSERIALIZER_CONTRACTID, &kNS_XMLSERIALIZER_CID },
   { NS_FILEREADER_CONTRACTID, &kNS_FILEREADER_CID },
   { NS_FORMDATA_CONTRACTID, &kNS_FORMDATA_CID },
   { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX FILEDATA_SCHEME, &kNS_FILEDATAPROTOCOLHANDLER_CID },
   { NS_XMLHTTPREQUEST_CONTRACTID, &kNS_XMLHTTPREQUEST_CID },
   { NS_WEBSOCKET_CONTRACTID, &kNS_WEBSOCKET_CID },
-  { NS_WSPROTOCOLHANDLER_CONTRACTID, &kNS_WSPROTOCOLHANDLER_CID },
-  { NS_WSSPROTOCOLHANDLER_CONTRACTID, &kNS_WSSPROTOCOLHANDLER_CID },
   { NS_DOMPARSER_CONTRACTID, &kNS_DOMPARSER_CID },
   { "@mozilla.org/dom/storage;1", &kNS_DOMSTORAGE_CID },
   { "@mozilla.org/dom/storage;2", &kNS_DOMSTORAGE2_CID },
   { "@mozilla.org/dom/storagemanager;1", &kNS_DOMSTORAGEMANAGER_CID },
   { "@mozilla.org/dom/json;1", &kNS_DOMJSON_CID },
   { "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
   { INDEXEDDB_MANAGER_CONTRACTID, &kINDEXEDDB_MANAGER_CID },
 #ifdef ENABLE_EDITOR_API_LOG
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -75,17 +75,16 @@
 #include "txMozillaXSLTProcessor.h"
 #include "nsDOMStorage.h"
 #include "nsCellMap.h"
 #include "nsTextFrameTextRunCache.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsTextFragment.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsCrossSiteListenerProxy.h"
-#include "nsWebSocket.h"
 #include "nsDOMThreadService.h"
 #include "nsHTMLDNSPrefetch.h"
 #include "nsHtml5Module.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsFocusManager.h"
 #include "nsFrameList.h"
 #include "nsListControlFrame.h"
 #include "nsHTMLInputElement.h"
@@ -364,18 +363,16 @@ nsLayoutStatics::Shutdown()
   nsDOMThreadService::Shutdown();
 
 #ifdef MOZ_SYDNEYAUDIO
   nsAudioStream::ShutdownLibrary();
 #endif
 
   nsCORSListenerProxy::Shutdown();
   
-  nsWebSocket::ReleaseGlobals();
-  
   nsIPresShell::ReleaseStatics();
 
   nsHtml5Module::ReleaseStatics();
 
   nsRegion::ShutdownStatic();
 
   NS_ShutdownChainItemPool();
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -777,16 +777,43 @@ pref("network.http.connection-retry-time
 // per Section 4.7 "Low-Latency Data Service Class".
 pref("network.ftp.data.qos", 0);
 pref("network.ftp.control.qos", 0);
 
 // </http>
 
 // <ws>: WebSocket
 pref("network.websocket.enabled", true);
+
+// mobile might want to set this much smaller
+pref("network.websocket.max-message-size", 16000000);
+
+// Should we automatically follow http 3xx redirects during handshake
+pref("network.websocket.auto-follow-http-redirects", false);
+
+// the number of seconds to wait for websocket connection to be opened
+pref("network.websocket.timeout.open", 20);
+
+// the number of seconds to wait for a clean close after sending the client
+// close message
+pref("network.websocket.timeout.close", 20);
+
+// the number of seconds of idle read activity to sustain before sending a
+// ping probe. 0 to disable.
+pref("network.websocket.timeout.ping.request", 0);
+
+// the deadline, expressed in seconds, for some read activity to occur after
+// generating a ping. If no activity happens then an error and unclean close
+// event is sent to the javascript websockets application
+pref("network.websocket.timeout.ping.response", 10);
+
+// Defines whether or not to try and negotiate the stream-deflate compression
+// extension with the websocket server
+pref("network.websocket.extensions.stream-deflate", true);
+
 // </ws>
 
 // If false, remote JAR files that are served with a content type other than
 // application/java-archive or application/x-jar will not be opened
 // by the jar channel.
 pref("network.jar.open-unsafe-types", false);
 
 // This preference controls whether or not internationalized domain names (IDN)
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -730,16 +730,36 @@
 { /* {0xe7509b46-2eB2-410a-9d7c-c3ce73284d01} */     \
   0xe7509b46,                                        \
   0x2eb2,                                            \
   0x410a,                                            \
   {0x9d, 0x7c, 0xc3, 0xce, 0x73, 0x28, 0x4d, 0x01}   \
 }
 
 /******************************************************************************
+ * netwerk/protocol/websocket/ classes
+ */
+
+#define NS_WEBSOCKETPROTOCOLHANDLER_CID              \
+{ /* {dc01db59-a513-4c90-824b-085cce06c0aa} */       \
+  0xdc01db59,                                        \
+  0xa513,                                            \
+  0x4c90,                                            \
+  {0x82, 0x4b, 0x08, 0x5c, 0xce, 0x06, 0xc0, 0xaa}   \
+}
+
+#define NS_WEBSOCKETSSLPROTOCOLHANDLER_CID           \
+{ /* {dc01dbbb-a5bb-4cbb-82bb-085cce06c0bb} */       \
+  0xdc01dbbb,                                        \
+  0xa5bb,                                            \
+  0x4cbb,                                            \
+  {0x82, 0xbb, 0x08, 0x5c, 0xce, 0x06, 0xc0, 0xbb}   \
+}
+
+/******************************************************************************
  * netwerk/protocol/about/ classes
  */
 
 #define NS_ABOUTPROTOCOLHANDLER_CLASSNAME \
     "About Protocol Handler"
 #define NS_ABOUTPROTOCOLHANDLER_CID                  \
 { /* 9e3b6c90-2f75-11d3-8cd0-0060b0fc14a3 */         \
     0x9e3b6c90,                                      \
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -276,16 +276,26 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsViewSou
 #include "nsDataHandler.h"
 #endif
 
 #ifdef NECKO_PROTOCOL_wyciwyg
 #include "nsWyciwygProtocolHandler.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWyciwygProtocolHandler)
 #endif
 
+#ifdef NECKO_PROTOCOL_websocket
+#include "nsWebSocketHandler.h"
+namespace mozilla {
+namespace net {
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebSocketHandler)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebSocketSSLHandler)
+} // namespace mozilla::net
+} // namespace mozilla
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "nsURIChecker.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsURIChecker)
 
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "nsURLParsers.h"
@@ -619,16 +629,19 @@ static void nsNetShutdown()
 #endif
     
     // Release necko strings
     delete gNetStrings;
     gNetStrings = nsnull;
     
     // Release DNS service reference.
     nsDNSPrefetch::Shutdown();
+
+    // Release the Websocket Admission Manager
+    mozilla::net::nsWebSocketHandler::Shutdown();
 }
 
 NS_DEFINE_NAMED_CID(NS_IOSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_STREAMTRANSPORTSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_SOCKETTRANSPORTSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_SERVERSOCKET_CID);
 NS_DEFINE_NAMED_CID(NS_SOCKETPROVIDERSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_DNSSERVICE_CID);
@@ -735,16 +748,20 @@ NS_DEFINE_NAMED_CID(NS_DATAPROTOCOLHANDL
 NS_DEFINE_NAMED_CID(NS_DEVICEPROTOCOLHANDLER_CID);
 #endif
 #ifdef NECKO_PROTOCOL_viewsource
 NS_DEFINE_NAMED_CID(NS_VIEWSOURCEHANDLER_CID);
 #endif
 #ifdef NECKO_PROTOCOL_wyciwyg
 NS_DEFINE_NAMED_CID(NS_WYCIWYGPROTOCOLHANDLER_CID);
 #endif
+#ifdef NECKO_PROTOCOL_websocket
+NS_DEFINE_NAMED_CID(NS_WEBSOCKETPROTOCOLHANDLER_CID);
+NS_DEFINE_NAMED_CID(NS_WEBSOCKETSSLPROTOCOLHANDLER_CID);
+#endif
 #if defined(XP_WIN)
 NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
 #elif defined(MOZ_WIDGET_COCOA)
 NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
 #elif defined(MOZ_ENABLE_LIBCONIC)
 NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
 #elif defined(MOZ_ENABLE_QTNETWORK)
 NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
@@ -864,16 +881,22 @@ static const mozilla::Module::CIDEntry k
     { &kNS_DEVICEPROTOCOLHANDLER_CID, false, NULL, nsDeviceProtocolHandlerConstructor},
 #endif
 #ifdef NECKO_PROTOCOL_viewsource
     { &kNS_VIEWSOURCEHANDLER_CID, false, NULL, nsViewSourceHandlerConstructor },
 #endif
 #ifdef NECKO_PROTOCOL_wyciwyg
     { &kNS_WYCIWYGPROTOCOLHANDLER_CID, false, NULL, nsWyciwygProtocolHandlerConstructor },
 #endif
+#ifdef NECKO_PROTOCOL_websocket
+    { &kNS_WEBSOCKETPROTOCOLHANDLER_CID, false, NULL,
+      mozilla::net::nsWebSocketHandlerConstructor },
+    { &kNS_WEBSOCKETSSLPROTOCOLHANDLER_CID, false, NULL,
+      mozilla::net::nsWebSocketSSLHandlerConstructor },
+#endif
 #if defined(XP_WIN)
     { &kNS_NETWORK_LINK_SERVICE_CID, false, NULL, nsNotifyAddrListenerConstructor },
 #elif defined(MOZ_WIDGET_COCOA)
     { &kNS_NETWORK_LINK_SERVICE_CID, false, NULL, nsNetworkLinkServiceConstructor },
 #elif defined(MOZ_ENABLE_LIBCONIC)
     { &kNS_NETWORK_LINK_SERVICE_CID, false, NULL, nsMaemoNetworkLinkServiceConstructor },
 #elif defined(MOZ_ENABLE_QTNETWORK)
     { &kNS_NETWORK_LINK_SERVICE_CID, false, NULL, nsQtNetworkLinkServiceConstructor },
@@ -1000,16 +1023,20 @@ static const mozilla::Module::ContractID
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "moz-device", &kNS_DEVICEPROTOCOLHANDLER_CID },
 #endif
 #ifdef NECKO_PROTOCOL_viewsource
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "view-source", &kNS_VIEWSOURCEHANDLER_CID },
 #endif
 #ifdef NECKO_PROTOCOL_wyciwyg
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "wyciwyg", &kNS_WYCIWYGPROTOCOLHANDLER_CID },
 #endif
+#ifdef NECKO_PROTOCOL_websocket
+    { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ws", &kNS_WEBSOCKETPROTOCOLHANDLER_CID },
+    { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "wss", &kNS_WEBSOCKETSSLPROTOCOLHANDLER_CID },
+#endif
 #if defined(XP_WIN)
     { NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
 #elif defined(MOZ_WIDGET_COCOA)
     { NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
 #elif defined(MOZ_ENABLE_LIBCONIC)
     { NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
 #elif defined(MOZ_ENABLE_QTNETWORK)
     { NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
--- a/netwerk/necko-config.h.in
+++ b/netwerk/necko-config.h.in
@@ -46,11 +46,12 @@
 #undef NECKO_PROTOCOL_about
 #undef NECKO_PROTOCOL_data
 #undef NECKO_PROTOCOL_device
 #undef NECKO_PROTOCOL_file
 #undef NECKO_PROTOCOL_ftp
 #undef NECKO_PROTOCOL_http
 #undef NECKO_PROTOCOL_res
 #undef NECKO_PROTOCOL_viewsource
+#undef NECKO_PROTOCOL_websocket
 #undef NECKO_PROTOCOL_wyciwyg
 
 #endif
--- a/netwerk/protocol/Makefile.in
+++ b/netwerk/protocol/Makefile.in
@@ -46,14 +46,15 @@ PARALLEL_DIRS = \
   about \
   data \
   device \
   file \
   ftp \
   http \
   res \
   viewsource \
+  websocket \
   wyciwyg \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 DEFINES += -DIMPL_NS_NET
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/websocket/Makefile.in
@@ -0,0 +1,69 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Patrick McManus <mcmanus@ducksong.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK ***** */
+
+DEPTH     = ../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE         = necko
+LIBRARY_NAME   = nkwebsocket_s
+LIBXUL_LIBRARY = 1
+XPIDL_MODULE   = necko_websocket
+GRE_MODULE     = 1
+FORCE_STATIC_LIB = 1
+
+XPIDLSRCS = \
+  nsIWebSocketProtocol.idl \
+  $(NULL)
+
+CPPSRCS = \
+  nsWebSocketHandler.cpp
+  $(NULL)
+
+LOCAL_INCLUDES = \
+  -I$(srcdir)/../../base/src \
+  -I$(topsrcdir)/xpcom/ds \
+  $(NULL)
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+include $(topsrcdir)/config/rules.mk
+
+DEFINES += -DIMPL_NS_NET
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/websocket/nsIWebSocketProtocol.idl
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Patrick McManus <mcmanus@ducksong.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+interface nsIURI;
+interface nsIInterfaceRequestor;
+interface nsIRunnable;
+interface nsILoadGroup;
+
+#include "nsISupports.idl"
+
+/**
+ * nsIWebSocketListener
+ */
+[scriptable, uuid(b0c27050-31e9-42e5-bc59-499d54b52f99)]
+interface nsIWebSocketListener : nsISupports
+{
+    /**
+     * Called to signify the establishment of the message stream.
+     * Any listener that receives onStart will also receive OnStop.
+     *
+     * @param aContext user defined context
+     */
+    void onStart(in nsISupports aContext);
+
+    /**
+     * Called to signify the completion of the message stream.
+     * OnStop is the final notification the listener will receive and it
+     * completes the WebSocket connection. This event can be received in error
+     * cases even if nsIWebSocketProtocol::Close() has not been called.
+     *
+     * @param aContext user defined context
+     * @param aStatusCode reason for stopping (NS_OK if completed successfully)
+     */
+    void onStop(in nsISupports aContext,
+                in nsresult aStatusCode);
+
+    /**
+     * Called to deliver text message.
+     *
+     * @param aContext user defined context
+     * @param aMsg the message data
+     */
+    void onMessageAvailable(in nsISupports aContext,
+                            in AUTF8String aMsg);
+
+    /**
+     * Called to deliver binary message.
+     *
+     * @param aContext user defined context
+     * @param aMsg the message data
+     */
+    void onBinaryMessageAvailable(in nsISupports aContext,
+                                  in ACString aMsg);
+
+    /**
+     * Called to acknowledge message sent via sendMsg() or sendBinaryMsg.
+     *
+     * @param aContext user defined context
+     * @param aSize number of bytes placed in OS send buffer
+     */
+    void onAcknowledge(in nsISupports aContext, in PRUint32 aSize);
+
+    /**
+     * Called to inform receipt of WebSocket Close message from server.
+     * In the case of errors onStop() can be called without ever
+     * receiving server close.
+     *
+     * No additional messages through onMessageAvailable(),
+     * onBinaryMessageAvailable() or onAcknowledge() will be delievered
+     * to the listener after onServerClose(), though outgoing messages can still
+     * be sent through the nsIWebSocketProtocol connection.
+     */
+    void onServerClose(in nsISupports aContext);
+    
+};
+
+[scriptable, uuid(dc01db59-a513-4c90-824b-085cce06c0aa)]
+interface nsIWebSocketProtocol : nsISupports
+{
+    /**
+     * The original URI used to construct the protocol connection. This is used
+     * in the case of a redirect or URI "resolution" (e.g. resolving a
+     * resource: URI to a file: URI) so that the original pre-redirect
+     * URI can still be obtained.  This is never null.
+     */
+    readonly attribute nsIURI originalURI;
+
+    /**
+     * The readonly URI corresponding to the protocol connection after any
+     * redirections are completed.
+     */
+    readonly attribute nsIURI URI;
+
+    /**
+     * The notification callbacks for authorization, etc..
+     */
+    attribute nsIInterfaceRequestor notificationCallbacks;
+
+    /**
+     * Transport-level security information (if any)
+     */
+    readonly attribute nsISupports securityInfo;
+
+    /**
+     * The load group of the websockets code.
+     */
+    attribute nsILoadGroup loadGroup;
+
+    /**
+     * Sec-Websocket-Protocol value
+     */
+    attribute ACString protocol;
+
+    /**
+     * Asynchronously open the websocket connection.  Received messages are fed
+     * to the socket listener as they arrive.  The socket listener's methods
+     * are called on the thread that calls asyncOpen and are not called until
+     * after asyncOpen returns.  If asyncOpen returns successfully, the
+     * protocol implementation promises to call at least onStart and onStop of
+     * the listener.
+     *
+     * NOTE: Implementations should throw NS_ERROR_ALREADY_OPENED if the
+     * websocket connection is reopened.
+     *
+     * @param aURI the uri of the websocket protocol - may be redirected
+     * @param aOrigin the uri of the originating resource
+     * @param aListener the nsIWebSocketListener implementation
+     * @param aContext an opaque parameter forwarded to aListener's methods
+     */
+    void asyncOpen(in nsIURI aURI,
+                   in ACString aOrigin,
+                   in nsIWebSocketListener aListener,
+                   in nsISupports aContext);
+
+    /*
+     * Close the websocket connection for writing - no more calls to sendMsg
+     * or sendBinaryMsg should be made after calling this. The listener object
+     * may receive more messages if a server close has not yet been received.
+     */
+    void close();
+    
+    /**
+     * Use to send text message down the connection to WebSocket peer.
+     *
+     * @param aMsg the utf8 string to send
+     */
+    void sendMsg(in AUTF8String aMsg);
+
+    /**
+     * Use to send binary message down the connection to WebSocket peer.
+     *
+     * @param aMsg the data to send
+     */
+    void sendBinaryMsg(in ACString aMsg);
+};
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/websocket/nsWebSocketHandler.cpp
@@ -0,0 +1,2549 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Wellington Fernando de Macedo <wfernandom2004@gmail.com> (original author)
+ *   Patrick McManus <mcmanus@ducksong.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsWebSocketHandler.h"
+
+#include "nsISocketTransportService.h"
+#include "nsIURI.h"
+#include "nsIChannel.h"
+#include "nsICryptoHash.h"
+#include "nsIRunnable.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsICancelable.h"
+#include "nsIDNSRecord.h"
+#include "nsIDNSService.h"
+#include "nsIStreamConverterService.h"
+#include "nsIIOService2.h"
+#include "nsIProtocolProxyService.h"
+
+#include "nsAutoPtr.h"
+#include "nsStandardURL.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsXPIDLString.h"
+#include "nsCRT.h"
+#include "nsThreadUtils.h"
+#include "nsNetError.h"
+#include "nsStringStream.h"
+#include "nsAlgorithm.h"
+#include "nsProxyRelease.h"
+
+#include "plbase64.h"
+#include "prmem.h"
+#include "prnetdb.h"
+#include "prbit.h"
+#include "prlog.h"
+#include "zlib.h"
+
+extern PRThread *gSocketThread;
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_THREADSAFE_ISUPPORTS11(nsWebSocketHandler,
+                               nsIWebSocketProtocol,
+                               nsIHttpUpgradeListener,
+                               nsIRequestObserver,
+                               nsIStreamListener,
+                               nsIProtocolHandler,
+                               nsIInputStreamCallback,
+                               nsIOutputStreamCallback,
+                               nsITimerCallback,
+                               nsIDNSListener,
+                               nsIInterfaceRequestor,
+                               nsIChannelEventSink)
+
+#if defined(PR_LOGGING)
+static PRLogModuleInfo *webSocketLog = nsnull;
+#endif
+#define LOG(args) PR_LOG(webSocketLog, PR_LOG_DEBUG, args)
+
+// Use this fake ptr so the Fin message stays in sequence in the
+// main transmit queue
+#define kFinMessage (reinterpret_cast<nsCString *>(0x01))
+
+// An implementation of draft-ietf-hybi-thewebsocketprotocol-07
+#define SEC_WEBSOCKET_VERSION "7"
+
+/*
+ * About using rand() without a srand() or initstate()
+ *
+ * rand() is commonly called throughout the codebase with the assumption
+ * that it has been seeded securely. That does indeed happen in
+ * the initialization of the nsUUIDGenerator sevice - getting secure
+ * seed information from  PR_GetRandomNoise() and giving that to
+ * initstate(). We won't repeat that here "just in case" because
+ * it is easy to drain some systems of their true random sources.
+ */
+
+
+/*
+ * About SSL unsigned certificates
+ *
+ * wss will not work to a host using an unsigned certificate unless there
+ * is already an exception (i.e. it cannot popup a dialog asking for
+ * a security exception). This is similar to how an inlined img will
+ * fail without a dialog if fails for the same reason. This should not
+ * be a problem in practice as it is expected the websocket javascript
+ * is served from the same host as the websocket server (or of course,
+ * a valid cert could just be provided).
+ *
+ */
+
+// some helper classes
+
+class CallOnMessageAvailable : public nsIRunnable
+{
+public:
+    NS_DECL_ISUPPORTS
+        
+    CallOnMessageAvailable(nsIWebSocketListener *aListener,
+                           nsISupports          *aContext,
+                           nsCString            &aData,
+                           PRInt32               aLen)
+      : mListener(aListener),
+        mContext(aContext),
+        mData(aData),
+        mLen(aLen) {}
+    
+    NS_SCRIPTABLE NS_IMETHOD Run()
+    {
+        if (mLen < 0)
+            mListener->OnMessageAvailable(mContext, mData);
+        else
+            mListener->OnBinaryMessageAvailable(mContext, mData);
+        return NS_OK;
+    }
+
+private:
+    ~CallOnMessageAvailable() {}
+
+    nsCOMPtr<nsIWebSocketListener>    mListener;
+    nsCOMPtr<nsISupports>             mContext;
+    nsCString                         mData;
+    PRInt32                           mLen;
+};
+NS_IMPL_THREADSAFE_ISUPPORTS1(CallOnMessageAvailable, nsIRunnable)
+
+class CallOnStop : public nsIRunnable
+{
+public:
+    NS_DECL_ISUPPORTS
+        
+    CallOnStop(nsIWebSocketListener *aListener,
+               nsISupports          *aContext,
+               nsresult              aData)
+    : mListener(aListener),
+      mContext(aContext),
+      mData(aData) {}
+    
+    NS_SCRIPTABLE NS_IMETHOD Run()
+    {
+        mListener->OnStop(mContext, mData);
+        return NS_OK;
+    }
+
+private:
+    ~CallOnStop() {}
+
+    nsCOMPtr<nsIWebSocketListener>    mListener;
+    nsCOMPtr<nsISupports>             mContext;
+    nsresult                          mData;
+};
+NS_IMPL_THREADSAFE_ISUPPORTS1(CallOnStop, nsIRunnable)
+
+class CallOnServerClose : public nsIRunnable
+{
+public:
+    NS_DECL_ISUPPORTS
+        
+    CallOnServerClose(nsIWebSocketListener *aListener,
+                      nsISupports          *aContext)
+    : mListener(aListener),
+      mContext(aContext) {}
+    
+    NS_SCRIPTABLE NS_IMETHOD Run()
+    {
+        mListener->OnServerClose(mContext);
+        return NS_OK;
+    }
+
+private:
+    ~CallOnServerClose() {}
+
+    nsCOMPtr<nsIWebSocketListener>    mListener;
+    nsCOMPtr<nsISupports>             mContext;
+};
+NS_IMPL_THREADSAFE_ISUPPORTS1(CallOnServerClose, nsIRunnable)
+
+class CallAcknowledge : public nsIRunnable
+{
+public:
+    NS_DECL_ISUPPORTS
+        
+    CallAcknowledge(nsIWebSocketListener *aListener,
+                    nsISupports          *aContext,
+                    PRUint32              aSize)
+    : mListener(aListener),
+      mContext(aContext),
+      mSize(aSize) {}
+
+    NS_SCRIPTABLE NS_IMETHOD Run()
+    {
+        LOG(("WebSocketHandler::CallAcknowledge Size %u\n", mSize));
+        mListener->OnAcknowledge(mContext, mSize);
+        return NS_OK;
+    }
+    
+private:
+    ~CallAcknowledge() {}
+
+    nsCOMPtr<nsIWebSocketListener>    mListener;
+    nsCOMPtr<nsISupports>             mContext;
+    PRUint32                          mSize;
+};
+NS_IMPL_THREADSAFE_ISUPPORTS1(CallAcknowledge, nsIRunnable)
+
+class nsPostMessage : public nsIRunnable
+{
+public:
+    NS_DECL_ISUPPORTS
+        
+    nsPostMessage(nsWebSocketHandler *handler,
+                  nsCString          *aData,
+                  PRInt32             aDataLen)
+        : mHandler(handler),
+        mData(aData),
+        mDataLen(aDataLen) {}
+    
+    NS_SCRIPTABLE NS_IMETHOD Run()
+    {
+        if (mData)
+            mHandler->SendMsgInternal(mData, mDataLen);
+        return NS_OK;
+    }
+
+private:
+    ~nsPostMessage() {}
+    
+    nsRefPtr<nsWebSocketHandler>    mHandler;
+    nsCString                      *mData;
+    PRInt32                         mDataLen;
+};
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsPostMessage, nsIRunnable)
+
+
+// Section 5.1 requires that a client rate limit its connects to a single
+// TCP session in the CONNECTING state (i.e. anything before the 101 upgrade
+// complete response comes back and an open javascript event is created)
+
+class nsWSAdmissionManager
+{
+public:
+    nsWSAdmissionManager() {}
+
+    class nsOpenConn
+    {
+    public:
+        nsOpenConn(nsCString &addr, nsWebSocketHandler *handler)
+            : mAddress(addr), mHandler(handler)
+        {}
+        ~nsOpenConn() {}
+        
+        nsCString mAddress;
+        nsRefPtr<nsWebSocketHandler> mHandler;
+    };
+    
+    ~nsWSAdmissionManager()
+    {
+        for (PRUint32 i = 0; i < mData.Length(); i++)
+            delete mData[i];
+    }
+
+    PRBool ConditionallyConnect(nsCString &aStr, nsWebSocketHandler *ws)
+    {
+        NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+
+        // if aStr is not in mData then we return true, else false.
+        // in either case aStr is then added to mData - meaning
+        // there will be duplicates when this function has been
+        // called with the same parameter multiple times.
+
+        // we could hash this, but the dataset is expected to be
+        // small
+        
+        PRBool found = (IndexOf(aStr) >= 0);
+        nsOpenConn *newdata = new nsOpenConn(aStr, ws);
+        mData.AppendElement(newdata);
+
+        if (!found)
+            ws->BeginOpen();
+        return !found;
+    }
+
+    PRBool Complete(nsCString &aStr)
+    {
+        NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+        PRInt32 index = IndexOf(aStr);
+        NS_ABORT_IF_FALSE(index >= 0, "completed connection not in open list");
+        
+        nsOpenConn *olddata = mData[index];
+        mData.RemoveElementAt(index);
+        delete olddata;
+        
+        // are there more of the same address pending dispatch?
+        index = IndexOf(aStr);
+        if (index >= 0) {
+            (mData[index])->mHandler->BeginOpen();
+            return PR_TRUE;
+        }
+        return PR_FALSE;
+    }
+    
+private:
+    nsTArray<nsOpenConn *> mData;
+
+    PRInt32 IndexOf(nsCString &aStr)
+    {
+        for (PRUint32 i = 0; i < mData.Length(); i++)
+            if (aStr == (mData[i])->mAddress)
+                return i;
+        return -1;
+    }
+};
+
+// similar to nsDeflateConverter except for the mandatory FLUSH calls
+// required by websocket and the absence of the deflate termination
+// block which is appropriate because it would create data bytes after
+// sending the websockets CLOSE message.
+
+class nsWSCompression
+{
+public:
+  nsWSCompression(nsIStreamListener *aListener,
+                  nsISupports *aContext)
+      : mActive(PR_FALSE),
+        mContext(aContext),
+        mListener(aListener)
+    {
+        mZlib.zalloc = allocator;
+        mZlib.zfree = destructor;
+        mZlib.opaque = Z_NULL;
+        
+        // Initialize the compressor - these are all the normal zlib
+        // defaults except window size is set to -15 instead of +15.
+        // This is the zlib way of specifying raw RFC 1951 output instead
+        // of the zlib rfc 1950 format which has a 2 byte header and
+        // adler checksum as a trailer
+        
+        nsresult rv;
+        mStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
+        if (NS_SUCCEEDED(rv) && aContext && aListener &&
+            deflateInit2(&mZlib, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
+                         -15, 8, Z_DEFAULT_STRATEGY) == Z_OK) {
+            mActive = PR_TRUE;
+        }
+    }
+
+    ~nsWSCompression()
+    {
+        if (mActive)
+            deflateEnd(&mZlib);
+    }
+
+    PRBool Active()
+    {
+        return mActive;
+    }
+
+    nsresult Deflate(PRUint8 *buf1, PRUint32 buf1Len,
+                     PRUint8 *buf2, PRUint32 buf2Len)
+    {
+        NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
+                          "not socket thread");
+        NS_ABORT_IF_FALSE(mActive, "not active");
+        
+        mZlib.avail_out = kBufferLen;
+        mZlib.next_out = mBuffer;
+        mZlib.avail_in = buf1Len;
+        mZlib.next_in = buf1;
+        
+        nsresult rv;
+        
+        while (mZlib.avail_in > 0) {
+            deflate(&mZlib, (buf2Len > 0) ? Z_NO_FLUSH : Z_SYNC_FLUSH);
+            rv = PushData();
+            if (NS_FAILED(rv))
+                return rv;
+            mZlib.avail_out = kBufferLen;
+            mZlib.next_out = mBuffer;
+        }
+        
+        mZlib.avail_in = buf2Len;
+        mZlib.next_in = buf2;
+
+        while (mZlib.avail_in > 0) {
+            deflate(&mZlib, Z_SYNC_FLUSH);
+            rv = PushData();
+            if (NS_FAILED(rv))
+                return rv;
+            mZlib.avail_out = kBufferLen;
+            mZlib.next_out = mBuffer;
+        }
+        
+        return NS_OK;
+    }
+    
+private:
+    
+    // use zlib data types
+    static void *allocator(void *opaque, uInt items, uInt size)
+    {
+        return moz_xmalloc(items * size);
+    }
+
+    static void destructor(void *opaque, void *addr)
+    {
+        moz_free(addr);
+    }
+
+    nsresult PushData()
+    {
+        PRUint32 bytesToWrite = kBufferLen - mZlib.avail_out;
+        if (bytesToWrite > 0) {
+            mStream->ShareData(reinterpret_cast<char *>(mBuffer), bytesToWrite);
+            nsresult rv;
+            rv = mListener->OnDataAvailable(nsnull, mContext,
+                                            mStream, 0, bytesToWrite);
+            if (NS_FAILED(rv))
+                return rv;
+        }
+        return NS_OK;
+    }
+
+    PRBool    mActive;
+    z_stream  mZlib;
+    nsCOMPtr<nsIStringInputStream>  mStream;
+
+    nsISupports *mContext;                        /* weak ref */
+    nsIStreamListener *mListener;                 /* weak ref */
+    
+    const static PRInt32 kBufferLen = 4096;
+    PRUint8   mBuffer[kBufferLen];
+};
+
+static nsWSAdmissionManager *sWebSocketAdmissions = nsnull;
+
+// nsWebSocketHandler
+
+nsWebSocketHandler::nsWebSocketHandler() :
+    mEncrypted(PR_FALSE),
+    mCloseTimeout(20000),
+    mOpenTimeout(20000),
+    mPingTimeout(0),
+    mPingResponseTimeout(10000),
+    mRecvdHttpOnStartRequest(0),
+    mRecvdHttpUpgradeTransport(0),
+    mRequestedClose(0),
+    mClientClosed(0),
+    mServerClosed(0),
+    mStopped(0),
+    mCalledOnStop(0),
+    mPingOutstanding(0),
+    mAllowCompression(1),
+    mAutoFollowRedirects(0),
+    mReleaseOnTransmit(0),
+    mMaxMessageSize(16000000),
+    mStopOnClose(NS_OK),
+    mFragmentOpcode(0),
+    mFragmentAccumulator(0),
+    mBuffered(0),
+    mBufferSize(16384),
+    mCurrentOut(nsnull),
+    mCurrentOutSent(0),
+    mCompressor(nsnull),
+    mDynamicOutputSize(0),
+    mDynamicOutput(nsnull)
+{
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+#if defined(PR_LOGGING)
+    if (!webSocketLog)
+        webSocketLog = PR_NewLogModule("nsWebSocket");
+#endif
+
+    LOG(("WebSocketHandler::nsWebSocketHandler() %p\n", this));
+    
+    if (!sWebSocketAdmissions)
+        sWebSocketAdmissions = new nsWSAdmissionManager();
+
+    mFramePtr = mBuffer = static_cast<PRUint8 *>(moz_xmalloc(mBufferSize));
+}
+
+nsWebSocketHandler::~nsWebSocketHandler()
+{
+    LOG(("WebSocketHandler::~nsWebSocketHandler() %p\n", this));
+
+    // this stop is a nop if the normal connect/close is followed
+    mStopped = 1;
+    StopSession(NS_ERROR_UNEXPECTED);
+    
+    moz_free(mBuffer);
+    moz_free(mDynamicOutput);
+    delete mCompressor;
+    delete mCurrentOut;
+
+    while ((mCurrentOut = (OutboundMessage *) mOutgoingPingMessages.PopFront()))
+        delete mCurrentOut;
+    while ((mCurrentOut = (OutboundMessage *) mOutgoingPongMessages.PopFront()))
+        delete mCurrentOut;
+    while ((mCurrentOut = (OutboundMessage *) mOutgoingMessages.PopFront()))
+        delete mCurrentOut;
+
+    nsCOMPtr<nsIThread> mainThread;
+    nsIURI *forgettable;
+    NS_GetMainThread(getter_AddRefs(mainThread));
+    
+    if (mURI) {
+        mURI.forget(&forgettable);
+        NS_ProxyRelease(mainThread, forgettable, PR_FALSE);
+    }
+    
+    if (mOriginalURI) {
+        mOriginalURI.forget(&forgettable);
+        NS_ProxyRelease(mainThread, forgettable, PR_FALSE);
+    }
+
+    if (mListener) {
+        nsIWebSocketListener *forgettableListener;
+        mListener.forget(&forgettableListener);
+        NS_ProxyRelease(mainThread, forgettableListener, PR_FALSE);
+    }
+
+    if (mContext) {
+        nsISupports *forgettableContext;
+        mContext.forget(&forgettableContext);
+        NS_ProxyRelease(mainThread, forgettableContext, PR_FALSE);
+    }
+}
+
+void
+nsWebSocketHandler::Shutdown()
+{
+    delete sWebSocketAdmissions;
+    sWebSocketAdmissions = nsnull;
+}
+
+nsresult
+nsWebSocketHandler::BeginOpen()
+{
+    LOG(("WebSocketHandler::BeginOpen() %p\n", this));
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+
+    nsresult rv;
+
+    if (mRedirectCallback) {
+        LOG(("WebSocketHandler::BeginOpen Resuming Redirect\n"));
+        rv = mRedirectCallback->OnRedirectVerifyCallback(NS_OK);
+        mRedirectCallback = nsnull;
+        return rv;
+    }
+
+    nsCOMPtr<nsIChannel> localChannel = do_QueryInterface(mChannel, &rv);
+    if (NS_FAILED(rv)) {
+        LOG(("WebSocketHandler::BeginOpen cannot async open\n"));
+        AbortSession(NS_ERROR_CONNECTION_REFUSED);
+        return rv;
+    }
+
+    rv = localChannel->AsyncOpen(this, mHttpChannel);
+    if (NS_FAILED(rv)) {
+        LOG(("WebSocketHandler::BeginOpen cannot async open\n"));
+        AbortSession(NS_ERROR_CONNECTION_REFUSED);
+        return rv;
+    }
+
+    mOpenTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+    if (NS_SUCCEEDED(rv))
+        mOpenTimer->InitWithCallback(this, mOpenTimeout,
+                                     nsITimer::TYPE_ONE_SHOT);
+
+    return rv;
+}
+
+PRBool
+nsWebSocketHandler::IsPersistentFramePtr()
+{
+    return (mFramePtr >= mBuffer && mFramePtr < mBuffer + mBufferSize);
+}
+
+// extends the internal buffer by count and returns the total
+// amount of data available for read
+PRUint32
+nsWebSocketHandler::UpdateReadBuffer(PRUint8 *buffer, PRUint32 count)
+{
+    LOG(("WebSocketHandler::UpdateReadBuffer() %p [%p %u]\n",
+         this, buffer, count));
+
+    if (!mBuffered)
+        mFramePtr = mBuffer;
+    
+    NS_ABORT_IF_FALSE(IsPersistentFramePtr(),
+                      "update read buffer bad mFramePtr");
+
+    if (mBuffered + count <= mBufferSize) {
+        // append to existing buffer
+        LOG(("WebSocketHandler:: update read buffer absorbed %u\n", count));
+    }
+    else if (mBuffered + count - (mFramePtr - mBuffer) <= mBufferSize) {
+        // make room in existing buffer by shifting unused data to start
+        mBuffered -= (mFramePtr - mBuffer);
+        LOG(("WebSocketHandler:: update read buffer shifted %u\n",
+             mBuffered));
+        ::memmove(mBuffer, mFramePtr, mBuffered);
+        mFramePtr = mBuffer;
+    }
+    else {
+        // existing buffer is not sufficient, extend it
+        mBufferSize += count + 8192;
+        LOG(("WebSocketHandler:: update read buffer extended to %u\n",
+             mBufferSize));
+        PRUint8 *old = mBuffer;
+        mBuffer = (PRUint8 *)moz_xrealloc(mBuffer, mBufferSize);
+        mFramePtr = mBuffer + (mFramePtr - old);
+    }
+    
+    ::memcpy(mBuffer + mBuffered, buffer, count);
+    mBuffered += count;
+    
+    return mBuffered - (mFramePtr - mBuffer);
+}
+
+nsresult
+nsWebSocketHandler::ProcessInput(PRUint8 *buffer, PRUint32 count)
+{
+    LOG(("WebSocketHandler::ProcessInput %p [%d %d]\n",
+         this, count, mBuffered));
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
+                      "not socket thread");
+    
+    // reset the ping timer
+    if (mPingTimer) {
+        // The purpose of ping/pong is to actively probe the peer so that an
+        // unreachable peer is not mistaken for a period of idleness. This
+        // implementation accepts any application level read activity as a
+        // sign of life, it does not necessarily have to be a pong.
+        
+        mPingOutstanding = 0;
+        mPingTimer->SetDelay(mPingTimeout);
+    }
+
+    PRUint32 avail;
+
+    if (!mBuffered) {
+        // most of the time we can process right off the stack buffer
+        // without having to accumulate anything
+
+        mFramePtr = buffer;
+        avail = count;
+    }
+    else {
+        avail = UpdateReadBuffer(buffer, count);
+    }
+
+    PRUint8 *payload;
+    PRUint32 totalAvail = avail;
+
+    while (avail >= 2) {
+
+        PRInt64 payloadLength = mFramePtr[1] & 0x7F;
+        PRUint8 finBit        = mFramePtr[0] & kFinalFragBit;
+        PRUint8 rsvBits       = mFramePtr[0] & 0x70;
+        PRUint8 maskBit       = mFramePtr[1] & kMaskBit;
+        PRUint8 opcode        = mFramePtr[0] & 0x0F;
+
+        PRUint32 framingLength = 2; 
+        if (maskBit)
+            framingLength += 4;
+
+        if (payloadLength < 126) {
+            if (avail < framingLength)
+                break;
+        }
+        else if (payloadLength == 126) {
+            // 16 bit length field
+            framingLength += 2;
+            if (avail < framingLength)
+                break;
+
+            payloadLength = mFramePtr[2] << 8 | mFramePtr[3];
+        }
+        else {
+            // 64 bit length
+            framingLength += 8;
+            if (avail < framingLength)
+                break;
+            // copy this in case it is unaligned
+            PRUint64 tempLen;
+            memcpy(&tempLen, mFramePtr + 2, 8);
+            payloadLength = PR_ntohll(tempLen);
+        }
+
+        payload = mFramePtr + framingLength;
+        avail -= framingLength;
+        
+        LOG(("WebSocketHandler:: ProcessInput payload %lld avail %lu\n",
+             payloadLength, avail));
+
+        // we don't deal in > 31 bit websocket lengths.. and probably
+        // something considerably shorter (16MB by default)
+        if (payloadLength + mFragmentAccumulator > mMaxMessageSize) {
+            AbortSession(NS_ERROR_FILE_TOO_BIG);
+            return NS_ERROR_FILE_TOO_BIG;
+        }
+        
+        if (avail < payloadLength)
+            break;
+
+        LOG(("WebSocketHandler::ProcessInput Frame accumulated - opcode %d\n",
+             opcode));
+
+        if (maskBit) {
+            // This is unexpected - the server does not generally send masked
+            // frames to the client, but it is allowed
+            LOG(("WebSocketHandler:: Client RECEIVING masked frame."));
+
+            PRUint32 mask;
+            memcpy(&mask, payload - 4, 4);
+            mask = PR_ntohl(mask);
+            ApplyMask(mask, payload, payloadLength);
+        }
+        
+        // control codes are required to have the fin bit set
+        if (!finBit && (opcode & kControlFrameMask)) {
+            LOG(("WebSocketHandler:: fragmented control frame code %d\n",
+                 opcode));
+            AbortSession(NS_ERROR_ILLEGAL_VALUE);
+            return NS_ERROR_ILLEGAL_VALUE;
+        }
+
+        if (rsvBits) {
+            LOG(("WebSocketHandler:: unexpected reserved bits %x\n", rsvBits));
+            AbortSession(NS_ERROR_ILLEGAL_VALUE);
+            return NS_ERROR_ILLEGAL_VALUE;
+        }
+
+        if (!finBit || opcode == kContinuation) {
+            // This is part of a fragment response
+
+            // only the first frame has a non zero op code
+            // make sure we don't see a first frame while some old
+            // fragments are open
+            if ((mFragmentAccumulator != 0) && (opcode != kContinuation)) {
+                LOG(("WebSocketHeandler:: nested fragments\n"));
+                AbortSession(NS_ERROR_ILLEGAL_VALUE);
+                return NS_ERROR_ILLEGAL_VALUE;
+            }
+
+            LOG(("WebSocketHandler:: Accumulating Fragment %lld\n",
+                 payloadLength));
+            
+            if (opcode == kContinuation) {
+                // for frag > 1 move the data body back on top of the headers
+                // so we have contiguous stream of data
+                NS_ABORT_IF_FALSE(mFramePtr + framingLength == payload,
+                                  "payload offset from frameptr wrong");
+                ::memmove (mFramePtr, payload, avail);
+                payload = mFramePtr;
+                if (mBuffered)
+                    mBuffered -= framingLength;
+            }
+            else {
+                mFragmentOpcode = opcode;
+            }
+            
+            if (finBit) {
+                LOG(("WebSocketHandler:: Finalizing Fragment\n"));
+                payload -= mFragmentAccumulator;
+                payloadLength += mFragmentAccumulator;
+                avail += mFragmentAccumulator;
+                mFragmentAccumulator = 0;
+                opcode = mFragmentOpcode;
+            } else {
+                opcode = kContinuation;
+                mFragmentAccumulator += payloadLength;
+            }
+        }
+        else if (mFragmentAccumulator != 0 && !(opcode & kControlFrameMask)) {
+            // this frame is not part of a fragment sequence but we
+            // have an open fragment.. it must be a control code or else
+            // we have a problem
+            LOG(("WebSocketHeandler:: illegal fragment sequence\n"));
+            AbortSession(NS_ERROR_ILLEGAL_VALUE);
+            return NS_ERROR_ILLEGAL_VALUE;
+        }
+
+        if (mServerClosed) {
+            LOG(("WebSocketHandler:: ignoring read frame code %d after close\n",
+                 opcode));
+            // nop
+        }
+        else if (mStopped) {
+            LOG(("WebSocketHandler:: "
+                 "ignoring read frame code %d after completion\n",
+                 opcode));
+        }
+        else if (opcode == kText) {
+            LOG(("WebSocketHandler:: text frame received\n"));
+            if (mListener) {
+                nsCString utf8Data((const char *)payload, payloadLength);
+                nsCOMPtr<nsIRunnable> event =
+                    new CallOnMessageAvailable(mListener, mContext,
+                                               utf8Data, -1);
+                NS_DispatchToMainThread(event);
+            }
+        }
+        else if (opcode & kControlFrameMask) {
+            // control frames
+            if (payloadLength > 125) {
+                LOG(("WebSocketHandler:: bad control frame code %d length %d\n",
+                     opcode, payloadLength));
+                AbortSession(NS_ERROR_ILLEGAL_VALUE);
+                return NS_ERROR_ILLEGAL_VALUE;
+            }
+            
+            if (opcode == kClose) {
+                LOG(("WebSocketHandler:: close received\n"));
+                mServerClosed = 1;
+                
+                if (payloadLength >= 2) {
+                    PRUint16 code;
+                    memcpy(&code, payload, 2);
+                    code = PR_ntohs(code);
+                    LOG(("WebSocketHandler:: close recvd code %u\n", code));
+                    PRUint16 msglen = payloadLength - 2;
+                    if (msglen > 0) {
+                        nsCString utf8Data((const char *)payload + 2, msglen);
+                        LOG(("WebSocketHandler:: close msg  %s\n",
+                             utf8Data.get()));
+                    }
+                }
+
+                if (mCloseTimer) {
+                    mCloseTimer->Cancel();
+                    mCloseTimer = nsnull;
+                }
+                nsCOMPtr<nsIRunnable> event =
+                    new CallOnServerClose(mListener, mContext);
+                NS_DispatchToMainThread(event);
+
+                if (mClientClosed)
+                    ReleaseSession();
+            }
+            else if (opcode == kPing) {
+                LOG(("WebSocketHandler:: ping received\n"));
+                GeneratePong(payload, payloadLength);
+            }
+            else {
+                // opcode kPong 
+                // The mere act of receiving the packet is all we need to
+                // do for the pong to trigger the activity timers
+                LOG(("WebSocketHandler:: pong received\n"));
+            }
+
+            if (mFragmentAccumulator) {
+                // we need to remove the control frame from the stream
+                // so we have a contiguous data buffer of reassembled fragments
+                LOG(("WebSocketHandler:: Removing Control From Read buffer\n"));
+                NS_ABORT_IF_FALSE(mFramePtr + framingLength == payload,
+                                  "payload offset from frameptr wrong");
+                ::memmove (mFramePtr, payload + payloadLength,
+                           avail - payloadLength);
+                payload = mFramePtr;
+                avail -= payloadLength;
+                payloadLength = 0;
+                if (mBuffered)
+                    mBuffered -= framingLength + payloadLength;
+            }
+        }
+        else if (opcode == kBinary) {
+            LOG(("WebSocketHandler:: binary frame received\n"));
+            if (mListener) {
+                nsCString utf8Data((const char *)payload, payloadLength);
+                nsCOMPtr<nsIRunnable> event =
+                    new CallOnMessageAvailable(mListener, mContext,
+                                               utf8Data, payloadLength);
+                NS_DispatchToMainThread(event);
+            }
+        }
+        else if (opcode != kContinuation) {
+            /* unknown opcode */
+            LOG(("WebSocketHandler:: unknown op code %d\n", opcode));
+            AbortSession(NS_ERROR_ILLEGAL_VALUE);
+            return NS_ERROR_ILLEGAL_VALUE;
+        }
+            
+        mFramePtr = payload + payloadLength;
+        avail -= payloadLength;
+        totalAvail = avail;
+    }
+
+    // Adjust the stateful buffer. If we were operating off the stack and
+    // now have a partial message then transition to the buffer, or if
+    // we were working off the buffer but no longer have any active state
+    // then transition to the stack
+    if (!IsPersistentFramePtr()) {
+        mBuffered = 0;
+        
+        if (mFragmentAccumulator) {
+            LOG(("WebSocketHandler:: Setup Buffer due to fragment"));
+            
+            UpdateReadBuffer(mFramePtr - mFragmentAccumulator,
+                             totalAvail + mFragmentAccumulator);
+
+            // UpdateReadBuffer will reset the frameptr to the beginning
+            // of new saved state, so we need to skip past processed framgents
+            mFramePtr += mFragmentAccumulator;
+        }
+        else if (totalAvail) {
+            LOG(("WebSocketHandler:: Setup Buffer due to partial frame"));
+            UpdateReadBuffer(mFramePtr, totalAvail);
+        }
+    }
+    else if (!mFragmentAccumulator && !totalAvail) {
+        // If we were working off a saved buffer state and there is no
+        // partial frame or fragment in process, then revert to stack
+        // behavior
+        LOG(("WebSocketHandler:: Internal buffering not needed anymore"));
+        mBuffered = 0;
+    }
+    return NS_OK;
+}
+
+void
+nsWebSocketHandler::ApplyMask(PRUint32 mask, PRUint8 *data, PRUint64 len)
+{
+    // Optimally we want to apply the mask 32 bits at a time,
+    // but the buffer might not be alligned. So we first deal with
+    // 0 to 3 bytes of preamble individually
+
+    while (len && ((unsigned long) data & 3)) {
+        *data ^= mask >> 24;
+        mask = PR_ROTATE_LEFT32(mask, 8);
+        data++;
+        len--;
+    }
+    
+    // perform mask on full words of data
+
+    PRUint32 *iData = (PRUint32 *) data;
+    PRUint32 *end = iData + (len / 4);
+    mask = PR_htonl(mask);
+    for (; iData < end; iData++)
+        *iData ^= mask;
+    mask = PR_ntohl(mask);
+    data = (PRUint8 *)iData;
+    len  = len % 4;
+    
+    // There maybe up to 3 trailing bytes that need to be dealt with
+    // individually 
+    
+    while (len) {
+        *data ^= mask >> 24;
+        mask = PR_ROTATE_LEFT32(mask, 8);
+        data++;
+        len--;
+    }
+}
+
+void
+nsWebSocketHandler::GeneratePing()
+{
+    LOG(("WebSocketHandler::GeneratePing() %p\n", this));
+
+    nsCString *buf = new nsCString();
+    buf->Assign("PING");
+    mOutgoingPingMessages.Push(new OutboundMessage(buf));
+    OnOutputStreamReady(mSocketOut);
+}
+
+void
+nsWebSocketHandler::GeneratePong(PRUint8 *payload, PRUint32 len)
+{
+    LOG(("WebSocketHandler::GeneratePong() %p [%p %u]\n", this, payload, len));
+
+    nsCString *buf = new nsCString();
+    buf->SetLength(len);
+    if (buf->Length() < len) {
+        LOG(("WebSocketHandler::GeneratePong Allocation Failure\n"));
+        delete buf;
+        return;
+    }
+    
+    memcpy(buf->BeginWriting(), payload, len);
+    mOutgoingPongMessages.Push(new OutboundMessage(buf));
+    OnOutputStreamReady(mSocketOut);
+}
+
+void
+nsWebSocketHandler::SendMsgInternal(nsCString *aMsg,
+                                    PRInt32 aDataLen)
+{
+    LOG(("WebSocketHandler::SendMsgInternal %p [%p len=%d]\n",
+         this, aMsg, aDataLen));
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
+                      "not socket thread");
+    if (aMsg == kFinMessage)
+        mOutgoingMessages.Push(new OutboundMessage());
+    else if (aDataLen < 0)
+        mOutgoingMessages.Push(new OutboundMessage(aMsg));
+    else
+        mOutgoingMessages.Push(new OutboundMessage(aMsg, aDataLen));
+    OnOutputStreamReady(mSocketOut);
+}
+
+void
+nsWebSocketHandler::PrimeNewOutgoingMessage()
+{
+    LOG(("WebSocketHandler::PrimeNewOutgoingMessage() %p\n", this));
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
+                      "not socket thread");
+    NS_ABORT_IF_FALSE(!mCurrentOut, "Current message in progress");
+
+    PRBool isPong = PR_FALSE;
+    PRBool isPing = PR_FALSE;
+    
+    mCurrentOut = (OutboundMessage *)mOutgoingPongMessages.PopFront();
+    if (mCurrentOut) {
+        isPong = PR_TRUE;
+    } else {
+        mCurrentOut = (OutboundMessage *)mOutgoingPingMessages.PopFront();
+        if (mCurrentOut)
+            isPing = PR_TRUE;
+        else
+            mCurrentOut = (OutboundMessage *)mOutgoingMessages.PopFront();
+    }
+
+    if (!mCurrentOut)
+        return;
+    mCurrentOutSent = 0;
+    mHdrOut = mOutHeader;
+
+    PRUint8 *payload = nsnull;
+    if (mCurrentOut->IsControl() && !isPing && !isPong) {
+        // This is a demand to create a close message
+        if (mClientClosed) {
+            PrimeNewOutgoingMessage();
+            return;
+        }
+            
+        LOG(("WebSocketHandler:: PrimeNewOutgoingMessage() "
+             "found close request\n"));
+        mClientClosed = 1;
+        mOutHeader[0] = kFinalFragBit | kClose;
+        mOutHeader[1] = 0x02; // payload len = 2
+        mOutHeader[1] |= kMaskBit;
+
+        // payload is offset 6 including 4 for the mask
+        payload = mOutHeader + 6;
+        
+        // The close reason code sits in the first 2 bytes of payload
+        if (NS_SUCCEEDED(mStopOnClose))
+            *((PRUint16 *)payload) = PR_htons(kCloseNormal);
+        else if (mStopOnClose == NS_ERROR_FILE_TOO_BIG)
+            *((PRUint16 *)payload) = PR_htons(kCloseTooLarge);
+        else
+            *((PRUint16 *)payload) = PR_htons(kCloseProtocolError);
+
+        mHdrOutToSend = 8;
+        if (mServerClosed) {
+            /* bidi close complete */
+            mReleaseOnTransmit = 1;
+        }
+        else if (NS_FAILED(mStopOnClose)) {
+            /* result of abort session - give up */
+            StopSession(mStopOnClose);
+        }
+        else {
+            /* wait for reciprocal close from server */
+            nsresult rv;
+            mCloseTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+            if (NS_SUCCEEDED(rv)) {
+                mCloseTimer->InitWithCallback(this, mCloseTimeout,
+                                              nsITimer::TYPE_ONE_SHOT);
+            }
+            else {
+                StopSession(rv);
+            }
+        }
+    }
+    else {
+        if (isPong) {
+            LOG(("WebSocketHandler:: PrimeNewOutgoingMessage() "
+                 "found pong request\n"));
+            mOutHeader[0] = kFinalFragBit | kPong;
+        }
+        else if (isPing) {
+            LOG(("WebSocketHandler:: PrimeNewOutgoingMessage() "
+                 "found ping request\n"));
+            mOutHeader[0] = kFinalFragBit | kPing;
+        }
+        else if (mCurrentOut->BinaryLen() < 0) {
+            LOG(("WebSocketHandler:: PrimeNewOutgoing Message() "
+                 "found queued text message len %d\n",
+                 mCurrentOut->Length()));
+            mOutHeader[0] = kFinalFragBit | kText;
+        }
+        else
+        {
+            LOG(("WebSocketHandler:: PrimeNewOutgoing Message() "
+                 "found queued binary message len %d\n",
+                 mCurrentOut->Length()));
+            mOutHeader[0] = kFinalFragBit | kBinary;
+        }
+
+        if (mCurrentOut->Length() < 126) {
+            mOutHeader[1] = mCurrentOut->Length() | kMaskBit;
+            mHdrOutToSend = 6;
+        }
+        else if (mCurrentOut->Length() < 0xffff) {
+            mOutHeader[1] = 126 | kMaskBit;
+            ((PRUint16 *)mOutHeader)[1] =
+                PR_htons(mCurrentOut->Length());
+            mHdrOutToSend = 8;
+        }
+        else {
+            mOutHeader[1] = 127 | kMaskBit;
+            PRUint64 tempLen = mCurrentOut->Length();
+            tempLen = PR_htonll(tempLen);
+            memcpy(mOutHeader + 2, &tempLen, 8);
+            mHdrOutToSend = 14;
+        }
+        payload = mOutHeader + mHdrOutToSend;
+    }
+    
+    NS_ABORT_IF_FALSE(payload, "payload offset not found");
+    
+    // Perfom the sending mask. never use a zero mask
+    PRUint32 mask;
+    while (!(mask = (PRUint32) rand()));
+    *(((PRUint32 *)payload) - 1) = PR_htonl(mask);
+
+    LOG(("WebSocketHandler:: PrimeNewOutgoingMessage() "
+         "using mask %08x\n", mask));
+
+    // We don't mask the framing, but occasionally we stick a little payload
+    // data in the buffer used for the framing. Close frames are the
+    // current example. This data needs to be
+    // masked, but it is never more than a handful of bytes and might rotate
+    // the mask, so we can just do it locally. For real data frames we
+    // ship the bulk of the payload off to ApplyMask()
+
+    while (payload < (mOutHeader + mHdrOutToSend)) {
+        *payload ^= mask >> 24;
+        mask = PR_ROTATE_LEFT32(mask, 8);
+        payload++;
+    }
+
+    // Mask the real message payloads
+
+    ApplyMask(mask,
+              mCurrentOut->BeginWriting(),
+              mCurrentOut->Length());
+    
+    // for small frames, copy it all together for a contiguous write
+    if (mCurrentOut->Length() <= kCopyBreak) {
+        memcpy(mOutHeader + mHdrOutToSend,
+               mCurrentOut->BeginWriting(),
+               mCurrentOut->Length());
+        mHdrOutToSend += mCurrentOut->Length();
+        mCurrentOutSent = mCurrentOut->Length();
+    }
+
+    if (mCompressor) {
+        // assume a 1/3 reduction in size for sizing the buffer
+        // the buffer is used multiple times if necessary
+        PRUint32 currentHeaderSize = mHdrOutToSend;
+        mHdrOutToSend = 0;
+        
+        EnsureHdrOut(32 +
+                     (currentHeaderSize +
+                      mCurrentOut->Length() - mCurrentOutSent) / 2 * 3);
+        mCompressor->
+            Deflate(mOutHeader, currentHeaderSize,
+                    mCurrentOut->BeginReading() + mCurrentOutSent,
+                    mCurrentOut->Length() - mCurrentOutSent);
+        
+        // all of the compressed data now resides in {mHdrOut, mHdrOutToSend}
+        // so do not send the body again
+        mCurrentOutSent = mCurrentOut->Length();
+    }
+
+    // now the transmitting begins - mHdrOutToSend bytes from mOutHeader
+    // and mCurrentOut->Length() bytes from mCurrentOut. The latter may
+    // be coaleseced into the former for small messages or as the result
+    // of the compression process, 
+}
+
+void
+nsWebSocketHandler::EnsureHdrOut(PRUint32 size)
+{
+    LOG(("WebSocketHandler::EnsureHdrOut() %p [%d]\n", this, size));
+
+    if (mDynamicOutputSize < size) {
+        mDynamicOutputSize = size;
+        mDynamicOutput =
+            (PRUint8 *) moz_xrealloc(mDynamicOutput, mDynamicOutputSize);
+    }
+    
+    mHdrOut = mDynamicOutput;
+}
+
+void
+nsWebSocketHandler::StopSession(nsresult reason)
+{
+    LOG(("WebSocketHandler::StopSession() %p [%x]\n", this, reason));
+
+    // normally this should be called on socket thread, but it is ok to call it
+    // from OnStartRequest before the socket thread machine has gotten underway
+
+    NS_ABORT_IF_FALSE(mStopped, "stopsession() has not transitioned "
+                      "through abort or close");
+
+    if (mCloseTimer) {
+        mCloseTimer->Cancel();
+        mCloseTimer = nsnull;
+    }
+
+    if (mOpenTimer) {
+        mOpenTimer->Cancel();
+        mOpenTimer = nsnull;
+    }
+
+    if (mPingTimer) {
+        mPingTimer->Cancel();
+        mPingTimer = nsnull;
+    }
+
+    if (mSocketIn) {
+        // drain, within reason, this socket. if we leave any data
+        // unconsumed (including the tcp fin) a RST will be generated
+        // The right thing to do here is shutdown(SHUT_WR) and then wait
+        // a little while to see if any data comes in.. but there is no
+        // reason to delay things for that when the websocket handshake
+        // is supposed to guarantee a quiet connection except for that fin.
+
+        char     buffer[512];
+        PRUint32 count = 0;
+        PRUint32 total = 0;
+        nsresult rv;
+        do {
+            total += count;
+            rv = mSocketIn->Read(buffer, 512, &count);
+        } while (NS_SUCCEEDED(rv) && count > 0 && total < 32000);
+        
+        mSocketIn->AsyncWait(nsnull, 0, 0, nsnull);
+        mSocketIn = nsnull;
+    }
+
+    if (mSocketOut) {
+        mSocketOut->AsyncWait(nsnull, 0, 0, nsnull);
+        mSocketOut = nsnull;
+    }
+
+    if (mTransport) {
+        mTransport->SetSecurityCallbacks(nsnull);
+        mTransport->SetEventSink(nsnull, nsnull);
+        mTransport->Close(NS_BASE_STREAM_CLOSED);
+        mTransport = nsnull;
+    }
+
+    if (mDNSRequest) {
+        mDNSRequest->Cancel(NS_ERROR_UNEXPECTED);
+        mDNSRequest = nsnull;
+    }
+
+    mInflateReader = nsnull;
+    mInflateStream = nsnull;
+    
+    delete mCompressor;
+    mCompressor = nsnull;
+
+    if (!mCalledOnStop) {
+        mCalledOnStop = 1;
+        nsCOMPtr<nsIRunnable> event =
+            new CallOnStop(mListener, mContext, reason);
+        NS_DispatchToMainThread(event);
+    }
+
+    return;
+}
+
+void
+nsWebSocketHandler::AbortSession(nsresult reason)
+{
+    LOG(("WebSocketHandler::AbortSession() %p [reason %x] stopped = %d\n",
+         this, reason, mStopped));
+
+    // normally this should be called on socket thread, but it is ok to call it
+    // from OnStartRequest before the socket thread machine has gotten underway
+
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread ||
+                      !(mRecvdHttpOnStartRequest &&
+                        mRecvdHttpUpgradeTransport), "wrong thread");
+
+    if (mStopped)
+        return;
+    mStopped = 1;
+
+    if (mTransport && reason != NS_BASE_STREAM_CLOSED &&
+        !mRequestedClose && !mClientClosed && !mServerClosed) {
+        mRequestedClose = 1;
+        nsCOMPtr<nsIRunnable> event =
+            new nsPostMessage(this, kFinMessage, -1);
+        mSocketThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
+        mStopOnClose = reason;
+    }
+    else {
+        StopSession(reason);
+    }
+}
+
+// ReleaseSession is called on orderly shutdown
+void
+nsWebSocketHandler::ReleaseSession()
+{
+    LOG(("WebSocketHandler::ReleaseSession() %p stopped = %d\n",
+         this, mStopped));
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
+                      "not socket thread");
+    
+    if (mStopped)
+        return;
+    mStopped = 1;
+    StopSession(NS_OK);
+}
+
+nsresult
+nsWebSocketHandler::HandleExtensions()
+{
+    LOG(("WebSocketHandler::HandleExtensions() %p\n", this));
+
+    nsresult rv;
+    nsCAutoString extensions;
+
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+
+    rv = mHttpChannel->GetResponseHeader(
+        NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"), extensions);
+    if (NS_SUCCEEDED(rv)) {
+        if (!extensions.IsEmpty()) {
+            if (!extensions.Equals(NS_LITERAL_CSTRING("deflate-stream"))) {
+                LOG(("WebSocketHandler::OnStartRequest "
+                     "HTTP Sec-WebSocket-Exensions negotiated "
+                     "unknown value %s\n",
+                     extensions.get()));
+                AbortSession(NS_ERROR_ILLEGAL_VALUE);
+                return NS_ERROR_ILLEGAL_VALUE;
+            }
+
+            if (!mAllowCompression) {
+                LOG(("WebSocketHandler::HandleExtensions "
+                     "Recvd Compression Extension that wasn't offered\n"));
+                AbortSession(NS_ERROR_ILLEGAL_VALUE);
+                return NS_ERROR_ILLEGAL_VALUE;
+            }
+            
+            nsCOMPtr<nsIStreamConverterService> serv =
+                do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
+            if (NS_FAILED(rv)) {
+                LOG(("WebSocketHandler:: Cannot find compression service\n"));
+                AbortSession(NS_ERROR_UNEXPECTED);
+                return NS_ERROR_UNEXPECTED;
+            }
+            
+            rv = serv->AsyncConvertData("deflate",
+                                        "uncompressed",
+                                        this,
+                                        nsnull,
+                                        getter_AddRefs(mInflateReader));
+            
+            if (NS_FAILED(rv)) {
+                LOG(("WebSocketHandler:: Cannot find inflate listener\n"));
+                AbortSession(NS_ERROR_UNEXPECTED);
+                return NS_ERROR_UNEXPECTED;
+            }
+            
+            mInflateStream =
+                do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
+            
+            if (NS_FAILED(rv)) {
+                LOG(("WebSocketHandler:: Cannot find inflate stream\n"));
+                AbortSession(NS_ERROR_UNEXPECTED);
+                return NS_ERROR_UNEXPECTED;
+            }
+            
+            mCompressor = new nsWSCompression(this, mSocketOut);
+            if (!mCompressor->Active()) {
+                LOG(("WebSocketHandler:: Cannot init deflate object\n"));
+                delete mCompressor;
+                mCompressor = nsnull;
+                AbortSession(NS_ERROR_UNEXPECTED);
+                return NS_ERROR_UNEXPECTED;
+            }
+        }
+    }
+    
+    return NS_OK;
+}
+
+nsresult
+nsWebSocketHandler::SetupRequest()
+{
+    LOG(("WebSocketHandler::SetupRequest() %p\n", this));
+
+    nsresult rv;
+    
+    if (mLoadGroup) {
+        rv = mHttpChannel->SetLoadGroup(mLoadGroup);
+        NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    rv = mHttpChannel->SetLoadFlags(nsIRequest::LOAD_BACKGROUND |
+                                    nsIRequest::INHIBIT_CACHING |
+                                    nsIRequest::LOAD_BYPASS_CACHE);
+    NS_ENSURE_SUCCESS(rv, rv);
+    
+    // draft-ietf-hybi-thewebsocketprotocol-07 illustrates Upgrade: websocket
+    // in lower case, so we will go with that. It is technically case
+    // insensitive.
+    rv = mChannel->HTTPUpgrade(NS_LITERAL_CSTRING("websocket"), this);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mHttpChannel->SetRequestHeader(
+        NS_LITERAL_CSTRING("Sec-WebSocket-Version"),
+        NS_LITERAL_CSTRING(SEC_WEBSOCKET_VERSION), PR_FALSE);
+
+    if (!mOrigin.IsEmpty())
+        mHttpChannel->SetRequestHeader(
+            NS_LITERAL_CSTRING("Sec-WebSocket-Origin"),
+            mOrigin, PR_FALSE);
+
+    if (!mProtocol.IsEmpty())
+        mHttpChannel->SetRequestHeader(
+            NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"),
+            mProtocol, PR_TRUE);
+
+    if (mAllowCompression)
+        mHttpChannel->SetRequestHeader(
+            NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"),
+            NS_LITERAL_CSTRING("deflate-stream"), PR_FALSE);
+
+    PRUint32      secKey[4];
+    nsCAutoString secKeyString;
+    secKey[0] = (PRUint32) rand();
+    secKey[1] = (PRUint32) rand();
+    secKey[2] = (PRUint32) rand();
+    secKey[3] = (PRUint32) rand();
+    char* b64 = PL_Base64Encode((const char *)secKey, 16, nsnull);
+    if (!b64) return NS_ERROR_OUT_OF_MEMORY;
+    secKeyString.Assign(b64);
+    PR_Free(b64);
+    mHttpChannel->SetRequestHeader(
+        NS_LITERAL_CSTRING("Sec-WebSocket-Key"), secKeyString, PR_FALSE);
+    LOG(("WebSocketHandler::AsyncOpen() client key %s\n", secKeyString.get()));
+
+    // prepare the value we expect to see in
+    // the sec-websocket-accept response header
+    secKeyString.AppendLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+    nsCOMPtr<nsICryptoHash> hasher =
+        do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = hasher->Init(nsICryptoHash::SHA1);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = hasher->Update((const PRUint8 *) secKeyString.BeginWriting(),
+                        secKeyString.Length());
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = hasher->Finish(PR_TRUE, mHashedSecret);
+    NS_ENSURE_SUCCESS(rv, rv);
+    LOG(("WebSocketHandler::AsyncOpen() expected server key %s\n",
+         mHashedSecret.get()));
+    
+    return NS_OK;
+}
+
+nsresult
+nsWebSocketHandler::ApplyForAdmission()
+{
+    LOG(("WebSocketHandler::ApplyForAdmission() %p\n", this));
+
+    // Websockets has a policy of 1 session at a time being allowed in the
+    // CONNECTING state per server IP address (not hostname)
+
+    nsresult rv;
+    nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    
+    nsCString hostName;
+    rv = mURI->GetHost(hostName);
+    NS_ENSURE_SUCCESS(rv, rv);
+    mAddress = hostName;
+    
+    // expect the callback in ::OnLookupComplete
+    LOG(("WebSocketHandler::AsyncOpen() checking for concurrent open\n"));
+    nsCOMPtr<nsIThread> mainThread;
+    NS_GetMainThread(getter_AddRefs(mainThread));
+    dns->AsyncResolve(hostName,
+                      0,
+                      this,
+                      mainThread,
+                      getter_AddRefs(mDNSRequest));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
+}
+
+// nsIDNSListener
+
+NS_IMETHODIMP
+nsWebSocketHandler::OnLookupComplete(nsICancelable *aRequest,
+                                     nsIDNSRecord *aRecord,
+                                     nsresult aStatus)
+{
+    LOG(("WebSocketHandler::OnLookupComplete() %p [%p %p %x]\n",
+         this, aRequest, aRecord, aStatus));
+
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+    NS_ABORT_IF_FALSE(aRequest == mDNSRequest, "wrong dns request");
+
+    mDNSRequest = nsnull;
+
+    // These failures are not fatal - we just use the hostname as the key
+    if (NS_FAILED(aStatus)) {
+        LOG(("WebSocketHandler::OnLookupComplete No DNS Response\n"));
+    }
+    else {
+        nsresult rv = aRecord->GetNextAddrAsString(mAddress);
+        if (NS_FAILED(rv))
+            LOG(("WebSocketHandler::OnLookupComplete Failed GetNextAddr\n"));
+    }
+    
+    if (sWebSocketAdmissions->ConditionallyConnect(mAddress, this)) {
+        LOG(("WebSocketHandler::OnLookupComplete Proceeding with Open\n"));
+    }
+    else {
+        LOG(("WebSocketHandler::OnLookupComplete Deferring Open\n"));
+    }
+    
+    return NS_OK;
+}
+
+// nsIInterfaceRequestor
+
+NS_IMETHODIMP
+nsWebSocketHandler::GetInterface(const nsIID & iid, void **result NS_OUTPARAM)
+{
+    LOG(("WebSocketHandler::GetInterface() %p\n", this));
+
+    if (iid.Equals(NS_GET_IID(nsIChannelEventSink)))
+        return QueryInterface(iid, result);
+    
+    if (mCallbacks)
+        return mCallbacks->GetInterface(iid, result);
+
+    return NS_ERROR_FAILURE;
+}
+
+// nsIChannelEventSink
+
+NS_IMETHODIMP
+nsWebSocketHandler::AsyncOnChannelRedirect(
+    nsIChannel *oldChannel,
+    nsIChannel *newChannel,
+    PRUint32 flags,
+    nsIAsyncVerifyRedirectCallback *callback)
+{
+    LOG(("WebSocketHandler::AsyncOnChannelRedirect() %p\n", this));
+    nsresult rv;
+    
+    nsCOMPtr<nsIURI> newuri;
+    rv = newChannel->GetURI(getter_AddRefs(newuri));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!mAutoFollowRedirects) {
+        nsCAutoString spec;
+        if (NS_SUCCEEDED(newuri->GetSpec(spec)))
+            LOG(("nsWebSocketHandler Redirect to %s denied by configuration\n",
+                 spec.get()));
+        callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
+        return NS_OK;
+    }
+
+    PRBool isHttps = PR_FALSE;
+    rv = newuri->SchemeIs("https", &isHttps);
+    NS_ENSURE_SUCCESS(rv, rv);
+    
+    if (mEncrypted && !isHttps) {
+        nsCAutoString spec;
+        if (NS_SUCCEEDED(newuri->GetSpec(spec)))
+            LOG(("nsWebSocketHandler Redirect to %s violates encryption rule\n",
+                 spec.get()));
+        callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
+        return NS_OK;
+    }
+    
+    nsCOMPtr<nsIHttpChannel> newHttpChannel =
+        do_QueryInterface(newChannel, &rv);
+    
+    if (NS_FAILED(rv)) {
+        LOG(("nsWebSocketHandler Redirect could not QI to HTTP\n"));
+        callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
+        return NS_OK;
+    }
+
+    nsCOMPtr<nsIHttpChannelInternal> newUpgradeChannel =
+        do_QueryInterface(newChannel, &rv);
+    
+    if (NS_FAILED(rv)) {
+        LOG(("nsWebSocketHandler Redirect could not QI to HTTP Upgrade\n"));
+        callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
+        return NS_OK;
+    }
+    
+    // The redirect is OK
+
+    newChannel->SetNotificationCallbacks(this);
+    mURI = newuri;
+    mHttpChannel = newHttpChannel;
+    mChannel = newUpgradeChannel;
+    SetupRequest();
+    
+    // We cannot just tell the callback OK right now due to the 1 connect at
+    // a time policy. First we need to complete the old location and then
+    // start the lookup chain for the new location - once that is complete
+    // and we have been admitted, OnRedirectVerifyCallback(NS_OK) will be called
+    // out of BeginOpen()
+
+    sWebSocketAdmissions->Complete(mAddress);
+    mAddress.Truncate();
+    mRedirectCallback = callback;
+
+    rv = ApplyForAdmission();
+    if (NS_FAILED(rv)) {
+        LOG(("nsWebSocketHandler Redirect failed due to DNS failure\n"));
+        callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
+        mRedirectCallback = nsnull;
+    }
+    
+    return NS_OK;
+}
+
+// nsITimerCallback
+
+NS_IMETHODIMP
+nsWebSocketHandler::Notify(nsITimer *timer)
+{
+    LOG(("WebSocketHandler::Notify() %p [%p]\n", this, timer));
+
+    if (timer == mCloseTimer) {
+        NS_ABORT_IF_FALSE(mClientClosed, "Close Timeout without local close");
+        NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
+                      "not socket thread");
+
+        mCloseTimer = nsnull;
+        if (mStopped || mServerClosed)                /* no longer relevant */
+            return NS_OK;
+    
+        LOG(("nsWebSocketHandler:: Expecting Server Close - Timed Out\n"));
+        AbortSession(NS_ERROR_NET_TIMEOUT);
+    }
+    else if (timer == mOpenTimer) {
+        NS_ABORT_IF_FALSE(!mRecvdHttpOnStartRequest,
+                          "Open Timer after open complete");
+        NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+
+        mOpenTimer = nsnull;
+        LOG(("nsWebSocketHandler:: Connection Timed Out\n"));
+        if (mStopped || mServerClosed)                /* no longer relevant */
+            return NS_OK;
+    
+        AbortSession(NS_ERROR_NET_TIMEOUT);
+    }
+    else if (timer == mPingTimer) {
+        NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
+                      "not socket thread");
+
+        if (mClientClosed || mServerClosed || mRequestedClose) {
+            // no point in worrying about ping now
+            mPingTimer = nsnull;
+            return NS_OK;
+        }
+
+        if (!mPingOutstanding) {
+            LOG(("nsWebSockethandler:: Generating Ping\n"));
+            mPingOutstanding = 1;
+            GeneratePing();
+            mPingTimer->InitWithCallback(this, mPingResponseTimeout,
+                                         nsITimer::TYPE_ONE_SHOT);
+        }
+        else {
+            LOG(("nsWebSockethandler:: Timed out Ping\n"));
+            mPingTimer = nsnull;
+            AbortSession(NS_ERROR_NET_TIMEOUT);
+        }
+    }
+    else {
+        NS_ABORT_IF_FALSE(0, "Unknown Timer");
+    }
+
+    return NS_OK;
+}
+
+// nsIWebSocketProtocol
+
+NS_IMETHODIMP
+nsWebSocketHandler::GetOriginalURI(nsIURI **aOriginalURI)
+{
+    LOG(("WebSocketHandler::GetOriginalURI() %p\n", this));
+
+    if (!mOriginalURI)
+        return NS_ERROR_NOT_INITIALIZED;
+    NS_ADDREF(*aOriginalURI = mOriginalURI);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::GetURI(nsIURI **aURI)
+{
+    LOG(("WebSocketHandler::GetURI() %p\n", this));
+
+    if (!mOriginalURI)
+        return NS_ERROR_NOT_INITIALIZED;
+    if (mURI)
+        NS_ADDREF(*aURI = mURI);
+    else
+        NS_ADDREF(*aURI = mOriginalURI);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::
+GetNotificationCallbacks(nsIInterfaceRequestor **aNotificationCallbacks)
+{
+    LOG(("WebSocketHandler::GetNotificationCallbacks() %p\n", this));
+    NS_IF_ADDREF(*aNotificationCallbacks = mCallbacks);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::
+SetNotificationCallbacks(nsIInterfaceRequestor *aNotificationCallbacks)
+{
+    LOG(("WebSocketHandler::SetNotificationCallbacks() %p\n", this));
+    mCallbacks = aNotificationCallbacks;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::GetSecurityInfo(nsISupports **aSecurityInfo)
+{
+    LOG(("WebSocketHandler::GetSecurityInfo() %p\n", this));
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+
+    if (mTransport) {
+        if (NS_FAILED(mTransport->GetSecurityInfo(aSecurityInfo)))
+            *aSecurityInfo = nsnull;
+    }
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::GetLoadGroup(nsILoadGroup **aLoadGroup)
+{
+    LOG(("WebSocketHandler::GetLoadGroup() %p\n", this));
+    NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::SetLoadGroup(nsILoadGroup *aLoadGroup)
+{
+    LOG(("WebSocketHandler::SetLoadGroup() %p\n", this));
+    mLoadGroup = aLoadGroup;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::GetProtocol(nsACString &aProtocol)
+{
+    LOG(("WebSocketHandler::GetProtocol() %p\n", this));
+    aProtocol = mProtocol;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::SetProtocol(const nsACString &aProtocol)
+{
+    LOG(("WebSocketHandler::SetProtocol() %p\n", this));
+    mProtocol = aProtocol;                        /* the sub protocol */
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::AsyncOpen(nsIURI *aURI,
+                              const nsACString &aOrigin,
+                              nsIWebSocketListener *aListener,
+                              nsISupports *aContext)
+{
+    LOG(("WebSocketHandler::AsyncOpen() %p\n", this));
+
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+    
+    if (!aURI || !aListener) {
+        LOG(("WebSocketHandler::AsyncOpen() Uri or Listener null"));
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    if (mListener)
+        return NS_ERROR_ALREADY_OPENED;
+
+    nsresult rv;
+
+    mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+    if (NS_FAILED(rv)) {
+        NS_WARNING("unable to continue without socket transport service");
+        return rv;
+    }
+
+    nsCOMPtr<nsIPrefBranch> prefService;
+    prefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
+
+    if (prefService) {
+        PRInt32 intpref;
+        PRBool boolpref;
+        rv = prefService->
+            GetIntPref("network.websocket.max-message-size", &intpref);
+        if (NS_SUCCEEDED(rv)) {
+            mMaxMessageSize = NS_CLAMP(intpref, 1024, 1 << 30);
+        }
+        rv = prefService->GetIntPref
+            ("network.websocket.timeout.close", &intpref);
+        if (NS_SUCCEEDED(rv)) {
+            mCloseTimeout = NS_CLAMP(intpref, 1, 1800) * 1000;
+        }
+        rv = prefService->GetIntPref
+            ("network.websocket.timeout.open", &intpref);
+        if (NS_SUCCEEDED(rv)) {
+            mOpenTimeout = NS_CLAMP(intpref, 1, 1800) * 1000;
+        }
+        rv = prefService->GetIntPref
+            ("network.websocket.timeout.ping.request", &intpref);
+        if (NS_SUCCEEDED(rv)) {
+            mPingTimeout = NS_CLAMP(intpref, 0, 86400) * 1000;
+        }
+        rv = prefService->GetIntPref
+            ("network.websocket.timeout.ping.response", &intpref);
+        if (NS_SUCCEEDED(rv)) {
+            mPingResponseTimeout = NS_CLAMP(intpref, 1, 3600) * 1000;
+        }
+        rv = prefService->GetBoolPref
+            ("network.websocket.extensions.stream-deflate", &boolpref);
+        if (NS_SUCCEEDED(rv)) {
+            mAllowCompression = boolpref ? 1 : 0;
+        }
+        rv = prefService->GetBoolPref
+            ("network.websocket.auto-follow-http-redirects", &boolpref);
+        if (NS_SUCCEEDED(rv)) {
+            mAutoFollowRedirects = boolpref ? 1 : 0;
+        }
+    }
+    
+    if (mPingTimeout) {
+        mPingTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+        if (NS_FAILED(rv)) {
+            NS_WARNING("unable to create ping timer. Carrying on.");
+        }
+        else {
+            LOG(("nsWebSocketHandler will generate ping after %d ms "
+                 "of receive silence\n", mPingTimeout));
+            mPingTimer->SetTarget(mSocketThread);
+            mPingTimer->InitWithCallback(this, mPingTimeout,
+                                         nsITimer::TYPE_ONE_SHOT);
+        }
+    }
+
+    mOriginalURI = aURI;
+    mURI = mOriginalURI;
+    mListener = aListener;
+    mContext = aContext;
+    mOrigin = aOrigin;
+
+    nsCOMPtr<nsIURI> localURI;
+    nsCOMPtr<nsIChannel> localChannel;
+    
+    mURI->Clone(getter_AddRefs(localURI));
+    if (mEncrypted)
+        rv = localURI->SetScheme(NS_LITERAL_CSTRING("https"));
+    else
+        rv = localURI->SetScheme(NS_LITERAL_CSTRING("http"));
+    NS_ENSURE_SUCCESS(rv, rv);
+    
+    nsCOMPtr<nsIIOService> ioService;
+    ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
+    if (NS_FAILED(rv)) {
+        NS_WARNING("unable to continue without io service");
+        return rv;
+    }
+
+    nsCOMPtr<nsIIOService2> io2 = do_QueryInterface(ioService, &rv);
+    if (NS_FAILED(rv)) {
+        NS_WARNING("unable to continue without ioservice2 interface");
+        return rv;
+    }
+
+    rv = io2->NewChannelFromURIWithProxyFlags(
+        localURI,
+        mURI,
+        nsIProtocolProxyService::RESOLVE_PREFER_SOCKS_PROXY |
+        nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
+        nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
+        getter_AddRefs(localChannel));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // pass most GetInterface() requests through to our instantiator, but handle
+    // nsIChannelEventSink in this object in order to deal with redirects
+    localChannel->SetNotificationCallbacks(this);
+
+    mChannel = do_QueryInterface(localChannel, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mHttpChannel = do_QueryInterface(localChannel, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    
+    rv = SetupRequest();
+    if (NS_FAILED(rv))
+        return rv;
+
+    return ApplyForAdmission();
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::Close()
+{
+    LOG(("WebSocketHandler::Close() %p\n", this));
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+    if (mRequestedClose) {
+        LOG(("WebSocketHandler:: Double close error\n"));
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    mRequestedClose = 1;
+    
+    nsCOMPtr<nsIRunnable> event =
+        new nsPostMessage(this, kFinMessage, -1);
+    return mSocketThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::SendMsg(const nsACString &aMsg)
+{
+    LOG(("WebSocketHandler::SendMsg() %p\n", this));
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+
+    if (mRequestedClose) {
+        LOG(("WebSocketHandler:: SendMsg when closed error\n"));
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    if (mStopped) {
+        LOG(("WebSocketHandler:: SendMsg when stopped error\n"));
+        return NS_ERROR_NOT_CONNECTED;
+    }
+    
+    nsCOMPtr<nsIRunnable> event =
+        new nsPostMessage(this, new nsCString(aMsg), -1);
+    return mSocketThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::SendBinaryMsg(const nsACString &aMsg)
+{
+    LOG(("WebSocketHandler::SendBinaryMsg() %p len=%d\n", this, aMsg.Length()));
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+
+    if (mRequestedClose) {
+        LOG(("WebSocketHandler:: SendBinaryMsg when closed error\n"));
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    if (mStopped) {
+        LOG(("WebSocketHandler:: SendBinaryMsg when stopped error\n"));
+        return NS_ERROR_NOT_CONNECTED;
+    }
+    
+    nsCOMPtr<nsIRunnable> event =
+        new nsPostMessage(this, new nsCString(aMsg), aMsg.Length());
+    return mSocketThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::OnTransportAvailable(nsISocketTransport *aTransport,
+                                         nsIAsyncInputStream *aSocketIn,
+                                         nsIAsyncOutputStream *aSocketOut)
+{
+    LOG(("WebSocketHandler::OnTransportAvailable "
+         "%p [%p %p %p] rcvdonstart=%d\n",
+         this, aTransport, aSocketIn, aSocketOut, mRecvdHttpOnStartRequest));
+
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+    NS_ABORT_IF_FALSE(!mRecvdHttpUpgradeTransport, "OTA duplicated");
+    
+    mTransport = aTransport;
+    mSocketIn = aSocketIn;
+    mSocketOut = aSocketOut;
+
+    nsresult rv;
+    rv = mTransport->SetEventSink(nsnull, nsnull);
+    if (NS_FAILED(rv)) return rv;
+    rv = mTransport->SetSecurityCallbacks(mCallbacks);
+    if (NS_FAILED(rv)) return rv;
+
+    mRecvdHttpUpgradeTransport = 1;
+    if (mRecvdHttpOnStartRequest)
+        return mSocketIn->AsyncWait(this, 0, 0, mSocketThread);
+    return NS_OK;
+}
+
+// nsIRequestObserver (from nsIStreamListener)
+
+NS_IMETHODIMP
+nsWebSocketHandler::OnStartRequest(nsIRequest *aRequest,
+                                   nsISupports *aContext)
+{
+    LOG(("WebSocketHandler::OnStartRequest() %p [%p %p] recvdhttpupgrade=%d\n",
+         this, aRequest, aContext, mRecvdHttpUpgradeTransport));
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+    NS_ABORT_IF_FALSE(!mRecvdHttpOnStartRequest, "OTA duplicated");
+
+    // Generating the onStart event will take us out of the
+    // CONNECTING state which means we can now open another,
+    // perhaps parallel, connection to the same host if one
+    // is pending
+
+    if (sWebSocketAdmissions->Complete(mAddress))
+        LOG(("nsWebSocketHandler::OnStartRequest Starting Pending Open\n"));
+    else
+        LOG(("nsWebSocketHandler::OnStartRequest No More Pending Opens\n"));
+
+    if (mOpenTimer) {
+        mOpenTimer->Cancel();
+        mOpenTimer = nsnull;
+    }
+
+    if (mStopped) {
+        LOG(("WebSocketHandler::OnStartRequest Handler Already Done\n"));
+        AbortSession(NS_ERROR_CONNECTION_REFUSED);
+        return NS_ERROR_CONNECTION_REFUSED;
+    }
+
+    nsresult rv;
+    PRUint32 status;
+    char *val, *token;
+
+    rv = mHttpChannel->GetResponseStatus(&status);
+    if (NS_FAILED(rv)) {
+        LOG(("WebSocketHandler::OnStartRequest No HTTP Response\n"));
+        AbortSession(NS_ERROR_CONNECTION_REFUSED);
+        return NS_ERROR_CONNECTION_REFUSED;
+    }
+
+    LOG(("WebSocketHandler::OnStartRequest HTTP status %d\n", status));
+    if (status != 101) {
+        AbortSession(NS_ERROR_CONNECTION_REFUSED);
+        return NS_ERROR_CONNECTION_REFUSED;
+    }
+    
+    nsCAutoString respUpgrade;
+    rv = mHttpChannel->GetResponseHeader(
+        NS_LITERAL_CSTRING("Upgrade"), respUpgrade);
+
+    if (NS_SUCCEEDED(rv)) {
+        rv = NS_ERROR_ILLEGAL_VALUE;
+        if (!respUpgrade.IsEmpty()) {
+            val = respUpgrade.BeginWriting();
+            while ((token = nsCRT::strtok(val, ", \t", &val))) {
+                if (PL_strcasecmp(token, "Websocket") == 0) {
+                    rv = NS_OK;
+                    break;
+                }
+            }
+        }
+    }
+    
+    if (NS_FAILED(rv)) {
+        LOG(("WebSocketHandler::OnStartRequest "
+             "HTTP response header Upgrade: websocket not found\n"));
+        AbortSession(rv);
+        return rv;
+    }
+
+    nsCAutoString respConnection;
+    rv = mHttpChannel->GetResponseHeader(
+        NS_LITERAL_CSTRING("Connection"), respConnection);
+
+    if (NS_SUCCEEDED(rv)) {
+        rv = NS_ERROR_ILLEGAL_VALUE;
+        if (!respConnection.IsEmpty()) {
+            val = respConnection.BeginWriting();
+            while ((token = nsCRT::strtok(val, ", \t", &val))) {
+                if (PL_strcasecmp(token, "Upgrade") == 0) {
+                    rv = NS_OK;
+                    break;
+                }
+            }
+        }
+    }
+    
+    if (NS_FAILED(rv)) {
+        LOG(("WebSocketHandler::OnStartRequest "
+             "HTTP response header Connection: Upgrade not found\n"));
+        AbortSession(rv);
+        return rv;
+    }
+
+    nsCAutoString respAccept;
+    rv = mHttpChannel->GetResponseHeader(
+        NS_LITERAL_CSTRING("Sec-WebSocket-Accept"), respAccept);
+
+    if (NS_FAILED(rv) ||
+        respAccept.IsEmpty() || !respAccept.Equals(mHashedSecret)) {
+        LOG(("WebSocketHandler::OnStartRequest "
+             "HTTP response header Sec-WebSocket-Accept check failed\n"));
+        LOG(("WebSocketHandler::OnStartRequest "
+             "Expected %s recevied %s\n",
+             mHashedSecret.get(), respAccept.get()));
+        AbortSession(NS_ERROR_ILLEGAL_VALUE);
+        return NS_ERROR_ILLEGAL_VALUE;
+    }
+
+    // If we sent a sub protocol header, verify the response matches
+    // If it does not, set mProtocol to "" so the protocol attribute
+    // of the WebSocket JS object reflects that
+    if (!mProtocol.IsEmpty()) {
+        nsCAutoString respProtocol;
+        rv = mHttpChannel->GetResponseHeader(
+            NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"), respProtocol);
+        if (NS_SUCCEEDED(rv)) {
+            rv = NS_ERROR_ILLEGAL_VALUE;
+            val = mProtocol.BeginWriting();
+            while ((token = nsCRT::strtok(val, ", \t", &val))) {
+                if (PL_strcasecmp(token, respProtocol.get()) == 0) {
+                    rv = NS_OK;
+                    break;
+                }
+            }
+
+            if (NS_SUCCEEDED(rv)) {
+                LOG(("WebsocketHandler::OnStartRequest "
+                     "subprotocol %s confirmed", respProtocol.get()));
+                mProtocol = respProtocol;
+            }
+            else {
+                LOG(("WebsocketHandler::OnStartRequest "
+                     "subprotocol [%s] not found - %s returned",
+                     mProtocol.get(), respProtocol.get()));
+                mProtocol.Truncate();
+            }
+        }
+        else {
+            LOG(("WebsocketHandler::OnStartRequest "
+                 "subprotocol [%s] not found - none returned",
+                 mProtocol.get()));
+            mProtocol.Truncate();
+        }
+    }
+
+    rv = HandleExtensions();
+    if (NS_FAILED(rv))
+        return rv;
+    
+    LOG(("WebSocketHandler::OnStartRequest Notifying Listener %p\n",
+         mListener.get()));
+    
+    if (mListener)
+        mListener->OnStart(mContext);
+
+    mRecvdHttpOnStartRequest = 1;
+    if (mRecvdHttpUpgradeTransport)
+        return mSocketIn->AsyncWait(this, 0, 0, mSocketThread);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::OnStopRequest(nsIRequest *aRequest,
+                                  nsISupports *aContext,
+                                  nsresult aStatusCode)
+{
+    LOG(("WebSocketHandler::OnStopRequest() %p [%p %p %x]\n",
+         this, aRequest, aContext, aStatusCode));
+    NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
+    
+    // this is the stop of the HTTP upgrade transaction, the
+    // upgraded streams live on
+
+    mChannel = nsnull;
+    mHttpChannel = nsnull;
+    mLoadGroup = nsnull;
+    mCallbacks = nsnull;
+    
+    return NS_OK;
+}
+
+// nsIInputStreamCallback
+
+NS_IMETHODIMP
+nsWebSocketHandler::OnInputStreamReady(nsIAsyncInputStream *aStream)
+{
+    LOG(("WebSocketHandler::OnInputStreamReady() %p\n", this));
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
+                      "not socket thread");
+    
+    if (mStopped)
+        return NS_ERROR_UNEXPECTED;
+
+    nsRefPtr<nsIStreamListener>    deleteProtector1(mInflateReader);
+    nsRefPtr<nsIStringInputStream> deleteProtector2(mInflateStream);
+
+    // this is after the  http upgrade - so we are speaking websockets
+    char  buffer[2048];
+    PRUint32 count;
+    nsresult rv;
+
+    do {
+        rv = mSocketIn->Read((char *)buffer, 2048, &count);
+        LOG(("WebSocketHandler::OnInputStreamReady read %u rv %x\n",
+             count, rv));
+
+        if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+            mSocketIn->AsyncWait(this, 0, 0, mSocketThread);
+            return NS_OK;
+        }
+        
+        if (NS_FAILED(rv)) {
+            AbortSession(rv);
+            return rv;
+        }
+        
+        if (count == 0) {
+            AbortSession(NS_BASE_STREAM_CLOSED);
+            return NS_OK;
+        }
+        
+        if (mInflateReader) {
+            mInflateStream->ShareData(buffer, count);
+            rv = mInflateReader->OnDataAvailable(nsnull, mSocketIn,
+                                                 mInflateStream, 0, count);
+        }
+        else {
+            rv = ProcessInput((PRUint8 *)buffer, count);
+        }
+
+        if (NS_FAILED(rv)) {
+            AbortSession(rv);
+            return rv;
+        }
+
+    } while (NS_SUCCEEDED(rv) && mSocketIn);
+
+    return NS_OK;
+}
+
+
+// nsIOutputStreamCallback
+
+NS_IMETHODIMP
+nsWebSocketHandler::OnOutputStreamReady(nsIAsyncOutputStream *aStream)
+{
+    LOG(("WebSocketHandler::OnOutputStreamReady() %p\n", this));
+    NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread,
+                      "not socket thread");
+    nsresult rv;
+
+    if (!mCurrentOut)
+        PrimeNewOutgoingMessage();
+
+    while (mCurrentOut && mSocketOut) {
+        const char *sndBuf;
+        PRUint32 toSend;
+        PRUint32 amtSent;
+        
+        if (mHdrOut) {
+            sndBuf = (const char *)mHdrOut;
+            toSend = mHdrOutToSend;
+            LOG(("WebSocketHandler::OnOutputStreamReady "
+                 "Try to send %u of hdr/copybreak\n",
+                 toSend));
+        }
+        else {
+            sndBuf = (char *) mCurrentOut->BeginReading() + mCurrentOutSent;
+            toSend = mCurrentOut->Length() - mCurrentOutSent;
+            if (toSend > 0) {
+                LOG(("WebSocketHandler::OnOutputStreamReady "
+                     "Try to send %u of data\n",
+                     toSend));
+            }
+        }
+        
+        if (toSend == 0) {
+            amtSent = 0;
+        }
+        else {
+            rv = mSocketOut->Write(sndBuf, toSend, &amtSent);
+            LOG(("WebSocketHandler::OnOutputStreamReady write %u rv %x\n",
+                 amtSent, rv));
+        
+            if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+                mSocketOut->AsyncWait(this, 0, 0, nsnull);
+                return NS_OK;
+            }
+
+            if (NS_FAILED(rv)) {
+                AbortSession(rv);
+                return NS_OK;
+            }
+        }
+        
+        if (mHdrOut) {
+            if (amtSent == toSend) {
+                mHdrOut = nsnull;
+                mHdrOutToSend = 0;
+            }
+            else {
+                mHdrOut += amtSent;
+                mHdrOutToSend -= amtSent;
+            }
+        }
+        else {
+            if (amtSent == toSend) {
+                if (!mStopped) {
+                    nsCOMPtr<nsIRunnable> event =
+                        new CallAcknowledge(mListener, mContext,
+                                            mCurrentOut->Length());
+                    NS_DispatchToMainThread(event);
+                }
+                delete mCurrentOut;
+                mCurrentOut = nsnull;
+                mCurrentOutSent = 0;
+                PrimeNewOutgoingMessage();
+            }
+            else {
+                mCurrentOutSent += amtSent;
+            }
+        }
+    }
+
+    if (mReleaseOnTransmit)
+        ReleaseSession();
+    return NS_OK;
+}
+
+// nsIStreamListener
+
+NS_IMETHODIMP
+nsWebSocketHandler::OnDataAvailable(nsIRequest *aRequest,
+                                    nsISupports *aContext,
+                                    nsIInputStream *aInputStream,
+                                    PRUint32 aOffset,
+                                    PRUint32 aCount)
+{
+    LOG(("WebSocketHandler::OnDataAvailable() %p [%p %p %p %u %u]\n",
+         this, aRequest, aContext, aInputStream, aOffset, aCount));
+
+    if (aContext == mSocketIn) {
+        // This is the deflate decoder
+
+        LOG(("WebSocketHandler::OnDataAvailable Deflate Data %u\n",
+             aCount));
+
+        PRUint8  buffer[2048];
+        PRUint32 maxRead;
+        PRUint32 count;
+        nsresult rv;
+
+        while (aCount > 0) {
+            if (mStopped)
+                return NS_BASE_STREAM_CLOSED;
+            
+            maxRead = NS_MIN(2048U, aCount);
+            rv = aInputStream->Read((char *)buffer, maxRead, &count);
+            LOG(("WebSocketHandler::OnDataAvailable "
+                 "InflateRead read %u rv %x\n",
+                 count, rv));
+            if (NS_FAILED(rv) || count == 0) {
+                AbortSession(rv);
+                break;
+            }
+            
+            aCount -= count;
+            rv = ProcessInput(buffer, count);
+        }
+        return NS_OK;
+    }
+
+    if (aContext == mSocketOut) {
+        // This is the deflate encoder
+        
+        PRUint32 maxRead;
+        PRUint32 count;
+        nsresult rv;
+
+        while (aCount > 0) {
+            if (mStopped)
+                return NS_BASE_STREAM_CLOSED;
+
+            maxRead = NS_MIN(2048U, aCount);
+            EnsureHdrOut(mHdrOutToSend + aCount);
+            rv = aInputStream->Read((char *)mHdrOut + mHdrOutToSend,
+                                    maxRead, &count);
+            LOG(("WebSocketHandler::OnDataAvailable "
+                 "DeflateWrite read %u rv %x\n", count, rv));
+            if (NS_FAILED(rv) || count == 0) {
+                AbortSession(rv);
+                break;
+            }
+
+            mHdrOutToSend += count;
+            aCount -= count;
+        }
+        return NS_OK;
+    }
+    
+
+    // Otherwise, this is the HTTP OnDataAvailable Method, which means
+    // this is http data in response to the upgrade request and
+    // there should be no http response body if the upgrade succeeded
+
+    // This generally should be caught by a non 101 response code in
+    // OnStartRequest().. so we can ignore the data here
+
+    LOG(("WebSocketHandler::OnDataAvailable HTTP data unexpected len>=%u\n",
+         aCount));
+
+    return NS_OK;
+}
+
+// nsIProtocolHandler
+
+NS_IMETHODIMP
+nsWebSocketHandler::GetScheme(nsACString &aScheme)
+{
+    LOG(("WebSocketHandler::GetScheme() %p\n", this));
+
+    if (mEncrypted)
+        aScheme.AssignLiteral("wss");
+    else
+        aScheme.AssignLiteral("ws");
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::GetDefaultPort(PRInt32 *aDefaultPort)
+{
+    LOG(("WebSocketHandler::GetDefaultPort() %p\n", this));
+
+    if (mEncrypted)
+        *aDefaultPort = kDefaultWSSPort;
+    else
+        *aDefaultPort = kDefaultWSPort;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::GetProtocolFlags(PRUint32 *aProtocolFlags)
+{
+    LOG(("WebSocketHandler::GetProtocolFlags() %p\n", this));
+
+    *aProtocolFlags = URI_NORELATIVE | URI_NON_PERSISTABLE | ALLOWS_PROXY | 
+        ALLOWS_PROXY_HTTP | URI_DOES_NOT_RETURN_DATA | URI_DANGEROUS_TO_LOAD;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::NewURI(const nsACString & aSpec, const char *aOriginCharset,
+                           nsIURI *aBaseURI, nsIURI **_retval NS_OUTPARAM)
+{
+    LOG(("WebSocketHandler::NewURI() %p\n", this));
+
+    PRInt32 port;
+    nsresult rv = GetDefaultPort(&port);
+    if (NS_FAILED(rv))
+        return rv;
+
+    nsRefPtr<nsStandardURL> url = new nsStandardURL();
+    rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY, port, aSpec,
+                   aOriginCharset, aBaseURI);
+    if (NS_FAILED(rv))
+        return rv;
+    NS_ADDREF(*_retval = url);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval NS_OUTPARAM)
+{
+    LOG(("WebSocketHandler::NewChannel() %p\n", this));
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsWebSocketHandler::AllowPort(PRInt32 port, const char *scheme,
+                              PRBool *_retval NS_OUTPARAM)
+{
+    LOG(("WebSocketHandler::AllowPort() %p\n", this));
+
+    // do not override any blacklisted ports
+    *_retval = PR_FALSE;
+    return NS_OK;
+}
+
+} // namespace mozilla::net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/websocket/nsWebSocketHandler.h
@@ -0,0 +1,268 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Wellington Fernando de Macedo <wfernandom2004@gmail.com> (original author)
+ *   Patrick McManus <mcmanus@ducksong.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+#include "nsIWebSocketProtocol.h"
+#include "nsIURI.h"
+#include "nsISupports.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIEventTarget.h"
+#include "nsIStreamListener.h"
+#include "nsIProtocolHandler.h"
+#include "nsISocketTransport.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsILoadGroup.h"
+#include "nsITimer.h"
+#include "nsIDNSListener.h"
+#include "nsIHttpChannel.h"
+#include "nsIChannelEventSink.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsIStringStream.h"
+#include "nsIHttpChannelInternal.h"
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsDeque.h"
+
+namespace mozilla { namespace net {
+
+class nsPostMessage;
+class nsWSAdmissionManager;
+class nsWSCompression;
+class WSCallOnInputStreamReady;
+
+class nsWebSocketHandler : public nsIWebSocketProtocol,
+                           public nsIHttpUpgradeListener,
+                           public nsIStreamListener,
+                           public nsIProtocolHandler,
+                           public nsIInputStreamCallback,
+                           public nsIOutputStreamCallback,
+                           public nsITimerCallback,
+                           public nsIDNSListener,
+                           public nsIInterfaceRequestor,
+                           public nsIChannelEventSink
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIWEBSOCKETPROTOCOL
+  NS_DECL_NSIHTTPUPGRADELISTENER
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSIPROTOCOLHANDLER
+  NS_DECL_NSIINPUTSTREAMCALLBACK
+  NS_DECL_NSIOUTPUTSTREAMCALLBACK
+  NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSIDNSLISTENER
+  NS_DECL_NSIINTERFACEREQUESTOR
+  NS_DECL_NSICHANNELEVENTSINK
+
+  nsWebSocketHandler();
+  static void Shutdown();
+  
+  enum {
+      // Non Control Frames
+      kContinuation = 0x0,
+      kText =         0x1,
+      kBinary =       0x2,
+
+      // Control Frames
+      kClose =        0x8,
+      kPing =         0x9,
+      kPong =         0xA
+  };
+  
+  const static PRUint32 kControlFrameMask = 0x8;
+  const static PRInt32 kDefaultWSPort     = 80;
+  const static PRInt32 kDefaultWSSPort    = 443;
+  const static PRUint8 kMaskBit           = 0x80;
+  const static PRUint8 kFinalFragBit      = 0x80;
+
+  // section 7.4.1 defines these
+  const static PRUint16 kCloseNormal        = 1000;
+  const static PRUint16 kCloseGoingAway     = 1001;
+  const static PRUint16 kCloseProtocolError = 1002;
+  const static PRUint16 kCloseUnsupported   = 1003;
+  const static PRUint16 kCloseTooLarge      = 1004;
+
+protected:
+  virtual ~nsWebSocketHandler();
+  PRBool  mEncrypted;
+  
+private:
+  friend class nsPostMessage;
+  friend class nsWSAdmissionManager;
+  friend class WSCallOnInputStreamReady;
+  
+  void SendMsgInternal(nsCString *aMsg, PRInt32 datalen);
+  void PrimeNewOutgoingMessage();
+  void GeneratePong(PRUint8 *payload, PRUint32 len);
+  void GeneratePing();
+
+  nsresult BeginOpen();
+  nsresult HandleExtensions();
+  nsresult SetupRequest();
+  nsresult ApplyForAdmission();
+  
+  void StopSession(nsresult reason);
+  void AbortSession(nsresult reason);
+  void ReleaseSession();
+
+  void EnsureHdrOut(PRUint32 size);
+  void ApplyMask(PRUint32 mask, PRUint8 *data, PRUint64 len);
+
+  PRBool   IsPersistentFramePtr();
+  nsresult ProcessInput(PRUint8 *buffer, PRUint32 count);
+  PRUint32 UpdateReadBuffer(PRUint8 *buffer, PRUint32 count);
+
+  class OutboundMessage
+  {
+  public:
+      OutboundMessage (nsCString *str)
+          : mMsg(str), mIsControl(PR_FALSE), mBinaryLen(-1) {}
+
+      OutboundMessage (nsCString *str, PRInt32 dataLen)
+          : mMsg(str), mIsControl(PR_FALSE), mBinaryLen(dataLen) {}
+
+      OutboundMessage ()
+          : mMsg(nsnull), mIsControl(PR_TRUE), mBinaryLen(-1) {}
+
+      ~OutboundMessage() { delete mMsg; }
+      
+      PRBool IsControl()  { return mIsControl; }
+      const nsCString *Msg()  { return mMsg; }
+      PRInt32 BinaryLen() { return mBinaryLen; }
+      PRInt32 Length() 
+      { 
+          if (mBinaryLen >= 0)
+              return mBinaryLen;
+          return mMsg ? mMsg->Length() : 0;
+      }
+      PRUint8 *BeginWriting() 
+      { return (PRUint8 *)(mMsg ? mMsg->BeginWriting() : nsnull); }
+      PRUint8 *BeginReading() 
+      { return (PRUint8 *)(mMsg ? mMsg->BeginReading() : nsnull); }
+
+  private:
+      nsCString *mMsg;
+      PRBool     mIsControl;
+      PRInt32    mBinaryLen;
+  };
+  
+  nsCOMPtr<nsIURI>                         mOriginalURI;
+  nsCOMPtr<nsIURI>                         mURI;
+  nsCOMPtr<nsIWebSocketListener>           mListener;
+  nsCOMPtr<nsISupports>                    mContext;
+  nsCOMPtr<nsIInterfaceRequestor>          mCallbacks;
+  nsCOMPtr<nsIEventTarget>                 mSocketThread;
+  nsCOMPtr<nsIHttpChannelInternal>         mChannel;
+  nsCOMPtr<nsIHttpChannel>                 mHttpChannel;
+  nsCOMPtr<nsILoadGroup>                   mLoadGroup;
+  nsCOMPtr<nsICancelable>                  mDNSRequest;
+  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
+  
+  nsCString                       mProtocol;
+  nsCString                       mOrigin;
+  nsCString                       mHashedSecret;
+  nsCString                       mAddress;
+
+  nsCOMPtr<nsISocketTransport>    mTransport;
+  nsCOMPtr<nsIAsyncInputStream>   mSocketIn;
+  nsCOMPtr<nsIAsyncOutputStream>  mSocketOut;
+
+  nsCOMPtr<nsITimer>              mCloseTimer;
+  PRUint32                        mCloseTimeout;  /* milliseconds */
+
+  nsCOMPtr<nsITimer>              mOpenTimer;
+  PRUint32                        mOpenTimeout;  /* milliseconds */
+
+  nsCOMPtr<nsITimer>              mPingTimer;
+  PRUint32                        mPingTimeout;  /* milliseconds */
+  PRUint32                        mPingResponseTimeout;  /* milliseconds */
+  
+  PRUint32                        mRecvdHttpOnStartRequest   : 1;
+  PRUint32                        mRecvdHttpUpgradeTransport : 1;
+  PRUint32                        mRequestedClose            : 1;
+  PRUint32                        mClientClosed              : 1;
+  PRUint32                        mServerClosed              : 1;
+  PRUint32                        mStopped                   : 1;
+  PRUint32                        mCalledOnStop              : 1;
+  PRUint32                        mPingOutstanding           : 1;
+  PRUint32                        mAllowCompression          : 1;
+  PRUint32                        mAutoFollowRedirects       : 1;
+  PRUint32                        mReleaseOnTransmit         : 1;
+  
+  PRInt32                         mMaxMessageSize;
+  nsresult                        mStopOnClose;
+
+  // These are for the read buffers
+  PRUint8                        *mFramePtr;
+  PRUint8                        *mBuffer;
+  PRUint8                         mFragmentOpcode;
+  PRUint32                        mFragmentAccumulator;
+  PRUint32                        mBuffered;
+  PRUint32                        mBufferSize;
+  nsCOMPtr<nsIStreamListener>     mInflateReader;
+  nsCOMPtr<nsIStringInputStream>  mInflateStream;
+
+  // These are for the send buffers
+  const static PRInt32 kCopyBreak = 1000;
+  
+  OutboundMessage                *mCurrentOut;
+  PRUint32                        mCurrentOutSent;
+  nsDeque                         mOutgoingMessages;
+  nsDeque                         mOutgoingPingMessages;
+  nsDeque                         mOutgoingPongMessages;
+  PRUint32                        mHdrOutToSend;
+  PRUint8                        *mHdrOut;
+  PRUint8                         mOutHeader[kCopyBreak + 16];
+  nsWSCompression                *mCompressor;
+  PRUint32                        mDynamicOutputSize;
+  PRUint8                        *mDynamicOutput;
+};
+
+class nsWebSocketSSLHandler : public nsWebSocketHandler
+{
+public:
+    nsWebSocketSSLHandler() {nsWebSocketHandler::mEncrypted = PR_TRUE;}
+protected:
+    virtual ~nsWebSocketSSLHandler() {}
+};
+
+}} // namespace mozilla::net
--- a/netwerk/streamconv/converters/nsHTTPCompressConv.cpp
+++ b/netwerk/streamconv/converters/nsHTTPCompressConv.cpp
@@ -45,20 +45,20 @@
 #include "nsCOMPtr.h"
 #include "nsReadableUtils.h"
 #include "nsNetError.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 #include "nsComponentManagerUtils.h"
 
 // nsISupports implementation
-NS_IMPL_ISUPPORTS3(nsHTTPCompressConv,
-                   nsIStreamConverter,
-                   nsIStreamListener,
-                   nsIRequestObserver)
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsHTTPCompressConv,
+                              nsIStreamConverter,
+                              nsIStreamListener,
+                              nsIRequestObserver)
 
 // nsFTPDirListingConv methods
 nsHTTPCompressConv::nsHTTPCompressConv()
     : mListener(nsnull)
     , mMode(HTTP_COMPRESS_IDENTITY)
     , mOutBuffer(NULL)
     , mInpBuffer(NULL)
     , mOutBufferLen(0)