diff options
author | rkc <rkc@chromium.org> | 2014-10-07 14:27:19 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-07 21:27:34 +0000 |
commit | a3e8914682cb7e046b5a76e93c053f070ea25a0b (patch) | |
tree | f86a43abc6257b908ffbcbee14b42f43f4d34a11 /extensions | |
parent | 5fb1e3aedf28059eb8bb51aa1b530f24be2f18ef (diff) | |
download | chromium_src-a3e8914682cb7e046b5a76e93c053f070ea25a0b.zip chromium_src-a3e8914682cb7e046b5a76e93c053f070ea25a0b.tar.gz chromium_src-a3e8914682cb7e046b5a76e93c053f070ea25a0b.tar.bz2 |
Prototype for copresenceSockets.
This is the prototype implementation of the chrome.copresenceSocket API. This
CL adds the API and the copresence_sockets component (which is a currently a
thin wrapper around the bluetooth APIs).
Only the receive parts of the code have been implemented with this CL.
Reviews requested,
rockot@ - Extension parts
armansito@ - BT parts
caitkp@ - Owners review for components
BUG=418615
Review URL: https://codereview.chromium.org/610633002
Cr-Commit-Position: refs/heads/master@{#298577}
Diffstat (limited to 'extensions')
-rw-r--r-- | extensions/browser/BUILD.gn | 3 | ||||
-rw-r--r-- | extensions/browser/api/copresence_socket/DEPS | 3 | ||||
-rw-r--r-- | extensions/browser/api/copresence_socket/OWNERS | 1 | ||||
-rw-r--r-- | extensions/browser/api/copresence_socket/copresence_socket_api.cc | 222 | ||||
-rw-r--r-- | extensions/browser/api/copresence_socket/copresence_socket_api.h | 120 | ||||
-rw-r--r-- | extensions/browser/extension_function_histogram_value.h | 4 | ||||
-rw-r--r-- | extensions/common/api/_api_features.json | 5 | ||||
-rw-r--r-- | extensions/common/api/copresence_socket.idl | 135 | ||||
-rw-r--r-- | extensions/common/api/schemas.gypi | 1 | ||||
-rw-r--r-- | extensions/extensions.gyp | 3 |
10 files changed, 497 insertions, 0 deletions
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn index a274a52..9ea3e4f 100644 --- a/extensions/browser/BUILD.gn +++ b/extensions/browser/BUILD.gn @@ -11,6 +11,7 @@ source_set("browser") { ] deps = [ + "//components/copresence_sockets", "//components/keyed_service/content", "//components/keyed_service/core", "//components/pref_registry", @@ -97,6 +98,8 @@ source_set("browser") { "api/cast_channel/logger.h", "api/cast_channel/logger_util.cc", "api/cast_channel/logger_util.h", + "api/copresence_socket/copresence_socket_api.cc", + "api/copresence_socket/copresence_socket_api.h", "api/declarative/declarative_api.cc", "api/declarative/declarative_api.h", "api/declarative/declarative_rule.h", diff --git a/extensions/browser/api/copresence_socket/DEPS b/extensions/browser/api/copresence_socket/DEPS new file mode 100644 index 0000000..bf5b843 --- /dev/null +++ b/extensions/browser/api/copresence_socket/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+components/copresence_sockets", +] diff --git a/extensions/browser/api/copresence_socket/OWNERS b/extensions/browser/api/copresence_socket/OWNERS new file mode 100644 index 0000000..6a2cb03 --- /dev/null +++ b/extensions/browser/api/copresence_socket/OWNERS @@ -0,0 +1 @@ +rkc@chromium.org diff --git a/extensions/browser/api/copresence_socket/copresence_socket_api.cc b/extensions/browser/api/copresence_socket/copresence_socket_api.cc new file mode 100644 index 0000000..0704297 --- /dev/null +++ b/extensions/browser/api/copresence_socket/copresence_socket_api.cc @@ -0,0 +1,222 @@ +// 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/copresence_socket/copresence_socket_api.h" + +#include "base/lazy_instance.h" +#include "components/copresence_sockets/public/copresence_peer.h" +#include "components/copresence_sockets/public/copresence_socket.h" +#include "content/public/browser/browser_context.h" +#include "extensions/browser/event_router.h" +#include "extensions/common/api/copresence_socket.h" +#include "net/base/io_buffer.h" + +using copresence_sockets::CopresencePeer; +using copresence_sockets::CopresenceSocket; + +namespace extensions { + +class CopresencePeerResource : public ApiResource { + public: + // Takes ownership of peer. + CopresencePeerResource(const std::string& owner_extension_id, + scoped_ptr<copresence_sockets::CopresencePeer> peer) + : ApiResource(owner_extension_id), peer_(peer.Pass()) {} + + virtual ~CopresencePeerResource() {} + + copresence_sockets::CopresencePeer* peer() { return peer_.get(); } + + static const content::BrowserThread::ID kThreadId = + content::BrowserThread::UI; + + private: + scoped_ptr<copresence_sockets::CopresencePeer> peer_; + + DISALLOW_COPY_AND_ASSIGN(CopresencePeerResource); +}; + +class CopresenceSocketResource : public ApiResource { + public: + // Takes ownership of socket. + CopresenceSocketResource( + const std::string& owner_extension_id, + scoped_ptr<copresence_sockets::CopresenceSocket> socket) + : ApiResource(owner_extension_id), socket_(socket.Pass()) {} + + virtual ~CopresenceSocketResource() {} + + copresence_sockets::CopresenceSocket* socket() { return socket_.get(); } + + static const content::BrowserThread::ID kThreadId = + content::BrowserThread::UI; + + private: + scoped_ptr<copresence_sockets::CopresenceSocket> socket_; + + DISALLOW_COPY_AND_ASSIGN(CopresenceSocketResource); +}; + +CopresenceSocketFunction::CopresenceSocketFunction() + : peers_manager_(nullptr), sockets_manager_(nullptr) { +} + +CopresenceSocketFunction::~CopresenceSocketFunction() { + delete peers_manager_; + delete sockets_manager_; +} + +void CopresenceSocketFunction::Initialize() { + peers_manager_ = + new ApiResourceManager<CopresencePeerResource>(browser_context()); + sockets_manager_ = + new ApiResourceManager<CopresenceSocketResource>(browser_context()); +} + +int CopresenceSocketFunction::AddPeer(CopresencePeerResource* peer) { + return peers_manager_->Add(peer); +} + +int CopresenceSocketFunction::AddSocket(CopresenceSocketResource* socket) { + return sockets_manager_->Add(socket); +} + +void CopresenceSocketFunction::ReplacePeer(const std::string& extension_id, + int peer_id, + CopresencePeerResource* peer) { + peers_manager_->Replace(extension_id, peer_id, peer); +} + +CopresencePeerResource* CopresenceSocketFunction::GetPeer(int peer_id) { + return peers_manager_->Get(extension_id(), peer_id); +} + +CopresenceSocketResource* CopresenceSocketFunction::GetSocket(int socket_id) { + return sockets_manager_->Get(extension_id(), socket_id); +} + +void CopresenceSocketFunction::RemovePeer(int peer_id) { + peers_manager_->Remove(extension_id(), peer_id); +} + +void CopresenceSocketFunction::RemoveSocket(int socket_id) { + sockets_manager_->Remove(extension_id(), socket_id); +} + +void CopresenceSocketFunction::DispatchOnReceiveEvent( + int socket_id, + const scoped_refptr<net::IOBuffer>& buffer, + int size) { + core_api::copresence_socket::ReceiveInfo info; + info.socket_id = socket_id; + info.data = std::string(buffer->data(), size); + // Send the data to the client app. + scoped_ptr<Event> event( + new Event(core_api::copresence_socket::OnReceive::kEventName, + core_api::copresence_socket::OnReceive::Create(info), + browser_context())); + EventRouter::Get(browser_context()) + ->DispatchEventToExtension(extension_id(), event.Pass()); + VLOG(2) << "Dispatched OnReceive event: socketId = " << socket_id + << " and data = " << info.data; +} + +void CopresenceSocketFunction::DispatchOnConnectedEvent( + int peer_id, + scoped_ptr<copresence_sockets::CopresenceSocket> socket) { + int socket_id = + AddSocket(new CopresenceSocketResource(extension_id(), socket.Pass())); + + // Send the messages to the client app. + scoped_ptr<Event> event(new Event( + core_api::copresence_socket::OnConnected::kEventName, + core_api::copresence_socket::OnConnected::Create(peer_id, socket_id), + browser_context())); + EventRouter::Get(browser_context()) + ->DispatchEventToExtension(extension_id(), event.Pass()); + VLOG(2) << "Dispatched OnConnected event: peerId = " << peer_id + << " and socketId = " << socket_id; + + socket->Receive(base::Bind(base::Bind( + &CopresenceSocketFunction::DispatchOnReceiveEvent, this, peer_id))); +} + +ExtensionFunction::ResponseAction CopresenceSocketFunction::Run() { + Initialize(); + return Execute(); +} + +// CopresenceSocketCreatePeerFunction implementation: +ExtensionFunction::ResponseAction +CopresenceSocketCreatePeerFunction::Execute() { + // Add an empty peer to create a placeholder peer_id. We will need to bind + // this id to the OnConnected event dispatcher, so we need it before we + // create the actual peer. Once we have the peer created, we'll replace the + // placeholder with the actual peer object. + int peer_id = AddPeer(new CopresencePeerResource(extension_id(), nullptr)); + + scoped_ptr<CopresencePeer> peer = make_scoped_ptr(new CopresencePeer( + base::Bind(&CopresenceSocketCreatePeerFunction::OnCreated, this, peer_id), + base::Bind( + &CopresenceSocketFunction::DispatchOnConnectedEvent, this, peer_id))); + + ReplacePeer(extension_id(), + peer_id, + new CopresencePeerResource(extension_id(), peer.Pass())); + + return RespondLater(); +} + +void CopresenceSocketCreatePeerFunction::OnCreated(int peer_id, + const std::string& locator) { + core_api::copresence_socket::PeerInfo peer_info; + peer_info.peer_id = peer_id; + peer_info.locator = locator; + Respond(ArgumentList( + core_api::copresence_socket::CreatePeer::Results::Create(peer_info))); +} + +// CopresenceSocketDestroyPeerFunction implementation: +ExtensionFunction::ResponseAction +CopresenceSocketDestroyPeerFunction::Execute() { + scoped_ptr<core_api::copresence_socket::DestroyPeer::Params> params( + core_api::copresence_socket::DestroyPeer::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + RemovePeer(params->peer_id); + return RespondNow(NoArguments()); +} + +// CopresenceSocketSendFunction implementation: +ExtensionFunction::ResponseAction CopresenceSocketSendFunction::Execute() { + scoped_ptr<core_api::copresence_socket::Send::Params> params( + core_api::copresence_socket::Send::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + CopresenceSocketResource* socket = GetSocket(params->socket_id); + if (!socket) { + VLOG(1) << "Socket not found. ID = " << params->socket_id; + return RespondNow( + ArgumentList(core_api::copresence_socket::Send::Results::Create( + core_api::copresence_socket::SOCKET_STATUS_INVALID_SOCKET))); + } + + socket->socket()->Send(new net::StringIOBuffer(params->data), + params->data.size()); + return RespondNow( + ArgumentList(core_api::copresence_socket::Send::Results::Create( + core_api::copresence_socket::SOCKET_STATUS_NO_ERROR))); +} + +// CopresenceSocketDisconnectFunction implementation: +ExtensionFunction::ResponseAction +CopresenceSocketDisconnectFunction::Execute() { + scoped_ptr<core_api::copresence_socket::Disconnect::Params> params( + core_api::copresence_socket::Disconnect::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + return RespondLater(); +} + +} // namespace extensions diff --git a/extensions/browser/api/copresence_socket/copresence_socket_api.h b/extensions/browser/api/copresence_socket/copresence_socket_api.h new file mode 100644 index 0000000..97f6782 --- /dev/null +++ b/extensions/browser/api/copresence_socket/copresence_socket_api.h @@ -0,0 +1,120 @@ +// 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 CHROME_BROWSER_EXTENSIONS_API_COPRESENCE_SOCKET_COPRESENCE_SOCKET_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_COPRESENCE_SOCKET_COPRESENCE_SOCKET_API_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "extensions/browser/api/api_resource.h" +#include "extensions/browser/api/api_resource_manager.h" +#include "extensions/browser/browser_context_keyed_api_factory.h" +#include "extensions/browser/extension_function.h" +#include "extensions/browser/extension_function_histogram_value.h" + +namespace copresence_sockets { +class CopresenceSocket; +} + +namespace net { +class IOBuffer; +} + +namespace extensions { + +class CopresencePeerResource; +class CopresenceSocketResource; + +class CopresenceSocketFunction : public UIThreadExtensionFunction { + public: + CopresenceSocketFunction(); + + void DispatchOnConnectedEvent( + int peer_id, + scoped_ptr<copresence_sockets::CopresenceSocket> socket); + void DispatchOnReceiveEvent(int socket_id, + const scoped_refptr<net::IOBuffer>& buffer, + int size); + + protected: + // ExtensionFunction overrides: + virtual ExtensionFunction::ResponseAction Run() override; + + // Override this and do actual work here. + virtual ExtensionFunction::ResponseAction Execute() = 0; + + // Takes ownership of peer. + int AddPeer(CopresencePeerResource* peer); + // Takes ownership of socket. + int AddSocket(CopresenceSocketResource* socket); + + // Takes ownership of peer. + void ReplacePeer(const std::string& extension_id, + int peer_id, + CopresencePeerResource* peer); + + CopresencePeerResource* GetPeer(int peer_id); + CopresenceSocketResource* GetSocket(int socket_id); + + void RemovePeer(int peer_id); + void RemoveSocket(int socket_id); + + virtual ~CopresenceSocketFunction(); + + private: + void Initialize(); + + ApiResourceManager<CopresencePeerResource>* peers_manager_; + ApiResourceManager<CopresenceSocketResource>* sockets_manager_; +}; + +class CopresenceSocketCreatePeerFunction : public CopresenceSocketFunction { + public: + DECLARE_EXTENSION_FUNCTION("copresenceSocket.createPeer", + COPRESENCESOCKET_CREATEPEER); + + protected: + virtual ~CopresenceSocketCreatePeerFunction() {} + virtual ExtensionFunction::ResponseAction Execute() override; + + private: + void OnCreated(int peer_id, const std::string& locator); +}; + +class CopresenceSocketDestroyPeerFunction : public CopresenceSocketFunction { + public: + DECLARE_EXTENSION_FUNCTION("copresenceSocket.destroyPeer", + COPRESENCESOCKET_DESTROYPEER); + + protected: + virtual ~CopresenceSocketDestroyPeerFunction() {} + virtual ExtensionFunction::ResponseAction Execute() override; +}; + +class CopresenceSocketSendFunction : public CopresenceSocketFunction { + public: + DECLARE_EXTENSION_FUNCTION("copresenceSocket.send", COPRESENCESOCKET_SEND); + + protected: + virtual ~CopresenceSocketSendFunction() {} + virtual ExtensionFunction::ResponseAction Execute() override; +}; + +class CopresenceSocketDisconnectFunction : public CopresenceSocketFunction { + public: + DECLARE_EXTENSION_FUNCTION("copresenceSocket.disconnect", + COPRESENCESOCKET_DISCONNECT); + + protected: + virtual ~CopresenceSocketDisconnectFunction() {} + virtual ExtensionFunction::ResponseAction Execute() override; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_COPRESENCE_SOCKET_COPRESENCE_SOCKET_API_H_ diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index a524997..e66feda 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h @@ -959,6 +959,10 @@ enum HistogramValue { HOTWORDPRIVATE_SETHOTWORDALWAYSONSEARCHENABLED, WEBVIEWINTERNAL_LOADDATAWITHBASEURL, GUESTVIEWINTERNAL_DESTROYGUEST, + COPRESENCESOCKET_CREATEPEER, + COPRESENCESOCKET_DESTROYPEER, + COPRESENCESOCKET_SEND, + COPRESENCESOCKET_DISCONNECT, // Last entry: Add new entries above and ensure to update // tools/metrics/histograms/histograms.xml. ENUM_BOUNDARY diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json index 343000d..537608a 100644 --- a/extensions/common/api/_api_features.json +++ b/extensions/common/api/_api_features.json @@ -89,6 +89,11 @@ "dependencies": ["permission:declarativeWebRequest"], "contexts": ["blessed_extension"] }, + "copresenceSocket": { + "channel" : "dev", + "dependencies": ["permission:copresence"], + "contexts": ["blessed_extension"] + }, "dns": { "dependencies": ["permission:dns"], "contexts": ["blessed_extension"] diff --git a/extensions/common/api/copresence_socket.idl b/extensions/common/api/copresence_socket.idl new file mode 100644 index 0000000..c26d854 --- /dev/null +++ b/extensions/common/api/copresence_socket.idl @@ -0,0 +1,135 @@ +// 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.copresenceSocket</code> API to create persistent +// sockets to send data to and receive from data nearby devices. +namespace copresenceSocket { + + enum TransportType { + // Only use the Internet as the transport. + online, + // Only use an offline transport. + offline + }; + + // The properties for the peer created by the $ref:createPeer function. + [noinline_doc] dictionary ConnectionProperties { + // Flag indicating whether the socket should use a low latency transport + // (if available). + boolean? lowLatency; + + // Flag to force the socket to use a particular type of transport. + TransportType? type; + }; + + + // Result of the <code>createPeer</code> call. + [noinline_doc] dictionary PeerInfo { + // The ID of the newly created peer. + long peerId; + + // An opaque string containing the locator data for this peer. This + // locator is needed to connect to this socket. + DOMString locator; + }; + + // Data from an <code>onReceive</code> event. + [noinline_doc] dictionary ReceiveInfo { + // The socket identifier. + long socketId; + + // The data received. + ArrayBuffer data; + }; + + // Status of a socket operation. + enum SocketStatus { + // There was no error in the previous operation. + no_error, + + // The socket was disconnected. + disconnected, + + // The socket id provided is invalid. + invalid_socket, + + // There was a failure during connection. + connect_failure, + + // There was an error while trying to send data. + send_failure, + + // There was an error while trying to receive data. + receive_failure + }; + + // Callback from the <code>createPeer</code> method. + // |peerInfo| : The result of the socket creation. + callback CreateCallback = void (PeerInfo peerInfo); + + // Callback from the <code>connectToPeer</code> method. + // |socketId| : ID of the socket created between the local and remote peers. + // This ID is only valid if status == no_error. + // |status| : Status of the connect operation. + callback ConnectCallback = void (long socketId, SocketStatus status); + + // Callback from the <code>send</code> method. + // |status| : Status of the send operation. + callback SendCallback = void (SocketStatus status); + + // Callback from the <code>disconnect</code> method. + callback DisconnectCallback = void (); + + // These functions all report failures via chrome.runtime.lastError. + interface Functions { + // Peer functions. + + // Creates a peer that can be connected to by a nearby devices. + // |callback| : Called when the peer has been created. + static void createPeer(CreateCallback callback); + + // Destroys the peer. This will close any connections to this peer + // from remote hosts and will prevent any further connections to it. + // |peerId|: Peer ID returned by <code>createPeer</code>. + static void destroyPeer(long peerId); + + // Socket functions. + + // Sends data on the given Copresence 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, + optional SendCallback callback); + + // Disconnects and destroys a socket. The socket id is no longer valid and any + // pending send callbacks are cancelled as soon at the function is called. + // However, the connection is guaranteed to be closed only when the callback + // is invoked. + // |socketId| : The socket identifier. + // |callback| : Called when the <code>disconnect</code> operation completes. + static void disconnect(long socketId, + optional DisconnectCallback 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 peer receives a new connection. A new socket is + // created and the id is passed to this event via socketId. + // |peerId| : ID of the peer that received this connection. + // |socketId| : ID of the new socket that was created which can be used to + // communicate on this connection. + static void onConnected(long peerId, long socketId); + + // Event raised when there is a status update for a socket. This can be an + // error or disconnection. After event is raised, since there has either + // been an error or disconnection, no more <code>onReceive</code> events + // are raised for this socket and the socketId is invalidated. + // |status| : The status update for the socket. + static void onSocketStatusUpdated(long socketId, SocketStatus status); + }; +}; diff --git a/extensions/common/api/schemas.gypi b/extensions/common/api/schemas.gypi index a7cc9e9..3ee747f 100644 --- a/extensions/common/api/schemas.gypi +++ b/extensions/common/api/schemas.gypi @@ -18,6 +18,7 @@ 'bluetooth_private.json', 'bluetooth_socket.idl', 'cast_channel.idl', + 'copresence_socket.idl', 'dns.idl', 'events.json', 'extensions_manifest_types.json', diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index e657635..0a80101 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -287,6 +287,7 @@ 'dependencies': [ '../base/base.gyp:base', '../base/base.gyp:base_prefs', + '../components/components.gyp:copresence_sockets', '../components/components.gyp:keyed_service_content', '../components/components.gyp:keyed_service_core', '../components/components.gyp:pref_registry', @@ -382,6 +383,8 @@ 'browser/api/cast_channel/logger.h', 'browser/api/cast_channel/logger_util.cc', 'browser/api/cast_channel/logger_util.h', + 'browser/api/copresence_socket/copresence_socket_api.cc', + 'browser/api/copresence_socket/copresence_socket_api.h', 'browser/api/declarative/deduping_factory.h', 'browser/api/declarative/declarative_api.cc', 'browser/api/declarative/declarative_api.h', |