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 idunknown
push userunknown
push dateunknown
reviewerssmaug, biesi, bz
bugs640003
milestone6.0a1
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)