summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authorrockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-12 06:41:29 +0000
committerrockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-12 06:41:29 +0000
commit68cb5657f36c7fcc75074146bedc77ae60e13b24 (patch)
treebe4466255116a981d0ad60a43f047fcd277f2d00 /extensions
parent21bbb2cea53c40cd2a463756e969a0ce1651fe92 (diff)
downloadchromium_src-68cb5657f36c7fcc75074146bedc77ae60e13b24.zip
chromium_src-68cb5657f36c7fcc75074146bedc77ae60e13b24.tar.gz
chromium_src-68cb5657f36c7fcc75074146bedc77ae60e13b24.tar.bz2
Move sockets APIs out of src/chrome
This moves sockets APIs out of src/chrome and into src/extensions. The IDLs and function implementations are moved, and the groundwork is set for moving additional APIs. The API tests remain in src/chrome, and several references to src/chrome still remain for permissions/features/manifest stuff. BUG=349787 TBR=erg@chromium.org for profiles change TBR=jar@chromium.org for +net to //extensions/browser/DEPS Review URL: https://codereview.chromium.org/183893041 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@256456 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions')
-rw-r--r--extensions/browser/DEPS4
-rw-r--r--extensions/browser/api/api_resource_manager.h17
-rw-r--r--extensions/browser/api/socket/socket.cc152
-rw-r--r--extensions/browser/api/socket/socket.h126
-rw-r--r--extensions/browser/api/socket/socket/OWNERS2
-rw-r--r--extensions/browser/api/socket/socket_api.cc891
-rw-r--r--extensions/browser/api/socket/socket_api.h507
-rw-r--r--extensions/browser/api/socket/tcp_socket.cc329
-rw-r--r--extensions/browser/api/socket/tcp_socket.h173
-rw-r--r--extensions/browser/api/socket/udp_socket.cc296
-rw-r--r--extensions/browser/api/socket/udp_socket.h117
-rw-r--r--extensions/browser/api/sockets_tcp/sockets_tcp/OWNERS1
-rw-r--r--extensions/browser/api/sockets_tcp/sockets_tcp_api.cc445
-rw-r--r--extensions/browser/api/sockets_tcp/sockets_tcp_api.h244
-rw-r--r--extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.cc198
-rw-r--r--extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h94
-rw-r--r--extensions/browser/api/sockets_tcp_server/sockets_tcp_server/OWNERS1
-rw-r--r--extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc298
-rw-r--r--extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h178
-rw-r--r--extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc199
-rw-r--r--extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h100
-rw-r--r--extensions/browser/api/sockets_udp/sockets_udp/OWNERS1
-rw-r--r--extensions/browser/api/sockets_udp/sockets_udp_api.cc505
-rw-r--r--extensions/browser/api/sockets_udp/sockets_udp_api.h279
-rw-r--r--extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.cc183
-rw-r--r--extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h93
-rw-r--r--extensions/common/api/api.gyp36
-rw-r--r--extensions/common/api/socket.idl339
-rw-r--r--extensions/common/api/sockets_tcp.idl252
-rw-r--r--extensions/common/api/sockets_tcp_server.idl193
-rw-r--r--extensions/common/api/sockets_udp.idl308
-rw-r--r--extensions/common/extension_api.cc24
-rw-r--r--extensions/common/extensions_client.h8
-rw-r--r--extensions/extensions.gyp25
34 files changed, 6601 insertions, 17 deletions
diff --git a/extensions/browser/DEPS b/extensions/browser/DEPS
index f445ff6..3f59f38 100644
--- a/extensions/browser/DEPS
+++ b/extensions/browser/DEPS
@@ -2,6 +2,7 @@ include_rules = [
"+components/browser_context_keyed_service",
"+components/user_prefs",
"+content/public/browser",
+ "+net",
"+sync",
"+third_party/leveldatabase",
@@ -13,14 +14,17 @@ include_rules = [
# TODO(jamescook): Remove these. http://crbug.com/162530
"+chrome/browser/chrome_notification_types.h",
"+chrome/browser/extensions/api/content_settings/content_settings_store.h",
+ "+chrome/browser/extensions/api/dns/host_resolver_wrapper.h",
"+chrome/browser/extensions/api/preference/preference_api.h",
"+chrome/browser/extensions/api/runtime/runtime_api.h",
"+chrome/browser/extensions/extension_function_dispatcher.h",
"+chrome/browser/extensions/extension_host.h",
"+chrome/browser/extensions/extension_service.h",
"+chrome/browser/renderer_host/chrome_render_message_filter.h",
+ "+chrome/common/extensions/api/sockets/sockets_manifest_data.h",
"+chrome/common/extensions/extension_messages.h",
"+chrome/common/extensions/features/feature_channel.h",
+ "+chrome/common/extensions/permissions",
"+grit/generated_resources.h",
]
diff --git a/extensions/browser/api/api_resource_manager.h b/extensions/browser/api/api_resource_manager.h
index 0513c2c..39e9b8e 100644
--- a/extensions/browser/api/api_resource_manager.h
+++ b/extensions/browser/api/api_resource_manager.h
@@ -23,15 +23,16 @@
#include "extensions/common/extension.h"
namespace extensions {
+
namespace api {
class SerialEventDispatcher;
+}
+
+namespace core_api {
class TCPServerSocketEventDispatcher;
class TCPSocketEventDispatcher;
class UDPSocketEventDispatcher;
}
-}
-
-namespace extensions {
// An ApiResourceManager manages the lifetime of a set of resources that
// ApiFunctions use. Examples are sockets or USB connections.
@@ -149,13 +150,17 @@ class ApiResourceManager : public BrowserContextKeyedAPI,
}
private:
+ // TODO(rockot): ApiResourceData could be moved out of ApiResourceManager and
+ // we could avoid maintaining a friends list here.
friend class api::SerialEventDispatcher;
- friend class api::TCPServerSocketEventDispatcher;
- friend class api::TCPSocketEventDispatcher;
- friend class api::UDPSocketEventDispatcher;
+ friend class core_api::TCPServerSocketEventDispatcher;
+ friend class core_api::TCPSocketEventDispatcher;
+ friend class core_api::UDPSocketEventDispatcher;
friend class BrowserContextKeyedAPIFactory<ApiResourceManager<T> >;
+
// BrowserContextKeyedAPI implementation.
static const char* service_name() { return T::service_name(); }
+
static const bool kServiceHasOwnInstanceInIncognito = true;
static const bool kServiceIsNULLWhileTesting = true;
diff --git a/extensions/browser/api/socket/socket.cc b/extensions/browser/api/socket/socket.cc
new file mode 100644
index 0000000..afec38f
--- /dev/null
+++ b/extensions/browser/api/socket/socket.cc
@@ -0,0 +1,152 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/api/socket/socket.h"
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "extensions/browser/api/api_resource_manager.h"
+#include "net/base/address_list.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/socket/socket.h"
+
+namespace extensions {
+
+const char kSocketTypeNotSupported[] = "Socket type does not support this API";
+
+static base::LazyInstance<
+ BrowserContextKeyedAPIFactory<ApiResourceManager<Socket> > > g_factory =
+ LAZY_INSTANCE_INITIALIZER;
+
+// static
+template <>
+BrowserContextKeyedAPIFactory<ApiResourceManager<Socket> >*
+ApiResourceManager<Socket>::GetFactoryInstance() {
+ return g_factory.Pointer();
+}
+
+Socket::Socket(const std::string& owner_extension_id)
+ : ApiResource(owner_extension_id), is_connected_(false) {}
+
+Socket::~Socket() {
+ // Derived destructors should make sure the socket has been closed.
+ DCHECK(!is_connected_);
+}
+
+void Socket::Write(scoped_refptr<net::IOBuffer> io_buffer,
+ int byte_count,
+ const CompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+ write_queue_.push(WriteRequest(io_buffer, byte_count, callback));
+ WriteData();
+}
+
+void Socket::WriteData() {
+ // IO is pending.
+ if (io_buffer_write_.get())
+ return;
+
+ WriteRequest& request = write_queue_.front();
+
+ DCHECK(request.byte_count >= request.bytes_written);
+ io_buffer_write_ = new net::WrappedIOBuffer(request.io_buffer->data() +
+ request.bytes_written);
+ int result =
+ WriteImpl(io_buffer_write_.get(),
+ request.byte_count - request.bytes_written,
+ base::Bind(&Socket::OnWriteComplete, base::Unretained(this)));
+
+ if (result != net::ERR_IO_PENDING)
+ OnWriteComplete(result);
+}
+
+void Socket::OnWriteComplete(int result) {
+ io_buffer_write_ = NULL;
+
+ WriteRequest& request = write_queue_.front();
+
+ if (result >= 0) {
+ request.bytes_written += result;
+ if (request.bytes_written < request.byte_count) {
+ WriteData();
+ return;
+ }
+ DCHECK(request.bytes_written == request.byte_count);
+ result = request.bytes_written;
+ }
+
+ request.callback.Run(result);
+ write_queue_.pop();
+
+ if (!write_queue_.empty())
+ WriteData();
+}
+
+bool Socket::SetKeepAlive(bool enable, int delay) { return false; }
+
+bool Socket::SetNoDelay(bool no_delay) { return false; }
+
+int Socket::Listen(const std::string& address,
+ int port,
+ int backlog,
+ std::string* error_msg) {
+ *error_msg = kSocketTypeNotSupported;
+ return net::ERR_FAILED;
+}
+
+void Socket::Accept(const AcceptCompletionCallback& callback) {
+ callback.Run(net::ERR_FAILED, NULL);
+}
+
+// static
+bool Socket::StringAndPortToIPEndPoint(const std::string& ip_address_str,
+ int port,
+ net::IPEndPoint* ip_end_point) {
+ DCHECK(ip_end_point);
+ net::IPAddressNumber ip_number;
+ if (!net::ParseIPLiteralToNumber(ip_address_str, &ip_number))
+ return false;
+
+ *ip_end_point = net::IPEndPoint(ip_number, port);
+ return true;
+}
+
+bool Socket::StringAndPortToAddressList(const std::string& ip_address_str,
+ int port,
+ net::AddressList* address_list) {
+ DCHECK(address_list);
+ net::IPAddressNumber ip_number;
+ if (!net::ParseIPLiteralToNumber(ip_address_str, &ip_number))
+ return false;
+
+ *address_list = net::AddressList::CreateFromIPAddress(ip_number, port);
+ return true;
+}
+
+void Socket::IPEndPointToStringAndPort(const net::IPEndPoint& address,
+ std::string* ip_address_str,
+ int* port) {
+ DCHECK(ip_address_str);
+ DCHECK(port);
+ *ip_address_str = address.ToStringWithoutPort();
+ if (ip_address_str->empty()) {
+ *port = 0;
+ } else {
+ *port = address.port();
+ }
+}
+
+Socket::WriteRequest::WriteRequest(scoped_refptr<net::IOBuffer> io_buffer,
+ int byte_count,
+ const CompletionCallback& callback)
+ : io_buffer(io_buffer),
+ byte_count(byte_count),
+ callback(callback),
+ bytes_written(0) {}
+
+Socket::WriteRequest::~WriteRequest() {}
+
+} // namespace extensions
diff --git a/extensions/browser/api/socket/socket.h b/extensions/browser/api/socket/socket.h
new file mode 100644
index 0000000..9ae45e7
--- /dev/null
+++ b/extensions/browser/api/socket/socket.h
@@ -0,0 +1,126 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_API_SOCKET_SOCKET_H_
+#define EXTENSIONS_BROWSER_API_SOCKET_SOCKET_H_
+
+#include <queue>
+#include <string>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "extensions/browser/api/api_resource.h"
+#include "extensions/browser/api/api_resource_manager.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/socket/tcp_client_socket.h"
+
+namespace net {
+class AddressList;
+class IPEndPoint;
+class Socket;
+}
+
+namespace extensions {
+
+typedef base::Callback<void(int)> CompletionCallback;
+typedef base::Callback<void(int, scoped_refptr<net::IOBuffer> io_buffer)>
+ ReadCompletionCallback;
+typedef base::Callback<
+ void(int, scoped_refptr<net::IOBuffer> io_buffer, const std::string&, int)>
+ RecvFromCompletionCallback;
+typedef base::Callback<void(int, net::TCPClientSocket*)>
+ AcceptCompletionCallback;
+
+// A Socket wraps a low-level socket and includes housekeeping information that
+// we need to manage it in the context of an extension.
+class Socket : public ApiResource {
+ public:
+ enum SocketType { TYPE_TCP, TYPE_UDP, };
+
+ virtual ~Socket();
+ virtual void Connect(const std::string& address,
+ int port,
+ const CompletionCallback& callback) = 0;
+ virtual void Disconnect() = 0;
+ virtual int Bind(const std::string& address, int port) = 0;
+
+ // The |callback| will be called with the number of bytes read into the
+ // buffer, or a negative number if an error occurred.
+ virtual void Read(int count, const ReadCompletionCallback& callback) = 0;
+
+ // The |callback| will be called with |byte_count| or a negative number if an
+ // error occurred.
+ void Write(scoped_refptr<net::IOBuffer> io_buffer,
+ int byte_count,
+ const CompletionCallback& callback);
+
+ virtual void RecvFrom(int count,
+ const RecvFromCompletionCallback& callback) = 0;
+ virtual void SendTo(scoped_refptr<net::IOBuffer> io_buffer,
+ int byte_count,
+ const std::string& address,
+ int port,
+ const CompletionCallback& callback) = 0;
+
+ virtual bool SetKeepAlive(bool enable, int delay);
+ virtual bool SetNoDelay(bool no_delay);
+ virtual int Listen(const std::string& address,
+ int port,
+ int backlog,
+ std::string* error_msg);
+ virtual void Accept(const AcceptCompletionCallback& callback);
+
+ virtual bool IsConnected() = 0;
+
+ virtual bool GetPeerAddress(net::IPEndPoint* address) = 0;
+ virtual bool GetLocalAddress(net::IPEndPoint* address) = 0;
+
+ virtual SocketType GetSocketType() const = 0;
+
+ static bool StringAndPortToAddressList(const std::string& ip_address_str,
+ int port,
+ net::AddressList* address_list);
+ static bool StringAndPortToIPEndPoint(const std::string& ip_address_str,
+ int port,
+ net::IPEndPoint* ip_end_point);
+ static void IPEndPointToStringAndPort(const net::IPEndPoint& address,
+ std::string* ip_address_str,
+ int* port);
+
+ protected:
+ explicit Socket(const std::string& owner_extension_id_);
+
+ void WriteData();
+ virtual int WriteImpl(net::IOBuffer* io_buffer,
+ int io_buffer_size,
+ const net::CompletionCallback& callback) = 0;
+ virtual void OnWriteComplete(int result);
+
+ const std::string address_;
+ bool is_connected_;
+
+ private:
+ friend class ApiResourceManager<Socket>;
+ static const char* service_name() { return "SocketManager"; }
+
+ struct WriteRequest {
+ WriteRequest(scoped_refptr<net::IOBuffer> io_buffer,
+ int byte_count,
+ const CompletionCallback& callback);
+ ~WriteRequest();
+ scoped_refptr<net::IOBuffer> io_buffer;
+ int byte_count;
+ CompletionCallback callback;
+ int bytes_written;
+ };
+ std::queue<WriteRequest> write_queue_;
+ scoped_refptr<net::IOBuffer> io_buffer_write_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_API_SOCKET_SOCKET_H_
diff --git a/extensions/browser/api/socket/socket/OWNERS b/extensions/browser/api/socket/socket/OWNERS
new file mode 100644
index 0000000..b0df7e7
--- /dev/null
+++ b/extensions/browser/api/socket/socket/OWNERS
@@ -0,0 +1,2 @@
+ikarienator@chromium.org
+rpaquay@chromium.org
diff --git a/extensions/browser/api/socket/socket_api.cc b/extensions/browser/api/socket/socket_api.cc
new file mode 100644
index 0000000..34a5517
--- /dev/null
+++ b/extensions/browser/api/socket/socket_api.cc
@@ -0,0 +1,891 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/api/socket/socket_api.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/containers/hash_tables.h"
+#include "chrome/browser/extensions/api/dns/host_resolver_wrapper.h"
+#include "chrome/common/extensions/permissions/socket_permission.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/resource_context.h"
+#include "extensions/browser/api/socket/socket.h"
+#include "extensions/browser/api/socket/tcp_socket.h"
+#include "extensions/browser/api/socket/udp_socket.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+
+namespace extensions {
+
+using content::SocketPermissionRequest;
+
+const char kAddressKey[] = "address";
+const char kPortKey[] = "port";
+const char kBytesWrittenKey[] = "bytesWritten";
+const char kDataKey[] = "data";
+const char kResultCodeKey[] = "resultCode";
+const char kSocketIdKey[] = "socketId";
+
+const char kSocketNotFoundError[] = "Socket not found";
+const char kDnsLookupFailedError[] = "DNS resolution failed";
+const char kPermissionError[] = "App does not have permission";
+const char kNetworkListError[] = "Network lookup failed or unsupported";
+const char kTCPSocketBindError[] =
+ "TCP socket does not support bind. For TCP server please use listen.";
+const char kMulticastSocketTypeError[] = "Only UDP socket supports multicast.";
+const char kWildcardAddress[] = "*";
+const int kWildcardPort = 0;
+
+SocketAsyncApiFunction::SocketAsyncApiFunction() {}
+
+SocketAsyncApiFunction::~SocketAsyncApiFunction() {}
+
+bool SocketAsyncApiFunction::PrePrepare() {
+ manager_ = CreateSocketResourceManager();
+ return manager_->SetBrowserContext(browser_context());
+}
+
+bool SocketAsyncApiFunction::Respond() { return error_.empty(); }
+
+scoped_ptr<SocketResourceManagerInterface>
+SocketAsyncApiFunction::CreateSocketResourceManager() {
+ return scoped_ptr<SocketResourceManagerInterface>(
+ new SocketResourceManager<Socket>()).Pass();
+}
+
+int SocketAsyncApiFunction::AddSocket(Socket* socket) {
+ return manager_->Add(socket);
+}
+
+Socket* SocketAsyncApiFunction::GetSocket(int api_resource_id) {
+ return manager_->Get(extension_->id(), api_resource_id);
+}
+
+base::hash_set<int>* SocketAsyncApiFunction::GetSocketIds() {
+ return manager_->GetResourceIds(extension_->id());
+}
+
+void SocketAsyncApiFunction::RemoveSocket(int api_resource_id) {
+ manager_->Remove(extension_->id(), api_resource_id);
+}
+
+SocketExtensionWithDnsLookupFunction::SocketExtensionWithDnsLookupFunction()
+ : resource_context_(NULL),
+ request_handle_(new net::HostResolver::RequestHandle),
+ addresses_(new net::AddressList) {}
+
+SocketExtensionWithDnsLookupFunction::~SocketExtensionWithDnsLookupFunction() {}
+
+bool SocketExtensionWithDnsLookupFunction::PrePrepare() {
+ if (!SocketAsyncApiFunction::PrePrepare())
+ return false;
+ resource_context_ = browser_context()->GetResourceContext();
+ return resource_context_ != NULL;
+}
+
+void SocketExtensionWithDnsLookupFunction::StartDnsLookup(
+ const std::string& hostname) {
+ net::HostResolver* host_resolver =
+ extensions::HostResolverWrapper::GetInstance()->GetHostResolver(
+ resource_context_->GetHostResolver());
+ DCHECK(host_resolver);
+
+ // Yes, we are passing zero as the port. There are some interesting but not
+ // presently relevant reasons why HostResolver asks for the port of the
+ // hostname you'd like to resolve, even though it doesn't use that value in
+ // determining its answer.
+ net::HostPortPair host_port_pair(hostname, 0);
+
+ net::HostResolver::RequestInfo request_info(host_port_pair);
+ int resolve_result = host_resolver->Resolve(
+ request_info,
+ net::DEFAULT_PRIORITY,
+ addresses_.get(),
+ base::Bind(&SocketExtensionWithDnsLookupFunction::OnDnsLookup, this),
+ request_handle_.get(),
+ net::BoundNetLog());
+
+ if (resolve_result != net::ERR_IO_PENDING)
+ OnDnsLookup(resolve_result);
+}
+
+void SocketExtensionWithDnsLookupFunction::OnDnsLookup(int resolve_result) {
+ if (resolve_result == net::OK) {
+ DCHECK(!addresses_->empty());
+ resolved_address_ = addresses_->front().ToStringWithoutPort();
+ } else {
+ error_ = kDnsLookupFailedError;
+ }
+ AfterDnsLookup(resolve_result);
+}
+
+SocketCreateFunction::SocketCreateFunction()
+ : socket_type_(kSocketTypeInvalid) {}
+
+SocketCreateFunction::~SocketCreateFunction() {}
+
+bool SocketCreateFunction::Prepare() {
+ params_ = core_api::socket::Create::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+
+ switch (params_->type) {
+ case extensions::core_api::socket::SOCKET_TYPE_TCP:
+ socket_type_ = kSocketTypeTCP;
+ break;
+ case extensions::core_api::socket::SOCKET_TYPE_UDP:
+ socket_type_ = kSocketTypeUDP;
+ break;
+ case extensions::core_api::socket::SOCKET_TYPE_NONE:
+ NOTREACHED();
+ break;
+ }
+
+ return true;
+}
+
+void SocketCreateFunction::Work() {
+ Socket* socket = NULL;
+ if (socket_type_ == kSocketTypeTCP) {
+ socket = new TCPSocket(extension_->id());
+ } else if (socket_type_ == kSocketTypeUDP) {
+ socket = new UDPSocket(extension_->id());
+ }
+ DCHECK(socket);
+
+ base::DictionaryValue* result = new base::DictionaryValue();
+ result->SetInteger(kSocketIdKey, AddSocket(socket));
+ SetResult(result);
+}
+
+bool SocketDestroyFunction::Prepare() {
+ EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_));
+ return true;
+}
+
+void SocketDestroyFunction::Work() { RemoveSocket(socket_id_); }
+
+SocketConnectFunction::SocketConnectFunction()
+ : socket_id_(0), hostname_(), port_(0), socket_(NULL) {}
+
+SocketConnectFunction::~SocketConnectFunction() {}
+
+bool SocketConnectFunction::Prepare() {
+ EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_));
+ EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &hostname_));
+ EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(2, &port_));
+ return true;
+}
+
+void SocketConnectFunction::AsyncWorkStart() {
+ socket_ = GetSocket(socket_id_);
+ if (!socket_) {
+ error_ = kSocketNotFoundError;
+ SetResult(new base::FundamentalValue(-1));
+ AsyncWorkCompleted();
+ return;
+ }
+
+ SocketPermissionRequest::OperationType operation_type;
+ switch (socket_->GetSocketType()) {
+ case Socket::TYPE_TCP:
+ operation_type = SocketPermissionRequest::TCP_CONNECT;
+ break;
+ case Socket::TYPE_UDP:
+ operation_type = SocketPermissionRequest::UDP_SEND_TO;
+ break;
+ default:
+ NOTREACHED() << "Unknown socket type.";
+ operation_type = SocketPermissionRequest::NONE;
+ break;
+ }
+
+ SocketPermission::CheckParam param(operation_type, hostname_, port_);
+ if (!PermissionsData::CheckAPIPermissionWithParam(
+ GetExtension(), APIPermission::kSocket, &param)) {
+ error_ = kPermissionError;
+ SetResult(new base::FundamentalValue(-1));
+ AsyncWorkCompleted();
+ return;
+ }
+
+ StartDnsLookup(hostname_);
+}
+
+void SocketConnectFunction::AfterDnsLookup(int lookup_result) {
+ if (lookup_result == net::OK) {
+ StartConnect();
+ } else {
+ SetResult(new base::FundamentalValue(lookup_result));
+ AsyncWorkCompleted();
+ }
+}
+
+void SocketConnectFunction::StartConnect() {
+ socket_->Connect(resolved_address_,
+ port_,
+ base::Bind(&SocketConnectFunction::OnConnect, this));
+}
+
+void SocketConnectFunction::OnConnect(int result) {
+ SetResult(new base::FundamentalValue(result));
+ AsyncWorkCompleted();
+}
+
+bool SocketDisconnectFunction::Prepare() {
+ EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_));
+ return true;
+}
+
+void SocketDisconnectFunction::Work() {
+ Socket* socket = GetSocket(socket_id_);
+ if (socket)
+ socket->Disconnect();
+ else
+ error_ = kSocketNotFoundError;
+ SetResult(base::Value::CreateNullValue());
+}
+
+bool SocketBindFunction::Prepare() {
+ EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_));
+ EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &address_));
+ EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(2, &port_));
+ return true;
+}
+
+void SocketBindFunction::Work() {
+ int result = -1;
+ Socket* socket = GetSocket(socket_id_);
+
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ if (socket->GetSocketType() == Socket::TYPE_UDP) {
+ SocketPermission::CheckParam param(
+ SocketPermissionRequest::UDP_BIND, address_, port_);
+ if (!PermissionsData::CheckAPIPermissionWithParam(
+ GetExtension(), APIPermission::kSocket, &param)) {
+ error_ = kPermissionError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+ } else if (socket->GetSocketType() == Socket::TYPE_TCP) {
+ error_ = kTCPSocketBindError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ result = socket->Bind(address_, port_);
+ SetResult(new base::FundamentalValue(result));
+}
+
+SocketListenFunction::SocketListenFunction() {}
+
+SocketListenFunction::~SocketListenFunction() {}
+
+bool SocketListenFunction::Prepare() {
+ params_ = core_api::socket::Listen::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketListenFunction::Work() {
+ int result = -1;
+
+ Socket* socket = GetSocket(params_->socket_id);
+ if (socket) {
+ SocketPermission::CheckParam param(
+ SocketPermissionRequest::TCP_LISTEN, params_->address, params_->port);
+ if (!PermissionsData::CheckAPIPermissionWithParam(
+ GetExtension(), APIPermission::kSocket, &param)) {
+ error_ = kPermissionError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ result =
+ socket->Listen(params_->address,
+ params_->port,
+ params_->backlog.get() ? *params_->backlog.get() : 5,
+ &error_);
+ } else {
+ error_ = kSocketNotFoundError;
+ }
+
+ SetResult(new base::FundamentalValue(result));
+}
+
+SocketAcceptFunction::SocketAcceptFunction() {}
+
+SocketAcceptFunction::~SocketAcceptFunction() {}
+
+bool SocketAcceptFunction::Prepare() {
+ params_ = core_api::socket::Accept::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketAcceptFunction::AsyncWorkStart() {
+ Socket* socket = GetSocket(params_->socket_id);
+ if (socket) {
+ socket->Accept(base::Bind(&SocketAcceptFunction::OnAccept, this));
+ } else {
+ error_ = kSocketNotFoundError;
+ OnAccept(-1, NULL);
+ }
+}
+
+void SocketAcceptFunction::OnAccept(int result_code,
+ net::TCPClientSocket* socket) {
+ base::DictionaryValue* result = new base::DictionaryValue();
+ result->SetInteger(kResultCodeKey, result_code);
+ if (socket) {
+ Socket* client_socket = new TCPSocket(socket, extension_id(), true);
+ result->SetInteger(kSocketIdKey, AddSocket(client_socket));
+ }
+ SetResult(result);
+
+ AsyncWorkCompleted();
+}
+
+SocketReadFunction::SocketReadFunction() {}
+
+SocketReadFunction::~SocketReadFunction() {}
+
+bool SocketReadFunction::Prepare() {
+ params_ = core_api::socket::Read::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketReadFunction::AsyncWorkStart() {
+ Socket* socket = GetSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ OnCompleted(-1, NULL);
+ return;
+ }
+
+ socket->Read(params_->buffer_size.get() ? *params_->buffer_size.get() : 4096,
+ base::Bind(&SocketReadFunction::OnCompleted, this));
+}
+
+void SocketReadFunction::OnCompleted(int bytes_read,
+ scoped_refptr<net::IOBuffer> io_buffer) {
+ base::DictionaryValue* result = new base::DictionaryValue();
+ result->SetInteger(kResultCodeKey, bytes_read);
+ if (bytes_read > 0) {
+ result->Set(kDataKey,
+ base::BinaryValue::CreateWithCopiedBuffer(io_buffer->data(),
+ bytes_read));
+ } else {
+ result->Set(kDataKey, new base::BinaryValue());
+ }
+ SetResult(result);
+
+ AsyncWorkCompleted();
+}
+
+SocketWriteFunction::SocketWriteFunction()
+ : socket_id_(0), io_buffer_(NULL), io_buffer_size_(0) {}
+
+SocketWriteFunction::~SocketWriteFunction() {}
+
+bool SocketWriteFunction::Prepare() {
+ EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_));
+ base::BinaryValue* data = NULL;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetBinary(1, &data));
+
+ io_buffer_size_ = data->GetSize();
+ io_buffer_ = new net::WrappedIOBuffer(data->GetBuffer());
+ return true;
+}
+
+void SocketWriteFunction::AsyncWorkStart() {
+ Socket* socket = GetSocket(socket_id_);
+
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ OnCompleted(-1);
+ return;
+ }
+
+ socket->Write(io_buffer_,
+ io_buffer_size_,
+ base::Bind(&SocketWriteFunction::OnCompleted, this));
+}
+
+void SocketWriteFunction::OnCompleted(int bytes_written) {
+ base::DictionaryValue* result = new base::DictionaryValue();
+ result->SetInteger(kBytesWrittenKey, bytes_written);
+ SetResult(result);
+
+ AsyncWorkCompleted();
+}
+
+SocketRecvFromFunction::SocketRecvFromFunction() {}
+
+SocketRecvFromFunction::~SocketRecvFromFunction() {}
+
+bool SocketRecvFromFunction::Prepare() {
+ params_ = core_api::socket::RecvFrom::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketRecvFromFunction::AsyncWorkStart() {
+ Socket* socket = GetSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ OnCompleted(-1, NULL, std::string(), 0);
+ return;
+ }
+
+ socket->RecvFrom(params_->buffer_size.get() ? *params_->buffer_size : 4096,
+ base::Bind(&SocketRecvFromFunction::OnCompleted, this));
+}
+
+void SocketRecvFromFunction::OnCompleted(int bytes_read,
+ scoped_refptr<net::IOBuffer> io_buffer,
+ const std::string& address,
+ int port) {
+ base::DictionaryValue* result = new base::DictionaryValue();
+ result->SetInteger(kResultCodeKey, bytes_read);
+ if (bytes_read > 0) {
+ result->Set(kDataKey,
+ base::BinaryValue::CreateWithCopiedBuffer(io_buffer->data(),
+ bytes_read));
+ } else {
+ result->Set(kDataKey, new base::BinaryValue());
+ }
+ result->SetString(kAddressKey, address);
+ result->SetInteger(kPortKey, port);
+ SetResult(result);
+
+ AsyncWorkCompleted();
+}
+
+SocketSendToFunction::SocketSendToFunction()
+ : socket_id_(0),
+ io_buffer_(NULL),
+ io_buffer_size_(0),
+ port_(0),
+ socket_(NULL) {}
+
+SocketSendToFunction::~SocketSendToFunction() {}
+
+bool SocketSendToFunction::Prepare() {
+ EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &socket_id_));
+ base::BinaryValue* data = NULL;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetBinary(1, &data));
+ EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &hostname_));
+ EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(3, &port_));
+
+ io_buffer_size_ = data->GetSize();
+ io_buffer_ = new net::WrappedIOBuffer(data->GetBuffer());
+ return true;
+}
+
+void SocketSendToFunction::AsyncWorkStart() {
+ socket_ = GetSocket(socket_id_);
+ if (!socket_) {
+ error_ = kSocketNotFoundError;
+ SetResult(new base::FundamentalValue(-1));
+ AsyncWorkCompleted();
+ return;
+ }
+
+ if (socket_->GetSocketType() == Socket::TYPE_UDP) {
+ SocketPermission::CheckParam param(
+ SocketPermissionRequest::UDP_SEND_TO, hostname_, port_);
+ if (!PermissionsData::CheckAPIPermissionWithParam(
+ GetExtension(), APIPermission::kSocket, &param)) {
+ error_ = kPermissionError;
+ SetResult(new base::FundamentalValue(-1));
+ AsyncWorkCompleted();
+ return;
+ }
+ }
+
+ StartDnsLookup(hostname_);
+}
+
+void SocketSendToFunction::AfterDnsLookup(int lookup_result) {
+ if (lookup_result == net::OK) {
+ StartSendTo();
+ } else {
+ SetResult(new base::FundamentalValue(lookup_result));
+ AsyncWorkCompleted();
+ }
+}
+
+void SocketSendToFunction::StartSendTo() {
+ socket_->SendTo(io_buffer_,
+ io_buffer_size_,
+ resolved_address_,
+ port_,
+ base::Bind(&SocketSendToFunction::OnCompleted, this));
+}
+
+void SocketSendToFunction::OnCompleted(int bytes_written) {
+ base::DictionaryValue* result = new base::DictionaryValue();
+ result->SetInteger(kBytesWrittenKey, bytes_written);
+ SetResult(result);
+
+ AsyncWorkCompleted();
+}
+
+SocketSetKeepAliveFunction::SocketSetKeepAliveFunction() {}
+
+SocketSetKeepAliveFunction::~SocketSetKeepAliveFunction() {}
+
+bool SocketSetKeepAliveFunction::Prepare() {
+ params_ = core_api::socket::SetKeepAlive::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketSetKeepAliveFunction::Work() {
+ bool result = false;
+ Socket* socket = GetSocket(params_->socket_id);
+ if (socket) {
+ int delay = 0;
+ if (params_->delay.get())
+ delay = *params_->delay;
+ result = socket->SetKeepAlive(params_->enable, delay);
+ } else {
+ error_ = kSocketNotFoundError;
+ }
+ SetResult(new base::FundamentalValue(result));
+}
+
+SocketSetNoDelayFunction::SocketSetNoDelayFunction() {}
+
+SocketSetNoDelayFunction::~SocketSetNoDelayFunction() {}
+
+bool SocketSetNoDelayFunction::Prepare() {
+ params_ = core_api::socket::SetNoDelay::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketSetNoDelayFunction::Work() {
+ bool result = false;
+ Socket* socket = GetSocket(params_->socket_id);
+ if (socket)
+ result = socket->SetNoDelay(params_->no_delay);
+ else
+ error_ = kSocketNotFoundError;
+ SetResult(new base::FundamentalValue(result));
+}
+
+SocketGetInfoFunction::SocketGetInfoFunction() {}
+
+SocketGetInfoFunction::~SocketGetInfoFunction() {}
+
+bool SocketGetInfoFunction::Prepare() {
+ params_ = core_api::socket::GetInfo::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketGetInfoFunction::Work() {
+ Socket* socket = GetSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ core_api::socket::SocketInfo info;
+ // This represents what we know about the socket, and does not call through
+ // to the system.
+ if (socket->GetSocketType() == Socket::TYPE_TCP)
+ info.socket_type = extensions::core_api::socket::SOCKET_TYPE_TCP;
+ else
+ info.socket_type = extensions::core_api::socket::SOCKET_TYPE_UDP;
+ info.connected = socket->IsConnected();
+
+ // Grab the peer address as known by the OS. This and the call below will
+ // always succeed while the socket is connected, even if the socket has
+ // been remotely closed by the peer; only reading the socket will reveal
+ // that it should be closed locally.
+ net::IPEndPoint peerAddress;
+ if (socket->GetPeerAddress(&peerAddress)) {
+ info.peer_address.reset(new std::string(peerAddress.ToStringWithoutPort()));
+ info.peer_port.reset(new int(peerAddress.port()));
+ }
+
+ // Grab the local address as known by the OS.
+ net::IPEndPoint localAddress;
+ if (socket->GetLocalAddress(&localAddress)) {
+ info.local_address.reset(
+ new std::string(localAddress.ToStringWithoutPort()));
+ info.local_port.reset(new int(localAddress.port()));
+ }
+
+ SetResult(info.ToValue().release());
+}
+
+bool SocketGetNetworkListFunction::RunImpl() {
+ content::BrowserThread::PostTask(
+ content::BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&SocketGetNetworkListFunction::GetNetworkListOnFileThread,
+ this));
+ return true;
+}
+
+void SocketGetNetworkListFunction::GetNetworkListOnFileThread() {
+ net::NetworkInterfaceList interface_list;
+ if (GetNetworkList(&interface_list,
+ net::INCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES)) {
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&SocketGetNetworkListFunction::SendResponseOnUIThread,
+ this,
+ interface_list));
+ return;
+ }
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&SocketGetNetworkListFunction::HandleGetNetworkListError,
+ this));
+}
+
+void SocketGetNetworkListFunction::HandleGetNetworkListError() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ error_ = kNetworkListError;
+ SendResponse(false);
+}
+
+void SocketGetNetworkListFunction::SendResponseOnUIThread(
+ const net::NetworkInterfaceList& interface_list) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ std::vector<linked_ptr<core_api::socket::NetworkInterface> > create_arg;
+ create_arg.reserve(interface_list.size());
+ for (net::NetworkInterfaceList::const_iterator i = interface_list.begin();
+ i != interface_list.end();
+ ++i) {
+ linked_ptr<core_api::socket::NetworkInterface> info =
+ make_linked_ptr(new core_api::socket::NetworkInterface);
+ info->name = i->name;
+ info->address = net::IPAddressToString(i->address);
+ info->prefix_length = i->network_prefix;
+ create_arg.push_back(info);
+ }
+
+ results_ = core_api::socket::GetNetworkList::Results::Create(create_arg);
+ SendResponse(true);
+}
+
+SocketJoinGroupFunction::SocketJoinGroupFunction() {}
+
+SocketJoinGroupFunction::~SocketJoinGroupFunction() {}
+
+bool SocketJoinGroupFunction::Prepare() {
+ params_ = core_api::socket::JoinGroup::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketJoinGroupFunction::Work() {
+ int result = -1;
+ Socket* socket = GetSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ if (socket->GetSocketType() != Socket::TYPE_UDP) {
+ error_ = kMulticastSocketTypeError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ SocketPermission::CheckParam param(
+ SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP,
+ kWildcardAddress,
+ kWildcardPort);
+
+ if (!PermissionsData::CheckAPIPermissionWithParam(
+ GetExtension(), APIPermission::kSocket, &param)) {
+ error_ = kPermissionError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ result = static_cast<UDPSocket*>(socket)->JoinGroup(params_->address);
+ if (result != 0) {
+ error_ = net::ErrorToString(result);
+ }
+ SetResult(new base::FundamentalValue(result));
+}
+
+SocketLeaveGroupFunction::SocketLeaveGroupFunction() {}
+
+SocketLeaveGroupFunction::~SocketLeaveGroupFunction() {}
+
+bool SocketLeaveGroupFunction::Prepare() {
+ params_ = core_api::socket::LeaveGroup::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketLeaveGroupFunction::Work() {
+ int result = -1;
+ Socket* socket = GetSocket(params_->socket_id);
+
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ if (socket->GetSocketType() != Socket::TYPE_UDP) {
+ error_ = kMulticastSocketTypeError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ SocketPermission::CheckParam param(
+ SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP,
+ kWildcardAddress,
+ kWildcardPort);
+ if (!PermissionsData::CheckAPIPermissionWithParam(
+ GetExtension(), APIPermission::kSocket, &param)) {
+ error_ = kPermissionError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ result = static_cast<UDPSocket*>(socket)->LeaveGroup(params_->address);
+ if (result != 0)
+ error_ = net::ErrorToString(result);
+ SetResult(new base::FundamentalValue(result));
+}
+
+SocketSetMulticastTimeToLiveFunction::SocketSetMulticastTimeToLiveFunction() {}
+
+SocketSetMulticastTimeToLiveFunction::~SocketSetMulticastTimeToLiveFunction() {}
+
+bool SocketSetMulticastTimeToLiveFunction::Prepare() {
+ params_ = core_api::socket::SetMulticastTimeToLive::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+void SocketSetMulticastTimeToLiveFunction::Work() {
+ int result = -1;
+ Socket* socket = GetSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ if (socket->GetSocketType() != Socket::TYPE_UDP) {
+ error_ = kMulticastSocketTypeError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ result =
+ static_cast<UDPSocket*>(socket)->SetMulticastTimeToLive(params_->ttl);
+ if (result != 0)
+ error_ = net::ErrorToString(result);
+ SetResult(new base::FundamentalValue(result));
+}
+
+SocketSetMulticastLoopbackModeFunction::
+ SocketSetMulticastLoopbackModeFunction() {}
+
+SocketSetMulticastLoopbackModeFunction::
+ ~SocketSetMulticastLoopbackModeFunction() {}
+
+bool SocketSetMulticastLoopbackModeFunction::Prepare() {
+ params_ = core_api::socket::SetMulticastLoopbackMode::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketSetMulticastLoopbackModeFunction::Work() {
+ int result = -1;
+ Socket* socket = GetSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ if (socket->GetSocketType() != Socket::TYPE_UDP) {
+ error_ = kMulticastSocketTypeError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ result = static_cast<UDPSocket*>(socket)
+ ->SetMulticastLoopbackMode(params_->enabled);
+ if (result != 0)
+ error_ = net::ErrorToString(result);
+ SetResult(new base::FundamentalValue(result));
+}
+
+SocketGetJoinedGroupsFunction::SocketGetJoinedGroupsFunction() {}
+
+SocketGetJoinedGroupsFunction::~SocketGetJoinedGroupsFunction() {}
+
+bool SocketGetJoinedGroupsFunction::Prepare() {
+ params_ = core_api::socket::GetJoinedGroups::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketGetJoinedGroupsFunction::Work() {
+ int result = -1;
+ Socket* socket = GetSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ if (socket->GetSocketType() != Socket::TYPE_UDP) {
+ error_ = kMulticastSocketTypeError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ SocketPermission::CheckParam param(
+ SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP,
+ kWildcardAddress,
+ kWildcardPort);
+ if (!PermissionsData::CheckAPIPermissionWithParam(
+ GetExtension(), APIPermission::kSocket, &param)) {
+ error_ = kPermissionError;
+ SetResult(new base::FundamentalValue(result));
+ return;
+ }
+
+ base::ListValue* values = new base::ListValue();
+ values->AppendStrings((std::vector<std::string>&)static_cast<UDPSocket*>(
+ socket)->GetJoinedGroups());
+ SetResult(values);
+}
+
+} // namespace extensions
diff --git a/extensions/browser/api/socket/socket_api.h b/extensions/browser/api/socket/socket_api.h
new file mode 100644
index 0000000..60bd765
--- /dev/null
+++ b/extensions/browser/api/socket/socket_api.h
@@ -0,0 +1,507 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_API_SOCKET_SOCKET_API_H_
+#define EXTENSIONS_BROWSER_API_SOCKET_SOCKET_API_H_
+
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "extensions/browser/api/api_resource_manager.h"
+#include "extensions/browser/api/async_api_function.h"
+#include "extensions/browser/extension_function.h"
+#include "extensions/common/api/socket.h"
+#include "net/base/address_list.h"
+#include "net/dns/host_resolver.h"
+#include "net/socket/tcp_client_socket.h"
+
+namespace content {
+class BrowserContext;
+class ResourceContext;
+}
+
+namespace net {
+class IOBuffer;
+}
+
+namespace extensions {
+
+class Socket;
+
+// A simple interface to ApiResourceManager<Socket> or derived class. The goal
+// of this interface is to allow Socket API functions to use distinct instances
+// of ApiResourceManager<> depending on the type of socket (old version in
+// "socket" namespace vs new version in "socket.xxx" namespaces).
+class SocketResourceManagerInterface {
+ public:
+ virtual ~SocketResourceManagerInterface() {}
+
+ virtual bool SetBrowserContext(content::BrowserContext* context) = 0;
+ virtual int Add(Socket* socket) = 0;
+ virtual Socket* Get(const std::string& extension_id, int api_resource_id) = 0;
+ virtual void Remove(const std::string& extension_id, int api_resource_id) = 0;
+ virtual base::hash_set<int>* GetResourceIds(
+ const std::string& extension_id) = 0;
+};
+
+// Implementation of SocketResourceManagerInterface using an
+// ApiResourceManager<T> instance (where T derives from Socket).
+template <typename T>
+class SocketResourceManager : public SocketResourceManagerInterface {
+ public:
+ SocketResourceManager() : manager_(NULL) {}
+
+ virtual bool SetBrowserContext(content::BrowserContext* context) OVERRIDE {
+ manager_ = ApiResourceManager<T>::Get(context);
+ DCHECK(manager_)
+ << "There is no socket manager. "
+ "If this assertion is failing during a test, then it is likely that "
+ "TestExtensionSystem is failing to provide an instance of "
+ "ApiResourceManager<Socket>.";
+ return manager_ != NULL;
+ }
+
+ virtual int Add(Socket* socket) OVERRIDE {
+ // Note: Cast needed here, because "T" may be a subclass of "Socket".
+ return manager_->Add(static_cast<T*>(socket));
+ }
+
+ virtual Socket* Get(const std::string& extension_id,
+ int api_resource_id) OVERRIDE {
+ return manager_->Get(extension_id, api_resource_id);
+ }
+
+ virtual void Remove(const std::string& extension_id,
+ int api_resource_id) OVERRIDE {
+ manager_->Remove(extension_id, api_resource_id);
+ }
+
+ virtual base::hash_set<int>* GetResourceIds(const std::string& extension_id)
+ OVERRIDE {
+ return manager_->GetResourceIds(extension_id);
+ }
+
+ private:
+ ApiResourceManager<T>* manager_;
+};
+
+class SocketAsyncApiFunction : public AsyncApiFunction {
+ public:
+ SocketAsyncApiFunction();
+
+ protected:
+ virtual ~SocketAsyncApiFunction();
+
+ // AsyncApiFunction:
+ virtual bool PrePrepare() OVERRIDE;
+ virtual bool Respond() OVERRIDE;
+
+ virtual scoped_ptr<SocketResourceManagerInterface>
+ CreateSocketResourceManager();
+
+ int AddSocket(Socket* socket);
+ Socket* GetSocket(int api_resource_id);
+ void RemoveSocket(int api_resource_id);
+ base::hash_set<int>* GetSocketIds();
+
+ private:
+ scoped_ptr<SocketResourceManagerInterface> manager_;
+};
+
+class SocketExtensionWithDnsLookupFunction : public SocketAsyncApiFunction {
+ protected:
+ SocketExtensionWithDnsLookupFunction();
+ virtual ~SocketExtensionWithDnsLookupFunction();
+
+ // AsyncApiFunction:
+ virtual bool PrePrepare() OVERRIDE;
+
+ void StartDnsLookup(const std::string& hostname);
+ virtual void AfterDnsLookup(int lookup_result) = 0;
+
+ std::string resolved_address_;
+
+ private:
+ void OnDnsLookup(int resolve_result);
+
+ // Weak pointer to the resource context.
+ content::ResourceContext* resource_context_;
+
+ scoped_ptr<net::HostResolver::RequestHandle> request_handle_;
+ scoped_ptr<net::AddressList> addresses_;
+};
+
+class SocketCreateFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.create", SOCKET_CREATE)
+
+ SocketCreateFunction();
+
+ protected:
+ virtual ~SocketCreateFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(SocketUnitTest, Create);
+ enum SocketType { kSocketTypeInvalid = -1, kSocketTypeTCP, kSocketTypeUDP };
+
+ scoped_ptr<core_api::socket::Create::Params> params_;
+ SocketType socket_type_;
+};
+
+class SocketDestroyFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.destroy", SOCKET_DESTROY)
+
+ protected:
+ virtual ~SocketDestroyFunction() {}
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ int socket_id_;
+};
+
+class SocketConnectFunction : public SocketExtensionWithDnsLookupFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.connect", SOCKET_CONNECT)
+
+ SocketConnectFunction();
+
+ protected:
+ virtual ~SocketConnectFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ // SocketExtensionWithDnsLookupFunction:
+ virtual void AfterDnsLookup(int lookup_result) OVERRIDE;
+
+ private:
+ void StartConnect();
+ void OnConnect(int result);
+
+ int socket_id_;
+ std::string hostname_;
+ int port_;
+ Socket* socket_;
+};
+
+class SocketDisconnectFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.disconnect", SOCKET_DISCONNECT)
+
+ protected:
+ virtual ~SocketDisconnectFunction() {}
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ int socket_id_;
+};
+
+class SocketBindFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.bind", SOCKET_BIND)
+
+ protected:
+ virtual ~SocketBindFunction() {}
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ int socket_id_;
+ std::string address_;
+ int port_;
+};
+
+class SocketListenFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.listen", SOCKET_LISTEN)
+
+ SocketListenFunction();
+
+ protected:
+ virtual ~SocketListenFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<core_api::socket::Listen::Params> params_;
+};
+
+class SocketAcceptFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.accept", SOCKET_ACCEPT)
+
+ SocketAcceptFunction();
+
+ protected:
+ virtual ~SocketAcceptFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ private:
+ void OnAccept(int result_code, net::TCPClientSocket* socket);
+
+ scoped_ptr<core_api::socket::Accept::Params> params_;
+};
+
+class SocketReadFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.read", SOCKET_READ)
+
+ SocketReadFunction();
+
+ protected:
+ virtual ~SocketReadFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+ void OnCompleted(int result, scoped_refptr<net::IOBuffer> io_buffer);
+
+ private:
+ scoped_ptr<core_api::socket::Read::Params> params_;
+};
+
+class SocketWriteFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.write", SOCKET_WRITE)
+
+ SocketWriteFunction();
+
+ protected:
+ virtual ~SocketWriteFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+ void OnCompleted(int result);
+
+ private:
+ int socket_id_;
+ scoped_refptr<net::IOBuffer> io_buffer_;
+ size_t io_buffer_size_;
+};
+
+class SocketRecvFromFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.recvFrom", SOCKET_RECVFROM)
+
+ SocketRecvFromFunction();
+
+ protected:
+ virtual ~SocketRecvFromFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+ void OnCompleted(int result,
+ scoped_refptr<net::IOBuffer> io_buffer,
+ const std::string& address,
+ int port);
+
+ private:
+ scoped_ptr<core_api::socket::RecvFrom::Params> params_;
+};
+
+class SocketSendToFunction : public SocketExtensionWithDnsLookupFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.sendTo", SOCKET_SENDTO)
+
+ SocketSendToFunction();
+
+ protected:
+ virtual ~SocketSendToFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+ void OnCompleted(int result);
+
+ // SocketExtensionWithDnsLookupFunction:
+ virtual void AfterDnsLookup(int lookup_result) OVERRIDE;
+
+ private:
+ void StartSendTo();
+
+ int socket_id_;
+ scoped_refptr<net::IOBuffer> io_buffer_;
+ size_t io_buffer_size_;
+ std::string hostname_;
+ int port_;
+ Socket* socket_;
+};
+
+class SocketSetKeepAliveFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.setKeepAlive", SOCKET_SETKEEPALIVE)
+
+ SocketSetKeepAliveFunction();
+
+ protected:
+ virtual ~SocketSetKeepAliveFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<core_api::socket::SetKeepAlive::Params> params_;
+};
+
+class SocketSetNoDelayFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.setNoDelay", SOCKET_SETNODELAY)
+
+ SocketSetNoDelayFunction();
+
+ protected:
+ virtual ~SocketSetNoDelayFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<core_api::socket::SetNoDelay::Params> params_;
+};
+
+class SocketGetInfoFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.getInfo", SOCKET_GETINFO)
+
+ SocketGetInfoFunction();
+
+ protected:
+ virtual ~SocketGetInfoFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<core_api::socket::GetInfo::Params> params_;
+};
+
+class SocketGetNetworkListFunction : public AsyncExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.getNetworkList", SOCKET_GETNETWORKLIST)
+
+ protected:
+ virtual ~SocketGetNetworkListFunction() {}
+ virtual bool RunImpl() OVERRIDE;
+
+ private:
+ void GetNetworkListOnFileThread();
+ void HandleGetNetworkListError();
+ void SendResponseOnUIThread(const net::NetworkInterfaceList& interface_list);
+};
+
+class SocketJoinGroupFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.joinGroup", SOCKET_MULTICAST_JOIN_GROUP)
+
+ SocketJoinGroupFunction();
+
+ protected:
+ virtual ~SocketJoinGroupFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<core_api::socket::JoinGroup::Params> params_;
+};
+
+class SocketLeaveGroupFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.leaveGroup", SOCKET_MULTICAST_LEAVE_GROUP)
+
+ SocketLeaveGroupFunction();
+
+ protected:
+ virtual ~SocketLeaveGroupFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<core_api::socket::LeaveGroup::Params> params_;
+};
+
+class SocketSetMulticastTimeToLiveFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.setMulticastTimeToLive",
+ SOCKET_MULTICAST_SET_TIME_TO_LIVE)
+
+ SocketSetMulticastTimeToLiveFunction();
+
+ protected:
+ virtual ~SocketSetMulticastTimeToLiveFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<core_api::socket::SetMulticastTimeToLive::Params> params_;
+};
+
+class SocketSetMulticastLoopbackModeFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.setMulticastLoopbackMode",
+ SOCKET_MULTICAST_SET_LOOPBACK_MODE)
+
+ SocketSetMulticastLoopbackModeFunction();
+
+ protected:
+ virtual ~SocketSetMulticastLoopbackModeFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<core_api::socket::SetMulticastLoopbackMode::Params> params_;
+};
+
+class SocketGetJoinedGroupsFunction : public SocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("socket.getJoinedGroups",
+ SOCKET_MULTICAST_GET_JOINED_GROUPS)
+
+ SocketGetJoinedGroupsFunction();
+
+ protected:
+ virtual ~SocketGetJoinedGroupsFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<core_api::socket::GetJoinedGroups::Params> params_;
+};
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_API_SOCKET_SOCKET_API_H_
diff --git a/extensions/browser/api/socket/tcp_socket.cc b/extensions/browser/api/socket/tcp_socket.cc
new file mode 100644
index 0000000..49f17dc
--- /dev/null
+++ b/extensions/browser/api/socket/tcp_socket.cc
@@ -0,0 +1,329 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/api/socket/tcp_socket.h"
+
+#include "extensions/browser/api/api_resource.h"
+#include "net/base/address_list.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/base/rand_callback.h"
+#include "net/socket/tcp_client_socket.h"
+
+namespace extensions {
+
+const char kTCPSocketTypeInvalidError[] =
+ "Cannot call both connect and listen on the same socket.";
+const char kSocketListenError[] = "Could not listen on the specified port.";
+
+static base::LazyInstance<
+ BrowserContextKeyedAPIFactory<ApiResourceManager<ResumableTCPSocket> > >
+ g_factory = LAZY_INSTANCE_INITIALIZER;
+
+// static
+template <>
+BrowserContextKeyedAPIFactory<ApiResourceManager<ResumableTCPSocket> >*
+ApiResourceManager<ResumableTCPSocket>::GetFactoryInstance() {
+ return g_factory.Pointer();
+}
+
+static base::LazyInstance<BrowserContextKeyedAPIFactory<
+ ApiResourceManager<ResumableTCPServerSocket> > > g_server_factory =
+ LAZY_INSTANCE_INITIALIZER;
+
+// static
+template <>
+BrowserContextKeyedAPIFactory<ApiResourceManager<ResumableTCPServerSocket> >*
+ApiResourceManager<ResumableTCPServerSocket>::GetFactoryInstance() {
+ return g_server_factory.Pointer();
+}
+
+TCPSocket::TCPSocket(const std::string& owner_extension_id)
+ : Socket(owner_extension_id), socket_mode_(UNKNOWN) {}
+
+TCPSocket::TCPSocket(net::TCPClientSocket* tcp_client_socket,
+ const std::string& owner_extension_id,
+ bool is_connected)
+ : Socket(owner_extension_id),
+ socket_(tcp_client_socket),
+ socket_mode_(CLIENT) {
+ this->is_connected_ = is_connected;
+}
+
+TCPSocket::TCPSocket(net::TCPServerSocket* tcp_server_socket,
+ const std::string& owner_extension_id)
+ : Socket(owner_extension_id),
+ server_socket_(tcp_server_socket),
+ socket_mode_(SERVER) {}
+
+// static
+TCPSocket* TCPSocket::CreateSocketForTesting(
+ net::TCPClientSocket* tcp_client_socket,
+ const std::string& owner_extension_id,
+ bool is_connected) {
+ return new TCPSocket(tcp_client_socket, owner_extension_id, is_connected);
+}
+
+// static
+TCPSocket* TCPSocket::CreateServerSocketForTesting(
+ net::TCPServerSocket* tcp_server_socket,
+ const std::string& owner_extension_id) {
+ return new TCPSocket(tcp_server_socket, owner_extension_id);
+}
+
+TCPSocket::~TCPSocket() { Disconnect(); }
+
+void TCPSocket::Connect(const std::string& address,
+ int port,
+ const CompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+
+ if (socket_mode_ == SERVER || !connect_callback_.is_null()) {
+ callback.Run(net::ERR_CONNECTION_FAILED);
+ return;
+ }
+ DCHECK(!server_socket_.get());
+ socket_mode_ = CLIENT;
+ connect_callback_ = callback;
+
+ int result = net::ERR_CONNECTION_FAILED;
+ do {
+ if (is_connected_)
+ break;
+
+ net::AddressList address_list;
+ if (!StringAndPortToAddressList(address, port, &address_list)) {
+ result = net::ERR_ADDRESS_INVALID;
+ break;
+ }
+
+ socket_.reset(
+ new net::TCPClientSocket(address_list, NULL, net::NetLog::Source()));
+
+ connect_callback_ = callback;
+ result = socket_->Connect(
+ base::Bind(&TCPSocket::OnConnectComplete, base::Unretained(this)));
+ } while (false);
+
+ if (result != net::ERR_IO_PENDING)
+ OnConnectComplete(result);
+}
+
+void TCPSocket::Disconnect() {
+ is_connected_ = false;
+ if (socket_.get())
+ socket_->Disconnect();
+ server_socket_.reset(NULL);
+ connect_callback_.Reset();
+ read_callback_.Reset();
+ accept_callback_.Reset();
+ accept_socket_.reset(NULL);
+}
+
+int TCPSocket::Bind(const std::string& address, int port) {
+ return net::ERR_FAILED;
+}
+
+void TCPSocket::Read(int count, const ReadCompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+
+ if (socket_mode_ != CLIENT) {
+ callback.Run(net::ERR_FAILED, NULL);
+ return;
+ }
+
+ if (!read_callback_.is_null()) {
+ callback.Run(net::ERR_IO_PENDING, NULL);
+ return;
+ }
+
+ if (count < 0) {
+ callback.Run(net::ERR_INVALID_ARGUMENT, NULL);
+ return;
+ }
+
+ if (!socket_.get() || !IsConnected()) {
+ callback.Run(net::ERR_SOCKET_NOT_CONNECTED, NULL);
+ return;
+ }
+
+ read_callback_ = callback;
+ scoped_refptr<net::IOBuffer> io_buffer = new net::IOBuffer(count);
+ int result = socket_->Read(
+ io_buffer.get(),
+ count,
+ base::Bind(
+ &TCPSocket::OnReadComplete, base::Unretained(this), io_buffer));
+
+ if (result != net::ERR_IO_PENDING)
+ OnReadComplete(io_buffer, result);
+}
+
+void TCPSocket::RecvFrom(int count,
+ const RecvFromCompletionCallback& callback) {
+ callback.Run(net::ERR_FAILED, NULL, NULL, 0);
+}
+
+void TCPSocket::SendTo(scoped_refptr<net::IOBuffer> io_buffer,
+ int byte_count,
+ const std::string& address,
+ int port,
+ const CompletionCallback& callback) {
+ callback.Run(net::ERR_FAILED);
+}
+
+bool TCPSocket::SetKeepAlive(bool enable, int delay) {
+ if (!socket_.get())
+ return false;
+ return socket_->SetKeepAlive(enable, delay);
+}
+
+bool TCPSocket::SetNoDelay(bool no_delay) {
+ if (!socket_.get())
+ return false;
+ return socket_->SetNoDelay(no_delay);
+}
+
+int TCPSocket::Listen(const std::string& address,
+ int port,
+ int backlog,
+ std::string* error_msg) {
+ if (socket_mode_ == CLIENT) {
+ *error_msg = kTCPSocketTypeInvalidError;
+ return net::ERR_NOT_IMPLEMENTED;
+ }
+ DCHECK(!socket_.get());
+ socket_mode_ = SERVER;
+
+ scoped_ptr<net::IPEndPoint> bind_address(new net::IPEndPoint());
+ if (!StringAndPortToIPEndPoint(address, port, bind_address.get()))
+ return net::ERR_INVALID_ARGUMENT;
+
+ if (!server_socket_.get()) {
+ server_socket_.reset(new net::TCPServerSocket(NULL, net::NetLog::Source()));
+ }
+ int result = server_socket_->Listen(*bind_address, backlog);
+ if (result)
+ *error_msg = kSocketListenError;
+ return result;
+}
+
+void TCPSocket::Accept(const AcceptCompletionCallback& callback) {
+ if (socket_mode_ != SERVER || !server_socket_.get()) {
+ callback.Run(net::ERR_FAILED, NULL);
+ return;
+ }
+
+ // Limits to only 1 blocked accept call.
+ if (!accept_callback_.is_null()) {
+ callback.Run(net::ERR_FAILED, NULL);
+ return;
+ }
+
+ int result = server_socket_->Accept(
+ &accept_socket_,
+ base::Bind(&TCPSocket::OnAccept, base::Unretained(this)));
+ if (result == net::ERR_IO_PENDING) {
+ accept_callback_ = callback;
+ } else if (result == net::OK) {
+ accept_callback_ = callback;
+ this->OnAccept(result);
+ } else {
+ callback.Run(result, NULL);
+ }
+}
+
+bool TCPSocket::IsConnected() {
+ RefreshConnectionStatus();
+ return is_connected_;
+}
+
+bool TCPSocket::GetPeerAddress(net::IPEndPoint* address) {
+ if (!socket_.get())
+ return false;
+ return !socket_->GetPeerAddress(address);
+}
+
+bool TCPSocket::GetLocalAddress(net::IPEndPoint* address) {
+ if (socket_.get()) {
+ return !socket_->GetLocalAddress(address);
+ } else if (server_socket_.get()) {
+ return !server_socket_->GetLocalAddress(address);
+ } else {
+ return false;
+ }
+}
+
+Socket::SocketType TCPSocket::GetSocketType() const { return Socket::TYPE_TCP; }
+
+int TCPSocket::WriteImpl(net::IOBuffer* io_buffer,
+ int io_buffer_size,
+ const net::CompletionCallback& callback) {
+ if (socket_mode_ != CLIENT)
+ return net::ERR_FAILED;
+ else if (!socket_.get() || !IsConnected())
+ return net::ERR_SOCKET_NOT_CONNECTED;
+ else
+ return socket_->Write(io_buffer, io_buffer_size, callback);
+}
+
+void TCPSocket::RefreshConnectionStatus() {
+ if (!is_connected_)
+ return;
+ if (server_socket_)
+ return;
+ if (!socket_->IsConnected()) {
+ Disconnect();
+ }
+}
+
+void TCPSocket::OnConnectComplete(int result) {
+ DCHECK(!connect_callback_.is_null());
+ DCHECK(!is_connected_);
+ is_connected_ = result == net::OK;
+ connect_callback_.Run(result);
+ connect_callback_.Reset();
+}
+
+void TCPSocket::OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer,
+ int result) {
+ DCHECK(!read_callback_.is_null());
+ read_callback_.Run(result, io_buffer);
+ read_callback_.Reset();
+}
+
+void TCPSocket::OnAccept(int result) {
+ DCHECK(!accept_callback_.is_null());
+ if (result == net::OK && accept_socket_.get()) {
+ accept_callback_.Run(
+ result, static_cast<net::TCPClientSocket*>(accept_socket_.release()));
+ } else {
+ accept_callback_.Run(result, NULL);
+ }
+ accept_callback_.Reset();
+}
+
+ResumableTCPSocket::ResumableTCPSocket(const std::string& owner_extension_id)
+ : TCPSocket(owner_extension_id),
+ persistent_(false),
+ buffer_size_(0),
+ paused_(false) {}
+
+ResumableTCPSocket::ResumableTCPSocket(net::TCPClientSocket* tcp_client_socket,
+ const std::string& owner_extension_id,
+ bool is_connected)
+ : TCPSocket(tcp_client_socket, owner_extension_id, is_connected),
+ persistent_(false),
+ buffer_size_(0),
+ paused_(false) {}
+
+bool ResumableTCPSocket::IsPersistent() const { return persistent(); }
+
+ResumableTCPServerSocket::ResumableTCPServerSocket(
+ const std::string& owner_extension_id)
+ : TCPSocket(owner_extension_id), persistent_(false), paused_(false) {}
+
+bool ResumableTCPServerSocket::IsPersistent() const { return persistent(); }
+
+} // namespace extensions
diff --git a/extensions/browser/api/socket/tcp_socket.h b/extensions/browser/api/socket/tcp_socket.h
new file mode 100644
index 0000000..fbb8c54
--- /dev/null
+++ b/extensions/browser/api/socket/tcp_socket.h
@@ -0,0 +1,173 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_API_SOCKET_TCP_SOCKET_H_
+#define EXTENSIONS_BROWSER_API_SOCKET_TCP_SOCKET_H_
+
+#include <string>
+
+#include "extensions/browser/api/socket/socket.h"
+
+// This looks like it should be forward-declarable, but it does some tricky
+// moves that make it easier to just include it.
+#include "net/socket/tcp_client_socket.h"
+#include "net/socket/tcp_server_socket.h"
+
+namespace net {
+class Socket;
+}
+
+namespace extensions {
+
+class TCPSocket : public Socket {
+ public:
+ explicit TCPSocket(const std::string& owner_extension_id);
+ TCPSocket(net::TCPClientSocket* tcp_client_socket,
+ const std::string& owner_extension_id,
+ bool is_connected = false);
+
+ virtual ~TCPSocket();
+
+ virtual void Connect(const std::string& address,
+ int port,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual void Disconnect() OVERRIDE;
+ virtual int Bind(const std::string& address, int port) OVERRIDE;
+ virtual void Read(int count, const ReadCompletionCallback& callback) OVERRIDE;
+ virtual void RecvFrom(int count,
+ const RecvFromCompletionCallback& callback) OVERRIDE;
+ virtual void SendTo(scoped_refptr<net::IOBuffer> io_buffer,
+ int byte_count,
+ const std::string& address,
+ int port,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual bool SetKeepAlive(bool enable, int delay) OVERRIDE;
+ virtual bool SetNoDelay(bool no_delay) OVERRIDE;
+ virtual int Listen(const std::string& address,
+ int port,
+ int backlog,
+ std::string* error_msg) OVERRIDE;
+ virtual void Accept(const AcceptCompletionCallback& callback) OVERRIDE;
+
+ virtual bool IsConnected() OVERRIDE;
+
+ virtual bool GetPeerAddress(net::IPEndPoint* address) OVERRIDE;
+ virtual bool GetLocalAddress(net::IPEndPoint* address) OVERRIDE;
+ virtual Socket::SocketType GetSocketType() const OVERRIDE;
+
+ static TCPSocket* CreateSocketForTesting(
+ net::TCPClientSocket* tcp_client_socket,
+ const std::string& owner_extension_id,
+ bool is_connected = false);
+ static TCPSocket* CreateServerSocketForTesting(
+ net::TCPServerSocket* tcp_server_socket,
+ const std::string& owner_extension_id);
+
+ protected:
+ virtual int WriteImpl(net::IOBuffer* io_buffer,
+ int io_buffer_size,
+ const net::CompletionCallback& callback) OVERRIDE;
+
+ private:
+ void RefreshConnectionStatus();
+ void OnConnectComplete(int result);
+ void OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer, int result);
+ void OnAccept(int result);
+
+ TCPSocket(net::TCPServerSocket* tcp_server_socket,
+ const std::string& owner_extension_id);
+
+ scoped_ptr<net::TCPClientSocket> socket_;
+ scoped_ptr<net::TCPServerSocket> server_socket_;
+
+ enum SocketMode { UNKNOWN = 0, CLIENT, SERVER, };
+ SocketMode socket_mode_;
+
+ CompletionCallback connect_callback_;
+
+ ReadCompletionCallback read_callback_;
+
+ scoped_ptr<net::StreamSocket> accept_socket_;
+ AcceptCompletionCallback accept_callback_;
+};
+
+// TCP Socket instances from the "sockets.tcp" namespace. These are regular
+// socket objects with additional properties related to the behavior defined in
+// the "sockets.tcp" namespace.
+class ResumableTCPSocket : public TCPSocket {
+ public:
+ explicit ResumableTCPSocket(const std::string& owner_extension_id);
+ explicit ResumableTCPSocket(net::TCPClientSocket* tcp_client_socket,
+ const std::string& owner_extension_id,
+ bool is_connected);
+
+ // Overriden from ApiResource
+ virtual bool IsPersistent() const OVERRIDE;
+
+ const std::string& name() const { return name_; }
+ void set_name(const std::string& name) { name_ = name; }
+
+ bool persistent() const { return persistent_; }
+ void set_persistent(bool persistent) { persistent_ = persistent; }
+
+ int buffer_size() const { return buffer_size_; }
+ void set_buffer_size(int buffer_size) { buffer_size_ = buffer_size; }
+
+ bool paused() const { return paused_; }
+ void set_paused(bool paused) { paused_ = paused; }
+
+ private:
+ friend class ApiResourceManager<ResumableTCPSocket>;
+ static const char* service_name() { return "ResumableTCPSocketManager"; }
+
+ // Application-defined string - see sockets_tcp.idl.
+ std::string name_;
+ // Flag indicating whether the socket is left open when the application is
+ // suspended - see sockets_tcp.idl.
+ bool persistent_;
+ // The size of the buffer used to receive data - see sockets_tcp.idl.
+ int buffer_size_;
+ // Flag indicating whether a connected socket blocks its peer from sending
+ // more data - see sockets_tcp.idl.
+ bool paused_;
+};
+
+// TCP Socket instances from the "sockets.tcpServer" namespace. These are
+// regular socket objects with additional properties related to the behavior
+// defined in the "sockets.tcpServer" namespace.
+class ResumableTCPServerSocket : public TCPSocket {
+ public:
+ explicit ResumableTCPServerSocket(const std::string& owner_extension_id);
+
+ // Overriden from ApiResource
+ virtual bool IsPersistent() const OVERRIDE;
+
+ const std::string& name() const { return name_; }
+ void set_name(const std::string& name) { name_ = name; }
+
+ bool persistent() const { return persistent_; }
+ void set_persistent(bool persistent) { persistent_ = persistent; }
+
+ bool paused() const { return paused_; }
+ void set_paused(bool paused) { paused_ = paused; }
+
+ private:
+ friend class ApiResourceManager<ResumableTCPServerSocket>;
+ static const char* service_name() {
+ return "ResumableTCPServerSocketManager";
+ }
+
+ // Application-defined string - see sockets_tcp_server.idl.
+ std::string name_;
+ // Flag indicating whether the socket is left open when the application is
+ // suspended - see sockets_tcp_server.idl.
+ bool persistent_;
+ // Flag indicating whether a connected socket blocks its peer from sending
+ // more data - see sockets_tcp_server.idl.
+ bool paused_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_API_SOCKET_TCP_SOCKET_H_
diff --git a/extensions/browser/api/socket/udp_socket.cc b/extensions/browser/api/socket/udp_socket.cc
new file mode 100644
index 0000000..8e36500
--- /dev/null
+++ b/extensions/browser/api/socket/udp_socket.cc
@@ -0,0 +1,296 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/api/socket/udp_socket.h"
+
+#include <algorithm>
+
+#include "extensions/browser/api/api_resource.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/udp/datagram_socket.h"
+#include "net/udp/udp_client_socket.h"
+
+namespace extensions {
+
+static base::LazyInstance<
+ BrowserContextKeyedAPIFactory<ApiResourceManager<ResumableUDPSocket> > >
+ g_factory = LAZY_INSTANCE_INITIALIZER;
+
+// static
+template <>
+BrowserContextKeyedAPIFactory<ApiResourceManager<ResumableUDPSocket> >*
+ApiResourceManager<ResumableUDPSocket>::GetFactoryInstance() {
+ return g_factory.Pointer();
+}
+
+UDPSocket::UDPSocket(const std::string& owner_extension_id)
+ : Socket(owner_extension_id),
+ socket_(net::DatagramSocket::DEFAULT_BIND,
+ net::RandIntCallback(),
+ NULL,
+ net::NetLog::Source()) {}
+
+UDPSocket::~UDPSocket() { Disconnect(); }
+
+void UDPSocket::Connect(const std::string& address,
+ int port,
+ const CompletionCallback& callback) {
+ int result = net::ERR_CONNECTION_FAILED;
+ do {
+ if (is_connected_)
+ break;
+
+ net::IPEndPoint ip_end_point;
+ if (!StringAndPortToIPEndPoint(address, port, &ip_end_point)) {
+ result = net::ERR_ADDRESS_INVALID;
+ break;
+ }
+
+ result = socket_.Connect(ip_end_point);
+ is_connected_ = (result == net::OK);
+ } while (false);
+
+ callback.Run(result);
+}
+
+int UDPSocket::Bind(const std::string& address, int port) {
+ if (IsBound())
+ return net::ERR_CONNECTION_FAILED;
+
+ net::IPEndPoint ip_end_point;
+ if (!StringAndPortToIPEndPoint(address, port, &ip_end_point))
+ return net::ERR_INVALID_ARGUMENT;
+
+ return socket_.Bind(ip_end_point);
+}
+
+void UDPSocket::Disconnect() {
+ is_connected_ = false;
+ socket_.Close();
+ read_callback_.Reset();
+ recv_from_callback_.Reset();
+ send_to_callback_.Reset();
+ multicast_groups_.clear();
+}
+
+void UDPSocket::Read(int count, const ReadCompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+
+ if (!read_callback_.is_null()) {
+ callback.Run(net::ERR_IO_PENDING, NULL);
+ return;
+ } else {
+ read_callback_ = callback;
+ }
+
+ int result = net::ERR_FAILED;
+ scoped_refptr<net::IOBuffer> io_buffer;
+ do {
+ if (count < 0) {
+ result = net::ERR_INVALID_ARGUMENT;
+ break;
+ }
+
+ if (!socket_.is_connected()) {
+ result = net::ERR_SOCKET_NOT_CONNECTED;
+ break;
+ }
+
+ io_buffer = new net::IOBuffer(count);
+ result = socket_.Read(
+ io_buffer.get(),
+ count,
+ base::Bind(
+ &UDPSocket::OnReadComplete, base::Unretained(this), io_buffer));
+ } while (false);
+
+ if (result != net::ERR_IO_PENDING)
+ OnReadComplete(io_buffer, result);
+}
+
+int UDPSocket::WriteImpl(net::IOBuffer* io_buffer,
+ int io_buffer_size,
+ const net::CompletionCallback& callback) {
+ if (!socket_.is_connected())
+ return net::ERR_SOCKET_NOT_CONNECTED;
+ else
+ return socket_.Write(io_buffer, io_buffer_size, callback);
+}
+
+void UDPSocket::RecvFrom(int count,
+ const RecvFromCompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+
+ if (!recv_from_callback_.is_null()) {
+ callback.Run(net::ERR_IO_PENDING, NULL, std::string(), 0);
+ return;
+ } else {
+ recv_from_callback_ = callback;
+ }
+
+ int result = net::ERR_FAILED;
+ scoped_refptr<net::IOBuffer> io_buffer;
+ scoped_refptr<IPEndPoint> address;
+ do {
+ if (count < 0) {
+ result = net::ERR_INVALID_ARGUMENT;
+ break;
+ }
+
+ if (!socket_.is_connected()) {
+ result = net::ERR_SOCKET_NOT_CONNECTED;
+ break;
+ }
+
+ io_buffer = new net::IOBuffer(count);
+ address = new IPEndPoint();
+ result = socket_.RecvFrom(io_buffer.get(),
+ count,
+ &address->data,
+ base::Bind(&UDPSocket::OnRecvFromComplete,
+ base::Unretained(this),
+ io_buffer,
+ address));
+ } while (false);
+
+ if (result != net::ERR_IO_PENDING)
+ OnRecvFromComplete(io_buffer, address, result);
+}
+
+void UDPSocket::SendTo(scoped_refptr<net::IOBuffer> io_buffer,
+ int byte_count,
+ const std::string& address,
+ int port,
+ const CompletionCallback& callback) {
+ DCHECK(!callback.is_null());
+
+ if (!send_to_callback_.is_null()) {
+ // TODO(penghuang): Put requests in a pending queue to support multiple
+ // sendTo calls.
+ callback.Run(net::ERR_IO_PENDING);
+ return;
+ } else {
+ send_to_callback_ = callback;
+ }
+
+ int result = net::ERR_FAILED;
+ do {
+ net::IPEndPoint ip_end_point;
+ if (!StringAndPortToIPEndPoint(address, port, &ip_end_point)) {
+ result = net::ERR_ADDRESS_INVALID;
+ break;
+ }
+
+ if (!socket_.is_connected()) {
+ result = net::ERR_SOCKET_NOT_CONNECTED;
+ break;
+ }
+
+ result = socket_.SendTo(
+ io_buffer.get(),
+ byte_count,
+ ip_end_point,
+ base::Bind(&UDPSocket::OnSendToComplete, base::Unretained(this)));
+ } while (false);
+
+ if (result != net::ERR_IO_PENDING)
+ OnSendToComplete(result);
+}
+
+bool UDPSocket::IsConnected() { return is_connected_; }
+
+bool UDPSocket::GetPeerAddress(net::IPEndPoint* address) {
+ return !socket_.GetPeerAddress(address);
+}
+
+bool UDPSocket::GetLocalAddress(net::IPEndPoint* address) {
+ return !socket_.GetLocalAddress(address);
+}
+
+Socket::SocketType UDPSocket::GetSocketType() const { return Socket::TYPE_UDP; }
+
+void UDPSocket::OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer,
+ int result) {
+ DCHECK(!read_callback_.is_null());
+ read_callback_.Run(result, io_buffer);
+ read_callback_.Reset();
+}
+
+void UDPSocket::OnRecvFromComplete(scoped_refptr<net::IOBuffer> io_buffer,
+ scoped_refptr<IPEndPoint> address,
+ int result) {
+ DCHECK(!recv_from_callback_.is_null());
+ std::string ip;
+ int port = 0;
+ if (result > 0 && address.get()) {
+ IPEndPointToStringAndPort(address->data, &ip, &port);
+ }
+ recv_from_callback_.Run(result, io_buffer, ip, port);
+ recv_from_callback_.Reset();
+}
+
+void UDPSocket::OnSendToComplete(int result) {
+ DCHECK(!send_to_callback_.is_null());
+ send_to_callback_.Run(result);
+ send_to_callback_.Reset();
+}
+
+bool UDPSocket::IsBound() { return socket_.is_connected(); }
+
+int UDPSocket::JoinGroup(const std::string& address) {
+ net::IPAddressNumber ip;
+ if (!net::ParseIPLiteralToNumber(address, &ip))
+ return net::ERR_ADDRESS_INVALID;
+
+ std::string normalized_address = net::IPAddressToString(ip);
+ std::vector<std::string>::iterator find_result = std::find(
+ multicast_groups_.begin(), multicast_groups_.end(), normalized_address);
+ if (find_result != multicast_groups_.end())
+ return net::ERR_ADDRESS_INVALID;
+
+ int rv = socket_.JoinGroup(ip);
+ if (rv == 0)
+ multicast_groups_.push_back(normalized_address);
+ return rv;
+}
+
+int UDPSocket::LeaveGroup(const std::string& address) {
+ net::IPAddressNumber ip;
+ if (!net::ParseIPLiteralToNumber(address, &ip))
+ return net::ERR_ADDRESS_INVALID;
+
+ std::string normalized_address = net::IPAddressToString(ip);
+ std::vector<std::string>::iterator find_result = std::find(
+ multicast_groups_.begin(), multicast_groups_.end(), normalized_address);
+ if (find_result == multicast_groups_.end())
+ return net::ERR_ADDRESS_INVALID;
+
+ int rv = socket_.LeaveGroup(ip);
+ if (rv == 0)
+ multicast_groups_.erase(find_result);
+ return rv;
+}
+
+int UDPSocket::SetMulticastTimeToLive(int ttl) {
+ return socket_.SetMulticastTimeToLive(ttl);
+}
+
+int UDPSocket::SetMulticastLoopbackMode(bool loopback) {
+ return socket_.SetMulticastLoopbackMode(loopback);
+}
+
+const std::vector<std::string>& UDPSocket::GetJoinedGroups() const {
+ return multicast_groups_;
+}
+
+ResumableUDPSocket::ResumableUDPSocket(const std::string& owner_extension_id)
+ : UDPSocket(owner_extension_id),
+ persistent_(false),
+ buffer_size_(0),
+ paused_(false) {}
+
+bool ResumableUDPSocket::IsPersistent() const { return persistent(); }
+
+} // namespace extensions
diff --git a/extensions/browser/api/socket/udp_socket.h b/extensions/browser/api/socket/udp_socket.h
new file mode 100644
index 0000000..7c1450f
--- /dev/null
+++ b/extensions/browser/api/socket/udp_socket.h
@@ -0,0 +1,117 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_API_SOCKET_UDP_SOCKET_H_
+#define EXTENSIONS_BROWSER_API_SOCKET_UDP_SOCKET_H_
+
+#include <string>
+#include <vector>
+
+#include "extensions/browser/api/socket/socket.h"
+#include "net/udp/udp_socket.h"
+
+namespace extensions {
+
+class UDPSocket : public Socket {
+ public:
+ explicit UDPSocket(const std::string& owner_extension_id);
+ virtual ~UDPSocket();
+
+ virtual void Connect(const std::string& address,
+ int port,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual void Disconnect() OVERRIDE;
+ virtual int Bind(const std::string& address, int port) OVERRIDE;
+ virtual void Read(int count, const ReadCompletionCallback& callback) OVERRIDE;
+ virtual void RecvFrom(int count,
+ const RecvFromCompletionCallback& callback) OVERRIDE;
+ virtual void SendTo(scoped_refptr<net::IOBuffer> io_buffer,
+ int byte_count,
+ const std::string& address,
+ int port,
+ const CompletionCallback& callback) OVERRIDE;
+
+ virtual bool IsConnected() OVERRIDE;
+
+ virtual bool GetPeerAddress(net::IPEndPoint* address) OVERRIDE;
+ virtual bool GetLocalAddress(net::IPEndPoint* address) OVERRIDE;
+ virtual Socket::SocketType GetSocketType() const OVERRIDE;
+
+ bool IsBound();
+
+ int JoinGroup(const std::string& address);
+ int LeaveGroup(const std::string& address);
+
+ int SetMulticastTimeToLive(int ttl);
+ int SetMulticastLoopbackMode(bool loopback);
+
+ const std::vector<std::string>& GetJoinedGroups() const;
+
+ protected:
+ virtual int WriteImpl(net::IOBuffer* io_buffer,
+ int io_buffer_size,
+ const net::CompletionCallback& callback) OVERRIDE;
+
+ private:
+ // Make net::IPEndPoint can be refcounted
+ typedef base::RefCountedData<net::IPEndPoint> IPEndPoint;
+
+ void OnReadComplete(scoped_refptr<net::IOBuffer> io_buffer, int result);
+ void OnRecvFromComplete(scoped_refptr<net::IOBuffer> io_buffer,
+ scoped_refptr<IPEndPoint> address,
+ int result);
+ void OnSendToComplete(int result);
+
+ net::UDPSocket socket_;
+
+ ReadCompletionCallback read_callback_;
+
+ RecvFromCompletionCallback recv_from_callback_;
+
+ CompletionCallback send_to_callback_;
+
+ std::vector<std::string> multicast_groups_;
+};
+
+// UDP Socket instances from the "sockets.udp" namespace. These are regular
+// socket objects with additional properties related to the behavior defined in
+// the "sockets.udp" namespace.
+class ResumableUDPSocket : public UDPSocket {
+ public:
+ explicit ResumableUDPSocket(const std::string& owner_extension_id);
+
+ // Overriden from ApiResource
+ virtual bool IsPersistent() const OVERRIDE;
+
+ const std::string& name() const { return name_; }
+ void set_name(const std::string& name) { name_ = name; }
+
+ bool persistent() const { return persistent_; }
+ void set_persistent(bool persistent) { persistent_ = persistent; }
+
+ int buffer_size() const { return buffer_size_; }
+ void set_buffer_size(int buffer_size) { buffer_size_ = buffer_size; }
+
+ bool paused() const { return paused_; }
+ void set_paused(bool paused) { paused_ = paused; }
+
+ private:
+ friend class ApiResourceManager<ResumableUDPSocket>;
+ static const char* service_name() { return "ResumableUDPSocketManager"; }
+
+ // Application-defined string - see sockets_udp.idl.
+ std::string name_;
+ // Flag indicating whether the socket is left open when the application is
+ // suspended - see sockets_udp.idl.
+ bool persistent_;
+ // The size of the buffer used to receive data - see sockets_udp.idl.
+ int buffer_size_;
+ // Flag indicating whether a connected socket blocks its peer from sending
+ // more data - see sockets_udp.idl.
+ bool paused_;
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_API_SOCKET_UDP_SOCKET_H_
diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp/OWNERS b/extensions/browser/api/sockets_tcp/sockets_tcp/OWNERS
new file mode 100644
index 0000000..3e30c82
--- /dev/null
+++ b/extensions/browser/api/sockets_tcp/sockets_tcp/OWNERS
@@ -0,0 +1 @@
+rpaquay@chromium.org
diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc b/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc
new file mode 100644
index 0000000..fabb950
--- /dev/null
+++ b/extensions/browser/api/sockets_tcp/sockets_tcp_api.cc
@@ -0,0 +1,445 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/api/sockets_tcp/sockets_tcp_api.h"
+
+#include "chrome/common/extensions/api/sockets/sockets_manifest_data.h"
+#include "content/public/common/socket_permission_request.h"
+#include "extensions/browser/api/socket/tcp_socket.h"
+#include "extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h"
+#include "net/base/net_errors.h"
+
+using extensions::ResumableTCPSocket;
+using extensions::core_api::sockets_tcp::SocketInfo;
+using extensions::core_api::sockets_tcp::SocketProperties;
+
+namespace {
+
+const char kSocketNotFoundError[] = "Socket not found";
+const char kPermissionError[] = "Does not have permission";
+
+linked_ptr<SocketInfo> CreateSocketInfo(int socket_id,
+ ResumableTCPSocket* socket) {
+ linked_ptr<SocketInfo> socket_info(new SocketInfo());
+ // This represents what we know about the socket, and does not call through
+ // to the system.
+ socket_info->socket_id = socket_id;
+ if (!socket->name().empty()) {
+ socket_info->name.reset(new std::string(socket->name()));
+ }
+ socket_info->persistent = socket->persistent();
+ if (socket->buffer_size() > 0) {
+ socket_info->buffer_size.reset(new int(socket->buffer_size()));
+ }
+ socket_info->paused = socket->paused();
+ socket_info->connected = socket->IsConnected();
+
+ // Grab the local address as known by the OS.
+ net::IPEndPoint localAddress;
+ if (socket->GetLocalAddress(&localAddress)) {
+ socket_info->local_address.reset(
+ new std::string(localAddress.ToStringWithoutPort()));
+ socket_info->local_port.reset(new int(localAddress.port()));
+ }
+
+ // Grab the peer address as known by the OS. This and the call below will
+ // always succeed while the socket is connected, even if the socket has
+ // been remotely closed by the peer; only reading the socket will reveal
+ // that it should be closed locally.
+ net::IPEndPoint peerAddress;
+ if (socket->GetPeerAddress(&peerAddress)) {
+ socket_info->peer_address.reset(
+ new std::string(peerAddress.ToStringWithoutPort()));
+ socket_info->peer_port.reset(new int(peerAddress.port()));
+ }
+
+ return socket_info;
+}
+
+void SetSocketProperties(ResumableTCPSocket* socket,
+ SocketProperties* properties) {
+ if (properties->name.get()) {
+ socket->set_name(*properties->name.get());
+ }
+ if (properties->persistent.get()) {
+ socket->set_persistent(*properties->persistent.get());
+ }
+ if (properties->buffer_size.get()) {
+ // buffer size is validated when issuing the actual Recv operation
+ // on the socket.
+ socket->set_buffer_size(*properties->buffer_size.get());
+ }
+}
+
+} // namespace
+
+namespace extensions {
+namespace core_api {
+
+using content::SocketPermissionRequest;
+
+TCPSocketAsyncApiFunction::~TCPSocketAsyncApiFunction() {}
+
+scoped_ptr<SocketResourceManagerInterface>
+TCPSocketAsyncApiFunction::CreateSocketResourceManager() {
+ return scoped_ptr<SocketResourceManagerInterface>(
+ new SocketResourceManager<ResumableTCPSocket>()).Pass();
+}
+
+ResumableTCPSocket* TCPSocketAsyncApiFunction::GetTcpSocket(int socket_id) {
+ return static_cast<ResumableTCPSocket*>(GetSocket(socket_id));
+}
+
+TCPSocketExtensionWithDnsLookupFunction::
+ ~TCPSocketExtensionWithDnsLookupFunction() {}
+
+scoped_ptr<SocketResourceManagerInterface>
+TCPSocketExtensionWithDnsLookupFunction::CreateSocketResourceManager() {
+ return scoped_ptr<SocketResourceManagerInterface>(
+ new SocketResourceManager<ResumableTCPSocket>()).Pass();
+}
+
+ResumableTCPSocket* TCPSocketExtensionWithDnsLookupFunction::GetTcpSocket(
+ int socket_id) {
+ return static_cast<ResumableTCPSocket*>(GetSocket(socket_id));
+}
+
+SocketsTcpCreateFunction::SocketsTcpCreateFunction() {}
+
+SocketsTcpCreateFunction::~SocketsTcpCreateFunction() {}
+
+bool SocketsTcpCreateFunction::Prepare() {
+ params_ = sockets_tcp::Create::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsTcpCreateFunction::Work() {
+ ResumableTCPSocket* socket = new ResumableTCPSocket(extension_->id());
+
+ sockets_tcp::SocketProperties* properties = params_.get()->properties.get();
+ if (properties) {
+ SetSocketProperties(socket, properties);
+ }
+
+ sockets_tcp::CreateInfo create_info;
+ create_info.socket_id = AddSocket(socket);
+ results_ = sockets_tcp::Create::Results::Create(create_info);
+}
+
+SocketsTcpUpdateFunction::SocketsTcpUpdateFunction() {}
+
+SocketsTcpUpdateFunction::~SocketsTcpUpdateFunction() {}
+
+bool SocketsTcpUpdateFunction::Prepare() {
+ params_ = sockets_tcp::Update::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsTcpUpdateFunction::Work() {
+ ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ SetSocketProperties(socket, &params_.get()->properties);
+ results_ = sockets_tcp::Update::Results::Create();
+}
+
+SocketsTcpSetPausedFunction::SocketsTcpSetPausedFunction()
+ : socket_event_dispatcher_(NULL) {}
+
+SocketsTcpSetPausedFunction::~SocketsTcpSetPausedFunction() {}
+
+bool SocketsTcpSetPausedFunction::Prepare() {
+ params_ = core_api::sockets_tcp::SetPaused::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+
+ socket_event_dispatcher_ = TCPSocketEventDispatcher::Get(browser_context());
+ DCHECK(socket_event_dispatcher_)
+ << "There is no socket event dispatcher. "
+ "If this assertion is failing during a test, then it is likely that "
+ "TestExtensionSystem is failing to provide an instance of "
+ "TCPSocketEventDispatcher.";
+ return socket_event_dispatcher_ != NULL;
+}
+
+void SocketsTcpSetPausedFunction::Work() {
+ ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ if (socket->paused() != params_->paused) {
+ socket->set_paused(params_->paused);
+ if (socket->IsConnected() && !params_->paused) {
+ socket_event_dispatcher_->OnSocketResume(extension_->id(),
+ params_->socket_id);
+ }
+ }
+
+ results_ = sockets_tcp::SetPaused::Results::Create();
+}
+
+SocketsTcpSetKeepAliveFunction::SocketsTcpSetKeepAliveFunction() {}
+
+SocketsTcpSetKeepAliveFunction::~SocketsTcpSetKeepAliveFunction() {}
+
+bool SocketsTcpSetKeepAliveFunction::Prepare() {
+ params_ = core_api::sockets_tcp::SetKeepAlive::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsTcpSetKeepAliveFunction::Work() {
+ ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ int delay = params_->delay ? *params_->delay.get() : 0;
+
+ bool success = socket->SetKeepAlive(params_->enable, delay);
+ int net_result = (success ? net::OK : net::ERR_FAILED);
+ if (net_result != net::OK)
+ error_ = net::ErrorToString(net_result);
+ results_ = sockets_tcp::SetKeepAlive::Results::Create(net_result);
+}
+
+SocketsTcpSetNoDelayFunction::SocketsTcpSetNoDelayFunction() {}
+
+SocketsTcpSetNoDelayFunction::~SocketsTcpSetNoDelayFunction() {}
+
+bool SocketsTcpSetNoDelayFunction::Prepare() {
+ params_ = core_api::sockets_tcp::SetNoDelay::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsTcpSetNoDelayFunction::Work() {
+ ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ bool success = socket->SetNoDelay(params_->no_delay);
+ int net_result = (success ? net::OK : net::ERR_FAILED);
+ if (net_result != net::OK)
+ error_ = net::ErrorToString(net_result);
+ results_ = sockets_tcp::SetNoDelay::Results::Create(net_result);
+}
+
+SocketsTcpConnectFunction::SocketsTcpConnectFunction()
+ : socket_event_dispatcher_(NULL) {}
+
+SocketsTcpConnectFunction::~SocketsTcpConnectFunction() {}
+
+bool SocketsTcpConnectFunction::Prepare() {
+ params_ = sockets_tcp::Connect::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+
+ socket_event_dispatcher_ = TCPSocketEventDispatcher::Get(browser_context());
+ DCHECK(socket_event_dispatcher_)
+ << "There is no socket event dispatcher. "
+ "If this assertion is failing during a test, then it is likely that "
+ "TestExtensionSystem is failing to provide an instance of "
+ "TCPSocketEventDispatcher.";
+ return socket_event_dispatcher_ != NULL;
+}
+
+void SocketsTcpConnectFunction::AsyncWorkStart() {
+ ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ AsyncWorkCompleted();
+ return;
+ }
+
+ content::SocketPermissionRequest param(SocketPermissionRequest::TCP_CONNECT,
+ params_->peer_address,
+ params_->peer_port);
+ if (!SocketsManifestData::CheckRequest(GetExtension(), param)) {
+ error_ = kPermissionError;
+ AsyncWorkCompleted();
+ return;
+ }
+
+ StartDnsLookup(params_->peer_address);
+}
+
+void SocketsTcpConnectFunction::AfterDnsLookup(int lookup_result) {
+ if (lookup_result == net::OK) {
+ StartConnect();
+ } else {
+ OnCompleted(lookup_result);
+ }
+}
+
+void SocketsTcpConnectFunction::StartConnect() {
+ ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ AsyncWorkCompleted();
+ return;
+ }
+
+ socket->Connect(resolved_address_,
+ params_->peer_port,
+ base::Bind(&SocketsTcpConnectFunction::OnCompleted, this));
+}
+
+void SocketsTcpConnectFunction::OnCompleted(int net_result) {
+ if (net_result == net::OK) {
+ socket_event_dispatcher_->OnSocketConnect(extension_->id(),
+ params_->socket_id);
+ }
+
+ if (net_result != net::OK)
+ error_ = net::ErrorToString(net_result);
+ results_ = sockets_tcp::Connect::Results::Create(net_result);
+ AsyncWorkCompleted();
+}
+
+SocketsTcpDisconnectFunction::SocketsTcpDisconnectFunction() {}
+
+SocketsTcpDisconnectFunction::~SocketsTcpDisconnectFunction() {}
+
+bool SocketsTcpDisconnectFunction::Prepare() {
+ params_ = sockets_tcp::Disconnect::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsTcpDisconnectFunction::Work() {
+ ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ socket->Disconnect();
+ results_ = sockets_tcp::Disconnect::Results::Create();
+}
+
+SocketsTcpSendFunction::SocketsTcpSendFunction() : io_buffer_size_(0) {}
+
+SocketsTcpSendFunction::~SocketsTcpSendFunction() {}
+
+bool SocketsTcpSendFunction::Prepare() {
+ params_ = sockets_tcp::Send::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ io_buffer_size_ = params_->data.size();
+ io_buffer_ = new net::WrappedIOBuffer(params_->data.data());
+ return true;
+}
+
+void SocketsTcpSendFunction::AsyncWorkStart() {
+ ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ AsyncWorkCompleted();
+ return;
+ }
+
+ socket->Write(io_buffer_,
+ io_buffer_size_,
+ base::Bind(&SocketsTcpSendFunction::OnCompleted, this));
+}
+
+void SocketsTcpSendFunction::OnCompleted(int net_result) {
+ if (net_result >= net::OK) {
+ SetSendResult(net::OK, net_result);
+ } else {
+ SetSendResult(net_result, -1);
+ }
+}
+
+void SocketsTcpSendFunction::SetSendResult(int net_result, int bytes_sent) {
+ CHECK(net_result <= net::OK) << "Network status code must be <= net::OK";
+
+ sockets_tcp::SendInfo send_info;
+ send_info.result_code = net_result;
+ if (net_result == net::OK) {
+ send_info.bytes_sent.reset(new int(bytes_sent));
+ }
+
+ if (net_result != net::OK)
+ error_ = net::ErrorToString(net_result);
+ results_ = sockets_tcp::Send::Results::Create(send_info);
+ AsyncWorkCompleted();
+}
+
+SocketsTcpCloseFunction::SocketsTcpCloseFunction() {}
+
+SocketsTcpCloseFunction::~SocketsTcpCloseFunction() {}
+
+bool SocketsTcpCloseFunction::Prepare() {
+ params_ = sockets_tcp::Close::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsTcpCloseFunction::Work() {
+ ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ RemoveSocket(params_->socket_id);
+ results_ = sockets_tcp::Close::Results::Create();
+}
+
+SocketsTcpGetInfoFunction::SocketsTcpGetInfoFunction() {}
+
+SocketsTcpGetInfoFunction::~SocketsTcpGetInfoFunction() {}
+
+bool SocketsTcpGetInfoFunction::Prepare() {
+ params_ = sockets_tcp::GetInfo::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsTcpGetInfoFunction::Work() {
+ ResumableTCPSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ linked_ptr<sockets_tcp::SocketInfo> socket_info =
+ CreateSocketInfo(params_->socket_id, socket);
+ results_ = sockets_tcp::GetInfo::Results::Create(*socket_info);
+}
+
+SocketsTcpGetSocketsFunction::SocketsTcpGetSocketsFunction() {}
+
+SocketsTcpGetSocketsFunction::~SocketsTcpGetSocketsFunction() {}
+
+bool SocketsTcpGetSocketsFunction::Prepare() { return true; }
+
+void SocketsTcpGetSocketsFunction::Work() {
+ std::vector<linked_ptr<sockets_tcp::SocketInfo> > socket_infos;
+ base::hash_set<int>* resource_ids = GetSocketIds();
+ if (resource_ids != NULL) {
+ for (base::hash_set<int>::iterator it = resource_ids->begin();
+ it != resource_ids->end();
+ ++it) {
+ int socket_id = *it;
+ ResumableTCPSocket* socket = GetTcpSocket(socket_id);
+ if (socket) {
+ socket_infos.push_back(CreateSocketInfo(socket_id, socket));
+ }
+ }
+ }
+ results_ = sockets_tcp::GetSockets::Results::Create(socket_infos);
+}
+
+} // namespace core_api
+} // namespace extensions
diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp_api.h b/extensions/browser/api/sockets_tcp/sockets_tcp_api.h
new file mode 100644
index 0000000..8195ca4
--- /dev/null
+++ b/extensions/browser/api/sockets_tcp/sockets_tcp_api.h
@@ -0,0 +1,244 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_API_SOCKETS_TCP_SOCKETS_TCP_API_H_
+#define EXTENSIONS_BROWSER_API_SOCKETS_TCP_SOCKETS_TCP_API_H_
+
+#include "extensions/browser/api/socket/socket_api.h"
+#include "extensions/common/api/sockets_tcp.h"
+
+namespace extensions {
+class ResumableTCPSocket;
+}
+
+namespace extensions {
+namespace core_api {
+
+class TCPSocketEventDispatcher;
+
+class TCPSocketAsyncApiFunction : public SocketAsyncApiFunction {
+ protected:
+ virtual ~TCPSocketAsyncApiFunction();
+
+ virtual scoped_ptr<SocketResourceManagerInterface>
+ CreateSocketResourceManager() OVERRIDE;
+
+ ResumableTCPSocket* GetTcpSocket(int socket_id);
+};
+
+class TCPSocketExtensionWithDnsLookupFunction
+ : public SocketExtensionWithDnsLookupFunction {
+ protected:
+ virtual ~TCPSocketExtensionWithDnsLookupFunction();
+
+ virtual scoped_ptr<SocketResourceManagerInterface>
+ CreateSocketResourceManager() OVERRIDE;
+
+ ResumableTCPSocket* GetTcpSocket(int socket_id);
+};
+
+class SocketsTcpCreateFunction : public TCPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcp.create", SOCKETS_TCP_CREATE)
+
+ SocketsTcpCreateFunction();
+
+ protected:
+ virtual ~SocketsTcpCreateFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(SocketsTcpUnitTest, Create);
+ scoped_ptr<sockets_tcp::Create::Params> params_;
+};
+
+class SocketsTcpUpdateFunction : public TCPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcp.update", SOCKETS_TCP_UPDATE)
+
+ SocketsTcpUpdateFunction();
+
+ protected:
+ virtual ~SocketsTcpUpdateFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_tcp::Update::Params> params_;
+};
+
+class SocketsTcpSetPausedFunction : public TCPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcp.setPaused", SOCKETS_TCP_SETPAUSED)
+
+ SocketsTcpSetPausedFunction();
+
+ protected:
+ virtual ~SocketsTcpSetPausedFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_tcp::SetPaused::Params> params_;
+ TCPSocketEventDispatcher* socket_event_dispatcher_;
+};
+
+class SocketsTcpSetKeepAliveFunction : public TCPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcp.setKeepAlive",
+ SOCKETS_TCP_SETKEEPALIVE)
+
+ SocketsTcpSetKeepAliveFunction();
+
+ protected:
+ virtual ~SocketsTcpSetKeepAliveFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_tcp::SetKeepAlive::Params> params_;
+};
+
+class SocketsTcpSetNoDelayFunction : public TCPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcp.setNoDelay", SOCKETS_TCP_SETNODELAY)
+
+ SocketsTcpSetNoDelayFunction();
+
+ protected:
+ virtual ~SocketsTcpSetNoDelayFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_tcp::SetNoDelay::Params> params_;
+};
+
+class SocketsTcpConnectFunction
+ : public TCPSocketExtensionWithDnsLookupFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcp.connect", SOCKETS_TCP_CONNECT)
+
+ SocketsTcpConnectFunction();
+
+ protected:
+ virtual ~SocketsTcpConnectFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ // SocketExtensionWithDnsLookupFunction:
+ virtual void AfterDnsLookup(int lookup_result) OVERRIDE;
+
+ private:
+ void StartConnect();
+ void OnCompleted(int net_result);
+
+ scoped_ptr<sockets_tcp::Connect::Params> params_;
+ TCPSocketEventDispatcher* socket_event_dispatcher_;
+};
+
+class SocketsTcpDisconnectFunction : public TCPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcp.disconnect", SOCKETS_TCP_DISCONNECT)
+
+ SocketsTcpDisconnectFunction();
+
+ protected:
+ virtual ~SocketsTcpDisconnectFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_tcp::Disconnect::Params> params_;
+};
+
+class SocketsTcpSendFunction : public TCPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcp.send", SOCKETS_TCP_SEND)
+
+ SocketsTcpSendFunction();
+
+ protected:
+ virtual ~SocketsTcpSendFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ private:
+ void OnCompleted(int net_result);
+ void SetSendResult(int net_result, int bytes_sent);
+
+ scoped_ptr<sockets_tcp::Send::Params> params_;
+ scoped_refptr<net::IOBuffer> io_buffer_;
+ size_t io_buffer_size_;
+};
+
+class SocketsTcpCloseFunction : public TCPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcp.close", SOCKETS_TCP_CLOSE)
+
+ SocketsTcpCloseFunction();
+
+ protected:
+ virtual ~SocketsTcpCloseFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_tcp::Close::Params> params_;
+};
+
+class SocketsTcpGetInfoFunction : public TCPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcp.getInfo", SOCKETS_TCP_GETINFO)
+
+ SocketsTcpGetInfoFunction();
+
+ protected:
+ virtual ~SocketsTcpGetInfoFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_tcp::GetInfo::Params> params_;
+};
+
+class SocketsTcpGetSocketsFunction : public TCPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcp.getSockets", SOCKETS_TCP_GETSOCKETS)
+
+ SocketsTcpGetSocketsFunction();
+
+ protected:
+ virtual ~SocketsTcpGetSocketsFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+};
+
+} // namespace core_api
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_API_SOCKETS_TCP_SOCKETS_TCP_API_H_
diff --git a/extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.cc b/extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.cc
new file mode 100644
index 0000000..11d4cee
--- /dev/null
+++ b/extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.cc
@@ -0,0 +1,198 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h"
+
+#include "extensions/browser/api/socket/tcp_socket.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/extensions_browser_client.h"
+#include "net/base/net_errors.h"
+
+namespace {
+int kDefaultBufferSize = 4096;
+}
+
+namespace extensions {
+namespace core_api {
+
+using content::BrowserThread;
+
+static base::LazyInstance<
+ BrowserContextKeyedAPIFactory<TCPSocketEventDispatcher> > g_factory =
+ LAZY_INSTANCE_INITIALIZER;
+
+// static
+BrowserContextKeyedAPIFactory<TCPSocketEventDispatcher>*
+TCPSocketEventDispatcher::GetFactoryInstance() {
+ return g_factory.Pointer();
+}
+
+// static
+TCPSocketEventDispatcher* TCPSocketEventDispatcher::Get(
+ content::BrowserContext* context) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ return BrowserContextKeyedAPIFactory<TCPSocketEventDispatcher>::Get(context);
+}
+
+TCPSocketEventDispatcher::TCPSocketEventDispatcher(
+ content::BrowserContext* context)
+ : thread_id_(Socket::kThreadId), browser_context_(context) {
+ ApiResourceManager<ResumableTCPSocket>* manager =
+ ApiResourceManager<ResumableTCPSocket>::Get(browser_context_);
+ DCHECK(manager)
+ << "There is no socket manager. "
+ "If this assertion is failing during a test, then it is likely that "
+ "TestExtensionSystem is failing to provide an instance of "
+ "ApiResourceManager<ResumableTCPSocket>.";
+ sockets_ = manager->data_;
+}
+
+TCPSocketEventDispatcher::~TCPSocketEventDispatcher() {}
+
+TCPSocketEventDispatcher::ReadParams::ReadParams() {}
+
+TCPSocketEventDispatcher::ReadParams::~ReadParams() {}
+
+void TCPSocketEventDispatcher::OnSocketConnect(const std::string& extension_id,
+ int socket_id) {
+ DCHECK(BrowserThread::CurrentlyOn(thread_id_));
+
+ StartSocketRead(extension_id, socket_id);
+}
+
+void TCPSocketEventDispatcher::OnSocketResume(const std::string& extension_id,
+ int socket_id) {
+ DCHECK(BrowserThread::CurrentlyOn(thread_id_));
+
+ StartSocketRead(extension_id, socket_id);
+}
+
+void TCPSocketEventDispatcher::StartSocketRead(const std::string& extension_id,
+ int socket_id) {
+ DCHECK(BrowserThread::CurrentlyOn(thread_id_));
+
+ ReadParams params;
+ params.thread_id = thread_id_;
+ params.browser_context_id = browser_context_;
+ params.extension_id = extension_id;
+ params.sockets = sockets_;
+ params.socket_id = socket_id;
+
+ StartRead(params);
+}
+
+// static
+void TCPSocketEventDispatcher::StartRead(const ReadParams& params) {
+ DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
+
+ ResumableTCPSocket* socket =
+ params.sockets->Get(params.extension_id, params.socket_id);
+ if (!socket) {
+ // This can happen if the socket is closed while our callback is active.
+ return;
+ }
+ DCHECK(params.extension_id == socket->owner_extension_id())
+ << "Socket has wrong owner.";
+
+ // Don't start another read if the socket has been paused.
+ if (socket->paused())
+ return;
+
+ int buffer_size = socket->buffer_size();
+ if (buffer_size <= 0)
+ buffer_size = kDefaultBufferSize;
+ socket->Read(buffer_size,
+ base::Bind(&TCPSocketEventDispatcher::ReadCallback, params));
+}
+
+// static
+void TCPSocketEventDispatcher::ReadCallback(
+ const ReadParams& params,
+ int bytes_read,
+ scoped_refptr<net::IOBuffer> io_buffer) {
+ DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
+
+ // If |bytes_read| == 0, the connection has been closed by the peer.
+ // If |bytes_read| < 0, there was a network error, and |bytes_read| is a value
+ // from "net::ERR_".
+
+ if (bytes_read == 0) {
+ bytes_read = net::ERR_CONNECTION_CLOSED;
+ }
+
+ if (bytes_read > 0) {
+ // Dispatch "onReceive" event.
+ sockets_tcp::ReceiveInfo receive_info;
+ receive_info.socket_id = params.socket_id;
+ receive_info.data = std::string(io_buffer->data(), bytes_read);
+ scoped_ptr<base::ListValue> args =
+ sockets_tcp::OnReceive::Create(receive_info);
+ scoped_ptr<Event> event(
+ new Event(sockets_tcp::OnReceive::kEventName, args.Pass()));
+ PostEvent(params, event.Pass());
+
+ // Post a task to delay the read until the socket is available, as
+ // calling StartReceive at this point would error with ERR_IO_PENDING.
+ BrowserThread::PostTask(
+ params.thread_id,
+ FROM_HERE,
+ base::Bind(&TCPSocketEventDispatcher::StartRead, params));
+ } else if (bytes_read == net::ERR_IO_PENDING) {
+ // This happens when resuming a socket which already had an
+ // active "read" callback.
+ } else {
+ // Dispatch "onReceiveError" event but don't start another read to avoid
+ // potential infinite reads if we have a persistent network error.
+ sockets_tcp::ReceiveErrorInfo receive_error_info;
+ receive_error_info.socket_id = params.socket_id;
+ receive_error_info.result_code = bytes_read;
+ scoped_ptr<base::ListValue> args =
+ sockets_tcp::OnReceiveError::Create(receive_error_info);
+ scoped_ptr<Event> event(
+ new Event(sockets_tcp::OnReceiveError::kEventName, args.Pass()));
+ PostEvent(params, event.Pass());
+
+ // Since we got an error, the socket is now "paused" until the application
+ // "resumes" it.
+ ResumableTCPSocket* socket =
+ params.sockets->Get(params.extension_id, params.socket_id);
+ if (socket) {
+ socket->set_paused(true);
+ }
+ }
+}
+
+// static
+void TCPSocketEventDispatcher::PostEvent(const ReadParams& params,
+ scoped_ptr<Event> event) {
+ DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
+
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&DispatchEvent,
+ params.browser_context_id,
+ params.extension_id,
+ base::Passed(event.Pass())));
+}
+
+// static
+void TCPSocketEventDispatcher::DispatchEvent(void* browser_context_id,
+ const std::string& extension_id,
+ scoped_ptr<Event> event) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ content::BrowserContext* context =
+ reinterpret_cast<content::BrowserContext*>(browser_context_id);
+ if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context))
+ return;
+
+ EventRouter* router = ExtensionSystem::Get(context)->event_router();
+ if (router)
+ router->DispatchEventToExtension(extension_id, event.Pass());
+}
+
+} // namespace core_api
+} // namespace extensions
diff --git a/extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h b/extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h
new file mode 100644
index 0000000..0e16690
--- /dev/null
+++ b/extensions/browser/api/sockets_tcp/tcp_socket_event_dispatcher.h
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_API_SOCKETS_TCP_TCP_SOCKET_EVENT_DISPATCHER_H_
+#define EXTENSIONS_BROWSER_API_SOCKETS_TCP_TCP_SOCKET_EVENT_DISPATCHER_H_
+
+#include "extensions/browser/api/api_resource_manager.h"
+#include "extensions/browser/api/sockets_tcp/sockets_tcp_api.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace extensions {
+struct Event;
+class ResumableTCPSocket;
+}
+
+namespace extensions {
+namespace core_api {
+
+// Dispatch events related to "sockets.tcp" sockets from callback on native
+// socket instances. There is one instance per profile.
+class TCPSocketEventDispatcher
+ : public BrowserContextKeyedAPI,
+ public base::SupportsWeakPtr<TCPSocketEventDispatcher> {
+ public:
+ explicit TCPSocketEventDispatcher(content::BrowserContext* context);
+ virtual ~TCPSocketEventDispatcher();
+
+ // Socket is active, start receving from it.
+ void OnSocketConnect(const std::string& extension_id, int socket_id);
+
+ // Socket is active again, start receiving data from it.
+ void OnSocketResume(const std::string& extension_id, int socket_id);
+
+ // BrowserContextKeyedAPI implementation.
+ static BrowserContextKeyedAPIFactory<TCPSocketEventDispatcher>*
+ GetFactoryInstance();
+
+ // Convenience method to get the SocketEventDispatcher for a profile.
+ static TCPSocketEventDispatcher* Get(content::BrowserContext* context);
+
+ private:
+ typedef ApiResourceManager<ResumableTCPSocket>::ApiResourceData SocketData;
+ friend class BrowserContextKeyedAPIFactory<TCPSocketEventDispatcher>;
+ // BrowserContextKeyedAPI implementation.
+ static const char* service_name() { return "TCPSocketEventDispatcher"; }
+ static const bool kServiceHasOwnInstanceInIncognito = true;
+ static const bool kServiceIsNULLWhileTesting = true;
+
+ // base::Bind supports methods with up to 6 parameters. ReadParams is used
+ // as a workaround that limitation for invoking StartReceive.
+ struct ReadParams {
+ ReadParams();
+ ~ReadParams();
+
+ content::BrowserThread::ID thread_id;
+ void* browser_context_id;
+ std::string extension_id;
+ scoped_refptr<SocketData> sockets;
+ int socket_id;
+ };
+
+ // Start a receive and register a callback.
+ void StartSocketRead(const std::string& extension_id, int socket_id);
+
+ // Start a receive and register a callback.
+ static void StartRead(const ReadParams& params);
+
+ // Called when socket receive data.
+ static void ReadCallback(const ReadParams& params,
+ int bytes_read,
+ scoped_refptr<net::IOBuffer> io_buffer);
+
+ // Post an extension event from IO to UI thread
+ static void PostEvent(const ReadParams& params, scoped_ptr<Event> event);
+
+ // Dispatch an extension event on to EventRouter instance on UI thread.
+ static void DispatchEvent(void* browser_context_id,
+ const std::string& extension_id,
+ scoped_ptr<Event> event);
+
+ // Usually IO thread (except for unit testing).
+ content::BrowserThread::ID thread_id_;
+ content::BrowserContext* const browser_context_;
+ scoped_refptr<SocketData> sockets_;
+};
+
+} // namespace core_api
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_API_SOCKETS_TCP_TCP_SOCKET_EVENT_DISPATCHER_H_
diff --git a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server/OWNERS b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server/OWNERS
new file mode 100644
index 0000000..3e30c82
--- /dev/null
+++ b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server/OWNERS
@@ -0,0 +1 @@
+rpaquay@chromium.org
diff --git a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc
new file mode 100644
index 0000000..6adc0fc
--- /dev/null
+++ b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.cc
@@ -0,0 +1,298 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h"
+
+#include "chrome/common/extensions/api/sockets/sockets_manifest_data.h"
+#include "chrome/common/extensions/permissions/socket_permission.h"
+#include "content/public/common/socket_permission_request.h"
+#include "extensions/browser/api/socket/tcp_socket.h"
+#include "extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "net/base/net_errors.h"
+
+using content::SocketPermissionRequest;
+using extensions::ResumableTCPServerSocket;
+using extensions::core_api::sockets_tcp_server::SocketInfo;
+using extensions::core_api::sockets_tcp_server::SocketProperties;
+
+namespace {
+
+const char kSocketNotFoundError[] = "Socket not found";
+const char kPermissionError[] = "Does not have permission";
+const int kDefaultListenBacklog = SOMAXCONN;
+
+linked_ptr<SocketInfo> CreateSocketInfo(int socket_id,
+ ResumableTCPServerSocket* socket) {
+ linked_ptr<SocketInfo> socket_info(new SocketInfo());
+ // This represents what we know about the socket, and does not call through
+ // to the system.
+ socket_info->socket_id = socket_id;
+ if (!socket->name().empty()) {
+ socket_info->name.reset(new std::string(socket->name()));
+ }
+ socket_info->persistent = socket->persistent();
+ socket_info->paused = socket->paused();
+
+ // Grab the local address as known by the OS.
+ net::IPEndPoint localAddress;
+ if (socket->GetLocalAddress(&localAddress)) {
+ socket_info->local_address.reset(
+ new std::string(localAddress.ToStringWithoutPort()));
+ socket_info->local_port.reset(new int(localAddress.port()));
+ }
+
+ return socket_info;
+}
+
+void SetSocketProperties(ResumableTCPServerSocket* socket,
+ SocketProperties* properties) {
+ if (properties->name.get()) {
+ socket->set_name(*properties->name.get());
+ }
+ if (properties->persistent.get()) {
+ socket->set_persistent(*properties->persistent.get());
+ }
+}
+
+} // namespace
+
+namespace extensions {
+namespace core_api {
+
+TCPServerSocketAsyncApiFunction::~TCPServerSocketAsyncApiFunction() {}
+
+scoped_ptr<SocketResourceManagerInterface>
+TCPServerSocketAsyncApiFunction::CreateSocketResourceManager() {
+ return scoped_ptr<SocketResourceManagerInterface>(
+ new SocketResourceManager<ResumableTCPServerSocket>()).Pass();
+}
+
+ResumableTCPServerSocket* TCPServerSocketAsyncApiFunction::GetTcpSocket(
+ int socket_id) {
+ return static_cast<ResumableTCPServerSocket*>(GetSocket(socket_id));
+}
+
+SocketsTcpServerCreateFunction::SocketsTcpServerCreateFunction() {}
+
+SocketsTcpServerCreateFunction::~SocketsTcpServerCreateFunction() {}
+
+bool SocketsTcpServerCreateFunction::Prepare() {
+ params_ = sockets_tcp_server::Create::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsTcpServerCreateFunction::Work() {
+ ResumableTCPServerSocket* socket =
+ new ResumableTCPServerSocket(extension_->id());
+
+ sockets_tcp_server::SocketProperties* properties =
+ params_.get()->properties.get();
+ if (properties) {
+ SetSocketProperties(socket, properties);
+ }
+
+ sockets_tcp_server::CreateInfo create_info;
+ create_info.socket_id = AddSocket(socket);
+ results_ = sockets_tcp_server::Create::Results::Create(create_info);
+}
+
+SocketsTcpServerUpdateFunction::SocketsTcpServerUpdateFunction() {}
+
+SocketsTcpServerUpdateFunction::~SocketsTcpServerUpdateFunction() {}
+
+bool SocketsTcpServerUpdateFunction::Prepare() {
+ params_ = sockets_tcp_server::Update::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsTcpServerUpdateFunction::Work() {
+ ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ SetSocketProperties(socket, &params_.get()->properties);
+ results_ = sockets_tcp_server::Update::Results::Create();
+}
+
+SocketsTcpServerSetPausedFunction::SocketsTcpServerSetPausedFunction()
+ : socket_event_dispatcher_(NULL) {}
+
+SocketsTcpServerSetPausedFunction::~SocketsTcpServerSetPausedFunction() {}
+
+bool SocketsTcpServerSetPausedFunction::Prepare() {
+ params_ = core_api::sockets_tcp_server::SetPaused::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+
+ socket_event_dispatcher_ =
+ TCPServerSocketEventDispatcher::Get(browser_context());
+ DCHECK(socket_event_dispatcher_)
+ << "There is no socket event dispatcher. "
+ "If this assertion is failing during a test, then it is likely that "
+ "TestExtensionSystem is failing to provide an instance of "
+ "TCPServerSocketEventDispatcher.";
+ return socket_event_dispatcher_ != NULL;
+}
+
+void SocketsTcpServerSetPausedFunction::Work() {
+ ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ if (socket->paused() != params_->paused) {
+ socket->set_paused(params_->paused);
+ if (socket->IsConnected() && !params_->paused) {
+ socket_event_dispatcher_->OnServerSocketResume(extension_->id(),
+ params_->socket_id);
+ }
+ }
+
+ results_ = sockets_tcp_server::SetPaused::Results::Create();
+}
+
+SocketsTcpServerListenFunction::SocketsTcpServerListenFunction()
+ : socket_event_dispatcher_(NULL) {}
+
+SocketsTcpServerListenFunction::~SocketsTcpServerListenFunction() {}
+
+bool SocketsTcpServerListenFunction::Prepare() {
+ params_ = core_api::sockets_tcp_server::Listen::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+
+ socket_event_dispatcher_ =
+ TCPServerSocketEventDispatcher::Get(browser_context());
+ DCHECK(socket_event_dispatcher_)
+ << "There is no socket event dispatcher. "
+ "If this assertion is failing during a test, then it is likely that "
+ "TestExtensionSystem is failing to provide an instance of "
+ "TCPServerSocketEventDispatcher.";
+ return socket_event_dispatcher_ != NULL;
+}
+
+void SocketsTcpServerListenFunction::Work() {
+ ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ SocketPermissionRequest param(
+ SocketPermissionRequest::TCP_LISTEN, params_->address, params_->port);
+ if (!SocketsManifestData::CheckRequest(GetExtension(), param)) {
+ error_ = kPermissionError;
+ return;
+ }
+
+ int net_result = socket->Listen(
+ params_->address,
+ params_->port,
+ params_->backlog.get() ? *params_->backlog.get() : kDefaultListenBacklog,
+ &error_);
+
+ if (net_result != net::OK)
+ error_ = net::ErrorToString(net_result);
+
+ if (net_result == net::OK) {
+ socket_event_dispatcher_->OnServerSocketListen(extension_->id(),
+ params_->socket_id);
+ }
+
+ results_ = sockets_tcp_server::Listen::Results::Create(net_result);
+}
+
+SocketsTcpServerDisconnectFunction::SocketsTcpServerDisconnectFunction() {}
+
+SocketsTcpServerDisconnectFunction::~SocketsTcpServerDisconnectFunction() {}
+
+bool SocketsTcpServerDisconnectFunction::Prepare() {
+ params_ = sockets_tcp_server::Disconnect::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsTcpServerDisconnectFunction::Work() {
+ ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ socket->Disconnect();
+ results_ = sockets_tcp_server::Disconnect::Results::Create();
+}
+
+SocketsTcpServerCloseFunction::SocketsTcpServerCloseFunction() {}
+
+SocketsTcpServerCloseFunction::~SocketsTcpServerCloseFunction() {}
+
+bool SocketsTcpServerCloseFunction::Prepare() {
+ params_ = sockets_tcp_server::Close::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsTcpServerCloseFunction::Work() {
+ ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ RemoveSocket(params_->socket_id);
+ results_ = sockets_tcp_server::Close::Results::Create();
+}
+
+SocketsTcpServerGetInfoFunction::SocketsTcpServerGetInfoFunction() {}
+
+SocketsTcpServerGetInfoFunction::~SocketsTcpServerGetInfoFunction() {}
+
+bool SocketsTcpServerGetInfoFunction::Prepare() {
+ params_ = sockets_tcp_server::GetInfo::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsTcpServerGetInfoFunction::Work() {
+ ResumableTCPServerSocket* socket = GetTcpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ linked_ptr<sockets_tcp_server::SocketInfo> socket_info =
+ CreateSocketInfo(params_->socket_id, socket);
+ results_ = sockets_tcp_server::GetInfo::Results::Create(*socket_info);
+}
+
+SocketsTcpServerGetSocketsFunction::SocketsTcpServerGetSocketsFunction() {}
+
+SocketsTcpServerGetSocketsFunction::~SocketsTcpServerGetSocketsFunction() {}
+
+bool SocketsTcpServerGetSocketsFunction::Prepare() { return true; }
+
+void SocketsTcpServerGetSocketsFunction::Work() {
+ std::vector<linked_ptr<sockets_tcp_server::SocketInfo> > socket_infos;
+ base::hash_set<int>* resource_ids = GetSocketIds();
+ if (resource_ids != NULL) {
+ for (base::hash_set<int>::iterator it = resource_ids->begin();
+ it != resource_ids->end();
+ ++it) {
+ int socket_id = *it;
+ ResumableTCPServerSocket* socket = GetTcpSocket(socket_id);
+ if (socket) {
+ socket_infos.push_back(CreateSocketInfo(socket_id, socket));
+ }
+ }
+ }
+ results_ = sockets_tcp_server::GetSockets::Results::Create(socket_infos);
+}
+
+} // namespace core_api
+} // namespace extensions
diff --git a/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h
new file mode 100644
index 0000000..89d8cb8
--- /dev/null
+++ b/extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h
@@ -0,0 +1,178 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_API_SOCKETS_TCP_SERVER_SOCKETS_TCP_SERVER_API_H_
+#define EXTENSIONS_BROWSER_API_SOCKETS_TCP_SERVER_SOCKETS_TCP_SERVER_API_H_
+
+#include "extensions/browser/api/socket/socket_api.h"
+#include "extensions/common/api/sockets_tcp_server.h"
+
+namespace extensions {
+class ResumableTCPServerSocket;
+}
+
+namespace extensions {
+namespace core_api {
+
+class TCPServerSocketAsyncApiFunction : public SocketAsyncApiFunction {
+ protected:
+ virtual ~TCPServerSocketAsyncApiFunction();
+
+ virtual scoped_ptr<SocketResourceManagerInterface>
+ CreateSocketResourceManager() OVERRIDE;
+
+ ResumableTCPServerSocket* GetTcpSocket(int socket_id);
+};
+
+class SocketsTcpServerCreateFunction : public TCPServerSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.create",
+ SOCKETS_TCP_SERVER_CREATE)
+
+ SocketsTcpServerCreateFunction();
+
+ protected:
+ virtual ~SocketsTcpServerCreateFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(SocketsTcpServerUnitTest, Create);
+ scoped_ptr<sockets_tcp_server::Create::Params> params_;
+};
+
+class SocketsTcpServerUpdateFunction : public TCPServerSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.update",
+ SOCKETS_TCP_SERVER_UPDATE)
+
+ SocketsTcpServerUpdateFunction();
+
+ protected:
+ virtual ~SocketsTcpServerUpdateFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_tcp_server::Update::Params> params_;
+};
+
+class SocketsTcpServerSetPausedFunction
+ : public TCPServerSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.setPaused",
+ SOCKETS_TCP_SERVER_SETPAUSED)
+
+ SocketsTcpServerSetPausedFunction();
+
+ protected:
+ virtual ~SocketsTcpServerSetPausedFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_tcp_server::SetPaused::Params> params_;
+ TCPServerSocketEventDispatcher* socket_event_dispatcher_;
+};
+
+class SocketsTcpServerListenFunction : public TCPServerSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.listen",
+ SOCKETS_TCP_SERVER_LISTEN)
+
+ SocketsTcpServerListenFunction();
+
+ protected:
+ virtual ~SocketsTcpServerListenFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_tcp_server::Listen::Params> params_;
+ TCPServerSocketEventDispatcher* socket_event_dispatcher_;
+};
+
+class SocketsTcpServerDisconnectFunction
+ : public TCPServerSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.disconnect",
+ SOCKETS_TCP_SERVER_DISCONNECT)
+
+ SocketsTcpServerDisconnectFunction();
+
+ protected:
+ virtual ~SocketsTcpServerDisconnectFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_tcp_server::Disconnect::Params> params_;
+};
+
+class SocketsTcpServerCloseFunction : public TCPServerSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.close",
+ SOCKETS_TCP_SERVER_CLOSE)
+
+ SocketsTcpServerCloseFunction();
+
+ protected:
+ virtual ~SocketsTcpServerCloseFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_tcp_server::Close::Params> params_;
+};
+
+class SocketsTcpServerGetInfoFunction : public TCPServerSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.getInfo",
+ SOCKETS_TCP_SERVER_GETINFO)
+
+ SocketsTcpServerGetInfoFunction();
+
+ protected:
+ virtual ~SocketsTcpServerGetInfoFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_tcp_server::GetInfo::Params> params_;
+};
+
+class SocketsTcpServerGetSocketsFunction
+ : public TCPServerSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.tcpServer.getSockets",
+ SOCKETS_TCP_SERVER_GETSOCKETS)
+
+ SocketsTcpServerGetSocketsFunction();
+
+ protected:
+ virtual ~SocketsTcpServerGetSocketsFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+};
+
+} // namespace core_api
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_API_SOCKETS_TCP_SERVER_SOCKETS_TCP_SERVER_API_H_
diff --git a/extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc b/extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc
new file mode 100644
index 0000000..31eeeab
--- /dev/null
+++ b/extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc
@@ -0,0 +1,199 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h"
+
+#include "extensions/browser/api/socket/tcp_socket.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/extensions_browser_client.h"
+#include "net/base/net_errors.h"
+
+namespace extensions {
+namespace core_api {
+
+using content::BrowserThread;
+
+static base::LazyInstance<
+ BrowserContextKeyedAPIFactory<TCPServerSocketEventDispatcher> > g_factory =
+ LAZY_INSTANCE_INITIALIZER;
+
+// static
+BrowserContextKeyedAPIFactory<TCPServerSocketEventDispatcher>*
+TCPServerSocketEventDispatcher::GetFactoryInstance() {
+ return g_factory.Pointer();
+}
+
+// static
+TCPServerSocketEventDispatcher* TCPServerSocketEventDispatcher::Get(
+ content::BrowserContext* context) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ return BrowserContextKeyedAPIFactory<TCPServerSocketEventDispatcher>::Get(
+ context);
+}
+
+TCPServerSocketEventDispatcher::TCPServerSocketEventDispatcher(
+ content::BrowserContext* context)
+ : thread_id_(Socket::kThreadId), browser_context_(context) {
+ ApiResourceManager<ResumableTCPServerSocket>* server_manager =
+ ApiResourceManager<ResumableTCPServerSocket>::Get(browser_context_);
+ DCHECK(server_manager)
+ << "There is no server socket manager. "
+ "If this assertion is failing during a test, then it is likely that "
+ "TestExtensionSystem is failing to provide an instance of "
+ "ApiResourceManager<ResumableTCPServerSocket>.";
+ server_sockets_ = server_manager->data_;
+
+ ApiResourceManager<ResumableTCPSocket>* client_manager =
+ ApiResourceManager<ResumableTCPSocket>::Get(browser_context_);
+ DCHECK(client_manager)
+ << "There is no client socket manager. "
+ "If this assertion is failing during a test, then it is likely that "
+ "TestExtensionSystem is failing to provide an instance of "
+ "ApiResourceManager<ResumableTCPSocket>.";
+ client_sockets_ = client_manager->data_;
+}
+
+TCPServerSocketEventDispatcher::~TCPServerSocketEventDispatcher() {}
+
+TCPServerSocketEventDispatcher::AcceptParams::AcceptParams() {}
+
+TCPServerSocketEventDispatcher::AcceptParams::~AcceptParams() {}
+
+void TCPServerSocketEventDispatcher::OnServerSocketListen(
+ const std::string& extension_id,
+ int socket_id) {
+ DCHECK(BrowserThread::CurrentlyOn(thread_id_));
+
+ StartSocketAccept(extension_id, socket_id);
+}
+
+void TCPServerSocketEventDispatcher::OnServerSocketResume(
+ const std::string& extension_id,
+ int socket_id) {
+ DCHECK(BrowserThread::CurrentlyOn(thread_id_));
+
+ StartSocketAccept(extension_id, socket_id);
+}
+
+void TCPServerSocketEventDispatcher::StartSocketAccept(
+ const std::string& extension_id,
+ int socket_id) {
+ DCHECK(BrowserThread::CurrentlyOn(thread_id_));
+
+ AcceptParams params;
+ params.thread_id = thread_id_;
+ params.browser_context_id = browser_context_;
+ params.extension_id = extension_id;
+ params.server_sockets = server_sockets_;
+ params.client_sockets = client_sockets_;
+ params.socket_id = socket_id;
+
+ StartAccept(params);
+}
+
+// static
+void TCPServerSocketEventDispatcher::StartAccept(const AcceptParams& params) {
+ DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
+
+ ResumableTCPServerSocket* socket =
+ params.server_sockets->Get(params.extension_id, params.socket_id);
+ if (!socket) {
+ // This can happen if the socket is closed while our callback is active.
+ return;
+ }
+ DCHECK(params.extension_id == socket->owner_extension_id())
+ << "Socket has wrong owner.";
+
+ // Don't start another accept if the socket has been paused.
+ if (socket->paused())
+ return;
+
+ socket->Accept(
+ base::Bind(&TCPServerSocketEventDispatcher::AcceptCallback, params));
+}
+
+// static
+void TCPServerSocketEventDispatcher::AcceptCallback(
+ const AcceptParams& params,
+ int result_code,
+ net::TCPClientSocket* socket) {
+ DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
+
+ if (result_code >= 0) {
+ ResumableTCPSocket* client_socket =
+ new ResumableTCPSocket(socket, params.extension_id, true);
+ client_socket->set_paused(true);
+ int client_socket_id = params.client_sockets->Add(client_socket);
+
+ // Dispatch "onAccept" event.
+ sockets_tcp_server::AcceptInfo accept_info;
+ accept_info.socket_id = params.socket_id;
+ accept_info.client_socket_id = client_socket_id;
+ scoped_ptr<base::ListValue> args =
+ sockets_tcp_server::OnAccept::Create(accept_info);
+ scoped_ptr<Event> event(
+ new Event(sockets_tcp_server::OnAccept::kEventName, args.Pass()));
+ PostEvent(params, event.Pass());
+
+ // Post a task to delay the "accept" until the socket is available, as
+ // calling StartAccept at this point would error with ERR_IO_PENDING.
+ BrowserThread::PostTask(
+ params.thread_id,
+ FROM_HERE,
+ base::Bind(&TCPServerSocketEventDispatcher::StartAccept, params));
+ } else {
+ // Dispatch "onAcceptError" event but don't start another accept to avoid
+ // potential infinite "accepts" if we have a persistent network error.
+ sockets_tcp_server::AcceptErrorInfo accept_error_info;
+ accept_error_info.socket_id = params.socket_id;
+ accept_error_info.result_code = result_code;
+ scoped_ptr<base::ListValue> args =
+ sockets_tcp_server::OnAcceptError::Create(accept_error_info);
+ scoped_ptr<Event> event(
+ new Event(sockets_tcp_server::OnAcceptError::kEventName, args.Pass()));
+ PostEvent(params, event.Pass());
+
+ // Since we got an error, the socket is now "paused" until the application
+ // "resumes" it.
+ ResumableTCPServerSocket* socket =
+ params.server_sockets->Get(params.extension_id, params.socket_id);
+ if (socket) {
+ socket->set_paused(true);
+ }
+ }
+}
+
+// static
+void TCPServerSocketEventDispatcher::PostEvent(const AcceptParams& params,
+ scoped_ptr<Event> event) {
+ DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
+
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&DispatchEvent,
+ params.browser_context_id,
+ params.extension_id,
+ base::Passed(event.Pass())));
+}
+
+// static
+void TCPServerSocketEventDispatcher::DispatchEvent(
+ void* browser_context_id,
+ const std::string& extension_id,
+ scoped_ptr<Event> event) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ content::BrowserContext* context =
+ reinterpret_cast<content::BrowserContext*>(browser_context_id);
+ if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context))
+ return;
+ EventRouter* router = ExtensionSystem::Get(context)->event_router();
+ if (router)
+ router->DispatchEventToExtension(extension_id, event.Pass());
+}
+
+} // namespace core_api
+} // namespace extensions
diff --git a/extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h b/extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h
new file mode 100644
index 0000000..d4d604c
--- /dev/null
+++ b/extensions/browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h
@@ -0,0 +1,100 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_API_SOCKETS_TCP_SERVER_TCP_SERVER_SOCKET_EVENT_DISPATCHER_H_
+#define EXTENSIONS_BROWSER_API_SOCKETS_TCP_SERVER_TCP_SERVER_SOCKET_EVENT_DISPATCHER_H_
+
+#include "extensions/browser/api/api_resource_manager.h"
+#include "extensions/browser/api/sockets_tcp/sockets_tcp_api.h"
+#include "extensions/browser/api/sockets_tcp_server/sockets_tcp_server_api.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace extensions {
+struct Event;
+class ResumableTCPSocket;
+}
+
+namespace extensions {
+namespace core_api {
+
+// Dispatch events related to "sockets.tcp" sockets from callback on native
+// socket instances. There is one instance per profile.
+class TCPServerSocketEventDispatcher
+ : public BrowserContextKeyedAPI,
+ public base::SupportsWeakPtr<TCPServerSocketEventDispatcher> {
+ public:
+ explicit TCPServerSocketEventDispatcher(content::BrowserContext* context);
+ virtual ~TCPServerSocketEventDispatcher();
+
+ // Server socket is active, start accepting connections from it.
+ void OnServerSocketListen(const std::string& extension_id, int socket_id);
+
+ // Server socket is active again, start accepting connections from it.
+ void OnServerSocketResume(const std::string& extension_id, int socket_id);
+
+ // BrowserContextKeyedAPI implementation.
+ static BrowserContextKeyedAPIFactory<TCPServerSocketEventDispatcher>*
+ GetFactoryInstance();
+
+ // Convenience method to get the SocketEventDispatcher for a profile.
+ static TCPServerSocketEventDispatcher* Get(content::BrowserContext* context);
+
+ private:
+ typedef ApiResourceManager<ResumableTCPServerSocket>::ApiResourceData
+ ServerSocketData;
+ typedef ApiResourceManager<ResumableTCPSocket>::ApiResourceData
+ ClientSocketData;
+ friend class BrowserContextKeyedAPIFactory<TCPServerSocketEventDispatcher>;
+ // BrowserContextKeyedAPI implementation.
+ static const char* service_name() { return "TCPServerSocketEventDispatcher"; }
+ static const bool kServiceHasOwnInstanceInIncognito = true;
+ static const bool kServiceIsNULLWhileTesting = true;
+
+ // base::Bind supports methods with up to 6 parameters. AcceptParams is used
+ // as a workaround that limitation for invoking StartAccept.
+ struct AcceptParams {
+ AcceptParams();
+ ~AcceptParams();
+
+ content::BrowserThread::ID thread_id;
+ void* browser_context_id;
+ std::string extension_id;
+ scoped_refptr<ServerSocketData> server_sockets;
+ scoped_refptr<ClientSocketData> client_sockets;
+ int socket_id;
+ };
+
+ // Start an accept and register a callback.
+ void StartSocketAccept(const std::string& extension_id, int socket_id);
+
+ // Start an accept and register a callback.
+ static void StartAccept(const AcceptParams& params);
+
+ // Called when socket accepts a new connection.
+ static void AcceptCallback(const AcceptParams& params,
+ int result_code,
+ net::TCPClientSocket* socket);
+
+ // Post an extension event from |thread_id| to UI thread
+ static void PostEvent(const AcceptParams& params, scoped_ptr<Event> event);
+
+ // Dispatch an extension event on to EventRouter instance on UI thread.
+ static void DispatchEvent(void* browser_context_id,
+ const std::string& extension_id,
+ scoped_ptr<Event> event);
+
+ // Usually IO thread (except for unit testing).
+ content::BrowserThread::ID thread_id_;
+ content::BrowserContext* const browser_context_;
+ scoped_refptr<ServerSocketData> server_sockets_;
+ scoped_refptr<ClientSocketData> client_sockets_;
+};
+
+} // namespace core_api
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_API_SOCKETS_TCP_SERVER_TCP_SERVER_SOCKET_EVENT_DISPATCHER_H_
diff --git a/extensions/browser/api/sockets_udp/sockets_udp/OWNERS b/extensions/browser/api/sockets_udp/sockets_udp/OWNERS
new file mode 100644
index 0000000..3e30c82
--- /dev/null
+++ b/extensions/browser/api/sockets_udp/sockets_udp/OWNERS
@@ -0,0 +1 @@
+rpaquay@chromium.org
diff --git a/extensions/browser/api/sockets_udp/sockets_udp_api.cc b/extensions/browser/api/sockets_udp/sockets_udp_api.cc
new file mode 100644
index 0000000..9ccb4a7
--- /dev/null
+++ b/extensions/browser/api/sockets_udp/sockets_udp_api.cc
@@ -0,0 +1,505 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/api/sockets_udp/sockets_udp_api.h"
+
+#include "chrome/common/extensions/api/sockets/sockets_manifest_data.h"
+#include "content/public/common/socket_permission_request.h"
+#include "extensions/browser/api/socket/udp_socket.h"
+#include "extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h"
+#include "net/base/net_errors.h"
+
+namespace extensions {
+namespace core_api {
+
+using content::SocketPermissionRequest;
+
+const char kSocketNotFoundError[] = "Socket not found";
+const char kPermissionError[] = "App does not have permission";
+const char kWildcardAddress[] = "*";
+const int kWildcardPort = 0;
+
+UDPSocketAsyncApiFunction::~UDPSocketAsyncApiFunction() {}
+
+scoped_ptr<SocketResourceManagerInterface>
+UDPSocketAsyncApiFunction::CreateSocketResourceManager() {
+ return scoped_ptr<SocketResourceManagerInterface>(
+ new SocketResourceManager<ResumableUDPSocket>()).Pass();
+}
+
+ResumableUDPSocket* UDPSocketAsyncApiFunction::GetUdpSocket(int socket_id) {
+ return static_cast<ResumableUDPSocket*>(GetSocket(socket_id));
+}
+
+UDPSocketExtensionWithDnsLookupFunction::
+ ~UDPSocketExtensionWithDnsLookupFunction() {}
+
+scoped_ptr<SocketResourceManagerInterface>
+UDPSocketExtensionWithDnsLookupFunction::CreateSocketResourceManager() {
+ return scoped_ptr<SocketResourceManagerInterface>(
+ new SocketResourceManager<ResumableUDPSocket>()).Pass();
+}
+
+ResumableUDPSocket* UDPSocketExtensionWithDnsLookupFunction::GetUdpSocket(
+ int socket_id) {
+ return static_cast<ResumableUDPSocket*>(GetSocket(socket_id));
+}
+
+linked_ptr<sockets_udp::SocketInfo> CreateSocketInfo(
+ int socket_id,
+ ResumableUDPSocket* socket) {
+ linked_ptr<sockets_udp::SocketInfo> socket_info(
+ new sockets_udp::SocketInfo());
+ // This represents what we know about the socket, and does not call through
+ // to the system.
+ socket_info->socket_id = socket_id;
+ if (!socket->name().empty()) {
+ socket_info->name.reset(new std::string(socket->name()));
+ }
+ socket_info->persistent = socket->persistent();
+ if (socket->buffer_size() > 0) {
+ socket_info->buffer_size.reset(new int(socket->buffer_size()));
+ }
+ socket_info->paused = socket->paused();
+
+ // Grab the local address as known by the OS.
+ net::IPEndPoint localAddress;
+ if (socket->GetLocalAddress(&localAddress)) {
+ socket_info->local_address.reset(
+ new std::string(localAddress.ToStringWithoutPort()));
+ socket_info->local_port.reset(new int(localAddress.port()));
+ }
+
+ return socket_info;
+}
+
+void SetSocketProperties(ResumableUDPSocket* socket,
+ sockets_udp::SocketProperties* properties) {
+ if (properties->name.get()) {
+ socket->set_name(*properties->name.get());
+ }
+ if (properties->persistent.get()) {
+ socket->set_persistent(*properties->persistent.get());
+ }
+ if (properties->buffer_size.get()) {
+ socket->set_buffer_size(*properties->buffer_size.get());
+ }
+}
+
+SocketsUdpCreateFunction::SocketsUdpCreateFunction() {}
+
+SocketsUdpCreateFunction::~SocketsUdpCreateFunction() {}
+
+bool SocketsUdpCreateFunction::Prepare() {
+ params_ = sockets_udp::Create::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsUdpCreateFunction::Work() {
+ ResumableUDPSocket* socket = new ResumableUDPSocket(extension_->id());
+
+ sockets_udp::SocketProperties* properties = params_.get()->properties.get();
+ if (properties) {
+ SetSocketProperties(socket, properties);
+ }
+
+ sockets_udp::CreateInfo create_info;
+ create_info.socket_id = AddSocket(socket);
+ results_ = sockets_udp::Create::Results::Create(create_info);
+}
+
+SocketsUdpUpdateFunction::SocketsUdpUpdateFunction() {}
+
+SocketsUdpUpdateFunction::~SocketsUdpUpdateFunction() {}
+
+bool SocketsUdpUpdateFunction::Prepare() {
+ params_ = sockets_udp::Update::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsUdpUpdateFunction::Work() {
+ ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ SetSocketProperties(socket, &params_.get()->properties);
+ results_ = sockets_udp::Update::Results::Create();
+}
+
+SocketsUdpSetPausedFunction::SocketsUdpSetPausedFunction()
+ : socket_event_dispatcher_(NULL) {}
+
+SocketsUdpSetPausedFunction::~SocketsUdpSetPausedFunction() {}
+
+bool SocketsUdpSetPausedFunction::Prepare() {
+ params_ = core_api::sockets_udp::SetPaused::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+
+ socket_event_dispatcher_ = UDPSocketEventDispatcher::Get(browser_context());
+ DCHECK(socket_event_dispatcher_)
+ << "There is no socket event dispatcher. "
+ "If this assertion is failing during a test, then it is likely that "
+ "TestExtensionSystem is failing to provide an instance of "
+ "UDPSocketEventDispatcher.";
+ return socket_event_dispatcher_ != NULL;
+}
+
+void SocketsUdpSetPausedFunction::Work() {
+ ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ if (socket->paused() != params_->paused) {
+ socket->set_paused(params_->paused);
+ if (socket->IsBound() && !params_->paused) {
+ socket_event_dispatcher_->OnSocketResume(extension_->id(),
+ params_->socket_id);
+ }
+ }
+
+ results_ = sockets_udp::SetPaused::Results::Create();
+}
+
+SocketsUdpBindFunction::SocketsUdpBindFunction()
+ : socket_event_dispatcher_(NULL) {}
+
+SocketsUdpBindFunction::~SocketsUdpBindFunction() {}
+
+bool SocketsUdpBindFunction::Prepare() {
+ params_ = sockets_udp::Bind::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+
+ socket_event_dispatcher_ = UDPSocketEventDispatcher::Get(browser_context());
+ DCHECK(socket_event_dispatcher_)
+ << "There is no socket event dispatcher. "
+ "If this assertion is failing during a test, then it is likely that "
+ "TestExtensionSystem is failing to provide an instance of "
+ "UDPSocketEventDispatcher.";
+ return socket_event_dispatcher_ != NULL;
+}
+
+void SocketsUdpBindFunction::Work() {
+ ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ content::SocketPermissionRequest param(
+ SocketPermissionRequest::UDP_BIND, params_->address, params_->port);
+ if (!SocketsManifestData::CheckRequest(GetExtension(), param)) {
+ error_ = kPermissionError;
+ return;
+ }
+
+ int net_result = socket->Bind(params_->address, params_->port);
+ if (net_result == net::OK) {
+ socket_event_dispatcher_->OnSocketBind(extension_->id(),
+ params_->socket_id);
+ }
+
+ if (net_result != net::OK)
+ error_ = net::ErrorToString(net_result);
+ results_ = sockets_udp::Bind::Results::Create(net_result);
+}
+
+SocketsUdpSendFunction::SocketsUdpSendFunction() : io_buffer_size_(0) {}
+
+SocketsUdpSendFunction::~SocketsUdpSendFunction() {}
+
+bool SocketsUdpSendFunction::Prepare() {
+ params_ = sockets_udp::Send::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ io_buffer_size_ = params_->data.size();
+ io_buffer_ = new net::WrappedIOBuffer(params_->data.data());
+
+ return true;
+}
+
+void SocketsUdpSendFunction::AsyncWorkStart() {
+ ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ AsyncWorkCompleted();
+ return;
+ }
+
+ content::SocketPermissionRequest param(
+ SocketPermissionRequest::UDP_SEND_TO, params_->address, params_->port);
+ if (!SocketsManifestData::CheckRequest(GetExtension(), param)) {
+ error_ = kPermissionError;
+ AsyncWorkCompleted();
+ return;
+ }
+
+ StartDnsLookup(params_->address);
+}
+
+void SocketsUdpSendFunction::AfterDnsLookup(int lookup_result) {
+ if (lookup_result == net::OK) {
+ StartSendTo();
+ } else {
+ SetSendResult(lookup_result, -1);
+ }
+}
+
+void SocketsUdpSendFunction::StartSendTo() {
+ ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ AsyncWorkCompleted();
+ return;
+ }
+
+ socket->SendTo(io_buffer_,
+ io_buffer_size_,
+ resolved_address_,
+ params_->port,
+ base::Bind(&SocketsUdpSendFunction::OnCompleted, this));
+}
+
+void SocketsUdpSendFunction::OnCompleted(int net_result) {
+ if (net_result >= net::OK) {
+ SetSendResult(net::OK, net_result);
+ } else {
+ SetSendResult(net_result, -1);
+ }
+}
+
+void SocketsUdpSendFunction::SetSendResult(int net_result, int bytes_sent) {
+ CHECK(net_result <= net::OK) << "Network status code must be < 0";
+
+ sockets_udp::SendInfo send_info;
+ send_info.result_code = net_result;
+ if (net_result == net::OK) {
+ send_info.bytes_sent.reset(new int(bytes_sent));
+ }
+
+ if (net_result != net::OK)
+ error_ = net::ErrorToString(net_result);
+ results_ = sockets_udp::Send::Results::Create(send_info);
+ AsyncWorkCompleted();
+}
+
+SocketsUdpCloseFunction::SocketsUdpCloseFunction() {}
+
+SocketsUdpCloseFunction::~SocketsUdpCloseFunction() {}
+
+bool SocketsUdpCloseFunction::Prepare() {
+ params_ = sockets_udp::Close::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsUdpCloseFunction::Work() {
+ ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ socket->Disconnect();
+ RemoveSocket(params_->socket_id);
+ results_ = sockets_udp::Close::Results::Create();
+}
+
+SocketsUdpGetInfoFunction::SocketsUdpGetInfoFunction() {}
+
+SocketsUdpGetInfoFunction::~SocketsUdpGetInfoFunction() {}
+
+bool SocketsUdpGetInfoFunction::Prepare() {
+ params_ = sockets_udp::GetInfo::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsUdpGetInfoFunction::Work() {
+ ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ linked_ptr<sockets_udp::SocketInfo> socket_info =
+ CreateSocketInfo(params_->socket_id, socket);
+ results_ = sockets_udp::GetInfo::Results::Create(*socket_info);
+}
+
+SocketsUdpGetSocketsFunction::SocketsUdpGetSocketsFunction() {}
+
+SocketsUdpGetSocketsFunction::~SocketsUdpGetSocketsFunction() {}
+
+bool SocketsUdpGetSocketsFunction::Prepare() { return true; }
+
+void SocketsUdpGetSocketsFunction::Work() {
+ std::vector<linked_ptr<sockets_udp::SocketInfo> > socket_infos;
+ base::hash_set<int>* resource_ids = GetSocketIds();
+ if (resource_ids != NULL) {
+ for (base::hash_set<int>::iterator it = resource_ids->begin();
+ it != resource_ids->end();
+ ++it) {
+ int socket_id = *it;
+ ResumableUDPSocket* socket = GetUdpSocket(socket_id);
+ if (socket) {
+ socket_infos.push_back(CreateSocketInfo(socket_id, socket));
+ }
+ }
+ }
+ results_ = sockets_udp::GetSockets::Results::Create(socket_infos);
+}
+
+SocketsUdpJoinGroupFunction::SocketsUdpJoinGroupFunction() {}
+
+SocketsUdpJoinGroupFunction::~SocketsUdpJoinGroupFunction() {}
+
+bool SocketsUdpJoinGroupFunction::Prepare() {
+ params_ = sockets_udp::JoinGroup::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsUdpJoinGroupFunction::Work() {
+ ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ content::SocketPermissionRequest param(
+ SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP,
+ kWildcardAddress,
+ kWildcardPort);
+ if (!SocketsManifestData::CheckRequest(GetExtension(), param)) {
+ error_ = kPermissionError;
+ return;
+ }
+
+ int net_result = socket->JoinGroup(params_->address);
+ if (net_result != net::OK)
+ error_ = net::ErrorToString(net_result);
+ results_ = sockets_udp::JoinGroup::Results::Create(net_result);
+}
+
+SocketsUdpLeaveGroupFunction::SocketsUdpLeaveGroupFunction() {}
+
+SocketsUdpLeaveGroupFunction::~SocketsUdpLeaveGroupFunction() {}
+
+bool SocketsUdpLeaveGroupFunction::Prepare() {
+ params_ = core_api::sockets_udp::LeaveGroup::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsUdpLeaveGroupFunction::Work() {
+ ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ content::SocketPermissionRequest param(
+ SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP,
+ kWildcardAddress,
+ kWildcardPort);
+ if (!SocketsManifestData::CheckRequest(GetExtension(), param)) {
+ error_ = kPermissionError;
+ return;
+ }
+
+ int net_result = socket->LeaveGroup(params_->address);
+ if (net_result != net::OK)
+ error_ = net::ErrorToString(net_result);
+ results_ = sockets_udp::LeaveGroup::Results::Create(net_result);
+}
+
+SocketsUdpSetMulticastTimeToLiveFunction::
+ SocketsUdpSetMulticastTimeToLiveFunction() {}
+
+SocketsUdpSetMulticastTimeToLiveFunction::
+ ~SocketsUdpSetMulticastTimeToLiveFunction() {}
+
+bool SocketsUdpSetMulticastTimeToLiveFunction::Prepare() {
+ params_ =
+ core_api::sockets_udp::SetMulticastTimeToLive::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsUdpSetMulticastTimeToLiveFunction::Work() {
+ ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ int net_result = socket->SetMulticastTimeToLive(params_->ttl);
+ if (net_result != net::OK)
+ error_ = net::ErrorToString(net_result);
+ results_ = sockets_udp::SetMulticastTimeToLive::Results::Create(net_result);
+}
+
+SocketsUdpSetMulticastLoopbackModeFunction::
+ SocketsUdpSetMulticastLoopbackModeFunction() {}
+
+SocketsUdpSetMulticastLoopbackModeFunction::
+ ~SocketsUdpSetMulticastLoopbackModeFunction() {}
+
+bool SocketsUdpSetMulticastLoopbackModeFunction::Prepare() {
+ params_ =
+ core_api::sockets_udp::SetMulticastLoopbackMode::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsUdpSetMulticastLoopbackModeFunction::Work() {
+ ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ int net_result = socket->SetMulticastLoopbackMode(params_->enabled);
+ if (net_result != net::OK)
+ error_ = net::ErrorToString(net_result);
+ results_ = sockets_udp::SetMulticastLoopbackMode::Results::Create(net_result);
+}
+
+SocketsUdpGetJoinedGroupsFunction::SocketsUdpGetJoinedGroupsFunction() {}
+
+SocketsUdpGetJoinedGroupsFunction::~SocketsUdpGetJoinedGroupsFunction() {}
+
+bool SocketsUdpGetJoinedGroupsFunction::Prepare() {
+ params_ = core_api::sockets_udp::GetJoinedGroups::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(params_.get());
+ return true;
+}
+
+void SocketsUdpGetJoinedGroupsFunction::Work() {
+ ResumableUDPSocket* socket = GetUdpSocket(params_->socket_id);
+ if (!socket) {
+ error_ = kSocketNotFoundError;
+ return;
+ }
+
+ content::SocketPermissionRequest param(
+ SocketPermissionRequest::UDP_MULTICAST_MEMBERSHIP,
+ kWildcardAddress,
+ kWildcardPort);
+ if (!SocketsManifestData::CheckRequest(GetExtension(), param)) {
+ error_ = kPermissionError;
+ return;
+ }
+
+ const std::vector<std::string>& groups = socket->GetJoinedGroups();
+ results_ = sockets_udp::GetJoinedGroups::Results::Create(groups);
+}
+
+} // namespace core_api
+} // namespace extensions
diff --git a/extensions/browser/api/sockets_udp/sockets_udp_api.h b/extensions/browser/api/sockets_udp/sockets_udp_api.h
new file mode 100644
index 0000000..16e61d4
--- /dev/null
+++ b/extensions/browser/api/sockets_udp/sockets_udp_api.h
@@ -0,0 +1,279 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_API_SOCKETS_UDP_SOCKETS_UDP_API_H_
+#define EXTENSIONS_BROWSER_API_SOCKETS_UDP_SOCKETS_UDP_API_H_
+
+#include "extensions/browser/api/socket/socket_api.h"
+#include "extensions/common/api/sockets_udp.h"
+
+namespace extensions {
+class ResumableUDPSocket;
+}
+
+namespace extensions {
+namespace core_api {
+
+class UDPSocketEventDispatcher;
+
+class UDPSocketAsyncApiFunction : public SocketAsyncApiFunction {
+ protected:
+ virtual ~UDPSocketAsyncApiFunction();
+
+ virtual scoped_ptr<SocketResourceManagerInterface>
+ CreateSocketResourceManager() OVERRIDE;
+
+ ResumableUDPSocket* GetUdpSocket(int socket_id);
+};
+
+class UDPSocketExtensionWithDnsLookupFunction
+ : public SocketExtensionWithDnsLookupFunction {
+ protected:
+ virtual ~UDPSocketExtensionWithDnsLookupFunction();
+
+ virtual scoped_ptr<SocketResourceManagerInterface>
+ CreateSocketResourceManager() OVERRIDE;
+
+ ResumableUDPSocket* GetUdpSocket(int socket_id);
+};
+
+class SocketsUdpCreateFunction : public UDPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.udp.create", SOCKETS_UDP_CREATE)
+
+ SocketsUdpCreateFunction();
+
+ protected:
+ virtual ~SocketsUdpCreateFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(SocketsUdpUnitTest, Create);
+ scoped_ptr<sockets_udp::Create::Params> params_;
+};
+
+class SocketsUdpUpdateFunction : public UDPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.udp.update", SOCKETS_UDP_UPDATE)
+
+ SocketsUdpUpdateFunction();
+
+ protected:
+ virtual ~SocketsUdpUpdateFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_udp::Update::Params> params_;
+};
+
+class SocketsUdpSetPausedFunction : public UDPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.udp.setPaused", SOCKETS_UDP_SETPAUSED)
+
+ SocketsUdpSetPausedFunction();
+
+ protected:
+ virtual ~SocketsUdpSetPausedFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_udp::SetPaused::Params> params_;
+ UDPSocketEventDispatcher* socket_event_dispatcher_;
+};
+
+class SocketsUdpBindFunction : public UDPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.udp.bind", SOCKETS_UDP_BIND)
+
+ SocketsUdpBindFunction();
+
+ protected:
+ virtual ~SocketsUdpBindFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_udp::Bind::Params> params_;
+ UDPSocketEventDispatcher* socket_event_dispatcher_;
+};
+
+class SocketsUdpSendFunction : public UDPSocketExtensionWithDnsLookupFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.udp.send", SOCKETS_UDP_SEND)
+
+ SocketsUdpSendFunction();
+
+ protected:
+ virtual ~SocketsUdpSendFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+ void OnCompleted(int net_result);
+ void SetSendResult(int net_result, int bytes_sent);
+
+ // SocketExtensionWithDnsLookupFunction:
+ virtual void AfterDnsLookup(int lookup_result) OVERRIDE;
+
+ private:
+ void StartSendTo();
+
+ scoped_ptr<sockets_udp::Send::Params> params_;
+ scoped_refptr<net::IOBuffer> io_buffer_;
+ size_t io_buffer_size_;
+};
+
+class SocketsUdpCloseFunction : public UDPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.udp.close", SOCKETS_UDP_CLOSE)
+
+ SocketsUdpCloseFunction();
+
+ protected:
+ virtual ~SocketsUdpCloseFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_udp::Close::Params> params_;
+};
+
+class SocketsUdpGetInfoFunction : public UDPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.udp.getInfo", SOCKETS_UDP_GETINFO)
+
+ SocketsUdpGetInfoFunction();
+
+ protected:
+ virtual ~SocketsUdpGetInfoFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_udp::GetInfo::Params> params_;
+};
+
+class SocketsUdpGetSocketsFunction : public UDPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.udp.getSockets", SOCKETS_UDP_GETSOCKETS)
+
+ SocketsUdpGetSocketsFunction();
+
+ protected:
+ virtual ~SocketsUdpGetSocketsFunction();
+
+ // AsyncApiFunction:
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+};
+
+class SocketsUdpJoinGroupFunction : public UDPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.udp.joinGroup", SOCKETS_UDP_JOINGROUP)
+
+ SocketsUdpJoinGroupFunction();
+
+ protected:
+ virtual ~SocketsUdpJoinGroupFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_udp::JoinGroup::Params> params_;
+};
+
+class SocketsUdpLeaveGroupFunction : public UDPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.udp.leaveGroup", SOCKETS_UDP_LEAVEGROUP)
+
+ SocketsUdpLeaveGroupFunction();
+
+ protected:
+ virtual ~SocketsUdpLeaveGroupFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_udp::LeaveGroup::Params> params_;
+};
+
+class SocketsUdpSetMulticastTimeToLiveFunction
+ : public UDPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.udp.setMulticastTimeToLive",
+ SOCKETS_UDP_SETMULTICASTTIMETOLIVE)
+
+ SocketsUdpSetMulticastTimeToLiveFunction();
+
+ protected:
+ virtual ~SocketsUdpSetMulticastTimeToLiveFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_udp::SetMulticastTimeToLive::Params> params_;
+};
+
+class SocketsUdpSetMulticastLoopbackModeFunction
+ : public UDPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.udp.setMulticastLoopbackMode",
+ SOCKETS_UDP_SETMULTICASTLOOPBACKMODE)
+
+ SocketsUdpSetMulticastLoopbackModeFunction();
+
+ protected:
+ virtual ~SocketsUdpSetMulticastLoopbackModeFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_udp::SetMulticastLoopbackMode::Params> params_;
+};
+
+class SocketsUdpGetJoinedGroupsFunction : public UDPSocketAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("sockets.udp.getJoinedGroups",
+ SOCKETS_UDP_GETJOINEDGROUPS)
+
+ SocketsUdpGetJoinedGroupsFunction();
+
+ protected:
+ virtual ~SocketsUdpGetJoinedGroupsFunction();
+
+ // AsyncApiFunction
+ virtual bool Prepare() OVERRIDE;
+ virtual void Work() OVERRIDE;
+
+ private:
+ scoped_ptr<sockets_udp::GetJoinedGroups::Params> params_;
+};
+
+} // namespace core_api
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_API_SOCKETS_UDP_SOCKETS_UDP_API_H_
diff --git a/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.cc b/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.cc
new file mode 100644
index 0000000..e985dfb
--- /dev/null
+++ b/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.cc
@@ -0,0 +1,183 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h"
+
+#include "extensions/browser/api/socket/udp_socket.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/extensions_browser_client.h"
+#include "net/base/net_errors.h"
+
+namespace extensions {
+namespace core_api {
+
+using content::BrowserThread;
+
+static base::LazyInstance<
+ BrowserContextKeyedAPIFactory<UDPSocketEventDispatcher> > g_factory =
+ LAZY_INSTANCE_INITIALIZER;
+
+// static
+BrowserContextKeyedAPIFactory<UDPSocketEventDispatcher>*
+UDPSocketEventDispatcher::GetFactoryInstance() {
+ return g_factory.Pointer();
+}
+
+// static
+UDPSocketEventDispatcher* UDPSocketEventDispatcher::Get(
+ content::BrowserContext* context) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ return BrowserContextKeyedAPIFactory<UDPSocketEventDispatcher>::Get(context);
+}
+
+UDPSocketEventDispatcher::UDPSocketEventDispatcher(
+ content::BrowserContext* context)
+ : thread_id_(Socket::kThreadId), browser_context_(context) {
+ ApiResourceManager<ResumableUDPSocket>* manager =
+ ApiResourceManager<ResumableUDPSocket>::Get(browser_context_);
+ DCHECK(manager)
+ << "There is no socket manager. "
+ "If this assertion is failing during a test, then it is likely that "
+ "TestExtensionSystem is failing to provide an instance of "
+ "ApiResourceManager<ResumableUDPSocket>.";
+ sockets_ = manager->data_;
+}
+
+UDPSocketEventDispatcher::~UDPSocketEventDispatcher() {}
+
+UDPSocketEventDispatcher::ReceiveParams::ReceiveParams() {}
+
+UDPSocketEventDispatcher::ReceiveParams::~ReceiveParams() {}
+
+void UDPSocketEventDispatcher::OnSocketBind(const std::string& extension_id,
+ int socket_id) {
+ OnSocketResume(extension_id, socket_id);
+}
+
+void UDPSocketEventDispatcher::OnSocketResume(const std::string& extension_id,
+ int socket_id) {
+ DCHECK(BrowserThread::CurrentlyOn(thread_id_));
+
+ ReceiveParams params;
+ params.thread_id = thread_id_;
+ params.browser_context_id = browser_context_;
+ params.extension_id = extension_id;
+ params.sockets = sockets_;
+ params.socket_id = socket_id;
+
+ StartReceive(params);
+}
+
+/* static */
+void UDPSocketEventDispatcher::StartReceive(const ReceiveParams& params) {
+ DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
+
+ ResumableUDPSocket* socket =
+ params.sockets->Get(params.extension_id, params.socket_id);
+ if (socket == NULL) {
+ // This can happen if the socket is closed while our callback is active.
+ return;
+ }
+ DCHECK(params.extension_id == socket->owner_extension_id())
+ << "Socket has wrong owner.";
+
+ // Don't start another read if the socket has been paused.
+ if (socket->paused())
+ return;
+
+ int buffer_size = (socket->buffer_size() <= 0 ? 4096 : socket->buffer_size());
+ socket->RecvFrom(
+ buffer_size,
+ base::Bind(&UDPSocketEventDispatcher::ReceiveCallback, params));
+}
+
+/* static */
+void UDPSocketEventDispatcher::ReceiveCallback(
+ const ReceiveParams& params,
+ int bytes_read,
+ scoped_refptr<net::IOBuffer> io_buffer,
+ const std::string& address,
+ int port) {
+ DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
+
+ // If |bytes_read| == 0, the message contained no data.
+ // If |bytes_read| < 0, there was a network error, and |bytes_read| is a value
+ // from "net::ERR_".
+
+ if (bytes_read >= 0) {
+ // Dispatch "onReceive" event.
+ sockets_udp::ReceiveInfo receive_info;
+ receive_info.socket_id = params.socket_id;
+ receive_info.data = std::string(io_buffer->data(), bytes_read);
+ receive_info.remote_address = address;
+ receive_info.remote_port = port;
+ scoped_ptr<base::ListValue> args =
+ sockets_udp::OnReceive::Create(receive_info);
+ scoped_ptr<Event> event(
+ new Event(sockets_udp::OnReceive::kEventName, args.Pass()));
+ PostEvent(params, event.Pass());
+
+ // Post a task to delay the read until the socket is available, as
+ // calling StartReceive at this point would error with ERR_IO_PENDING.
+ BrowserThread::PostTask(
+ params.thread_id,
+ FROM_HERE,
+ base::Bind(&UDPSocketEventDispatcher::StartReceive, params));
+ } else if (bytes_read == net::ERR_IO_PENDING) {
+ // This happens when resuming a socket which already had an
+ // active "recv" callback.
+ } else {
+ // Dispatch "onReceiveError" event but don't start another read to avoid
+ // potential infinite reads if we have a persistent network error.
+ sockets_udp::ReceiveErrorInfo receive_error_info;
+ receive_error_info.socket_id = params.socket_id;
+ receive_error_info.result_code = bytes_read;
+ scoped_ptr<base::ListValue> args =
+ sockets_udp::OnReceiveError::Create(receive_error_info);
+ scoped_ptr<Event> event(
+ new Event(sockets_udp::OnReceiveError::kEventName, args.Pass()));
+ PostEvent(params, event.Pass());
+
+ // Since we got an error, the socket is now "paused" until the application
+ // "resumes" it.
+ ResumableUDPSocket* socket =
+ params.sockets->Get(params.extension_id, params.socket_id);
+ if (socket) {
+ socket->set_paused(true);
+ }
+ }
+}
+
+/* static */
+void UDPSocketEventDispatcher::PostEvent(const ReceiveParams& params,
+ scoped_ptr<Event> event) {
+ DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
+
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&DispatchEvent,
+ params.browser_context_id,
+ params.extension_id,
+ base::Passed(event.Pass())));
+}
+
+/*static*/
+void UDPSocketEventDispatcher::DispatchEvent(void* browser_context_id,
+ const std::string& extension_id,
+ scoped_ptr<Event> event) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ content::BrowserContext* context =
+ reinterpret_cast<content::BrowserContext*>(browser_context_id);
+ if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context))
+ return;
+ EventRouter* router = ExtensionSystem::Get(context)->event_router();
+ if (router)
+ router->DispatchEventToExtension(extension_id, event.Pass());
+}
+
+} // namespace core_api
+} // namespace extensions
diff --git a/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h b/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h
new file mode 100644
index 0000000..e42ecb4
--- /dev/null
+++ b/extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h
@@ -0,0 +1,93 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_API_SOCKETS_UDP_UDP_SOCKET_EVENT_DISPATCHER_H_
+#define EXTENSIONS_BROWSER_API_SOCKETS_UDP_UDP_SOCKET_EVENT_DISPATCHER_H_
+
+#include "extensions/browser/api/api_resource_manager.h"
+#include "extensions/browser/api/sockets_udp/sockets_udp_api.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace extensions {
+struct Event;
+class ResumableUDPSocket;
+}
+
+namespace extensions {
+namespace core_api {
+
+// Dispatch events related to "sockets.udp" sockets from callback on native
+// socket instances. There is one instance per profile.
+class UDPSocketEventDispatcher
+ : public BrowserContextKeyedAPI,
+ public base::SupportsWeakPtr<UDPSocketEventDispatcher> {
+ public:
+ explicit UDPSocketEventDispatcher(content::BrowserContext* context);
+ virtual ~UDPSocketEventDispatcher();
+
+ // Socket is active, start receving from it.
+ void OnSocketBind(const std::string& extension_id, int socket_id);
+
+ // Socket is active again, start receiving data from it.
+ void OnSocketResume(const std::string& extension_id, int socket_id);
+
+ // BrowserContextKeyedAPI implementation.
+ static BrowserContextKeyedAPIFactory<UDPSocketEventDispatcher>*
+ GetFactoryInstance();
+
+ // Convenience method to get the SocketEventDispatcher for a profile.
+ static UDPSocketEventDispatcher* Get(content::BrowserContext* context);
+
+ private:
+ typedef ApiResourceManager<ResumableUDPSocket>::ApiResourceData SocketData;
+ friend class BrowserContextKeyedAPIFactory<UDPSocketEventDispatcher>;
+ // BrowserContextKeyedAPI implementation.
+ static const char* service_name() { return "UDPSocketEventDispatcher"; }
+ static const bool kServiceHasOwnInstanceInIncognito = true;
+ static const bool kServiceIsNULLWhileTesting = true;
+
+ // base::Bind supports methods with up to 6 parameters. ReceiveParams is used
+ // as a workaround that limitation for invoking StartReceive.
+ struct ReceiveParams {
+ ReceiveParams();
+ ~ReceiveParams();
+
+ content::BrowserThread::ID thread_id;
+ void* browser_context_id;
+ std::string extension_id;
+ scoped_refptr<SocketData> sockets;
+ int socket_id;
+ };
+
+ // Start a receive and register a callback.
+ static void StartReceive(const ReceiveParams& params);
+
+ // Called when socket receive data.
+ static void ReceiveCallback(const ReceiveParams& params,
+ int bytes_read,
+ scoped_refptr<net::IOBuffer> io_buffer,
+ const std::string& address,
+ int port);
+
+ // Post an extension event from IO to UI thread
+ static void PostEvent(const ReceiveParams& params, scoped_ptr<Event> event);
+
+ // Dispatch an extension event on to EventRouter instance on UI thread.
+ static void DispatchEvent(void* browser_context_id,
+ const std::string& extension_id,
+ scoped_ptr<Event> event);
+
+ // Usually IO thread (except for unit testing).
+ content::BrowserThread::ID thread_id_;
+ content::BrowserContext* const browser_context_;
+ scoped_refptr<SocketData> sockets_;
+};
+
+} // namespace core_api
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_API_SOCKETS_UDP_UDP_SOCKET_EVENT_DISPATCHER_H_
diff --git a/extensions/common/api/api.gyp b/extensions/common/api/api.gyp
new file mode 100644
index 0000000..e6406c7
--- /dev/null
+++ b/extensions/common/api/api.gyp
@@ -0,0 +1,36 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'extensions_api',
+ 'type': 'static_library',
+ 'sources': [
+ '<@(schema_files)',
+ ],
+ 'includes': [
+ '../../../build/json_schema_bundle_compile.gypi',
+ '../../../build/json_schema_compile.gypi',
+ ],
+ 'variables': {
+ 'chromium_code': 1,
+ 'non_compiled_schema_files': [
+ ],
+ 'schema_files': [
+ 'socket.idl',
+ 'sockets_tcp.idl',
+ 'sockets_tcp_server.idl',
+ 'sockets_udp.idl',
+ ],
+ 'cc_dir': 'extensions/common/api',
+ 'root_namespace': 'extensions::core_api',
+ 'impl_dir': 'extensions/browser/api',
+ },
+ 'dependencies': [
+ '<(DEPTH)/skia/skia.gyp:skia',
+ ],
+ },
+ ],
+}
diff --git a/extensions/common/api/socket.idl b/extensions/common/api/socket.idl
new file mode 100644
index 0000000..805996d
--- /dev/null
+++ b/extensions/common/api/socket.idl
@@ -0,0 +1,339 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Use the <code>chrome.socket</code> API to send and receive data over the
+// network using TCP and UDP connections. <b>Note:</b> Starting with Chrome 33,
+// this API is deprecated in favor of the $ref:sockets.udp, $ref:sockets.tcp and
+// $ref:sockets.tcpServer APIs.
+namespace socket {
+ enum SocketType {
+ tcp,
+ udp
+ };
+
+ // The socket options.
+ dictionary CreateOptions {
+ };
+
+ dictionary CreateInfo {
+ // The id of the newly created socket.
+ long socketId;
+ };
+
+ callback CreateCallback = void (CreateInfo createInfo);
+
+ callback ConnectCallback = void (long result);
+
+ callback BindCallback = void (long result);
+
+ callback ListenCallback = void (long result);
+
+ dictionary AcceptInfo {
+ long resultCode;
+ // The id of the accepted socket.
+ long? socketId;
+ };
+
+ callback AcceptCallback = void (AcceptInfo acceptInfo);
+
+ dictionary ReadInfo {
+ // The resultCode returned from the underlying read() call.
+ long resultCode;
+
+ ArrayBuffer data;
+ };
+
+ callback ReadCallback = void (ReadInfo readInfo);
+
+ dictionary WriteInfo {
+ // The number of bytes sent, or a negative error code.
+ long bytesWritten;
+ };
+
+ callback WriteCallback = void (WriteInfo writeInfo);
+
+ dictionary RecvFromInfo {
+ // The resultCode returned from the underlying recvfrom() call.
+ long resultCode;
+
+ ArrayBuffer data;
+
+ // The address of the remote machine.
+ DOMString address;
+
+ long port;
+ };
+
+ dictionary SocketInfo {
+ // The type of the passed socket. This will be <code>tcp</code> or
+ // <code>udp</code>.
+ SocketType socketType;
+
+ // Whether or not the underlying socket is connected.
+ //
+ // For <code>tcp</code> sockets, this will remain true even if the remote
+ // peer has disconnected. Reading or writing to the socket may then result
+ // in an error, hinting that this socket should be disconnected via
+ // <code>disconnect()</code>.
+ //
+ // For <code>udp</code> sockets, this just represents whether a default
+ // remote address has been specified for reading and writing packets.
+ boolean connected;
+
+ // If the underlying socket is connected, contains the IPv4/6 address of
+ // the peer.
+ DOMString? peerAddress;
+
+ // If the underlying socket is connected, contains the port of the
+ // connected peer.
+ long? peerPort;
+
+ // If the underlying socket is bound or connected, contains its local
+ // IPv4/6 address.
+ DOMString? localAddress;
+
+ // If the underlying socket is bound or connected, contains its local port.
+ long? localPort;
+ };
+
+ dictionary NetworkInterface {
+ // The underlying name of the adapter. On *nix, this will typically be
+ // "eth0", "lo", etc.
+ DOMString name;
+
+ // The available IPv4/6 address.
+ DOMString address;
+
+ // The prefix length
+ long prefixLength;
+ };
+
+ callback RecvFromCallback = void (RecvFromInfo recvFromInfo);
+
+ callback SendToCallback = void (WriteInfo writeInfo);
+
+ callback SetKeepAliveCallback = void (boolean result);
+
+ callback SetNoDelayCallback = void (boolean result);
+
+ callback GetInfoCallback = void (SocketInfo result);
+
+ callback GetNetworkCallback = void (NetworkInterface[] result);
+
+ callback JoinGroupCallback = void (long result);
+
+ callback LeaveGroupCallback = void (long result);
+
+ callback SetMulticastTimeToLiveCallback = void (long result);
+
+ callback SetMulticastLoopbackModeCallback = void (long result);
+
+ callback GetJoinedGroupsCallback = void (DOMString[] groups);
+
+ interface Functions {
+ // Creates a socket of the specified type that will connect to the specified
+ // remote machine.
+ // |type| : The type of socket to create. Must be <code>tcp</code> or
+ // <code>udp</code>.
+ // |options| : The socket options.
+ // |callback| : Called when the socket has been created.
+ static void create(SocketType type,
+ optional CreateOptions options,
+ CreateCallback callback);
+
+ // Destroys the socket. Each socket created should be destroyed after use.
+ // |socketId| : The socketId.
+ static void destroy(long socketId);
+
+ // Connects the socket to the remote machine (for a <code>tcp</code>
+ // socket). For a <code>udp</code> socket, this sets the default address
+ // which packets are sent to and read from for <code>read()</code>
+ // and <code>write()</code> calls.
+ // |socketId| : The socketId.
+ // |hostname| : The hostname or IP address of the remote machine.
+ // |port| : The port of the remote machine.
+ // |callback| : Called when the connection attempt is complete.
+ static void connect(long socketId,
+ DOMString hostname,
+ long port,
+ ConnectCallback callback);
+
+ // Binds the local address for socket. Currently, it does not support
+ // TCP socket.
+ // |socketId| : The socketId.
+ // |address| : The address of the local machine.
+ // |port| : The port of the local machine.
+ // |callback| : Called when the bind attempt is complete.
+ static void bind(long socketId,
+ DOMString address,
+ long port,
+ BindCallback callback);
+
+ // Disconnects the socket. For UDP sockets, <code>disconnect</code> is a
+ // non-operation but is safe to call.
+ // |socketId| : The socketId.
+ static void disconnect(long socketId);
+
+ // Reads data from the given connected socket.
+ // |socketId| : The socketId.
+ // |bufferSize| : The read buffer size.
+ // |callback| : Delivers data that was available to be read without
+ // blocking.
+ static void read(long socketId,
+ optional long bufferSize,
+ ReadCallback callback);
+
+ // Writes data on the given connected socket.
+ // |socketId| : The socketId.
+ // |data| : The data to write.
+ // |callback| : Called when the write operation completes without blocking
+ // or an error occurs.
+ static void write(long socketId,
+ ArrayBuffer data,
+ WriteCallback callback);
+
+ // Receives data from the given UDP socket.
+ // |socketId| : The socketId.
+ // |bufferSize| : The receive buffer size.
+ // |callback| : Returns result of the recvFrom operation.
+ static void recvFrom(long socketId,
+ optional long bufferSize,
+ RecvFromCallback callback);
+
+ // Sends data on the given UDP socket to the given address and port.
+ // |socketId| : The socketId.
+ // |data| : The data to write.
+ // |address| : The address of the remote machine.
+ // |port| : The port of the remote machine.
+ // |callback| : Called when the send operation completes without blocking
+ // or an error occurs.
+ static void sendTo(long socketId,
+ ArrayBuffer data,
+ DOMString address,
+ long port,
+ SendToCallback callback);
+
+ // This method applies to TCP sockets only.
+ // Listens for connections on the specified port and address. This
+ // effectively makes this a server socket, and client socket
+ // functions (connect, read, write) can no longer be used on this socket.
+ // |socketId| : The socketId.
+ // |address| : The address of the local machine.
+ // |port| : The port of the local machine.
+ // |backlog| : Length of the socket's listen queue.
+ // |callback| : Called when listen operation completes.
+ static void listen(long socketId,
+ DOMString address,
+ long port,
+ optional long backlog,
+ ListenCallback callback);
+
+ // This method applies to TCP sockets only.
+ // Registers a callback function to be called when a connection is
+ // accepted on this listening server socket. Listen must be called first.
+ // If there is already an active accept callback, this callback will be
+ // invoked immediately with an error as the resultCode.
+ // |socketId| : The socketId.
+ // |callback| : The callback is invoked when a new socket is accepted.
+ static void accept(long socketId,
+ AcceptCallback callback);
+
+ // Enables or disables the keep-alive functionality for a TCP connection.
+ // |socketId| : The socketId.
+ // |enable| : If true, enable keep-alive functionality.
+ // |delay| : Set the delay seconds between the last data packet received
+ // and the first keepalive probe. Default is 0.
+ // |callback| : Called when the setKeepAlive attempt is complete.
+ static void setKeepAlive(long socketId,
+ boolean enable,
+ optional long delay,
+ SetKeepAliveCallback callback);
+
+ // Sets or clears <code>TCP_NODELAY</code> for a TCP connection. Nagle's
+ // algorithm will be disabled when <code>TCP_NODELAY</code> is set.
+ // |socketId| : The socketId.
+ // |noDelay| : If true, disables Nagle's algorithm.
+ // |callback| : Called when the setNoDelay attempt is complete.
+ static void setNoDelay(long socketId,
+ boolean noDelay,
+ SetNoDelayCallback callback);
+
+ // Retrieves the state of the given socket.
+ // |socketId| : The socketId.
+ // |callback| : Called when the state is available.
+ static void getInfo(long socketId,
+ GetInfoCallback callback);
+
+ // Retrieves information about local adapters on this system.
+ // |callback| : Called when local adapter information is available.
+ static void getNetworkList(GetNetworkCallback callback);
+
+ // Join the multicast group and start to receive packets from that group.
+ // The socket must be of UDP type and must be bound to a local port
+ // before calling this method.
+ // |socketId| : The socketId.
+ // |address| : The group address to join. Domain names are not supported.
+ // |callback| : Called when the join group operation is done with an
+ // integer parameter indicating the platform-independent error code.
+ static void joinGroup(long socketId,
+ DOMString address,
+ JoinGroupCallback callback);
+
+ // Leave the multicast group previously joined using <code>joinGroup</code>.
+ // It's not necessary to leave the multicast group before destroying the
+ // socket or exiting. This is automatically called by the OS.
+ //
+ // Leaving the group will prevent the router from sending multicast
+ // datagrams to the local host, presuming no other process on the host is
+ // still joined to the group.
+ //
+ // |socketId| : The socketId.
+ // |address| : The group address to leave. Domain names are not supported.
+ // |callback| : Called when the leave group operation is done with an
+ // integer parameter indicating the platform-independent error code.
+ static void leaveGroup(long socketId, DOMString address,
+ LeaveGroupCallback callback);
+
+ // Set the time-to-live of multicast packets sent to the multicast group.
+ //
+ // Calling this method does not require multicast permissions.
+ //
+ // |socketId| : The socketId.
+ // |ttl| : The time-to-live value.
+ // |callback| : Called when the configuration operation is done.
+ static void setMulticastTimeToLive(
+ long socketId,
+ long ttl,
+ SetMulticastTimeToLiveCallback callback);
+
+ // Set whether multicast packets sent from the host to the multicast
+ // group will be looped back to the host.
+ //
+ // Note: the behavior of <code>setMulticastLoopbackMode</code> is slightly
+ // different between Windows and Unix-like systems. The inconsistency
+ // happens only when there is more than one application on the same host
+ // joined to the same multicast group while having different settings on
+ // multicast loopback mode. On Windows, the applications with loopback off
+ // will not RECEIVE the loopback packets; while on Unix-like systems, the
+ // applications with loopback off will not SEND the loopback packets to
+ // other applications on the same host. See MSDN: http://goo.gl/6vqbj
+ //
+ // Calling this method does not require multicast permissions.
+ //
+ // |socketId| : The socketId.
+ // |enabled| : Indicate whether to enable loopback mode.
+ // |callback| : Called when the configuration operation is done.
+ static void setMulticastLoopbackMode(
+ long socketId,
+ boolean enabled,
+ SetMulticastLoopbackModeCallback callback);
+
+ // Get the multicast group addresses the socket is currently joined to.
+ // |socketId| : The socketId.
+ // |callback| : Called with an array of strings of the result.
+ static void getJoinedGroups(long socketId,
+ GetJoinedGroupsCallback callback);
+ };
+
+};
diff --git a/extensions/common/api/sockets_tcp.idl b/extensions/common/api/sockets_tcp.idl
new file mode 100644
index 0000000..c1cff1e
--- /dev/null
+++ b/extensions/common/api/sockets_tcp.idl
@@ -0,0 +1,252 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Use the <code>chrome.sockets.tcp</code> API to send and receive data over the
+// network using TCP connections. This API supersedes the TCP functionality
+// previously found in the <code>chrome.socket</code> API.
+namespace sockets.tcp {
+ // The socket properties specified in the <code>create</code> or
+ // <code>update</code> function. Each property is optional. If a property
+ // value is not specified, a default value is used when calling
+ // <code>create</code>, or the existing value if preserved when calling
+ // <code>update</code>.
+ dictionary SocketProperties {
+ // Flag indicating if the socket is left open when the event page of
+ // the application is unloaded (see
+ // <a href="http://developer.chrome.com/apps/app_lifecycle.html">Manage App
+ // Lifecycle</a>). The default value is "false." When the application is
+ // loaded, any sockets previously opened with persistent=true can be fetched
+ // with <code>getSockets</code>.
+ boolean? persistent;
+
+ // An application-defined string associated with the socket.
+ DOMString? name;
+
+ // The size of the buffer used to receive data. The default value is 4096.
+ long? bufferSize;
+ };
+
+ // Result of <code>create</code> call.
+ dictionary CreateInfo {
+ // The ID of the newly created socket. Note that socket IDs created from
+ // this API are not compatible with socket IDs created from other APIs, such
+ // as the deprecated <code>$ref:socket</code> API.
+ long socketId;
+ };
+
+ // Callback from the <code>create</code> method.
+ // |createInfo| : The result of the socket creation.
+ callback CreateCallback = void (CreateInfo createInfo);
+
+ // Callback from the <code>connect</code> method.
+ // |result| : The result code returned from the underlying network call.
+ // A negative value indicates an error.
+ callback ConnectCallback = void (long result);
+
+ // Callback from the <code>disconnect</code> method.
+ callback DisconnectCallback = void ();
+
+ // Result of the <code>send</code> method.
+ dictionary SendInfo {
+ // The result code returned from the underlying network call.
+ // A negative value indicates an error.
+ long resultCode;
+
+ // The number of bytes sent (if result == 0)
+ long? bytesSent;
+ };
+
+ // Callback from the <code>send</code> method.
+ // |sendInfo| : Result of the <code>send</code> method.
+ callback SendCallback = void (SendInfo sendInfo);
+
+ // Callback from the <code>close</code> method.
+ callback CloseCallback = void ();
+
+ // Callback from the <code>update</code> method.
+ callback UpdateCallback = void ();
+
+ // Callback from the <code>setPaused</code> method.
+ callback SetPausedCallback = void ();
+
+ // Callback from the <code>setKeepAliveCallback</code> method.
+ // |result| : The result code returned from the underlying network call.
+ // A negative value indicates an error.
+ callback SetKeepAliveCallback = void (long result);
+
+ // Callback from the <code>setNodeDelay</code> method.
+ // |result| : The result code returned from the underlying network call.
+ // A negative value indicates an error.
+ callback SetNoDelayCallback = void (long result);
+
+ // Result of the <code>getInfo</code> method.
+ dictionary SocketInfo {
+ // The socket identifier.
+ long socketId;
+
+ // Flag indicating whether the socket is left open when the application is
+ // suspended (see <code>SocketProperties.persistent</code>).
+ boolean persistent;
+
+ // Application-defined string associated with the socket.
+ DOMString? name;
+
+ // The size of the buffer used to receive data. If no buffer size has been
+ // specified explictly, the value is not provided.
+ long? bufferSize;
+
+ // Flag indicating whether a connected socket blocks its peer from sending
+ // more data (see <code>setPaused</code>).
+ boolean paused;
+
+ // Flag indicating whether the socket is connected to a remote peer.
+ boolean connected;
+
+ // If the underlying socket is connected, contains its local IPv4/6 address.
+ DOMString? localAddress;
+
+ // If the underlying socket is connected, contains its local port.
+ long? localPort;
+
+ // If the underlying socket is connected, contains the peer/ IPv4/6 address.
+ DOMString? peerAddress;
+
+ // If the underlying socket is connected, contains the peer port.
+ long? peerPort;
+ };
+
+ // Callback from the <code>getInfo</code> method.
+ // |socketInfo| : Object containing the socket information.
+ callback GetInfoCallback = void (SocketInfo socketInfo);
+
+ // Callback from the <code>getSockets</code> method.
+ // |socketInfos| : Array of object containing socket information.
+ callback GetSocketsCallback = void (SocketInfo[] socketInfos);
+
+ // Data from an <code>onReceive</code> event.
+ dictionary ReceiveInfo {
+ // The socket identifier.
+ long socketId;
+
+ // The data received, with a maxium size of <code>bufferSize</code>.
+ ArrayBuffer data;
+ };
+
+ // Data from an <code>onReceiveError</code> event.
+ dictionary ReceiveErrorInfo {
+ // The socket identifier.
+ long socketId;
+
+ // The result code returned from the underlying network call.
+ long resultCode;
+ };
+
+ interface Functions {
+ // Creates a TCP socket.
+ // |properties| : The socket properties (optional).
+ // |callback| : Called when the socket has been created.
+ static void create(optional SocketProperties properties,
+ CreateCallback callback);
+
+ // Updates the socket properties.
+ // |socketId| : The socket identifier.
+ // |properties| : The properties to update.
+ // |callback| : Called when the properties are updated.
+ static void update(long socketId,
+ SocketProperties properties,
+ optional UpdateCallback callback);
+
+ // Enables or disables the application from receiving messages from its
+ // peer. The default value is "false". Pausing a socket is typically used
+ // by an application to throttle data sent by its peer. When a socket is
+ // paused, no <code>onReceive</code> event is raised. When a socket is
+ // connected and un-paused, <code>onReceive</code> events are raised again
+ // when messages are received.
+ static void setPaused(long socketId,
+ boolean paused,
+ optional SetPausedCallback callback);
+
+ // Enables or disables the keep-alive functionality for a TCP connection.
+ // |socketId| : The socket identifier.
+ // |enable| : If true, enable keep-alive functionality.
+ // |delay| : Set the delay seconds between the last data packet received
+ // and the first keepalive probe. Default is 0.
+ // |callback| : Called when the setKeepAlive attempt is complete.
+ static void setKeepAlive(long socketId,
+ boolean enable,
+ optional long delay,
+ SetKeepAliveCallback callback);
+
+ // Sets or clears <code>TCP_NODELAY</code> for a TCP connection. Nagle's
+ // algorithm will be disabled when <code>TCP_NODELAY</code> is set.
+ // |socketId| : The socket identifier.
+ // |noDelay| : If true, disables Nagle's algorithm.
+ // |callback| : Called when the setNoDelay attempt is complete.
+ static void setNoDelay(long socketId,
+ boolean noDelay,
+ SetNoDelayCallback callback);
+
+ // Connects the socket to a remote machine. When the <code>connect</code>
+ // operation completes successfully, <code>onReceive</code> events are
+ // raised when data is received from the peer. If a network error occurs
+ // while the runtime is receiving packets, a <code>onReceiveError</code>
+ // event is raised, at which point no more <code>onReceive</code> event will
+ // be raised for this socket until the <code>resume</code> method is called.
+ // |socketId| : The socket identifier.
+ // |peerAddress| : The address of the remote machine. DNS name, IPv4 and
+ // IPv6 formats are supported.
+ // |peerPort| : The port of the remote machine.
+ // |callback| : Called when the connect attempt is complete.
+ static void connect(long socketId,
+ DOMString peerAddress,
+ long peerPort,
+ ConnectCallback callback);
+
+ // Disconnects the socket.
+ // |socketId| : The socket identifier.
+ // |callback| : Called when the disconnect attempt is complete.
+ static void disconnect(long socketId,
+ optional DisconnectCallback callback);
+
+ // Sends data on the given TCP socket.
+ // |socketId| : The socket identifier.
+ // |data| : The data to send.
+ // |callback| : Called when the <code>send</code> operation completes.
+ static void send(long socketId,
+ ArrayBuffer data,
+ SendCallback callback);
+
+ // Closes the socket and releases the address/port the socket is bound to.
+ // Each socket created should be closed after use. The socket id is no
+ // no longer valid as soon at the function is called. However, the socket is
+ // guaranteed to be closed only when the callback is invoked.
+ // |socketId| : The socket identifier.
+ // |callback| : Called when the <code>close</code> operation completes.
+ static void close(long socketId,
+ optional CloseCallback callback);
+
+ // Retrieves the state of the given socket.
+ // |socketId| : The socket identifier.
+ // |callback| : Called when the socket state is available.
+ static void getInfo(long socketId,
+ GetInfoCallback callback);
+
+ // Retrieves the list of currently opened sockets owned by the application.
+ // |callback| : Called when the list of sockets is available.
+ static void getSockets(GetSocketsCallback callback);
+ };
+
+ interface Events {
+ // Event raised when data has been received for a given socket.
+ // |info| : The event data.
+ static void onReceive(ReceiveInfo info);
+
+ // Event raised when a network error occured while the runtime was waiting
+ // for data on the socket address and port. Once this event is raised, the
+ // socket is set to <code>paused</code> and no more <code>onReceive</code>
+ // events are raised for this socket.
+ // |info| : The event data.
+ static void onReceiveError(ReceiveErrorInfo info);
+ };
+};
diff --git a/extensions/common/api/sockets_tcp_server.idl b/extensions/common/api/sockets_tcp_server.idl
new file mode 100644
index 0000000..ab69b9c
--- /dev/null
+++ b/extensions/common/api/sockets_tcp_server.idl
@@ -0,0 +1,193 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Use the <code>chrome.sockets.tcpServer</code> API to create server
+// applications using TCP connections. This API supersedes the TCP functionality
+// previously found in the <code>chrome.socket</code> API.
+namespace sockets.tcpServer {
+ // The socket properties specified in the <code>create</code> or
+ // <code>update</code> function. Each property is optional. If a property
+ // value is not specified, a default value is used when calling
+ // <code>create</code>, or the existing value if preserved when calling
+ // <code>update</code>.
+ dictionary SocketProperties {
+ // Flag indicating if the socket remains open when the event page of the
+ // application is unloaded (see
+ // <a href="http://developer.chrome.com/apps/app_lifecycle.html">Manage App
+ // Lifecycle</a>). The default value is "false." When the application is
+ // loaded, any sockets previously opened with persistent=true can be fetched
+ // with <code>getSockets</code>.
+ boolean? persistent;
+
+ // An application-defined string associated with the socket.
+ DOMString? name;
+ };
+
+ // Result of <code>create</code> call.
+ dictionary CreateInfo {
+ // The ID of the newly created server socket. Note that socket IDs created
+ // from this API are not compatible with socket IDs created from other APIs,
+ // such as the deprecated <code>$ref:socket</code> API.
+ long socketId;
+ };
+
+ // Callback from the <code>create</code> method.
+ // |createInfo| : The result of the socket creation.
+ callback CreateCallback = void (CreateInfo createInfo);
+
+ // Callback from the <code>listen</code> method.
+ // |result| : The result code returned from the underlying network call.
+ // A negative value indicates an error.
+ callback ListenCallback = void (long result);
+
+ // Callback from the <code>disconnect</code> method.
+ callback DisconnectCallback = void ();
+
+ // Callback from the <code>close</code> method.
+ callback CloseCallback = void ();
+
+ // Callback from the <code>update</code> method.
+ callback UpdateCallback = void ();
+
+ // Callback from the <code>setPaused</code> method.
+ callback SetPausedCallback = void ();
+
+ // Result of the <code>getInfo</code> method.
+ dictionary SocketInfo {
+ // The socket identifier.
+ long socketId;
+
+ // Flag indicating if the socket remains open when the event page of the
+ // application is unloaded (see <code>SocketProperties.persistent</code>).
+ // The default value is "false".
+ boolean persistent;
+
+ // Application-defined string associated with the socket.
+ DOMString? name;
+
+ // Flag indicating whether connection requests on a listening socket are
+ // dispatched through the <code>onAccept</code> event or queued up in the
+ // listen queue backlog.
+ // See <code>setPaused</code>. The default value is "false".
+ boolean paused;
+
+ // If the socket is listening, contains its local IPv4/6 address.
+ DOMString? localAddress;
+
+ // If the socket is listening, contains its local port.
+ long? localPort;
+ };
+
+ // Callback from the <code>getInfo</code> method.
+ // |socketInfo| : Object containing the socket information.
+ callback GetInfoCallback = void (SocketInfo socketInfo);
+
+ // Callback from the <code>getSockets</code> method.
+ // |socketInfos| : Array of object containing socket information.
+ callback GetSocketsCallback = void (SocketInfo[] socketInfos);
+
+ // Data from an <code>onAccept</code> event.
+ dictionary AcceptInfo {
+ // The server socket identifier.
+ long socketId;
+
+ // The client socket identifier, i.e. the socket identifier of the newly
+ // established connection. This socket identifier should be used only with
+ // functions from the <code>chrome.sockets.tcp</code> namespace. Note the
+ // client socket is initially paused and must be explictly un-paused by the
+ // application to start receiving data.
+ long clientSocketId;
+ };
+
+ // Data from an <code>onAcceptError</code> event.
+ dictionary AcceptErrorInfo {
+ // The server socket identifier.
+ long socketId;
+
+ // The result code returned from the underlying network call.
+ long resultCode;
+ };
+
+ interface Functions {
+ // Creates a TCP server socket.
+ // |properties| : The socket properties (optional).
+ // |callback| : Called when the socket has been created.
+ static void create(optional SocketProperties properties,
+ CreateCallback callback);
+
+ // Updates the socket properties.
+ // |socketId| : The socket identifier.
+ // |properties| : The properties to update.
+ // |callback| : Called when the properties are updated.
+ static void update(long socketId,
+ SocketProperties properties,
+ optional UpdateCallback callback);
+
+ // Enables or disables a listening socket from accepting new connections.
+ // When paused, a listening socket accepts new connections until its backlog
+ // (see <code>listen</code> function) is full then refuses additional
+ // connection requests. <code>onAccept</code> events are raised only when
+ // the socket is un-paused.
+ static void setPaused(long socketId,
+ boolean paused,
+ optional SetPausedCallback callback);
+
+ // Listens for connections on the specified port and address.
+ // If the port/address is in use, the callback indicates a failure.
+ // |socketId| : The socket identifier.
+ // |address| : The address of the local machine.
+ // |port| : The port of the local machine.
+ // |backlog| : Length of the socket's listen queue. The default value
+ // depends on the Operating System (SOMAXCONN), which ensures a reasonable
+ // queue length for most applications.
+ // |callback| : Called when listen operation completes.
+ static void listen(long socketId,
+ DOMString address,
+ long port,
+ optional long backlog,
+ ListenCallback callback);
+
+ // Disconnects the listening socket, i.e. stops accepting new connections
+ // and releases the address/port the socket is bound to. The socket
+ // identifier remains valid, e.g. it can be used with <code>listen</code> to
+ // accept connections on a new port and address.
+ // |socketId| : The socket identifier.
+ // |callback| : Called when the disconnect attempt is complete.
+ static void disconnect(long socketId,
+ optional DisconnectCallback callback);
+
+ // Disconnects and destroys the socket. Each socket created should be
+ // closed after use. The socket id is no longer valid as soon at the
+ // function is called. However, the socket is guaranteed to be closed only
+ // when the callback is invoked.
+ // |socketId| : The socket identifier.
+ // |callback| : Called when the <code>close</code> operation completes.
+ static void close(long socketId,
+ optional CloseCallback callback);
+
+ // Retrieves the state of the given socket.
+ // |socketId| : The socket identifier.
+ // |callback| : Called when the socket state is available.
+ static void getInfo(long socketId,
+ GetInfoCallback callback);
+
+ // Retrieves the list of currently opened sockets owned by the application.
+ // |callback| : Called when the list of sockets is available.
+ static void getSockets(GetSocketsCallback callback);
+ };
+
+ interface Events {
+ // Event raised when a connection has been made to the server socket.
+ // |info| : The event data.
+ static void onAccept(AcceptInfo info);
+
+ // Event raised when a network error occured while the runtime was waiting
+ // for new connections on the socket address and port. Once this event is
+ // raised, the socket is set to <code>paused</code> and no more
+ // <code>onAccept</code> events are raised for this socket until the socket
+ // is resumed.
+ // |info| : The event data.
+ static void onAcceptError(AcceptErrorInfo info);
+ };
+};
diff --git a/extensions/common/api/sockets_udp.idl b/extensions/common/api/sockets_udp.idl
new file mode 100644
index 0000000..61abc88
--- /dev/null
+++ b/extensions/common/api/sockets_udp.idl
@@ -0,0 +1,308 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Use the <code>chrome.sockets.udp</code> API to send and receive data over the
+// network using UDP connections. This API supersedes the UDP functionality
+// previously found in the "socket" API.
+namespace sockets.udp {
+ // The socket properties specified in the <code>create</code> or
+ // <code>update</code> function. Each property is optional. If a property
+ // value is not specified, a default value is used when calling
+ // <code>create</code>, or the existing value if preserved when calling
+ // <code>update</code>.
+ dictionary SocketProperties {
+ // Flag indicating if the socket is left open when the event page of the
+ // application is unloaded (see
+ // <a href="http://developer.chrome.com/apps/app_lifecycle.html">Manage App
+ // Lifecycle</a>). The default value is "false." When the application is
+ // loaded, any sockets previously opened with persistent=true can be fetched
+ // with <code>getSockets</code>.
+ boolean? persistent;
+
+ // An application-defined string associated with the socket.
+ DOMString? name;
+
+ // The size of the buffer used to receive data. If the buffer is too small
+ // to receive the UDP packet, data is lost. The default value is 4096.
+ long? bufferSize;
+ };
+
+ // Result of <code>create</code> call.
+ dictionary CreateInfo {
+ // The ID of the newly created socket. Note that socket IDs created from
+ // this API are not compatible with socket IDs created from other APIs, such
+ // as the deprecated <code>$ref:socket</code> API.
+ long socketId;
+ };
+
+ // Callback from the <code>create</code> method.
+ // |createInfo| : The result of the socket creation.
+ callback CreateCallback = void (CreateInfo createInfo);
+
+ // Callback from the <code>bind</code> method.
+ // |result| : The result code returned from the underlying network call.
+ // A negative value indicates an error.
+ callback BindCallback = void (long result);
+
+ // Result of the <code>send</code> method.
+ dictionary SendInfo {
+ // The result code returned from the underlying network call.
+ // A negative value indicates an error.
+ long resultCode;
+
+ // The number of bytes sent (if result == 0)
+ long? bytesSent;
+ };
+
+ // Callback from the <code>send</code> method.
+ // |sendInfo| : Result of the <code>send</code> method.
+ callback SendCallback = void (SendInfo sendInfo);
+
+ // Callback from the <code>close</code> method.
+ callback CloseCallback = void ();
+
+ // Callback from the <code>update</code> method.
+ callback UpdateCallback = void ();
+
+ // Callback from the <code>setPaused</code> method.
+ callback SetPausedCallback = void ();
+
+ // Result of the <code>getInfo</code> method.
+ dictionary SocketInfo {
+ // The socket identifier.
+ long socketId;
+
+ // Flag indicating whether the socket is left open when the application is
+ // suspended (see <code>SocketProperties.persistent</code>).
+ boolean persistent;
+
+ // Application-defined string associated with the socket.
+ DOMString? name;
+
+ // The size of the buffer used to receive data. If no buffer size has been
+ // specified explictly, the value is not provided.
+ long? bufferSize;
+
+ // Flag indicating whether the socket is blocked from firing onReceive
+ // events.
+ boolean paused;
+
+ // If the underlying socket is bound, contains its local
+ // IPv4/6 address.
+ DOMString? localAddress;
+
+ // If the underlying socket is bound, contains its local port.
+ long? localPort;
+ };
+
+ // Callback from the <code>getInfo</code> method.
+ // |socketInfo| : Object containing the socket information.
+ callback GetInfoCallback = void (SocketInfo socketInfo);
+
+ // Callback from the <code>getSockets</code> method.
+ // |socketInfos| : Array of object containing socket information.
+ callback GetSocketsCallback = void (SocketInfo[] socketInfos);
+
+ // Callback from the <code>joinGroup</code> method.
+ // |result| : The result code returned from the underlying network call.
+ // A negative value indicates an error.
+ callback JoinGroupCallback = void (long result);
+
+ // Callback from the <code>leaveGroup</code> method.
+ // |result| : The result code returned from the underlying network call.
+ // A negative value indicates an error.
+ callback LeaveGroupCallback = void (long result);
+
+ // Callback from the <code>setMulticastTimeToLive</code> method.
+ // |result| : The result code returned from the underlying network call.
+ // A negative value indicates an error.
+ callback SetMulticastTimeToLiveCallback = void (long result);
+
+ // Callback from the <code>setMulticastLoopbackMode</code> method.
+ // |result| : The result code returned from the underlying network call.
+ // A negative value indicates an error.
+ callback SetMulticastLoopbackModeCallback = void (long result);
+
+ // Callback from the <code>getJoinedGroupsCallback</code> method.
+ // |groups| : Array of groups the socket joined.
+ callback GetJoinedGroupsCallback = void (DOMString[] groups);
+
+ // Data from an <code>onReceive</code> event.
+ dictionary ReceiveInfo {
+ // The socket ID.
+ long socketId;
+
+ // The UDP packet content (truncated to the current buffer size).
+ ArrayBuffer data;
+
+ // The address of the host the packet comes from.
+ DOMString remoteAddress;
+
+ // The port of the host the packet comes from.
+ long remotePort;
+ };
+
+ // Data from an <code>onReceiveError</code> event.
+ dictionary ReceiveErrorInfo {
+ // The socket ID.
+ long socketId;
+
+ // The result code returned from the underlying recvfrom() call.
+ long resultCode;
+ };
+
+ interface Functions {
+ // Creates a UDP socket with the given properties.
+ // |properties| : The socket properties (optional).
+ // |callback| : Called when the socket has been created.
+ static void create(optional SocketProperties properties,
+ CreateCallback callback);
+
+ // Updates the socket properties.
+ // |socketId| : The socket ID.
+ // |properties| : The properties to update.
+ // |callback| : Called when the properties are updated.
+ static void update(long socketId,
+ SocketProperties properties,
+ optional UpdateCallback callback);
+
+ // Pauses or unpauses a socket. A paused socket is blocked from firing
+ // <code>onReceive</code> events.
+ // |connectionId| : The socket ID.
+ // |paused| : Flag to indicate whether to pause or unpause.
+ // |callback| : Called when the socket has been successfully paused or
+ // unpaused.
+ static void setPaused(long socketId,
+ boolean paused,
+ optional SetPausedCallback callback);
+
+ // Binds the local address and port for the socket. For a client socket, it
+ // is recommended to use port 0 to let the platform pick a free port.
+ //
+ // Once the <code>bind</code> operation completes successfully,
+ // <code>onReceive</code> events are raised when UDP packets arrive on the
+ // address/port specified -- unless the socket is paused.
+ //
+ // |socketId| : The socket ID.
+ // |address| : The address of the local machine. DNS name, IPv4 and IPv6
+ // formats are supported. Use "0.0.0.0" to accept packets from all local
+ // available network interfaces.
+ // |port| : The port of the local machine. Use "0" to bind to a free port.
+ // |callback| : Called when the <code>bind</code> operation completes.
+ static void bind(long socketId,
+ DOMString address,
+ long port,
+ BindCallback callback);
+
+ // Sends data on the given socket to the given address and port. The socket
+ // must be bound to a local port before calling this method.
+ // |socketId| : The socket ID.
+ // |data| : The data to send.
+ // |address| : The address of the remote machine.
+ // |port| : The port of the remote machine.
+ // |callback| : Called when the <code>send</code> operation completes.
+ static void send(long socketId,
+ ArrayBuffer data,
+ DOMString address,
+ long port,
+ SendCallback callback);
+
+ // Closes the socket and releases the address/port the socket is bound to.
+ // Each socket created should be closed after use. The socket id is no
+ // longer valid as soon at the function is called. However, the socket is
+ // guaranteed to be closed only when the callback is invoked.
+ // |socketId| : The socket ID.
+ // |callback| : Called when the <code>close</code> operation completes.
+ static void close(long socketId,
+ optional CloseCallback callback);
+
+ // Retrieves the state of the given socket.
+ // |socketId| : The socket ID.
+ // |callback| : Called when the socket state is available.
+ static void getInfo(long socketId,
+ GetInfoCallback callback);
+
+ // Retrieves the list of currently opened sockets owned by the application.
+ // |callback| : Called when the list of sockets is available.
+ static void getSockets(GetSocketsCallback callback);
+
+ // Joins the multicast group and starts to receive packets from that group.
+ // The socket must be bound to a local port before calling this method.
+ // |socketId| : The socket ID.
+ // |address| : The group address to join. Domain names are not supported.
+ // |callback| : Called when the <code>joinGroup</code> operation completes.
+ static void joinGroup(long socketId,
+ DOMString address,
+ JoinGroupCallback callback);
+
+ // Leaves the multicast group previously joined using
+ // <code>joinGroup</code>. This is only necessary to call if you plan to
+ // keep using the socketafterwards, since it will be done automatically by
+ // the OS when the socket is closed.
+ //
+ // Leaving the group will prevent the router from sending multicast
+ // datagrams to the local host, presuming no other process on the host is
+ // still joined to the group.
+ //
+ // |socketId| : The socket ID.
+ // |address| : The group address to leave. Domain names are not supported.
+ // |callback| : Called when the <code>leaveGroup</code> operation completes.
+ static void leaveGroup(long socketId,
+ DOMString address,
+ LeaveGroupCallback callback);
+
+ // Sets the time-to-live of multicast packets sent to the multicast group.
+ //
+ // Calling this method does not require multicast permissions.
+ //
+ // |socketId| : The socket ID.
+ // |ttl| : The time-to-live value.
+ // |callback| : Called when the configuration operation completes.
+ static void setMulticastTimeToLive(
+ long socketId,
+ long ttl,
+ SetMulticastTimeToLiveCallback callback);
+
+ // Sets whether multicast packets sent from the host to the multicast group
+ // will be looped back to the host.
+ //
+ // Note: the behavior of <code>setMulticastLoopbackMode</code> is slightly
+ // different between Windows and Unix-like systems. The inconsistency
+ // happens only when there is more than one application on the same host
+ // joined to the same multicast group while having different settings on
+ // multicast loopback mode. On Windows, the applications with loopback off
+ // will not RECEIVE the loopback packets; while on Unix-like systems, the
+ // applications with loopback off will not SEND the loopback packets to
+ // other applications on the same host. See MSDN: http://goo.gl/6vqbj
+ //
+ // Calling this method does not require multicast permissions.
+ //
+ // |socketId| : The socket ID.
+ // |enabled| : Indicate whether to enable loopback mode.
+ // |callback| : Called when the configuration operation completes.
+ static void setMulticastLoopbackMode(
+ long socketId,
+ boolean enabled,
+ SetMulticastLoopbackModeCallback callback);
+
+ // Gets the multicast group addresses the socket is currently joined to.
+ // |socketId| : The socket ID.
+ // |callback| : Called with an array of strings of the result.
+ static void getJoinedGroups(long socketId,
+ GetJoinedGroupsCallback callback);
+ };
+
+ interface Events {
+ // Event raised when a UDP packet has been received for the given socket.
+ // |info| : The event data.
+ static void onReceive(ReceiveInfo info);
+
+ // Event raised when a network error occured while the runtime was waiting
+ // for data on the socket address and port. Once this event is raised, the
+ // socket is paused and no more <code>onReceive</code> events will be raised
+ // for this socket until the socket is resumed.
+ // |info| : The event data.
+ static void onReceiveError(ReceiveErrorInfo info);
+ };
+};
diff --git a/extensions/common/extension_api.cc b/extensions/common/extension_api.cc
index 8e27f2b..b418734 100644
--- a/extensions/common/extension_api.cc
+++ b/extensions/common/extension_api.cc
@@ -16,8 +16,8 @@
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/values.h"
-#include "chrome/common/extensions/api/generated_schemas.h"
#include "extensions/common/extension.h"
+#include "extensions/common/extensions_client.h"
#include "extensions/common/features/feature.h"
#include "extensions/common/features/feature_provider.h"
#include "extensions/common/permissions/permission_set.h"
@@ -29,8 +29,6 @@
namespace extensions {
-using api::GeneratedSchemas;
-
namespace {
const char* kChildKinds[] = {
@@ -200,7 +198,9 @@ void ExtensionAPI::LoadSchema(const std::string& name,
const base::StringPiece& schema) {
scoped_ptr<base::ListValue> schema_list(LoadSchemaList(name, schema));
std::string schema_namespace;
-
+ extensions::ExtensionsClient* extensions_client =
+ extensions::ExtensionsClient::Get();
+ DCHECK(extensions_client);
while (!schema_list->empty()) {
base::DictionaryValue* schema = NULL;
{
@@ -212,7 +212,7 @@ void ExtensionAPI::LoadSchema(const std::string& name,
CHECK(schema->GetString("namespace", &schema_namespace));
PrefixWithNamespace(schema_namespace, schema);
schemas_[schema_namespace] = make_linked_ptr(schema);
- if (!GeneratedSchemas::IsGenerated(schema_namespace))
+ if (!extensions_client->IsAPISchemaGenerated(schema_namespace))
CHECK_EQ(1u, unloaded_schemas_.erase(schema_namespace));
}
}
@@ -344,14 +344,17 @@ const base::DictionaryValue* ExtensionAPI::GetSchema(
result = maybe_schema->second.get();
} else {
// Might not have loaded yet; or might just not exist.
- UnloadedSchemaMap::iterator maybe_schema_resource =
+ UnloadedSchemaMap::iterator maybe_schema_resource =
unloaded_schemas_.find(api_name);
+ extensions::ExtensionsClient* extensions_client =
+ extensions::ExtensionsClient::Get();
+ DCHECK(extensions_client);
if (maybe_schema_resource != unloaded_schemas_.end()) {
LoadSchema(maybe_schema_resource->first,
ReadFromResource(maybe_schema_resource->second));
} else if (default_configuration_initialized_ &&
- GeneratedSchemas::IsGenerated(api_name)) {
- LoadSchema(api_name, GeneratedSchemas::Get(api_name));
+ extensions_client->IsAPISchemaGenerated(api_name)) {
+ LoadSchema(api_name, extensions_client->GetAPISchema(api_name));
} else {
return NULL;
}
@@ -390,9 +393,12 @@ Feature* ExtensionAPI::GetFeatureDependency(const std::string& full_name) {
std::string ExtensionAPI::GetAPINameFromFullName(const std::string& full_name,
std::string* child_name) {
std::string api_name_candidate = full_name;
+ extensions::ExtensionsClient* extensions_client =
+ extensions::ExtensionsClient::Get();
+ DCHECK(extensions_client);
while (true) {
if (schemas_.find(api_name_candidate) != schemas_.end() ||
- GeneratedSchemas::IsGenerated(api_name_candidate) ||
+ extensions_client->IsAPISchemaGenerated(api_name_candidate) ||
unloaded_schemas_.find(api_name_candidate) != unloaded_schemas_.end()) {
std::string result = api_name_candidate;
diff --git a/extensions/common/extensions_client.h b/extensions/common/extensions_client.h
index d309fd7..843f985 100644
--- a/extensions/common/extensions_client.h
+++ b/extensions/common/extensions_client.h
@@ -9,6 +9,8 @@
#include <string>
#include <vector>
+#include "base/strings/string_piece.h"
+
class GURL;
namespace extensions {
@@ -71,6 +73,12 @@ class ExtensionsClient {
// Returns false if content scripts are forbidden from running on |url|.
virtual bool IsScriptableURL(const GURL& url, std::string* error) const = 0;
+ // Returns true iff a schema named |name| is generated.
+ virtual bool IsAPISchemaGenerated(const std::string& name) const = 0;
+
+ // Gets the API schema named |name|.
+ virtual base::StringPiece GetAPISchema(const std::string& name) const = 0;
+
// Return the extensions client.
static ExtensionsClient* Get();
diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp
index a5992ea..0bf3e27 100644
--- a/extensions/extensions.gyp
+++ b/extensions/extensions.gyp
@@ -11,6 +11,7 @@
'target_name': 'extensions_common',
'type': 'static_library',
'dependencies': [
+ 'common/api/api.gyp:extensions_api',
'../third_party/re2/re2.gyp:re2',
# TODO(benwells): figure out what to do with the api target and
# api resources compiled into the chrome resource bundle.
@@ -18,7 +19,7 @@
'../chrome/chrome_resources.gyp:chrome_resources',
# TODO(jamescook|derat): Pull strings into extensions module.
'../chrome/chrome_resources.gyp:chrome_strings',
- '../chrome/common/extensions/api/api.gyp:api',
+ '../chrome/common/extensions/api/api.gyp:chrome_api',
'../components/components.gyp:url_matcher',
'../content/content.gyp:content_common',
],
@@ -152,7 +153,7 @@
'extensions_common',
# TODO(jamescook|derat): Pull strings into extensions module.
'../chrome/chrome_resources.gyp:chrome_strings',
- '../chrome/common/extensions/api/api.gyp:api',
+ '../chrome/common/extensions/api/api.gyp:chrome_api',
'../content/content.gyp:content_browser',
'../skia/skia.gyp:skia',
'../third_party/leveldatabase/leveldatabase.gyp:leveldatabase',
@@ -175,6 +176,26 @@
'browser/api/async_api_function.h',
'browser/api/extensions_api_client.cc',
'browser/api/extensions_api_client.h',
+ 'browser/api/socket/socket.cc',
+ 'browser/api/socket/socket.h',
+ 'browser/api/socket/socket_api.cc',
+ 'browser/api/socket/socket_api.h',
+ 'browser/api/socket/tcp_socket.cc',
+ 'browser/api/socket/tcp_socket.h',
+ 'browser/api/socket/udp_socket.cc',
+ 'browser/api/socket/udp_socket.h',
+ 'browser/api/sockets_tcp/sockets_tcp_api.cc',
+ 'browser/api/sockets_tcp/sockets_tcp_api.h',
+ 'browser/api/sockets_tcp/tcp_socket_event_dispatcher.cc',
+ 'browser/api/sockets_tcp/tcp_socket_event_dispatcher.h',
+ 'browser/api/sockets_tcp_server/sockets_tcp_server_api.cc',
+ 'browser/api/sockets_tcp_server/sockets_tcp_server_api.h',
+ 'browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.cc',
+ 'browser/api/sockets_tcp_server/tcp_server_socket_event_dispatcher.h',
+ 'browser/api/sockets_udp/sockets_udp_api.cc',
+ 'browser/api/sockets_udp/sockets_udp_api.h',
+ 'browser/api/sockets_udp/udp_socket_event_dispatcher.cc',
+ 'browser/api/sockets_udp/udp_socket_event_dispatcher.h',
'browser/api/storage/leveldb_settings_storage_factory.cc',
'browser/api/storage/leveldb_settings_storage_factory.h',
'browser/api/storage/local_storage_backend.cc',