PPB_TCPSocket: add support for TCP server socket operations.

BUG=262601
TEST=new tests in test_tcp_socket.

Review URL: https://chromiumcodereview.appspot.com/24195004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@224383 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h
index 95bbd8b..24beb03 100644
--- a/ppapi/proxy/ppapi_messages.h
+++ b/ppapi/proxy/ppapi_messages.h
@@ -62,6 +62,7 @@
 #include "ppapi/shared_impl/ppapi_preferences.h"
 #include "ppapi/shared_impl/ppb_device_ref_shared.h"
 #include "ppapi/shared_impl/ppb_input_event_shared.h"
+#include "ppapi/shared_impl/ppb_tcp_socket_shared.h"
 #include "ppapi/shared_impl/ppb_view_shared.h"
 #include "ppapi/shared_impl/ppp_flash_browser_operations_shared.h"
 #include "ppapi/shared_impl/private/ppb_x509_certificate_private_shared.h"
@@ -74,6 +75,8 @@
 
 #define IPC_MESSAGE_START PpapiMsgStart
 
+IPC_ENUM_TRAITS_MAX_VALUE(ppapi::TCPSocketVersion,
+                          ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE)
 IPC_ENUM_TRAITS(PP_AudioSampleRate)
 IPC_ENUM_TRAITS(PP_DeviceType_Dev)
 IPC_ENUM_TRAITS(PP_DecryptorStreamType)
@@ -99,7 +102,7 @@
 IPC_ENUM_TRAITS_MAX_VALUE(PP_TalkPermission,
                           PP_TALKPERMISSION_NUM_PERMISSIONS - 1)
 IPC_ENUM_TRAITS_MAX_VALUE(PP_TCPSocket_Option,
-                          PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE)
+                          PP_TCPSOCKET_OPTION_ADDRESS_REUSE)
 IPC_ENUM_TRAITS(PP_TextInput_Type)
 IPC_ENUM_TRAITS(PP_TrueTypeFontFamily_Dev)
 IPC_ENUM_TRAITS(PP_TrueTypeFontStyle_Dev)
@@ -1450,11 +1453,16 @@
 
 // TCP Socket ------------------------------------------------------------------
 // Creates a PPB_TCPSocket resource.
-IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPSocket_Create)
+IPC_MESSAGE_CONTROL1(PpapiHostMsg_TCPSocket_Create,
+                     ppapi::TCPSocketVersion /* version */)
 
 // Creates a PPB_TCPSocket_Private resource.
 IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPSocket_CreatePrivate)
 
+IPC_MESSAGE_CONTROL1(PpapiHostMsg_TCPSocket_Bind,
+                     PP_NetAddress_Private /* net_addr */)
+IPC_MESSAGE_CONTROL1(PpapiPluginMsg_TCPSocket_BindReply,
+                     PP_NetAddress_Private /* local_addr */)
 IPC_MESSAGE_CONTROL2(PpapiHostMsg_TCPSocket_Connect,
                      std::string /* host */,
                      uint16_t /* port */)
@@ -1477,7 +1485,15 @@
 IPC_MESSAGE_CONTROL1(PpapiHostMsg_TCPSocket_Write,
                      std::string /* data */)
 IPC_MESSAGE_CONTROL0(PpapiPluginMsg_TCPSocket_WriteReply)
-IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPSocket_Disconnect)
+IPC_MESSAGE_CONTROL1(PpapiHostMsg_TCPSocket_Listen,
+                     int32_t /* backlog */)
+IPC_MESSAGE_CONTROL0(PpapiPluginMsg_TCPSocket_ListenReply)
+IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPSocket_Accept)
+IPC_MESSAGE_CONTROL3(PpapiPluginMsg_TCPSocket_AcceptReply,
+                     int /* pending_host_id*/,
+                     PP_NetAddress_Private /* local_addr */,
+                     PP_NetAddress_Private /* remote_addr */)
+IPC_MESSAGE_CONTROL0(PpapiHostMsg_TCPSocket_Close)
 IPC_MESSAGE_CONTROL2(PpapiHostMsg_TCPSocket_SetOption,
                      PP_TCPSocket_Option /* name */,
                      ppapi::SocketOptionData /* value */)
diff --git a/ppapi/proxy/resource_creation_proxy.cc b/ppapi/proxy/resource_creation_proxy.cc
index 60841022..ea01f6e 100644
--- a/ppapi/proxy/resource_creation_proxy.cc
+++ b/ppapi/proxy/resource_creation_proxy.cc
@@ -323,9 +323,17 @@
       GetReference();
 }
 
+PP_Resource ResourceCreationProxy::CreateTCPSocket1_0(
+    PP_Instance instance) {
+  return (new TCPSocketResource(GetConnection(), instance,
+                                TCP_SOCKET_VERSION_1_0))->GetReference();
+}
+
 PP_Resource ResourceCreationProxy::CreateTCPSocket(
     PP_Instance instance) {
-  return (new TCPSocketResource(GetConnection(), instance))->GetReference();
+  return (new TCPSocketResource(
+      GetConnection(), instance, TCP_SOCKET_VERSION_1_1_OR_ABOVE))->
+          GetReference();
 }
 
 PP_Resource ResourceCreationProxy::CreateTCPSocketPrivate(
diff --git a/ppapi/proxy/resource_creation_proxy.h b/ppapi/proxy/resource_creation_proxy.h
index 210e23c..509b1557 100644
--- a/ppapi/proxy/resource_creation_proxy.h
+++ b/ppapi/proxy/resource_creation_proxy.h
@@ -138,6 +138,7 @@
   virtual PP_Resource CreatePrinting(PP_Instance) OVERRIDE;
   virtual PP_Resource CreateTCPServerSocketPrivate(
       PP_Instance instance) OVERRIDE;
+  virtual PP_Resource CreateTCPSocket1_0(PP_Instance instance) OVERRIDE;
   virtual PP_Resource CreateTCPSocket(PP_Instance instance) OVERRIDE;
   virtual PP_Resource CreateTCPSocketPrivate(PP_Instance instance) OVERRIDE;
   virtual PP_Resource CreateUDPSocket(PP_Instance instance) OVERRIDE;
diff --git a/ppapi/proxy/tcp_socket_private_resource.cc b/ppapi/proxy/tcp_socket_private_resource.cc
index 0698e49..76ed4b9e1 100644
--- a/ppapi/proxy/tcp_socket_private_resource.cc
+++ b/ppapi/proxy/tcp_socket_private_resource.cc
@@ -5,13 +5,14 @@
 #include "ppapi/proxy/tcp_socket_private_resource.h"
 
 #include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/shared_impl/ppb_tcp_socket_shared.h"
 
 namespace ppapi {
 namespace proxy {
 
 TCPSocketPrivateResource::TCPSocketPrivateResource(Connection connection,
                                                    PP_Instance instance)
-    : TCPSocketResourceBase(connection, instance, true) {
+    : TCPSocketResourceBase(connection, instance, TCP_SOCKET_VERSION_PRIVATE) {
   SendCreate(BROWSER, PpapiHostMsg_TCPSocket_CreatePrivate());
 }
 
@@ -21,14 +22,12 @@
     int pending_resource_id,
     const PP_NetAddress_Private& local_addr,
     const PP_NetAddress_Private& remote_addr)
-    : TCPSocketResourceBase(connection, instance, true,
-                            local_addr,
-                            remote_addr) {
+    : TCPSocketResourceBase(connection, instance, TCP_SOCKET_VERSION_PRIVATE,
+                            local_addr, remote_addr) {
   AttachToPendingHost(BROWSER, pending_resource_id);
 }
 
 TCPSocketPrivateResource::~TCPSocketPrivateResource() {
-  DisconnectImpl();
 }
 
 thunk::PPB_TCPSocket_Private_API*
@@ -91,7 +90,7 @@
 }
 
 void TCPSocketPrivateResource::Disconnect() {
-  DisconnectImpl();
+  CloseImpl();
 }
 
 int32_t TCPSocketPrivateResource::SetOption(
@@ -109,5 +108,13 @@
   }
 }
 
+PP_Resource TCPSocketPrivateResource::CreateAcceptedSocket(
+    int /* pending_host_id */,
+    const PP_NetAddress_Private& /* local_addr */,
+    const PP_NetAddress_Private& /* remote_addr */) {
+  NOTREACHED();
+  return 0;
+}
+
 }  // namespace proxy
 }  // namespace ppapi
diff --git a/ppapi/proxy/tcp_socket_private_resource.h b/ppapi/proxy/tcp_socket_private_resource.h
index f79ab9d..9ae9bb2 100644
--- a/ppapi/proxy/tcp_socket_private_resource.h
+++ b/ppapi/proxy/tcp_socket_private_resource.h
@@ -59,6 +59,12 @@
                             const PP_Var& value,
                             scoped_refptr<TrackedCallback> callback) OVERRIDE;
 
+  // TCPSocketResourceBase implementation.
+  virtual PP_Resource CreateAcceptedSocket(
+      int pending_host_id,
+      const PP_NetAddress_Private& local_addr,
+      const PP_NetAddress_Private& remote_addr) OVERRIDE;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(TCPSocketPrivateResource);
 };
diff --git a/ppapi/proxy/tcp_socket_resource.cc b/ppapi/proxy/tcp_socket_resource.cc
index 085aab3..f8f8f68 100644
--- a/ppapi/proxy/tcp_socket_resource.cc
+++ b/ppapi/proxy/tcp_socket_resource.cc
@@ -4,7 +4,9 @@
 
 #include "ppapi/proxy/tcp_socket_resource.h"
 
+#include "base/logging.h"
 #include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/shared_impl/ppb_tcp_socket_shared.h"
 #include "ppapi/thunk/enter.h"
 #include "ppapi/thunk/ppb_net_address_api.h"
 
@@ -19,19 +21,40 @@
 }  // namespace
 
 TCPSocketResource::TCPSocketResource(Connection connection,
-                                     PP_Instance instance)
-    : TCPSocketResourceBase(connection, instance, false) {
-  SendCreate(BROWSER, PpapiHostMsg_TCPSocket_Create());
+                                     PP_Instance instance,
+                                     TCPSocketVersion version)
+    : TCPSocketResourceBase(connection, instance, version) {
+  DCHECK_NE(version, TCP_SOCKET_VERSION_PRIVATE);
+  SendCreate(BROWSER, PpapiHostMsg_TCPSocket_Create(version));
+}
+
+TCPSocketResource::TCPSocketResource(Connection connection,
+                                     PP_Instance instance,
+                                     int pending_host_id,
+                                     const PP_NetAddress_Private& local_addr,
+                                     const PP_NetAddress_Private& remote_addr)
+    : TCPSocketResourceBase(connection, instance,
+                            TCP_SOCKET_VERSION_1_1_OR_ABOVE, local_addr,
+                            remote_addr) {
+  AttachToPendingHost(BROWSER, pending_host_id);
 }
 
 TCPSocketResource::~TCPSocketResource() {
-  DisconnectImpl();
 }
 
 thunk::PPB_TCPSocket_API* TCPSocketResource::AsPPB_TCPSocket_API() {
   return this;
 }
 
+int32_t TCPSocketResource::Bind(PP_Resource addr,
+                                scoped_refptr<TrackedCallback> callback) {
+  EnterNetAddressNoLock enter(addr, true);
+  if (enter.failed())
+    return PP_ERROR_BADARGUMENT;
+
+  return BindImpl(&enter.object()->GetNetAddressPrivate(), callback);
+}
+
 int32_t TCPSocketResource::Connect(PP_Resource addr,
                                    scoped_refptr<TrackedCallback> callback) {
   EnterNetAddressNoLock enter(addr, true);
@@ -78,8 +101,18 @@
   return WriteImpl(buffer, bytes_to_write, callback);
 }
 
+int32_t TCPSocketResource::Listen(int32_t backlog,
+                                  scoped_refptr<TrackedCallback> callback) {
+  return ListenImpl(backlog, callback);
+}
+
+int32_t TCPSocketResource::Accept(PP_Resource* accepted_tcp_socket,
+                                  scoped_refptr<TrackedCallback> callback) {
+  return AcceptImpl(accepted_tcp_socket, callback);
+}
+
 void TCPSocketResource::Close() {
-  DisconnectImpl();
+  CloseImpl();
 }
 
 int32_t TCPSocketResource::SetOption(PP_TCPSocket_Option name,
@@ -88,5 +121,13 @@
   return SetOptionImpl(name, value, callback);
 }
 
+PP_Resource TCPSocketResource::CreateAcceptedSocket(
+    int pending_host_id,
+    const PP_NetAddress_Private& local_addr,
+    const PP_NetAddress_Private& remote_addr) {
+  return (new TCPSocketResource(connection(), pp_instance(), pending_host_id,
+                                local_addr, remote_addr))->GetReference();
+}
+
 }  // namespace proxy
 }  // namespace ppapi
diff --git a/ppapi/proxy/tcp_socket_resource.h b/ppapi/proxy/tcp_socket_resource.h
index 05b4fe38..5dbfdd54 100644
--- a/ppapi/proxy/tcp_socket_resource.h
+++ b/ppapi/proxy/tcp_socket_resource.h
@@ -11,18 +11,27 @@
 #include "ppapi/thunk/ppb_tcp_socket_api.h"
 
 namespace ppapi {
+
+enum TCPSocketVersion;
+
 namespace proxy {
 
 class PPAPI_PROXY_EXPORT TCPSocketResource : public thunk::PPB_TCPSocket_API,
                                              public TCPSocketResourceBase {
  public:
-  TCPSocketResource(Connection connection, PP_Instance instance);
+  // C-tor used for new sockets created.
+  TCPSocketResource(Connection connection,
+                    PP_Instance instance,
+                    TCPSocketVersion version);
+
   virtual ~TCPSocketResource();
 
   // PluginResource overrides.
   virtual thunk::PPB_TCPSocket_API* AsPPB_TCPSocket_API() OVERRIDE;
 
   // thunk::PPB_TCPSocket_API implementation.
+  virtual int32_t Bind(PP_Resource addr,
+                       scoped_refptr<TrackedCallback> callback) OVERRIDE;
   virtual int32_t Connect(PP_Resource addr,
                           scoped_refptr<TrackedCallback> callback) OVERRIDE;
   virtual PP_Resource GetLocalAddress() OVERRIDE;
@@ -33,12 +42,29 @@
   virtual int32_t Write(const char* buffer,
                         int32_t bytes_to_write,
                         scoped_refptr<TrackedCallback> callback) OVERRIDE;
+  virtual int32_t Listen(int32_t backlog,
+                         scoped_refptr<TrackedCallback> callback) OVERRIDE;
+  virtual int32_t Accept(PP_Resource* accepted_tcp_socket,
+                         scoped_refptr<TrackedCallback> callback) OVERRIDE;
   virtual void Close() OVERRIDE;
   virtual int32_t SetOption(PP_TCPSocket_Option name,
                             const PP_Var& value,
                             scoped_refptr<TrackedCallback> callback) OVERRIDE;
 
+  // TCPSocketResourceBase implementation.
+  virtual PP_Resource CreateAcceptedSocket(
+      int pending_host_id,
+      const PP_NetAddress_Private& local_addr,
+      const PP_NetAddress_Private& remote_addr) OVERRIDE;
+
  private:
+  // C-tor used for accepted sockets.
+  TCPSocketResource(Connection connection,
+                    PP_Instance instance,
+                    int pending_host_id,
+                    const PP_NetAddress_Private& local_addr,
+                    const PP_NetAddress_Private& remote_addr);
+
   DISALLOW_COPY_AND_ASSIGN(TCPSocketResource);
 };
 
diff --git a/ppapi/proxy/tcp_socket_resource_base.cc b/ppapi/proxy/tcp_socket_resource_base.cc
index f02895b..2590c41 100644
--- a/ppapi/proxy/tcp_socket_resource_base.cc
+++ b/ppapi/proxy/tcp_socket_resource_base.cc
@@ -32,12 +32,13 @@
 
 TCPSocketResourceBase::TCPSocketResourceBase(Connection connection,
                                              PP_Instance instance,
-                                             bool private_api)
+                                             TCPSocketVersion version)
     : PluginResource(connection, instance),
-      connection_state_(BEFORE_CONNECT),
+      state_(TCPSocketState::INITIAL),
       read_buffer_(NULL),
       bytes_to_read_(-1),
-      private_api_(private_api) {
+      accepted_tcp_socket_(NULL),
+      version_(version) {
   local_addr_.size = 0;
   memset(local_addr_.data, 0,
          arraysize(local_addr_.data) * sizeof(*local_addr_.data));
@@ -49,19 +50,42 @@
 TCPSocketResourceBase::TCPSocketResourceBase(
     Connection connection,
     PP_Instance instance,
-    bool private_api,
+    TCPSocketVersion version,
     const PP_NetAddress_Private& local_addr,
     const PP_NetAddress_Private& remote_addr)
     : PluginResource(connection, instance),
-      connection_state_(CONNECTED),
+      state_(TCPSocketState::CONNECTED),
       read_buffer_(NULL),
       bytes_to_read_(-1),
       local_addr_(local_addr),
       remote_addr_(remote_addr),
-      private_api_(private_api) {
+      accepted_tcp_socket_(NULL),
+      version_(version) {
 }
 
 TCPSocketResourceBase::~TCPSocketResourceBase() {
+  CloseImpl();
+}
+
+int32_t TCPSocketResourceBase::BindImpl(
+    const PP_NetAddress_Private* addr,
+    scoped_refptr<TrackedCallback> callback) {
+  if (!addr)
+    return PP_ERROR_BADARGUMENT;
+  if (state_.IsPending(TCPSocketState::BIND))
+    return PP_ERROR_INPROGRESS;
+  if (!state_.IsValidTransition(TCPSocketState::BIND))
+    return PP_ERROR_FAILED;
+
+  bind_callback_ = callback;
+  state_.SetPendingTransition(TCPSocketState::BIND);
+
+  Call<PpapiPluginMsg_TCPSocket_BindReply>(
+      BROWSER,
+      PpapiHostMsg_TCPSocket_Bind(*addr),
+      base::Bind(&TCPSocketResourceBase::OnPluginMsgBindReply,
+                 base::Unretained(this)));
+  return PP_OK_COMPLETIONPENDING;
 }
 
 int32_t TCPSocketResourceBase::ConnectImpl(
@@ -70,12 +94,13 @@
     scoped_refptr<TrackedCallback> callback) {
   if (!host)
     return PP_ERROR_BADARGUMENT;
-  if (connection_state_ != BEFORE_CONNECT)
+  if (state_.IsPending(TCPSocketState::CONNECT))
+    return PP_ERROR_INPROGRESS;
+  if (!state_.IsValidTransition(TCPSocketState::CONNECT))
     return PP_ERROR_FAILED;
-  if (TrackedCallback::IsPending(connect_callback_))
-    return PP_ERROR_INPROGRESS;  // Can only have one pending request.
 
   connect_callback_ = callback;
+  state_.SetPendingTransition(TCPSocketState::CONNECT);
 
   Call<PpapiPluginMsg_TCPSocket_ConnectReply>(
       BROWSER,
@@ -90,12 +115,13 @@
     scoped_refptr<TrackedCallback> callback) {
   if (!addr)
     return PP_ERROR_BADARGUMENT;
-  if (connection_state_ != BEFORE_CONNECT)
+  if (state_.IsPending(TCPSocketState::CONNECT))
+    return PP_ERROR_INPROGRESS;
+  if (!state_.IsValidTransition(TCPSocketState::CONNECT))
     return PP_ERROR_FAILED;
-  if (TrackedCallback::IsPending(connect_callback_))
-    return PP_ERROR_INPROGRESS;  // Can only have one pending request.
 
   connect_callback_ = callback;
+  state_.SetPendingTransition(TCPSocketState::CONNECT);
 
   Call<PpapiPluginMsg_TCPSocket_ConnectReply>(
       BROWSER,
@@ -107,7 +133,7 @@
 
 PP_Bool TCPSocketResourceBase::GetLocalAddressImpl(
     PP_NetAddress_Private* local_addr) {
-  if (!IsConnected() || !local_addr)
+  if (!state_.IsBound() || !local_addr)
     return PP_FALSE;
   *local_addr = local_addr_;
   return PP_TRUE;
@@ -115,7 +141,7 @@
 
 PP_Bool TCPSocketResourceBase::GetRemoteAddressImpl(
     PP_NetAddress_Private* remote_addr) {
-  if (!IsConnected() || !remote_addr)
+  if (!state_.IsConnected() || !remote_addr)
     return PP_FALSE;
   *remote_addr = remote_addr_;
   return PP_TRUE;
@@ -128,15 +154,16 @@
   if (!server_name)
     return PP_ERROR_BADARGUMENT;
 
-  if (connection_state_ != CONNECTED)
-    return PP_ERROR_FAILED;
-  if (TrackedCallback::IsPending(ssl_handshake_callback_) ||
+  if (state_.IsPending(TCPSocketState::SSL_CONNECT) ||
       TrackedCallback::IsPending(read_callback_) ||
       TrackedCallback::IsPending(write_callback_)) {
     return PP_ERROR_INPROGRESS;
   }
+  if (!state_.IsValidTransition(TCPSocketState::SSL_CONNECT))
+    return PP_ERROR_FAILED;
 
   ssl_handshake_callback_ = callback;
+  state_.SetPendingTransition(TCPSocketState::SSL_CONNECT);
 
   Call<PpapiPluginMsg_TCPSocket_SSLHandshakeReply>(
       BROWSER,
@@ -193,10 +220,10 @@
   if (!buffer || bytes_to_read <= 0)
     return PP_ERROR_BADARGUMENT;
 
-  if (!IsConnected())
+  if (!state_.IsConnected())
     return PP_ERROR_FAILED;
   if (TrackedCallback::IsPending(read_callback_) ||
-      TrackedCallback::IsPending(ssl_handshake_callback_))
+      state_.IsPending(TCPSocketState::SSL_CONNECT))
     return PP_ERROR_INPROGRESS;
   read_buffer_ = buffer;
   bytes_to_read_ = std::min(bytes_to_read, kMaxReadSize);
@@ -217,10 +244,10 @@
   if (!buffer || bytes_to_write <= 0)
     return PP_ERROR_BADARGUMENT;
 
-  if (!IsConnected())
+  if (!state_.IsConnected())
     return PP_ERROR_FAILED;
   if (TrackedCallback::IsPending(write_callback_) ||
-      TrackedCallback::IsPending(ssl_handshake_callback_))
+      state_.IsPending(TCPSocketState::SSL_CONNECT))
     return PP_ERROR_INPROGRESS;
 
   if (bytes_to_write > kMaxWriteSize)
@@ -236,33 +263,79 @@
   return PP_OK_COMPLETIONPENDING;
 }
 
-void TCPSocketResourceBase::DisconnectImpl() {
-  if (connection_state_ == DISCONNECTED)
+int32_t TCPSocketResourceBase::ListenImpl(
+    int32_t backlog,
+    scoped_refptr<TrackedCallback> callback) {
+  if (backlog <= 0)
+    return PP_ERROR_BADARGUMENT;
+  if (state_.IsPending(TCPSocketState::LISTEN))
+    return PP_ERROR_INPROGRESS;
+  if (!state_.IsValidTransition(TCPSocketState::LISTEN))
+    return PP_ERROR_FAILED;
+
+  listen_callback_ = callback;
+  state_.SetPendingTransition(TCPSocketState::LISTEN);
+
+  Call<PpapiPluginMsg_TCPSocket_ListenReply>(
+      BROWSER,
+      PpapiHostMsg_TCPSocket_Listen(backlog),
+      base::Bind(&TCPSocketResourceBase::OnPluginMsgListenReply,
+                 base::Unretained(this)));
+  return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t TCPSocketResourceBase::AcceptImpl(
+    PP_Resource* accepted_tcp_socket,
+    scoped_refptr<TrackedCallback> callback) {
+  if (!accepted_tcp_socket)
+    return PP_ERROR_BADARGUMENT;
+  if (TrackedCallback::IsPending(accept_callback_))
+    return PP_ERROR_INPROGRESS;
+  if (state_.state() != TCPSocketState::LISTENING)
+    return PP_ERROR_FAILED;
+
+  accept_callback_ = callback;
+  accepted_tcp_socket_ = accepted_tcp_socket;
+
+  Call<PpapiPluginMsg_TCPSocket_AcceptReply>(
+      BROWSER,
+      PpapiHostMsg_TCPSocket_Accept(),
+      base::Bind(&TCPSocketResourceBase::OnPluginMsgAcceptReply,
+                 base::Unretained(this)));
+  return PP_OK_COMPLETIONPENDING;
+}
+
+void TCPSocketResourceBase::CloseImpl() {
+  if (state_.state() == TCPSocketState::CLOSED)
     return;
 
-  connection_state_ = DISCONNECTED;
+  state_.DoTransition(TCPSocketState::CLOSE, true);
 
-  Post(BROWSER, PpapiHostMsg_TCPSocket_Disconnect());
+  Post(BROWSER, PpapiHostMsg_TCPSocket_Close());
 
+  PostAbortIfNecessary(&bind_callback_);
   PostAbortIfNecessary(&connect_callback_);
   PostAbortIfNecessary(&ssl_handshake_callback_);
   PostAbortIfNecessary(&read_callback_);
   PostAbortIfNecessary(&write_callback_);
+  PostAbortIfNecessary(&listen_callback_);
+  PostAbortIfNecessary(&accept_callback_);
   read_buffer_ = NULL;
   bytes_to_read_ = -1;
   server_certificate_ = NULL;
+  accepted_tcp_socket_ = NULL;
 }
 
 int32_t TCPSocketResourceBase::SetOptionImpl(
     PP_TCPSocket_Option name,
     const PP_Var& value,
     scoped_refptr<TrackedCallback> callback) {
-  if (!IsConnected())
-    return PP_ERROR_FAILED;
-
   SocketOptionData option_data;
   switch (name) {
     case PP_TCPSOCKET_OPTION_NO_DELAY: {
+      if (!state_.IsConnected())
+        return PP_ERROR_FAILED;
+
       if (value.type != PP_VARTYPE_BOOL)
         return PP_ERROR_BADARGUMENT;
       option_data.SetBool(PP_ToBool(value.value.as_bool));
@@ -270,11 +343,25 @@
     }
     case PP_TCPSOCKET_OPTION_SEND_BUFFER_SIZE:
     case PP_TCPSOCKET_OPTION_RECV_BUFFER_SIZE: {
+      if (!state_.IsConnected())
+        return PP_ERROR_FAILED;
+
       if (value.type != PP_VARTYPE_INT32)
         return PP_ERROR_BADARGUMENT;
       option_data.SetInt32(value.value.as_int);
       break;
     }
+    case PP_TCPSOCKET_OPTION_ADDRESS_REUSE: {
+      if (version_ != TCP_SOCKET_VERSION_1_1_OR_ABOVE)
+        return PP_ERROR_NOTSUPPORTED;
+      if (state_.state() != TCPSocketState::INITIAL)
+        return PP_ERROR_FAILED;
+
+      if (value.type != PP_VARTYPE_BOOL)
+        return PP_ERROR_BADARGUMENT;
+      option_data.SetBool(PP_ToBool(value.value.as_bool));
+      break;
+    }
     default: {
       NOTREACHED();
       return PP_ERROR_BADARGUMENT;
@@ -291,34 +378,52 @@
   return PP_OK_COMPLETIONPENDING;
 }
 
-bool TCPSocketResourceBase::IsConnected() const {
-  return connection_state_ == CONNECTED || connection_state_ == SSL_CONNECTED;
-}
-
 void TCPSocketResourceBase::PostAbortIfNecessary(
     scoped_refptr<TrackedCallback>* callback) {
   if (TrackedCallback::IsPending(*callback))
     (*callback)->PostAbort();
 }
 
+void TCPSocketResourceBase::OnPluginMsgBindReply(
+    const ResourceMessageReplyParams& params,
+    const PP_NetAddress_Private& local_addr) {
+  // It is possible that CloseImpl() has been called. We don't want to update
+  // class members in this case.
+  if (!state_.IsPending(TCPSocketState::BIND))
+    return;
+
+  DCHECK(TrackedCallback::IsPending(bind_callback_));
+  if (params.result() == PP_OK) {
+    local_addr_ = local_addr;
+    state_.CompletePendingTransition(true);
+  } else {
+    state_.CompletePendingTransition(false);
+  }
+  RunCallback(bind_callback_, params.result());
+}
+
 void TCPSocketResourceBase::OnPluginMsgConnectReply(
     const ResourceMessageReplyParams& params,
     const PP_NetAddress_Private& local_addr,
     const PP_NetAddress_Private& remote_addr) {
-  // It is possible that |connect_callback_| is pending while
-  // |connection_state_| is not BEFORE_CONNECT: DisconnectImpl() has been
-  // called, but a ConnectCompleted notification came earlier than the task to
-  // abort |connect_callback_|. We don't want to update |connection_state_| or
-  // other members in that case.
-  if (connection_state_ != BEFORE_CONNECT ||
-      !TrackedCallback::IsPending(connect_callback_)) {
+  // It is possible that CloseImpl() has been called. We don't want to update
+  // class members in this case.
+  if (!state_.IsPending(TCPSocketState::CONNECT))
     return;
-  }
 
+  DCHECK(TrackedCallback::IsPending(connect_callback_));
   if (params.result() == PP_OK) {
     local_addr_ = local_addr;
     remote_addr_ = remote_addr;
-    connection_state_ = CONNECTED;
+    state_.CompletePendingTransition(true);
+  } else {
+    if (version_ == TCP_SOCKET_VERSION_1_1_OR_ABOVE) {
+      state_.CompletePendingTransition(false);
+    } else {
+      // In order to maintain backward compatibility, allow to connect the
+      // socket again.
+      state_ = TCPSocketState(TCPSocketState::INITIAL);
+    }
   }
   RunCallback(connect_callback_, params.result());
 }
@@ -326,42 +431,33 @@
 void TCPSocketResourceBase::OnPluginMsgSSLHandshakeReply(
       const ResourceMessageReplyParams& params,
       const PPB_X509Certificate_Fields& certificate_fields) {
-  // It is possible that |ssl_handshake_callback_| is pending while
-  // |connection_state_| is not CONNECT: DisconnectImpl() has been
-  // called, but a SSLHandshakeCompleted notification came earlier than the task
-  // to abort |ssl_handshake_callback_|. We don't want to update
-  // |connection_state_| or other members in that case.
-  if (connection_state_ != CONNECTED ||
-      !TrackedCallback::IsPending(ssl_handshake_callback_)) {
+  // It is possible that CloseImpl() has been called. We don't want to
+  // update class members in this case.
+  if (!state_.IsPending(TCPSocketState::SSL_CONNECT))
     return;
-  }
 
+  DCHECK(TrackedCallback::IsPending(ssl_handshake_callback_));
   if (params.result() == PP_OK) {
-    connection_state_ = SSL_CONNECTED;
+    state_.CompletePendingTransition(true);
     server_certificate_ = new PPB_X509Certificate_Private_Shared(
         OBJECT_IS_PROXY,
         pp_instance(),
         certificate_fields);
-    RunCallback(ssl_handshake_callback_, params.result());
   } else {
-    // The resource might be released in the callback so we need to hold
-    // a reference so we can Disconnect() first.
-    AddRef();
-    RunCallback(ssl_handshake_callback_, params.result());
-    DisconnectImpl();
-    Release();
+    state_.CompletePendingTransition(false);
   }
+  RunCallback(ssl_handshake_callback_, params.result());
 }
 
 void TCPSocketResourceBase::OnPluginMsgReadReply(
     const ResourceMessageReplyParams& params,
     const std::string& data) {
-  // It is possible that |read_callback_| is pending while |read_buffer_| is
-  // NULL: DisconnectImpl() has been called, but a ReadCompleted notification
-  // came earlier than the task to abort |read_callback_|. We shouldn't access
-  // the buffer in that case. The user may have released it.
-  if (!TrackedCallback::IsPending(read_callback_) || !read_buffer_)
+  // It is possible that CloseImpl() has been called. We shouldn't access the
+  // buffer in that case. The user may have released it.
+  if (!state_.IsConnected() || !TrackedCallback::IsPending(read_callback_) ||
+      !read_buffer_) {
     return;
+  }
 
   const bool succeeded = params.result() == PP_OK;
   if (succeeded) {
@@ -372,19 +468,48 @@
   read_buffer_ = NULL;
   bytes_to_read_ = -1;
 
-  read_callback_->Run(succeeded ?
-                      static_cast<int32_t>(data.size()) :
-                      ConvertNetworkAPIErrorForCompatibility(params.result(),
-                                                             private_api_));
+  RunCallback(read_callback_,
+              succeeded ? static_cast<int32_t>(data.size()) : params.result());
 }
 
 void TCPSocketResourceBase::OnPluginMsgWriteReply(
     const ResourceMessageReplyParams& params) {
-  if (!TrackedCallback::IsPending(write_callback_))
+  if (!state_.IsConnected() || !TrackedCallback::IsPending(write_callback_))
     return;
   RunCallback(write_callback_, params.result());
 }
 
+void TCPSocketResourceBase::OnPluginMsgListenReply(
+    const ResourceMessageReplyParams& params) {
+  if (!state_.IsPending(TCPSocketState::LISTEN))
+    return;
+
+  DCHECK(TrackedCallback::IsPending(listen_callback_));
+  state_.CompletePendingTransition(params.result() == PP_OK);
+
+  RunCallback(listen_callback_, params.result());
+}
+
+void TCPSocketResourceBase::OnPluginMsgAcceptReply(
+    const ResourceMessageReplyParams& params,
+    int pending_host_id,
+    const PP_NetAddress_Private& local_addr,
+    const PP_NetAddress_Private& remote_addr) {
+  // It is possible that CloseImpl() has been called. We shouldn't access the
+  // output parameter in that case. The user may have released it.
+  if (state_.state() != TCPSocketState::LISTENING ||
+      !TrackedCallback::IsPending(accept_callback_) || !accepted_tcp_socket_) {
+    return;
+  }
+
+  if (params.result() == PP_OK) {
+    *accepted_tcp_socket_ = CreateAcceptedSocket(pending_host_id, local_addr,
+                                                 remote_addr);
+  }
+  accepted_tcp_socket_ = NULL;
+  RunCallback(accept_callback_, params.result());
+}
+
 void TCPSocketResourceBase::OnPluginMsgSetOptionReply(
     const ResourceMessageReplyParams& params) {
   if (set_option_callbacks_.empty()) {
@@ -399,8 +524,8 @@
 
 void TCPSocketResourceBase::RunCallback(scoped_refptr<TrackedCallback> callback,
                                         int32_t pp_result) {
-  callback->Run(ConvertNetworkAPIErrorForCompatibility(pp_result,
-                                                       private_api_));
+  callback->Run(ConvertNetworkAPIErrorForCompatibility(
+      pp_result, version_ == TCP_SOCKET_VERSION_PRIVATE));
 }
 
 }  // namespace ppapi
diff --git a/ppapi/proxy/tcp_socket_resource_base.h b/ppapi/proxy/tcp_socket_resource_base.h
index 7e8a481..8835ab97 100644
--- a/ppapi/proxy/tcp_socket_resource_base.h
+++ b/ppapi/proxy/tcp_socket_resource_base.h
@@ -15,6 +15,7 @@
 #include "ppapi/c/private/ppb_net_address_private.h"
 #include "ppapi/proxy/plugin_resource.h"
 #include "ppapi/proxy/ppapi_proxy_export.h"
+#include "ppapi/shared_impl/ppb_tcp_socket_shared.h"
 #include "ppapi/shared_impl/tracked_callback.h"
 
 namespace ppapi {
@@ -27,6 +28,7 @@
 
 class PPAPI_PROXY_EXPORT TCPSocketResourceBase : public PluginResource {
  public:
+  // TODO(yzshen): Move these constants to ppb_tcp_socket_shared.
   // The maximum number of bytes that each PpapiHostMsg_PPBTCPSocket_Read
   // message is allowed to request.
   static const int32_t kMaxReadSize;
@@ -46,33 +48,28 @@
   static const int32_t kMaxReceiveBufferSize;
 
  protected:
-  enum ConnectionState {
-    // Before a connection is successfully established (including a connect
-    // request is pending or a previous connect request failed).
-    BEFORE_CONNECT,
-    // A connection has been successfully established (including a request of
-    // initiating SSL is pending).
-    CONNECTED,
-    // An SSL connection has been successfully established.
-    SSL_CONNECTED,
-    // The connection has been ended.
-    DISCONNECTED
-  };
-
   // C-tor used for new sockets.
   TCPSocketResourceBase(Connection connection,
                         PP_Instance instance,
-                        bool private_api);
+                        TCPSocketVersion version);
 
   // C-tor used for already accepted sockets.
   TCPSocketResourceBase(Connection connection,
                         PP_Instance instance,
-                        bool private_api,
+                        TCPSocketVersion version,
                         const PP_NetAddress_Private& local_addr,
                         const PP_NetAddress_Private& remote_addr);
 
   virtual ~TCPSocketResourceBase();
 
+  // Implemented by subclasses to create resources for accepted sockets.
+  virtual PP_Resource CreateAcceptedSocket(
+      int pending_host_id,
+      const PP_NetAddress_Private& local_addr,
+      const PP_NetAddress_Private& remote_addr) = 0;
+
+  int32_t BindImpl(const PP_NetAddress_Private* addr,
+                   scoped_refptr<TrackedCallback> callback);
   int32_t ConnectImpl(const char* host,
                       uint16_t port,
                       scoped_refptr<TrackedCallback> callback);
@@ -92,15 +89,19 @@
   int32_t WriteImpl(const char* buffer,
                     int32_t bytes_to_write,
                     scoped_refptr<TrackedCallback> callback);
-  void DisconnectImpl();
+  int32_t ListenImpl(int32_t backlog, scoped_refptr<TrackedCallback> callback);
+  int32_t AcceptImpl(PP_Resource* accepted_tcp_socket,
+                     scoped_refptr<TrackedCallback> callback);
+  void CloseImpl();
   int32_t SetOptionImpl(PP_TCPSocket_Option name,
                         const PP_Var& value,
                         scoped_refptr<TrackedCallback> callback);
 
-  bool IsConnected() const;
   void PostAbortIfNecessary(scoped_refptr<TrackedCallback>* callback);
 
   // IPC message handlers.
+  void OnPluginMsgBindReply(const ResourceMessageReplyParams& params,
+                            const PP_NetAddress_Private& local_addr);
   void OnPluginMsgConnectReply(const ResourceMessageReplyParams& params,
                                const PP_NetAddress_Private& local_addr,
                                const PP_NetAddress_Private& remote_addr);
@@ -110,15 +111,23 @@
   void OnPluginMsgReadReply(const ResourceMessageReplyParams& params,
                             const std::string& data);
   void OnPluginMsgWriteReply(const ResourceMessageReplyParams& params);
+  void OnPluginMsgListenReply(const ResourceMessageReplyParams& params);
+  void OnPluginMsgAcceptReply(const ResourceMessageReplyParams& params,
+                              int pending_host_id,
+                              const PP_NetAddress_Private& local_addr,
+                              const PP_NetAddress_Private& remote_addr);
   void OnPluginMsgSetOptionReply(const ResourceMessageReplyParams& params);
 
+  scoped_refptr<TrackedCallback> bind_callback_;
   scoped_refptr<TrackedCallback> connect_callback_;
   scoped_refptr<TrackedCallback> ssl_handshake_callback_;
   scoped_refptr<TrackedCallback> read_callback_;
   scoped_refptr<TrackedCallback> write_callback_;
+  scoped_refptr<TrackedCallback> listen_callback_;
+  scoped_refptr<TrackedCallback> accept_callback_;
   std::queue<scoped_refptr<TrackedCallback> > set_option_callbacks_;
 
-  ConnectionState connection_state_;
+  TCPSocketState state_;
   char* read_buffer_;
   int32_t bytes_to_read_;
 
@@ -130,10 +139,12 @@
   std::vector<std::vector<char> > trusted_certificates_;
   std::vector<std::vector<char> > untrusted_certificates_;
 
+  PP_Resource* accepted_tcp_socket_;
+
  private:
   void RunCallback(scoped_refptr<TrackedCallback> callback, int32_t pp_result);
 
-  bool private_api_;
+  TCPSocketVersion version_;
 
   DISALLOW_COPY_AND_ASSIGN(TCPSocketResourceBase);
 };