diff --git a/remoting/client/chromoting_client.cc b/remoting/client/chromoting_client.cc index 2d308c92c47a8..f87d9e8dccd74 100644 --- a/remoting/client/chromoting_client.cc +++ b/remoting/client/chromoting_client.cc @@ -119,6 +119,13 @@ void ChromotingClient::SetPairingResponse( user_interface_->SetPairingResponse(pairing_response); } +void ChromotingClient::DeliverHostMessage( + const protocol::ExtensionMessage& message) { + DCHECK(task_runner_->BelongsToCurrentThread()); + + user_interface_->DeliverHostMessage(message); +} + void ChromotingClient::InjectClipboardEvent( const protocol::ClipboardEvent& event) { DCHECK(task_runner_->BelongsToCurrentThread()); diff --git a/remoting/client/chromoting_client.h b/remoting/client/chromoting_client.h index 625a4d8fd2e2e..58c8050d2771f 100644 --- a/remoting/client/chromoting_client.h +++ b/remoting/client/chromoting_client.h @@ -67,6 +67,8 @@ class ChromotingClient : public protocol::ConnectionToHost::HostEventCallback, const protocol::Capabilities& capabilities) OVERRIDE; virtual void SetPairingResponse( const protocol::PairingResponse& pairing_response) OVERRIDE; + virtual void DeliverHostMessage( + const protocol::ExtensionMessage& message) OVERRIDE; // ClipboardStub implementation for receiving clipboard data from host. virtual void InjectClipboardEvent( diff --git a/remoting/client/client_user_interface.h b/remoting/client/client_user_interface.h index 9779835cc9715..88d3e79699158 100644 --- a/remoting/client/client_user_interface.h +++ b/remoting/client/client_user_interface.h @@ -42,6 +42,10 @@ class ClientUserInterface { virtual void SetPairingResponse( const protocol::PairingResponse& pairing_response) = 0; + // Deliver an extension message from the host to the client. + virtual void DeliverHostMessage( + const protocol::ExtensionMessage& message) = 0; + // Get the view's ClipboardStub implementation. virtual protocol::ClipboardStub* GetClipboardStub() = 0; diff --git a/remoting/client/jni/chromoting_jni_instance.cc b/remoting/client/jni/chromoting_jni_instance.cc index b68aab9f2c62b..067e954bca05e 100644 --- a/remoting/client/jni/chromoting_jni_instance.cc +++ b/remoting/client/jni/chromoting_jni_instance.cc @@ -176,6 +176,11 @@ void ChromotingJniInstance::SetPairingResponse( response.shared_secret())); } +void ChromotingJniInstance::DeliverHostMessage( + const protocol::ExtensionMessage& message) { + NOTIMPLEMENTED(); +} + protocol::ClipboardStub* ChromotingJniInstance::GetClipboardStub() { return this; } diff --git a/remoting/client/jni/chromoting_jni_instance.h b/remoting/client/jni/chromoting_jni_instance.h index a8684bf4b8220..d066d6fe7dbbe 100644 --- a/remoting/client/jni/chromoting_jni_instance.h +++ b/remoting/client/jni/chromoting_jni_instance.h @@ -78,6 +78,8 @@ class ChromotingJniInstance virtual void SetCapabilities(const std::string& capabilities) OVERRIDE; virtual void SetPairingResponse( const protocol::PairingResponse& response) OVERRIDE; + virtual void DeliverHostMessage( + const protocol::ExtensionMessage& message) OVERRIDE; virtual protocol::ClipboardStub* GetClipboardStub() OVERRIDE; virtual protocol::CursorShapeStub* GetCursorShapeStub() OVERRIDE; virtual scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher> diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc index 8be169c9d2278..e22452a42168a 100644 --- a/remoting/client/plugin/chromoting_instance.cc +++ b/remoting/client/plugin/chromoting_instance.cc @@ -144,7 +144,7 @@ logging::LogMessageHandlerFunction g_logging_old_handler = NULL; const char ChromotingInstance::kApiFeatures[] = "highQualityScaling injectKeyEvent sendClipboardItem remapKey trapKey " "notifyClientDimensions notifyClientResolution pauseVideo pauseAudio " - "asyncPin thirdPartyAuth pinlessAuth"; + "asyncPin thirdPartyAuth pinlessAuth extensionMessage"; const char ChromotingInstance::kRequestedCapabilities[] = ""; const char ChromotingInstance::kSupportedCapabilities[] = "desktopShape"; @@ -433,6 +433,13 @@ void ChromotingInstance::HandleMessage(const pp::Var& message) { return; } RequestPairing(client_name); + } else if (method == "extensionMessage") { + std::string type, message; + if (!data->GetString("type", &type) || !data->GetString("data", &message)) { + LOG(ERROR) << "Invalid extensionMessage."; + return; + } + SendClientMessage(type, message); } } @@ -537,6 +544,14 @@ void ChromotingInstance::SetPairingResponse( PostChromotingMessage("pairingResponse", data.Pass()); } +void ChromotingInstance::DeliverHostMessage( + const protocol::ExtensionMessage& message) { + scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); + data->SetString("type", message.type()); + data->SetString("data", message.data()); + PostChromotingMessage("extensionMessage", data.Pass()); +} + void ChromotingInstance::FetchSecretFromDialog( bool pairing_supported, const protocol::SecretFetchedCallback& secret_fetched_callback) { @@ -839,6 +854,17 @@ void ChromotingInstance::RequestPairing(const std::string& client_name) { host_connection_->host_stub()->RequestPairing(pairing_request); } +void ChromotingInstance::SendClientMessage(const std::string& type, + const std::string& data) { + if (!IsConnected()) { + return; + } + protocol::ExtensionMessage message; + message.set_type(type); + message.set_data(data); + host_connection_->host_stub()->DeliverClientMessage(message); +} + ChromotingStats* ChromotingInstance::GetStats() { if (!client_.get()) return NULL; diff --git a/remoting/client/plugin/chromoting_instance.h b/remoting/client/plugin/chromoting_instance.h index d7fd4481f3422..23c4c4996c9dc 100644 --- a/remoting/client/plugin/chromoting_instance.h +++ b/remoting/client/plugin/chromoting_instance.h @@ -120,6 +120,8 @@ class ChromotingInstance : virtual void SetCapabilities(const std::string& capabilities) OVERRIDE; virtual void SetPairingResponse( const protocol::PairingResponse& pairing_response) OVERRIDE; + virtual void DeliverHostMessage( + const protocol::ExtensionMessage& message) OVERRIDE; virtual protocol::ClipboardStub* GetClipboardStub() OVERRIDE; virtual protocol::CursorShapeStub* GetCursorShapeStub() OVERRIDE; virtual scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher> @@ -198,6 +200,7 @@ class ChromotingInstance : void OnThirdPartyTokenFetched(const std::string& token, const std::string& shared_secret); void RequestPairing(const std::string& client_name); + void SendClientMessage(const std::string& type, const std::string& data); // Helper method to post messages to the webapp. void PostChromotingMessage(const std::string& method, diff --git a/remoting/host/client_session.cc b/remoting/host/client_session.cc index 86bbe7ffa1280..933ded3e4748c 100644 --- a/remoting/host/client_session.cc +++ b/remoting/host/client_session.cc @@ -187,6 +187,13 @@ void ClientSession::RequestPairing( } } +void ClientSession::DeliverClientMessage( + const protocol::ExtensionMessage& message) { + // No messages are currently supported. + LOG(INFO) << "Unexpected message received: " + << message.type() << ": " << message.data(); +} + void ClientSession::OnConnectionAuthenticated( protocol::ConnectionToClient* connection) { DCHECK(CalledOnValidThread()); diff --git a/remoting/host/client_session.h b/remoting/host/client_session.h index f023abd8ddd72..474e13b38a022 100644 --- a/remoting/host/client_session.h +++ b/remoting/host/client_session.h @@ -111,6 +111,8 @@ class ClientSession const protocol::Capabilities& capabilities) OVERRIDE; virtual void RequestPairing( const remoting::protocol::PairingRequest& pairing_request) OVERRIDE; + virtual void DeliverClientMessage( + const protocol::ExtensionMessage& message) OVERRIDE; // protocol::ConnectionToClient::EventHandler interface. virtual void OnConnectionAuthenticated( diff --git a/remoting/proto/control.proto b/remoting/proto/control.proto index 888c0b01b9010..2d2baf620b9b0 100644 --- a/remoting/proto/control.proto +++ b/remoting/proto/control.proto @@ -66,3 +66,14 @@ message PairingResponse { // Shared secret for this client. optional string shared_secret = 2; } + +message ExtensionMessage { + // The message type. This is used to dispatch the message to the correct + // recipient. + optional string type = 1; + + // String-encoded message data. The client and host must agree on the encoding + // for each message type; different message types need not shared the same + // encoding. + optional string data = 2; +} \ No newline at end of file diff --git a/remoting/proto/internal.proto b/remoting/proto/internal.proto index 219e3ef799c14..6ae35f4e3c279 100644 --- a/remoting/proto/internal.proto +++ b/remoting/proto/internal.proto @@ -24,6 +24,7 @@ message ControlMessage { optional Capabilities capabilities = 6; optional PairingRequest pairing_request = 7; optional PairingResponse pairing_response = 8; + optional ExtensionMessage extension_message = 9; } // Defines an event message on the event channel. diff --git a/remoting/protocol/client_control_dispatcher.cc b/remoting/protocol/client_control_dispatcher.cc index 8bb918efcb58f..a42c3f628d8b0 100644 --- a/remoting/protocol/client_control_dispatcher.cc +++ b/remoting/protocol/client_control_dispatcher.cc @@ -74,6 +74,13 @@ void ClientControlDispatcher::RequestPairing( writer_.Write(SerializeAndFrameMessage(message), base::Closure()); } +void ClientControlDispatcher::DeliverClientMessage( + const ExtensionMessage& message) { + ControlMessage control_message; + control_message.mutable_extension_message()->CopyFrom(message); + writer_.Write(SerializeAndFrameMessage(control_message), base::Closure()); +} + void ClientControlDispatcher::OnMessageReceived( scoped_ptr<ControlMessage> message, const base::Closure& done_task) { DCHECK(client_stub_); @@ -88,6 +95,8 @@ void ClientControlDispatcher::OnMessageReceived( client_stub_->SetCursorShape(message->cursor_shape()); } else if (message->has_pairing_response()) { client_stub_->SetPairingResponse(message->pairing_response()); + } else if (message->has_extension_message()) { + client_stub_->DeliverHostMessage(message->extension_message()); } else { LOG(WARNING) << "Unknown control message received."; } diff --git a/remoting/protocol/client_control_dispatcher.h b/remoting/protocol/client_control_dispatcher.h index b2a0bfa5a9af2..556ce72190d24 100644 --- a/remoting/protocol/client_control_dispatcher.h +++ b/remoting/protocol/client_control_dispatcher.h @@ -40,6 +40,7 @@ class ClientControlDispatcher : public ChannelDispatcherBase, virtual void ControlAudio(const AudioControl& audio_control) OVERRIDE; virtual void SetCapabilities(const Capabilities& capabilities) OVERRIDE; virtual void RequestPairing(const PairingRequest& pairing_request) OVERRIDE; + virtual void DeliverClientMessage(const ExtensionMessage& message) OVERRIDE; // Sets the ClientStub that will be called for each incoming control // message. |client_stub| must outlive this object. diff --git a/remoting/protocol/client_stub.h b/remoting/protocol/client_stub.h index 4507ba7f821ad..d57f948d35ea3 100644 --- a/remoting/protocol/client_stub.h +++ b/remoting/protocol/client_stub.h @@ -18,6 +18,7 @@ namespace remoting { namespace protocol { class Capabilities; +class ExtensionMessage; class PairingResponse; class ClientStub : public ClipboardStub, @@ -32,6 +33,9 @@ class ClientStub : public ClipboardStub, // Passes a pairing response message to the client. virtual void SetPairingResponse(const PairingResponse& pairing_response) = 0; + // Deliver an extension message from the host to the client. + virtual void DeliverHostMessage(const ExtensionMessage& message) = 0; + private: DISALLOW_COPY_AND_ASSIGN(ClientStub); }; diff --git a/remoting/protocol/host_control_dispatcher.cc b/remoting/protocol/host_control_dispatcher.cc index b979e36060d89..26f09fc3e55e7 100644 --- a/remoting/protocol/host_control_dispatcher.cc +++ b/remoting/protocol/host_control_dispatcher.cc @@ -46,6 +46,13 @@ void HostControlDispatcher::SetPairingResponse( writer_.Write(SerializeAndFrameMessage(message), base::Closure()); } +void HostControlDispatcher::DeliverHostMessage( + const ExtensionMessage& message) { + ControlMessage control_message; + control_message.mutable_extension_message()->CopyFrom(message); + writer_.Write(SerializeAndFrameMessage(control_message), base::Closure()); +} + void HostControlDispatcher::InjectClipboardEvent(const ClipboardEvent& event) { ControlMessage message; message.mutable_clipboard_event()->CopyFrom(event); @@ -78,6 +85,8 @@ void HostControlDispatcher::OnMessageReceived( host_stub_->SetCapabilities(message->capabilities()); } else if (message->has_pairing_request()) { host_stub_->RequestPairing(message->pairing_request()); + } else if (message->has_extension_message()) { + host_stub_->DeliverClientMessage(message->extension_message()); } else { LOG(WARNING) << "Unknown control message received."; } diff --git a/remoting/protocol/host_control_dispatcher.h b/remoting/protocol/host_control_dispatcher.h index 4620be10b877f..82aa7938f5e8a 100644 --- a/remoting/protocol/host_control_dispatcher.h +++ b/remoting/protocol/host_control_dispatcher.h @@ -37,6 +37,8 @@ class HostControlDispatcher : public ChannelDispatcherBase, virtual void SetCapabilities(const Capabilities& capabilities) OVERRIDE; virtual void SetPairingResponse( const PairingResponse& pairing_response) OVERRIDE; + virtual void DeliverHostMessage( + const ExtensionMessage& message) OVERRIDE; // ClipboardStub implementation for sending clipboard data to client. virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE; diff --git a/remoting/protocol/host_stub.h b/remoting/protocol/host_stub.h index 46d75346bd62d..cf9fa0b95c3fb 100644 --- a/remoting/protocol/host_stub.h +++ b/remoting/protocol/host_stub.h @@ -17,6 +17,7 @@ namespace protocol { class AudioControl; class Capabilities; class ClientResolution; +class ExtensionMessage; class PairingResponse; class PairingRequest; class VideoControl; @@ -43,6 +44,9 @@ class HostStub { // Requests pairing between the host and client for PIN-less authentication. virtual void RequestPairing(const PairingRequest& pairing_request) = 0; + // Deliver an extension message from the client to the host. + virtual void DeliverClientMessage(const ExtensionMessage& message) = 0; + protected: virtual ~HostStub() {} diff --git a/remoting/protocol/protocol_mock_objects.h b/remoting/protocol/protocol_mock_objects.h index 74435dbb486a5..16bb8663d7fdf 100644 --- a/remoting/protocol/protocol_mock_objects.h +++ b/remoting/protocol/protocol_mock_objects.h @@ -114,6 +114,7 @@ class MockHostStub : public HostStub { MOCK_METHOD1(SetCapabilities, void(const Capabilities& capabilities)); MOCK_METHOD1(RequestPairing, void(const PairingRequest& pairing_request)); + MOCK_METHOD1(DeliverClientMessage, void(const ExtensionMessage& message)); private: DISALLOW_COPY_AND_ASSIGN(MockHostStub); @@ -128,6 +129,7 @@ class MockClientStub : public ClientStub { MOCK_METHOD1(SetCapabilities, void(const Capabilities& capabilities)); MOCK_METHOD1(SetPairingResponse, void(const PairingResponse& pairing_response)); + MOCK_METHOD1(DeliverHostMessage, void(const ExtensionMessage& message)); // ClipboardStub mock implementation. MOCK_METHOD1(InjectClipboardEvent, void(const ClipboardEvent& event)); diff --git a/remoting/webapp/client_plugin.js b/remoting/webapp/client_plugin.js index e98b345825da2..247d06821576d 100644 --- a/remoting/webapp/client_plugin.js +++ b/remoting/webapp/client_plugin.js @@ -67,7 +67,8 @@ remoting.ClientPlugin.Feature = { SEND_CLIPBOARD_ITEM: 'sendClipboardItem', THIRD_PARTY_AUTH: 'thirdPartyAuth', TRAP_KEY: 'trapKey', - PINLESS_AUTH: 'pinlessAuth' + PINLESS_AUTH: 'pinlessAuth', + EXTENSION_MESSAGE: 'extensionMessage' }; /** diff --git a/remoting/webapp/client_plugin_async.js b/remoting/webapp/client_plugin_async.js index f457d159fe466..ac82c9f914177 100644 --- a/remoting/webapp/client_plugin_async.js +++ b/remoting/webapp/client_plugin_async.js @@ -313,6 +313,10 @@ remoting.ClientPluginAsync.prototype.handleMessage_ = function(messageStr) { return; } this.onPairingComplete_(clientId, sharedSecret); + } else if (message.method == 'extensionMessage') { + // No messages currently supported. + console.log('Unexpected message received: ' + + message.data.type + ': ' + message.data.data); } }; @@ -606,6 +610,23 @@ remoting.ClientPluginAsync.prototype.requestPairing = { method: 'requestPairing', data: { clientName: clientName } })); }; +/** + * Send an extension message to the host. + * + * @param {string} type The message type. + * @param {Object} message The message payload. + */ +remoting.ClientPluginAsync.prototype.sendClientMessage = + function(type, message) { + if (!this.hasFeature(remoting.ClientPlugin.Feature.EXTENSION_MESSAGE)) { + return; + } + this.plugin.postMessage(JSON.stringify( + { method: 'extensionMessage', + data: { type: type, data: JSON.stringify(message) } })); + +}; + /** * If we haven't yet received a "hello" message from the plugin, change its * size so that the user can confirm it if click-to-play is enabled, or can