Bug 562681, websockets (protocol v76), r=smaug
authorwfernandom2004@gmail.com
Thu, 17 Jun 2010 21:36:01 +0300
changeset 43834 c181a80b03deb56439b0bc742341ab5af31d1c28
parent 43833 bcd52abd2495fb9b9a0dc2008d130e2fcd6f5ccb
child 43835 403e9f69ec1aa941e37711b50faf67505dd58bbc
push idunknown
push userunknown
push dateunknown
reviewerssmaug
bugs562681
milestone1.9.3a6pre
Bug 562681, websockets (protocol v76), r=smaug
content/base/public/nsIWebSocket.idl
content/base/src/nsWebSocket.cpp
content/base/src/nsWebSocket.h
content/events/public/nsIPrivateDOMEvent.h
content/events/src/Makefile.in
content/events/src/nsDOMCloseEvent.cpp
content/events/src/nsDOMCloseEvent.h
content/events/src/nsEventDispatcher.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/interfaces/events/Makefile.in
dom/interfaces/events/nsIDOMCloseEvent.idl
netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
netwerk/protocol/http/nsHttpChannelAuthProvider.h
--- a/content/base/public/nsIWebSocket.idl
+++ b/content/base/public/nsIWebSocket.idl
@@ -46,31 +46,34 @@ 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(14b32727-d501-4a3b-b0f9-5d9109a0d5f0)]
+[scriptable, uuid(af0d293a-d18c-43e6-84ed-d1de5e85b885)]
 interface nsIWebSocket : nsISupports
 {
   readonly attribute DOMString URL;
 
   //ready state
   const unsigned short CONNECTING = 0;
   const unsigned short OPEN = 1;
-  const unsigned short CLOSED = 2;
-  readonly attribute long readyState;
+  const unsigned short CLOSING = 2;
+  const unsigned short CLOSED = 3;
+  readonly attribute unsigned short readyState;
+
   readonly attribute unsigned long bufferedAmount;
 
   // event handler attributes
   attribute nsIDOMEventListener onopen;
   attribute nsIDOMEventListener onmessage;
+  attribute nsIDOMEventListener onerror;
   attribute nsIDOMEventListener onclose;
 
   /**
    * Transmits data using the connection.
    *
    * @param data The data to be transmited.
    * @return if the connection is still established (and the data was queued or
    *         sent successfully).
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -81,30 +81,33 @@
 #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"
 
 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_TRY_CONNECT_AGAIN         1000
+#define TIMEOUT_WAIT_FOR_SERVER_RESPONSE  20000
+#define TIMEOUT_WAIT_FOR_CLOSING          5000
 
 #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;                                                        \
     }                                                                     \
@@ -186,104 +189,129 @@ static nsIThread *gWebSocketThread = nsn
 #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
 {
-friend class nsNetAddressComparator;
-friend class nsAutoCloseOwner;
+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_NSIREQUEST
   NS_DECL_NSICHANNEL
   NS_DECL_NSIPROXIEDCHANNEL
 
-  // nsIHttpAuthenticableChannel. We can't use
-  // NS_DECL_NSIHTTPAUTHENTICABLECHANNEL because it duplicates cancel() and
-  // others.
+  // 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)
+  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(Close)
+  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(FailConnection)
+
+  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:
   // 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);
+  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);
+  void GenerateSecKey(nsCString& aKey,
+                      PRUint32 *aNumber);
+  nsresult GenerateRequestKeys(nsCString& aKey1,
+                               nsCString& aKey2,
+                               nsCString& aKey3);
   PRBool UsingHttpProxy();
   nsresult Reset();
   void RemoveFromLoadGroup();
   nsresult ProcessHeaders();
   nsresult PostData(nsCString *aBuffer, PRBool aIsMessage);
   nsresult PrintErrorOnConsole(const char       *aBundleURI,
                                const PRUnichar  *aError,
                                const PRUnichar **aFormatStrings,
                                PRUint32          aFormatStringsLen);
+  PRBool SentAlreadyTheCloseFrame()
+  { return mPostedCloseFrame && mOutgoingMessages.GetSize() == 0; }
 
   // 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(FailConnection)
   DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(Connected)
-  DECL_RUNNABLE_ON_MAIN_THREAD_METHOD(CloseOwner)
+  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)
 
   // called to cause the underlying socket to start speaking SSL
   nsresult ProxyStartSSL();
 
   // shared by both threads (the main and the socket ones)
@@ -295,25 +323,27 @@ private:
   nsCOMPtr<nsIAsyncOutputStream>  mSocketOutput;
   nsCOMPtr<nsIProxyInfo>          mProxyInfo;
   nsDeque mOutgoingMessages; // has nsCString* 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 {
-    kWebSocketOriginPos = 0,
-    kWebSocketLocationPos,
-    kWebSocketProtocolPos,
+    kUpgradePos = 0,
+    kConnectionPos,
+    kSecWebSocketOriginPos,
+    kSecWebSocketLocationPos,
+    kSecWebSocketProtocolPos,
     kSetCookiePos,
-    kSetCookie2Pos,
     kProxyAuthenticatePos,
     kServerPos,  // for digest auth
     kHeadersLen
   };
 
   // used only by the socket thread
   nsCString mBuffer;
   PRUint32 mBytesInBuffer; // it is needed because mBuffer.SetLength() does
@@ -335,31 +365,40 @@ private:
 
   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;
 
   // auth specific data
   nsCString mProxyCredentials;
   nsCString mCredentials;
+  nsCOMPtr<nsIHttpChannelAuthProvider> mAuthProvider;
   PRPackedBool mAuthenticating;
-  nsCOMPtr<nsIHttpChannelAuthProvider> mAuthProvider;
-
-  // for nsIRequest implementation
-  nsCString mRequestName;
-  nsresult mFailureStatus;
+
+  PRPackedBool mPostedCloseFrame;
+  PRPackedBool mClosedCleanly;
 
   /**
-   * A simple state machine used to manage connection status
+   * 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
@@ -390,94 +429,115 @@ private:
    *                          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_RETRYING_TO_AUTHENTICATE |
    *                          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)
    *
    */
 
   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 nsNetAddressComparator
+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 nsAutoCloseOwner
+class nsWSAutoClose
 {
 public:
-  nsAutoCloseOwner(nsWebSocketEstablishedConnection* conn) : mConnection(conn)
+  nsWSAutoClose(nsWebSocketEstablishedConnection* conn) : mConnection(conn)
   {}
 
-  ~nsAutoCloseOwner() { mConnection->CloseOwner(); }
+  ~nsWSAutoClose() { mConnection->Close(); }
 
 private:
   nsWebSocketEstablishedConnection* mConnection;
 };
 
 PRBool
-nsNetAddressComparator::Equals(nsWebSocketEstablishedConnection* a,
-                               nsWebSocketEstablishedConnection* b) const
+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;
   }
 
@@ -498,18 +558,18 @@ nsNetAddressComparator::Equals(nsWebSock
 
   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
-nsNetAddressComparator::LessThan(nsWebSocketEstablishedConnection* a,
-                                 nsWebSocketEstablishedConnection* b) const
+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) {
@@ -573,34 +633,43 @@ nsWebSocketEstablishedConnection::nsWebS
   mBytesAlreadySentOfFirstOutString(0),
   mOutgoingBufferedAmount(0),
   mOwner(nsnull),
   mBytesInBuffer(0),
   mLengthToDiscard(0),
   mReadingProxyConnectResponse(PR_FALSE),
   mCurrentProxyConfig(eNotResolvingProxy),
   mProxyFailureReason(NS_OK),
+  mFailureStatus(NS_OK),
   mAuthenticating(PR_FALSE),
-  mFailureStatus(NS_OK),
+  mPostedCloseFrame(PR_FALSE),
+  mClosedCleanly(PR_FALSE),
   mStatus(CONN_NOT_CONNECTED)
 {
   NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
   nsLayoutStatics::AddRef();
 }
 
 nsWebSocketEstablishedConnection::~nsWebSocketEstablishedConnection()
 {
 }
 
 nsresult
 nsWebSocketEstablishedConnection::PostData(nsCString *aBuffer,
                                            PRBool aIsMessage)
 {
   NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
 
+  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);
 
   nsresult rv;
   PRInt32 sizeBefore = mOutgoingMessages.GetSize();
   mOutgoingMessages.Push(aBuffer);
   NS_ENSURE_TRUE(mOutgoingMessages.GetSize() == sizeBefore + 1,
                  NS_ERROR_OUT_OF_MEMORY);
   if (aIsMessage) {
@@ -628,38 +697,38 @@ nsWebSocketEstablishedConnection::PostMe
 
   // only send messages when connected
   NS_ENSURE_STATE(mStatus >= CONN_CONNECTED_AND_READY);
 
   nsresult rv;
 
   nsCOMPtr<nsICharsetConverterManager> ccm =
     do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
+  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
 
   nsCOMPtr<nsIUnicodeEncoder> converter;
   rv = ccm->GetUnicodeEncoder("UTF-8", getter_AddRefs(converter));
-  NS_ENSURE_SUCCESS(rv, rv);
+  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
 
   rv = converter->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace,
                                          nsnull, UTF_8_REPLACEMENT_CHAR);
-  NS_ENSURE_SUCCESS(rv, rv);
+  ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
 
   PRInt32 inLen = aMessage.Length();
   PRInt32 maxLen;
   rv = converter->GetMaxLength(aMessage.BeginReading(), inLen, &maxLen);
-  NS_ENSURE_SUCCESS(rv, rv);
+  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());
-  NS_ENSURE_TRUE(buf.get(), NS_ERROR_OUT_OF_MEMORY);
+  ENSURE_TRUE_AND_FAIL_IF_FAILED(buf.get(), NS_ERROR_OUT_OF_MEMORY);
 
   buf->SetLength(maxLen);
-  NS_ENSURE_TRUE(buf->Length() == static_cast<PRUint32>(maxLen),
-                 NS_ERROR_OUT_OF_MEMORY);
+  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;
 
   PRInt32 outLen = maxLen;
   rv = converter->Convert(aMessage.BeginReading(), &inLen, start, &outLen);
   if (NS_SUCCEEDED(rv)) {
@@ -673,62 +742,137 @@ nsWebSocketEstablishedConnection::PostMe
   }
 
   char* end = buf->BeginWriting() + outLen + 1;
   *end = static_cast<char>(END_BYTE_OF_MESSAGE);
 
   outLen += 2;
 
   buf->SetLength(outLen);
-  NS_ENSURE_TRUE(buf->Length() == static_cast<PRUint32>(outLen),
-                 NS_ERROR_UNEXPECTED);
-
-  return PostData(buf.forget(), PR_TRUE);
+  ENSURE_TRUE_AND_FAIL_IF_FAILED(buf->Length() == static_cast<PRUint32>(outLen),
+                                 NS_ERROR_UNEXPECTED);
+
+  rv = PostData(buf.forget(), PR_TRUE);
+  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"
-                     "Upgrade: WebSocket\r\n"
-                     "Connection: Upgrade\r\n");
-
-  // Host
-  buf->AppendLiteral("Host: ");
-  buf->Append(mOwner->mAsciiHost);
-  buf->AppendLiteral(":");
-  buf->AppendInt(mOwner->mPort);
-  buf->AppendLiteral("\r\n");
-
-  // Origin
-  buf->AppendLiteral("Origin: ");
-  buf->Append(mOwner->mOrigin);
+  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 = random() % 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;
+    }
+
+    headersToSend.RemoveElementAt(headerPosToSendNow);
+  }
+
   buf->AppendLiteral("\r\n");
-  // WebSocket-Protocol
-  if (!mOwner->mProtocol.IsEmpty()) {
-    buf->AppendLiteral("WebSocket-Protocol: ");
-    buf->Append(mOwner->mProtocol);
-    buf->AppendLiteral("\r\n");
-  }
-  // Authorization
-  rv = AddAuthorizationHeaders(*buf, PR_FALSE);
-  CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
-  // Cookie
-  rv = AddCookiesToRequest(*buf);
-  CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
-  // CR LF
-  buf->AppendLiteral("\r\n");
+  buf->Append(key_3);
 
   mStatus = CONN_SENDING_INITIAL_REQUEST;
 
   rv = PostData(buf.forget(), PR_FALSE);
   CHECK_SUCCESS_AND_FAIL_IF_FAILED(rv);
 }
 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
 
@@ -754,16 +898,19 @@ GetHttpResponseCode(const nsCString& aLi
   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
@@ -814,44 +961,49 @@ nsWebSocketEstablishedConnection::Handle
         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:
     {
-      const char strResponseCheck[] =
-        "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
-        "Upgrade: WebSocket\r\n"
-        "Connection: Upgrade\r\n";
-      PRUint32 lengthStr = NS_ARRAY_LENGTH(strResponseCheck) - 1;
-
-      if (mBytesInBuffer < lengthStr) {
-        return NS_OK;
-      }
-
-      if (memcmp(mBuffer.BeginReading(), strResponseCheck, lengthStr)) {
-        return NS_ERROR_UNEXPECTED;
+      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);
       }
-
-      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);
@@ -913,26 +1065,28 @@ nsWebSocketEstablishedConnection::Handle
       NS_ENSURE_STATE(!headerName.IsEmpty());
 
       PRInt32 headerPos = -1;
       if (mReadingProxyConnectResponse) {
         if (headerName.LowerCaseEqualsLiteral("proxy-authenticate")) {
           headerPos = kProxyAuthenticatePos;
         }
       } else {
-        if (headerName.LowerCaseEqualsLiteral("websocket-origin")) {
-          headerPos = kWebSocketOriginPos;
-        } else if (headerName.LowerCaseEqualsLiteral("websocket-location")) {
-          headerPos = kWebSocketLocationPos;
-        } else if (headerName.LowerCaseEqualsLiteral("websocket-protocol")) {
-          headerPos = kWebSocketProtocolPos;
+        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;
-        } else if (headerName.LowerCaseEqualsLiteral("set-cookie2")) {
-          headerPos = kSetCookie2Pos;
         }
       }
       if (headerPos == -1 && headerName.LowerCaseEqualsLiteral("server")) {
         headerPos = kServerPos;
       }
 
       if (headerPos != -1) {
         NS_ENSURE_STATE(mHeaders[headerPos].IsEmpty());
@@ -965,22 +1119,42 @@ nsWebSocketEstablishedConnection::Handle
           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 + 1);
-      mBytesInBuffer -= aStart + 1;
+      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];
 
@@ -993,27 +1167,40 @@ nsWebSocketEstablishedConnection::Handle
 
       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 (SentAlreadyTheCloseFrame()) {
+              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;
 
@@ -1038,33 +1225,35 @@ nsWebSocketEstablishedConnection::Handle
       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
-            nsCString* dataMessage = new nsCString();
-            NS_ENSURE_TRUE(dataMessage, NS_ERROR_OUT_OF_MEMORY);
-            *dataMessage = Substring(mBuffer, 1, i - 1);
+            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);
+              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);
         }
@@ -1130,16 +1319,118 @@ nsWebSocketEstablishedConnection::AddCoo
     aStr.AppendLiteral("Cookie: ");
     aStr.Append(cookieValue);
     aStr.AppendLiteral("\r\n");
   }
 
   return NS_OK;
 }
 
+void
+nsWebSocketEstablishedConnection::GenerateSecKey(nsCString& aKey,
+                                                 PRUint32 *aNumber)
+{
+  PRUint32 i;
+
+  PRUint32 spaces = random() % 12 + 1;
+  PRUint32 max = PR_UINT32_MAX / spaces;
+  PRUint32 number = random() % 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 = random() % 12 + 1;
+  for (i = 0; i < numberOfCharsToInsert; ++i) {
+    PRUint32 posToInsert = random() % key.Length();
+    char charToInsert =
+      random() % 2 == 0 ?
+        static_cast<char>(0x21 + (random() % (0x2F - 0x21 + 1))) :
+        static_cast<char>(0x3A + (random() % (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 = random() % (key.Length() - 1) + 1;
+    key.Insert(static_cast<char>(0x20), posToInsert);
+  }
+
+  aKey = key;
+  *aNumber = 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
+  GenerateSecKey(key_1, &number_1);
+  GenerateSecKey(key_2, &number_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>(random() % 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;
@@ -1184,19 +1475,23 @@ IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGI
     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(CloseOwner)
+IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(FrameError)
 {
-  mOwner->Close();
+  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) {
@@ -1207,58 +1502,19 @@ IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGI
 
       if (mReceivedMessages.GetSize() == 0) {
         return;
       }
 
       data = static_cast<nsCString*>(mReceivedMessages.PopFront());
     }
 
-    nsCOMPtr<nsIDocument> doc =
-       nsContentUtils::GetDocumentFromScriptContext(mOwner->mScriptContext);
-    rv = mOwner->CheckInnerWindowCorrectness();
-    if (NS_FAILED(rv) || !doc) {
-      continue;
-    }
-
-    // create an event that uses the MessageEvent interface,
-    // which does not bubble, is cancelable, and has no default action
-
-    nsCOMPtr<nsIDOMEvent> event;
-    rv = nsEventDispatcher::CreateEvent(nsnull, nsnull,
-                                        NS_LITERAL_STRING("messageevent"),
-                                        getter_AddRefs(event));
+    rv = mOwner->CreateAndDispatchMessageEvent(data);
     if (NS_FAILED(rv)) {
-      NS_WARNING("failed creating event");
-      return;
-    }
-
-    nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
-    rv = messageEvent->InitMessageEvent(NS_LITERAL_STRING("message"),
-                                        PR_FALSE, PR_FALSE,
-                                        NS_ConvertUTF8toUTF16(*data),
-                                        NS_ConvertUTF8toUTF16(mOwner->mOrigin),
-                                        EmptyString(), nsnull);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("failed initializing message event");
-      return;
-    }
-
-    nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
-    rv = privateEvent->SetTrusted(PR_TRUE);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("failed trusting event");
-      return;
-    }
-
-    rv = nsEventDispatcher::DispatchDOMEvent(static_cast<nsPIDOMEventTarget*>(mOwner),
-                                             nsnull, event, nsnull, nsnull);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("failed dispatching message event");
-      return;
+      NS_WARNING("Failed to dispatch the message event");
     }
   }
 }
 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
 
 nsresult
 nsWebSocketEstablishedConnection::ProxyStartSSL()
 {
@@ -1339,16 +1595,18 @@ nsWebSocketEstablishedConnection::Init(n
 
 nsresult
 nsWebSocketEstablishedConnection::DoConnect()
 {
   NS_ASSERTION(NS_IsMainThread(), "Not running on main thread");
 
   nsresult rv;
 
+  mStatus = CONN_CONNECTING;
+
   rv = AddWSConnecting();
   ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
 
   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.
@@ -1393,17 +1651,17 @@ nsWebSocketEstablishedConnection::DoConn
 
   if (!UsingHttpProxy()) {
     rv = DoInitialRequest();
     ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
     return NS_OK;
   }
 
   nsAutoPtr<nsCString> buf(new nsCString());
-  NS_ENSURE_TRUE(buf.get(), NS_ERROR_OUT_OF_MEMORY);
+  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);
@@ -1431,33 +1689,36 @@ nsWebSocketEstablishedConnection::DoConn
 
   return NS_OK;
 }
 
 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(AddWSConnecting)
 {
 #ifdef DEBUG
   PRUint32 index =
-    sWSsConnecting->BinaryIndexOf(this, nsNetAddressComparator());
+    sWSsConnecting->BinaryIndexOf(this, nsWSNetAddressComparator());
   NS_ASSERTION(index == nsTArray<PRNetAddr>::NoIndex,
                "The ws connection shouldn't be already added in the "
                "serialization list.");
 #endif
 
   PRBool inserted =
-    !!(sWSsConnecting->InsertElementSorted(this, nsNetAddressComparator()));
+    !!(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, nsNetAddressComparator());
+    sWSsConnecting->BinaryIndexOf(this, nsWSNetAddressComparator());
   if (index != nsTArray<PRNetAddr>::NoIndex) {
     sWSsConnecting->RemoveElementAt(index);
   }
 }
 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
 
 // static
 void
@@ -1470,17 +1731,17 @@ nsWebSocketEstablishedConnection::TryCon
   nsRefPtr<nsWebSocketEstablishedConnection> thisObject =
     static_cast<nsWebSocketEstablishedConnection*>(aClosure);
 
   if (!thisObject->mOwner) { // we have been disconnected
     return;
   }
 
   PRUint32 index = sWSsConnecting->BinaryIndexOf(thisObject,
-                                                 nsNetAddressComparator());
+                                                 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();
@@ -1501,16 +1762,33 @@ nsWebSocketEstablishedConnection::
 
   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) {
@@ -1518,26 +1796,38 @@ nsWebSocketEstablishedConnection::Proces
       return NS_ERROR_UNEXPECTED;
     return NS_OK;
   }
 
   if (mReadingProxyConnectResponse) {
     return NS_OK;
   }
 
-  // test the websocket-origin header
-
-  nsCString responseOriginHeader = mHeaders[kWebSocketOriginPos];
+  // 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 websocket-location header
+  // 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);
 
@@ -1545,37 +1835,35 @@ nsWebSocketEstablishedConnection::Proces
       (!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[kWebSocketLocationPos].Equals(validWebSocketLocation1) &&
-      !mHeaders[kWebSocketLocationPos].Equals(validWebSocketLocation2)) {
+  if (!mHeaders[kSecWebSocketLocationPos].Equals(validWebSocketLocation1) &&
+      !mHeaders[kSecWebSocketLocationPos].Equals(validWebSocketLocation2)) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  // handle the websocket-protocol header
+  // handle the sec-websocket-protocol header
   if (!mOwner->mProtocol.IsEmpty() &&
-      !mHeaders[kWebSocketProtocolPos].
+      !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);
   }
 
-  // TODO: handle the set-cookie2 header
-
   return NS_OK;
 }
 
 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(HandleSetCookieHeader)
 {
   nsresult rv;
 
   nsCOMPtr<nsICookieService> cookieService =
@@ -1647,20 +1935,95 @@ nsWebSocketEstablishedConnection::PrintE
 
   // print the error message directly to the JS console
   rv = console->LogStringMessage(message.get());
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
+IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(Close)
+{
+  nsresult rv;
+
+  if (mOwner->mReadyState == nsIWebSocket::CONNECTING) {
+    // we must not convey any failure information to scripts, so we just
+    // disconnect and maintain the owner WebSocket object in the CONNECTING
+    // state.
+    Disconnect();
+    return;
+  }
+
+  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);
+
+    rv = PostData(closeFrame.forget(), PR_FALSE);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to post the close frame");
+      return;
+    }
+
+    mPostedCloseFrame = PR_TRUE;
+  } else {
+    // Probably failed to send the close frame. Just disconnect.
+    Disconnect();
+  }
+}
+IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_END
+
+void
+nsWebSocketEstablishedConnection::ForceClose()
+{
+  if (mOwner->mReadyState == nsIWebSocket::CONNECTING) {
+    // we must not convey any failure information to scripts, so we just
+    // disconnect and maintain the owner WebSocket object in the CONNECTING
+    // state.
+    Disconnect();
+    return;
+  }
+  mOwner->SetReadyState(nsIWebSocket::CLOSING);
+  mOwner->SetReadyState(nsIWebSocket::CLOSED);
+  Disconnect();
+}
+
 IMPL_RUNNABLE_ON_MAIN_THREAD_METHOD_BEGIN(FailConnection)
 {
   nsresult rv;
-  nsAutoCloseOwner autoClose(this);
+  nsWSAutoClose autoClose(this);
 
   if (mFailureStatus == NS_OK) {
     mFailureStatus = NS_ERROR_UNEXPECTED;
   }
 
   nsCAutoString targetSpec;
   rv = mOwner->mURI->GetSpec(targetSpec);
   WARN_IF_FALSE_AND_RETURN(NS_SUCCEEDED(rv), "Failed to get targetSpec");
@@ -1712,16 +2075,21 @@ nsWebSocketEstablishedConnection::Discon
       mTryConnectTimer = nsnull;
     }
 
     if (mInitialServerResponseTimer) {
       mInitialServerResponseTimer->Cancel();
       mInitialServerResponseTimer = nsnull;
     }
 
+    if (mCloseFrameServerResponseTimer) {
+      mCloseFrameServerResponseTimer->Cancel();
+      mCloseFrameServerResponseTimer = nsnull;
+    }
+
     if (mProxyResolveCancelable) {
       mProxyResolveCancelable->Cancel(NS_ERROR_ABORT);
       mProxyResolveCancelable = nsnull;
     }
 
     if (mDNSRequest) {
       mDNSRequest->Cancel(NS_ERROR_ABORT);
       mDNSRequest = nsnull;
@@ -1935,23 +2303,17 @@ NS_IMETHODIMP
 nsWebSocketEstablishedConnection::Cancel(nsresult aStatus)
 {
   if (!mOwner) {
     return NS_OK;
   }
 
   mFailureStatus = aStatus;
 
-  nsCOMPtr<nsIRunnable> event =
-    NS_NewRunnableMethod(this, &nsWebSocketEstablishedConnection::CloseOwner);
-  if (event) {
-    NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
-  }
-
-  return NS_OK;
+  return Close();
 }
 
 NOT_IMPLEMENTED_IF_FUNC_0(Suspend)
 NOT_IMPLEMENTED_IF_FUNC_0(Resume)
 
 NS_IMETHODIMP
 nsWebSocketEstablishedConnection::GetLoadGroup(nsILoadGroup **aLoadGroup)
 {
@@ -2121,30 +2483,34 @@ nsWebSocketEstablishedConnection::OnAuth
   if (!mOwner) {
     return NS_OK;
   }
 
   if (!userCancel) {
     return FailConnection();
   }
 
-  return CloseOwner();
+  return Close();
 }
 
 //-----------------------------------------------------------------------------
 // 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;
+  }
+
   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);
@@ -2233,17 +2599,22 @@ nsWebSocketEstablishedConnection::OnInpu
       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) {
-          CloseOwner();
+          mStatus = CONN_CLOSED;
+          if (mStatus < CONN_CONNECTED_AND_READY) {
+            FailConnection();
+          } else {
+            Close();
+          }
         }
         mFailureStatus = NS_BASE_STREAM_CLOSED;
         return NS_BASE_STREAM_CLOSED;
       }
 
       PRUint32 start = mBytesInBuffer;
       mBytesInBuffer += read;
       rv = HandleNewInputString(start);
@@ -2313,17 +2684,22 @@ nsWebSocketEstablishedConnection::OnOutp
             mProxyFailureReason = rv;
             return ResolveNextProxyAndConnect();
           }
 
           mFailureStatus = rv;
           ENSURE_SUCCESS_AND_FAIL_IF_FAILED(rv, rv);
 
           if (written == 0) {
-            CloseOwner();
+            mStatus = CONN_CLOSED;
+            if (mStatus < CONN_CONNECTED_AND_READY) {
+              FailConnection();
+            } else {
+              Close();
+            }
             mFailureStatus = NS_BASE_STREAM_CLOSED;
             return NS_BASE_STREAM_CLOSED;
           }
 
           if (strIsMessage) {
             PRBool currentStrHasEndFrameByte =
               (mBytesAlreadySentOfFirstOutString + written ==
                strToSend->Length());
@@ -2357,16 +2733,23 @@ nsWebSocketEstablishedConnection::OnOutp
         delete strToSend;
         mBytesAlreadySentOfFirstOutString = 0;
       }
 
       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 &&
+            SentAlreadyTheCloseFrame()) {
+          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,
@@ -2476,44 +2859,44 @@ NS_IMPL_RELEASE_INHERITED(nsWebSocket, n
 /**
  * This Initialize method is called from XPConnect via nsIJSNativeInitializer.
  * It is used for constructing our nsWebSocket from JavaScript. It expects a URL
  * string parameter and an optional protocol parameter. It also initializes the
  * principal, the script context and the window owner.
  */
 NS_IMETHODIMP
 nsWebSocket::Initialize(nsISupports* aOwner,
-                        JSContext* cx,
-                        JSObject* obj,
-                        PRUint32 argc,
-                        jsval* argv)
+                        JSContext* aContext,
+                        JSObject* aObject,
+                        PRUint32 aArgc,
+                        jsval* aArgv)
 {
   nsAutoString urlParam, protocolParam;
 
   PRBool prefEnabled =
     nsContentUtils::GetBoolPref("network.websocket.enabled", PR_TRUE);
   if (!prefEnabled) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  if (argc != 1 && argc != 2) {
+  if (aArgc != 1 && aArgc != 2) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
-  JSAutoRequest ar(cx);
-
-  JSString* jsstr = JS_ValueToString(cx, argv[0]);
+  JSAutoRequest ar(aContext);
+
+  JSString* jsstr = JS_ValueToString(aContext, aArgv[0]);
   if (!jsstr) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
   urlParam.Assign(reinterpret_cast<const PRUnichar*>(JS_GetStringChars(jsstr)),
                   JS_GetStringLength(jsstr));
 
-  if (argc == 2) {
-    jsstr = JS_ValueToString(cx, argv[1]);
+  if (aArgc == 2) {
+    jsstr = JS_ValueToString(aContext, aArgv[1]);
     if (!jsstr) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
     protocolParam.
       Assign(reinterpret_cast<const PRUnichar*>(JS_GetStringChars(jsstr)),
              JS_GetStringLength(jsstr));
   }
 
@@ -2551,59 +2934,172 @@ nsWebSocket::EstablishConnection()
   rv = conn->Init(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mConnection = conn;
 
   return NS_OK;
 }
 
-nsresult
-nsWebSocket::SetReadyState(PRInt32 aNewReadyState)
+class nsWSCloseEvent : public nsRunnable
 {
-  if (mReadyState == aNewReadyState) {
-    return NS_OK;
+public:
+  nsWSCloseEvent(nsWebSocket *aWebSocket, PRBool aWasClean)
+    : mWebSocket(aWebSocket),
+      mWasClean(aWasClean)
+  {}
+
+  NS_IMETHOD Run()
+  {
+    return mWebSocket->CreateAndDispatchCloseEvent(mWasClean);
   }
 
-  NS_ENSURE_TRUE((aNewReadyState == nsIWebSocket::OPEN) ||
-                 (aNewReadyState == nsIWebSocket::CLOSED),
-                 NS_ERROR_UNEXPECTED);
-
-  mReadyState = aNewReadyState;
-
+private:
+  nsRefPtr<nsWebSocket> mWebSocket;
+  PRBool mWasClean;
+};
+
+nsresult
+nsWebSocket::CreateAndDispatchSimpleEvent(const nsString& aName)
+{
   nsresult rv;
 
-  nsCOMPtr<nsIDocument> doc =
-     nsContentUtils::GetDocumentFromScriptContext(mScriptContext);
   rv = CheckInnerWindowCorrectness();
-  if (NS_FAILED(rv) || !doc) {
+  if (NS_FAILED(rv)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMEvent> event;
   rv = NS_NewDOMEvent(getter_AddRefs(event), nsnull, nsnull);
-  NS_ENSURE_SUCCESS(rv, NS_OK);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // it doesn't bubble, and it isn't cancelable
-  if (mReadyState == nsIWebSocket::OPEN) {
-    rv = event->InitEvent(NS_LITERAL_STRING("open"), PR_FALSE, PR_FALSE);
-    NS_ENSURE_SUCCESS(rv, rv);
-  } else if (mReadyState == nsIWebSocket::CLOSED) {
-    rv = event->InitEvent(NS_LITERAL_STRING("close"), PR_FALSE, PR_FALSE);
-    NS_ENSURE_SUCCESS(rv, NS_OK);
+  rv = event->InitEvent(aName, PR_FALSE, PR_FALSE);
+  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::CreateAndDispatchMessageEvent(nsCString *aData)
+{
+  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),
+                                      EmptyString(), nsnull);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
   rv = privateEvent->SetTrusted(PR_TRUE);
-  NS_ENSURE_SUCCESS(rv, NS_OK);
-
-  rv = DispatchDOMEvent(nsnull, event, nsnull, nsnull);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
+}
+
+nsresult
+nsWebSocket::CreateAndDispatchCloseEvent(PRBool aWasClean)
+{
+  nsresult rv;
+
+  rv = CheckInnerWindowCorrectness();
+  if (NS_FAILED(rv)) {
+    return NS_OK;
+  }
+
+  // create an event that uses the CloseEvent interface,
+  // which does not bubble, is not cancelable, and has no default action
+
+  nsCOMPtr<nsIDOMEvent> event;
+  rv = NS_NewDOMCloseEvent(getter_AddRefs(event), nsnull, nsnull);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDOMCloseEvent> closeEvent = do_QueryInterface(event);
+  rv = closeEvent->InitCloseEvent(NS_LITERAL_STRING("close"),
+                                  PR_FALSE, PR_FALSE,
+                                  aWasClean);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
+  rv = privateEvent->SetTrusted(PR_TRUE);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return NS_OK;
+  return DispatchDOMEvent(nsnull, event, nsnull, nsnull);
+}
+
+void
+nsWebSocket::SetReadyState(PRUint16 aNewReadyState)
+{
+  nsresult rv;
+
+  if (mReadyState == aNewReadyState) {
+    return;
+  }
+
+  NS_ASSERTION((aNewReadyState == nsIWebSocket::OPEN) ||
+               (aNewReadyState == nsIWebSocket::CLOSING) ||
+               (aNewReadyState == nsIWebSocket::CLOSED),
+               "unexpected readyState");
+
+  if (aNewReadyState == nsIWebSocket::OPEN) {
+    NS_ASSERTION(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");
+    }
+    return;
+  }
+
+  if (aNewReadyState == nsIWebSocket::CLOSING) {
+    NS_ASSERTION((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());
+
+    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");
+    }
+  }
 }
 
 nsresult
 nsWebSocket::ParseURL(const nsString& aURL)
 {
   nsresult rv;
 
   NS_ENSURE_TRUE(!aURL.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
@@ -2715,17 +3211,17 @@ nsWebSocket::SetProtocol(const nsString&
 NS_IMETHODIMP
 nsWebSocket::GetURL(nsAString& aURL)
 {
   aURL = mOriginalURL;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWebSocket::GetReadyState(PRInt32 *aReadyState)
+nsWebSocket::GetReadyState(PRUint16 *aReadyState)
 {
   *aReadyState = mReadyState;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebSocket::GetBufferedAmount(PRUint32 *aBufferedAmount)
 {
@@ -2747,16 +3243,17 @@ nsWebSocket::GetBufferedAmount(PRUint32 
   NS_IMETHODIMP                                                                \
   nsWebSocket::SetOn##_eventlistenername(nsIDOMEventListener * aEventListener) \
   {                                                                            \
     return RemoveAddEventListener(NS_LITERAL_STRING(#_eventlistenername),      \
                                   _eventlistener, aEventListener);             \
   }
 
 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)
 {
   *aRet = PR_FALSE;
 
@@ -2774,40 +3271,49 @@ nsWebSocket::Send(const nsAString& aData
       if (i + 1 == length || !NS_IS_LOW_SURROGATE(aData[i + 1])) {
         return NS_ERROR_DOM_SYNTAX_ERR;
       }
       ++i;
       continue;
     }
   }
 
-  if (mReadyState == nsIWebSocket::CLOSED) {
+  if (mReadyState == nsIWebSocket::CLOSING ||
+      mReadyState == nsIWebSocket::CLOSED) {
     mOutgoingBufferedAmount += NS_ConvertUTF16toUTF8(aData).Length();
     return NS_OK;
   }
 
   nsresult rv = mConnection->PostMessage(PromiseFlatString(aData));
   *aRet = NS_SUCCEEDED(rv);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebSocket::Close()
 {
-  if (mReadyState == nsIWebSocket::CLOSED) {
+  if (mReadyState == nsIWebSocket::CLOSING ||
+      mReadyState == nsIWebSocket::CLOSED) {
     return NS_OK;
   }
 
-  if (mConnection) {
-    mOutgoingBufferedAmount = mConnection->GetOutgoingBufferedAmount();
-    mConnection->Disconnect();
-    mConnection = nsnull;
+  if (mReadyState == nsIWebSocket::CONNECTING) {
+    mConnection->FailConnection();
+
+    // We need to set the readyState here because mConnection would set it
+    // only if first connected. Also, let the two readyState changes here
+    // for future extensions (for instance an onreadystatechange event)
+    SetReadyState(nsIWebSocket::CLOSING);
+    SetReadyState(nsIWebSocket::CLOSED);
+    return NS_OK;
   }
 
-  SetReadyState(nsIWebSocket::CLOSED);
+  // mReadyState == nsIWebSocket::OPEN
+  mConnection->Close();
 
   return NS_OK;
 }
 
 /**
  * This Init method should only be called by C++ consumers.
  */
 NS_IMETHODIMP
--- a/content/base/src/nsWebSocket.h
+++ b/content/base/src/nsWebSocket.h
@@ -57,62 +57,71 @@
 
 #define NS_WEBSOCKET_CID                            \
  { /* 7ca25214-98dc-40a6-bc1f-41ddbe41f46c */       \
   0x7ca25214, 0x98dc, 0x40a6,                       \
  {0xbc, 0x1f, 0x41, 0xdd, 0xbe, 0x41, 0xf4, 0x6c} }
 
 #define NS_WEBSOCKET_CONTRACTID "@mozilla.org/websocket;1"
 
-class nsNetAddressComparator;
+class nsWSNetAddressComparator;
 class nsWebSocketEstablishedConnection;
+class nsWSCloseEvent;
 
 class nsWebSocket: public nsDOMEventTargetWrapperCache,
                    public nsIWebSocket,
                    public nsIJSNativeInitializer
 {
-friend class nsNetAddressComparator;
+friend class nsWSNetAddressComparator;
 friend class nsWebSocketEstablishedConnection;
+friend class nsWSCloseEvent;
 
 public:
   nsWebSocket();
   virtual ~nsWebSocket();
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsWebSocket,
                                            nsDOMEventTargetWrapperCache)
   NS_DECL_NSIWEBSOCKET
 
   // nsIJSNativeInitializer
-  NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
-                        PRUint32 argc, jsval* argv);
+  NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* aContext,
+                        JSObject* aObject, PRUint32 aArgc, jsval* aArgv);
 
   static void ReleaseGlobals();
 
 protected:
   nsresult ParseURL(const nsString& aURL);
   nsresult SetProtocol(const nsString& aProtocol);
   nsresult EstablishConnection();
-  nsresult SetReadyState(PRInt32 aNewReadyState);
+
+  nsresult CreateAndDispatchSimpleEvent(const nsString& aName);
+  nsresult CreateAndDispatchMessageEvent(nsCString *aData);
+  nsresult CreateAndDispatchCloseEvent(PRBool aWasClean);
+
+  // called from mConnection accordingly to the situation
+  void SetReadyState(PRUint16 aNewReadyState);
 
   nsRefPtr<nsDOMEventListenerWrapper> mOnOpenListener;
+  nsRefPtr<nsDOMEventListenerWrapper> mOnErrorListener;
   nsRefPtr<nsDOMEventListenerWrapper> mOnMessageListener;
   nsRefPtr<nsDOMEventListenerWrapper> mOnCloseListener;
 
   // related to the WebSocket constructor steps
   nsString mOriginalURL;
   PRPackedBool mSecure; // if true it is using SSL and the wss scheme,
                         // otherwise it is using the ws scheme with no SSL
   nsCString mAsciiHost;  // hostname
   PRUint32  mPort;
   nsCString mResource; // [filepath[?query]]
   nsCString mOrigin;
   nsCOMPtr<nsIURI> mURI;
   nsCString mProtocol;
 
-  PRInt32 mReadyState;
+  PRUint16 mReadyState;
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   nsRefPtr<nsWebSocketEstablishedConnection> mConnection;
   PRUint32 mOutgoingBufferedAmount; // actually, we get this value from
                                     // mConnection when we are connected,
                                     // but we need this one after disconnecting.
 
--- a/content/events/public/nsIPrivateDOMEvent.h
+++ b/content/events/public/nsIPrivateDOMEvent.h
@@ -112,9 +112,11 @@ NS_NewDOMNotifyPaintEvent(nsIDOMEvent** 
                           PRUint32 aEventType = 0,
                           nsInvalidateRequestList* aInvalidateRequests = nsnull);
 nsresult
 NS_NewDOMSimpleGestureEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsSimpleGestureEvent* aEvent);
 nsresult
 NS_NewDOMScrollAreaEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsScrollAreaEvent* aEvent);
 nsresult
 NS_NewDOMTransitionEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsTransitionEvent* aEvent);
+nsresult
+NS_NewDOMCloseEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsEvent* aEvent);
 #endif // nsIPrivateDOMEvent_h__
--- a/content/events/src/Makefile.in
+++ b/content/events/src/Makefile.in
@@ -78,16 +78,17 @@ CPPSRCS		= \
 		nsDOMProgressEvent.cpp \
 		nsDOMDataTransfer.cpp \
 		nsDOMNotifyPaintEvent.cpp \
 		nsDOMSimpleGestureEvent.cpp \
 		nsDOMEventTargetHelper.cpp \
 		nsDOMScrollAreaEvent.cpp \
 		nsDOMTransitionEvent.cpp \
 		nsDOMPopStateEvent.cpp \
+		nsDOMCloseEvent.cpp \
 		$(NULL)
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES	= \
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMCloseEvent.cpp
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Wellington Fernando de Macedo.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Wellington Fernando de Macedo <wfernandom2004@gmail.com> (original author)
+ *
+ * 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 "nsDOMCloseEvent.h"
+#include "nsContentUtils.h"
+
+NS_IMPL_ADDREF_INHERITED(nsDOMCloseEvent, nsDOMEvent)
+NS_IMPL_RELEASE_INHERITED(nsDOMCloseEvent, nsDOMEvent)
+
+DOMCI_DATA(CloseEvent, nsDOMCloseEvent)
+
+NS_INTERFACE_MAP_BEGIN(nsDOMCloseEvent)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMCloseEvent)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CloseEvent)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
+
+NS_IMETHODIMP
+nsDOMCloseEvent::GetWasClean(PRBool *aWasClean)
+{
+  *aWasClean = mWasClean;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMCloseEvent::InitCloseEvent(const nsAString& aType,
+                                PRBool aCanBubble,
+                                PRBool aCancelable,
+                                PRBool aWasClean)
+{
+  nsresult rv = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mWasClean = aWasClean;
+
+  return NS_OK;
+}
+
+nsresult
+NS_NewDOMCloseEvent(nsIDOMEvent** aInstancePtrResult,
+                    nsPresContext* aPresContext,
+                    nsEvent* aEvent) 
+{
+  nsDOMCloseEvent* it = new nsDOMCloseEvent(aPresContext, aEvent);
+  if (nsnull == it) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  return CallQueryInterface(it, aInstancePtrResult);
+}
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMCloseEvent.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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.org code.
+ *
+ * The Initial Developer of the Original Code is 
+ * Wellington Fernando de Macedo.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Wellington Fernando de Macedo <wfernandom2004@gmail.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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 ***** */
+
+#ifndef nsDOMCloseEvent_h__
+#define nsDOMCloseEvent_h__
+
+#include "nsIDOMCloseEvent.h"
+#include "nsDOMEvent.h"
+
+/**
+ * Implements the CloseEvent event, used for notifying that a WebSocket
+ * connection has been closed.
+ *
+ * See http://dev.w3.org/html5/websockets/#closeevent for further details.
+ */
+class nsDOMCloseEvent : public nsDOMEvent,
+                        public nsIDOMCloseEvent
+{
+public:
+  nsDOMCloseEvent(nsPresContext* aPresContext, nsEvent* aEvent)
+    : nsDOMEvent(aPresContext, aEvent), mWasClean(PR_FALSE)
+  {
+  }
+                     
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // Forward to base class
+  NS_FORWARD_TO_NSDOMEVENT
+
+  NS_DECL_NSIDOMCLOSEEVENT
+
+private:
+  PRBool mWasClean;
+};
+
+#endif // nsDOMCloseEvent_h__
--- a/content/events/src/nsEventDispatcher.cpp
+++ b/content/events/src/nsEventDispatcher.cpp
@@ -821,11 +821,13 @@ nsEventDispatcher::CreateEvent(nsPresCon
   if (aEventType.LowerCaseEqualsLiteral("scrollareaevent"))
     return NS_NewDOMScrollAreaEvent(aDOMEvent, aPresContext, nsnull);
   // FIXME: Should get spec to say what the right string is here!  This
   // is probably wrong!
   if (aEventType.LowerCaseEqualsLiteral("transitionevent"))
     return NS_NewDOMTransitionEvent(aDOMEvent, aPresContext, nsnull);
   if (aEventType.LowerCaseEqualsLiteral("popstateevent"))
     return NS_NewDOMPopStateEvent(aDOMEvent, aPresContext, nsnull);
+  if (aEventType.LowerCaseEqualsLiteral("closeevent"))
+    return NS_NewDOMCloseEvent(aDOMEvent, aPresContext, nsnull);
 
   return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 }
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -199,16 +199,17 @@
 #include "nsIXSLTProcessorObsolete.h"
 #include "nsIXSLTProcessorPrivate.h"
 
 #include "nsIDOMLSProgressEvent.h"
 #include "nsIDOMParser.h"
 #include "nsIDOMSerializer.h"
 #include "nsXMLHttpRequest.h"
 #include "nsWebSocket.h"
+#include "nsIDOMCloseEvent.h"
 
 // includes needed for the prototype chain interfaces
 #include "nsIDOMNavigator.h"
 #include "nsIDOMBarProp.h"
 #include "nsIDOMScreen.h"
 #include "nsIDOMDocumentType.h"
 #include "nsIDOMDOMImplementation.h"
 #include "nsIDOMDocumentFragment.h"
@@ -1395,16 +1396,19 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(ContentFrameMessageManager, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(FormData, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(WebSocket, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
+
+  NS_DEFINE_CLASSINFO_DATA(CloseEvent, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 };
 
 // Objects that should be constructable through |new Name();|
 struct nsContractIDMapData
 {
   PRInt32 mDOMClassInfoID;
   const char *mContractID;
 };
@@ -3865,16 +3869,21 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(WebSocket, nsIWebSocket)
     DOM_CLASSINFO_MAP_ENTRY(nsIWebSocket)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(CloseEvent, nsIDOMCloseEvent)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMCloseEvent)
+    DOM_CLASSINFO_EVENT_MAP_ENTRIES
+  DOM_CLASSINFO_MAP_END
+
 #ifdef NS_DEBUG
   {
     PRUint32 i = NS_ARRAY_LENGTH(sClassInfoData);
 
     if (i != eDOMClassInfoIDCount) {
       NS_ERROR("The number of items in sClassInfoData doesn't match the "
                "number of nsIDOMClassInfo ID's, this is bad! Fix it!");
 
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -470,9 +470,11 @@ DOMCI_CLASS(PopStateEvent)
 
 DOMCI_CLASS(EventListenerInfo)
 
 DOMCI_CLASS(TransitionEvent)
 DOMCI_CLASS(ContentFrameMessageManager)
 
 DOMCI_CLASS(FormData)
 
+// WebSocket
 DOMCI_CLASS(WebSocket)
+DOMCI_CLASS(CloseEvent)
--- a/dom/interfaces/events/Makefile.in
+++ b/dom/interfaces/events/Makefile.in
@@ -79,11 +79,12 @@ XPIDLSRCS =					\
 	nsIDOMPaintRequest.idl			\
 	nsIDOMPaintRequestList.idl		\
 	nsIDOMSimpleGestureEvent.idl		\
 	nsIDOMNSMouseEvent.idl			\
 	nsIDOMOrientationEvent.idl              \
 	nsIDOMScrollAreaEvent.idl		\
 	nsIDOMTransitionEvent.idl		\
 	nsIDOMPopStateEvent.idl			\
+	nsIDOMCloseEvent.idl			\
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/events/nsIDOMCloseEvent.idl
@@ -0,0 +1,57 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Wellington Fernando de Macedo.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Wellington Fernando de Macedo <wfernandom2004@gmail.com> (original author)
+ *
+ * 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 "nsIDOMEvent.idl"
+
+/**
+ * The nsIDOMCloseEvent interface is the interface to the event
+ * close on a WebSocket object.
+ *
+ * For more information on this interface, please see
+ * http://dev.w3.org/html5/websockets/#closeevent
+ */
+[scriptable, uuid(a94d4379-eba2-45f4-be3a-6cc2fa1453a8)]
+interface nsIDOMCloseEvent : nsIDOMEvent
+{
+  readonly attribute boolean wasClean;
+  
+  void initCloseEvent(in DOMString aType,
+                        in boolean aCanBubble,
+                        in boolean aCancelable,
+                        in boolean aWasClean);
+};