[email protected] | 91e4b7f6 | 2012-01-25 23:23:02 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 44f6076 | 2011-03-23 12:13:35 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "remoting/host/client_session.h" |
| 6 | |
[email protected] | 4fe827a | 2011-08-10 03:30:19 | [diff] [blame] | 7 | #include <algorithm> |
| 8 | |
wez | 43ac266 | 2015-06-10 18:22:29 | [diff] [blame] | 9 | #include "base/command_line.h" |
anujk.sharma | bb80293 | 2015-05-05 21:57:29 | [diff] [blame] | 10 | #include "base/single_thread_task_runner.h" |
| 11 | #include "base/thread_task_runner_handle.h" |
[email protected] | a5d181f | 2013-04-19 14:55:37 | [diff] [blame] | 12 | #include "remoting/base/capabilities.h" |
[email protected] | 6f742dd0 | 2013-11-26 23:19:50 | [diff] [blame] | 13 | #include "remoting/base/logging.h" |
[email protected] | 170cba4 | 2012-09-12 22:28:39 | [diff] [blame] | 14 | #include "remoting/codec/audio_encoder.h" |
[email protected] | a6ccb772 | 2012-10-23 21:10:43 | [diff] [blame] | 15 | #include "remoting/codec/audio_encoder_opus.h" |
[email protected] | 170cba4 | 2012-09-12 22:28:39 | [diff] [blame] | 16 | #include "remoting/codec/audio_encoder_verbatim.h" |
| 17 | #include "remoting/codec/video_encoder.h" |
[email protected] | 2149bef | 2012-10-18 03:26:53 | [diff] [blame] | 18 | #include "remoting/codec/video_encoder_verbatim.h" |
[email protected] | a43cfae | 2013-10-19 22:14:54 | [diff] [blame] | 19 | #include "remoting/codec/video_encoder_vpx.h" |
[email protected] | ce404ca | 2013-01-16 17:23:53 | [diff] [blame] | 20 | #include "remoting/host/audio_capturer.h" |
sergeyu | 7bc8760 | 2015-02-13 20:33:28 | [diff] [blame] | 21 | #include "remoting/host/audio_pump.h" |
sergeyu | 1afb35a1 | 2015-02-13 18:45:30 | [diff] [blame] | 22 | #include "remoting/host/desktop_capturer_proxy.h" |
[email protected] | 170cba4 | 2012-09-12 22:28:39 | [diff] [blame] | 23 | #include "remoting/host/desktop_environment.h" |
[email protected] | 399b4f6 | 2014-05-30 20:13:15 | [diff] [blame] | 24 | #include "remoting/host/host_extension_session.h" |
[email protected] | b0b72f11 | 2013-03-24 03:42:42 | [diff] [blame] | 25 | #include "remoting/host/input_injector.h" |
sergeyu | 1afb35a1 | 2015-02-13 18:45:30 | [diff] [blame] | 26 | #include "remoting/host/mouse_shape_pump.h" |
[email protected] | 231316a | 2013-03-25 06:01:12 | [diff] [blame] | 27 | #include "remoting/host/screen_controls.h" |
[email protected] | 739e280 | 2013-03-18 01:03:48 | [diff] [blame] | 28 | #include "remoting/host/screen_resolution.h" |
sergeyu | 10ce97b | 2015-02-05 23:53:14 | [diff] [blame] | 29 | #include "remoting/host/video_frame_pump.h" |
[email protected] | 50d71c7 | 2012-05-03 01:28:55 | [diff] [blame] | 30 | #include "remoting/proto/control.pb.h" |
[email protected] | c78669c9 | 2011-06-13 22:42:38 | [diff] [blame] | 31 | #include "remoting/proto/event.pb.h" |
[email protected] | ff3761a1 | 2012-05-22 22:29:24 | [diff] [blame] | 32 | #include "remoting/protocol/client_stub.h" |
[email protected] | 7f44ba4 | 2012-05-31 20:26:29 | [diff] [blame] | 33 | #include "remoting/protocol/clipboard_thread_proxy.h" |
[email protected] | 8835692 | 2013-06-04 06:26:01 | [diff] [blame] | 34 | #include "remoting/protocol/pairing_registry.h" |
sergeyu | b031cd2 | 2015-11-19 22:17:13 | [diff] [blame^] | 35 | #include "remoting/protocol/session.h" |
| 36 | #include "remoting/protocol/session_config.h" |
[email protected] | 4e719f4 | 2014-08-12 18:04:37 | [diff] [blame] | 37 | #include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" |
sergeyu | 1afb35a1 | 2015-02-13 18:45:30 | [diff] [blame] | 38 | #include "third_party/webrtc/modules/desktop_capture/mouse_cursor_monitor.h" |
[email protected] | c78669c9 | 2011-06-13 22:42:38 | [diff] [blame] | 39 | |
[email protected] | 48a8ca3 | 2013-02-13 04:31:01 | [diff] [blame] | 40 | // Default DPI to assume for old clients that use notifyClientDimensions. |
| 41 | const int kDefaultDPI = 96; |
[email protected] | 739e280 | 2013-03-18 01:03:48 | [diff] [blame] | 42 | |
| 43 | namespace remoting { |
[email protected] | 48a8ca3 | 2013-02-13 04:31:01 | [diff] [blame] | 44 | |
sergeyu | 7bc8760 | 2015-02-13 20:33:28 | [diff] [blame] | 45 | namespace { |
| 46 | |
wez | 6e95a37 | 2015-07-08 22:33:31 | [diff] [blame] | 47 | // Name of command-line flag to disable use of I444 by default. |
| 48 | const char kDisableI444SwitchName[] = "disable-i444"; |
wez | 43ac266 | 2015-06-10 18:22:29 | [diff] [blame] | 49 | |
sergeyu | 7bc8760 | 2015-02-13 20:33:28 | [diff] [blame] | 50 | scoped_ptr<VideoEncoder> CreateVideoEncoder( |
| 51 | const protocol::SessionConfig& config) { |
| 52 | const protocol::ChannelConfig& video_config = config.video_config(); |
| 53 | |
| 54 | if (video_config.codec == protocol::ChannelConfig::CODEC_VP8) { |
| 55 | return VideoEncoderVpx::CreateForVP8().Pass(); |
| 56 | } else if (video_config.codec == protocol::ChannelConfig::CODEC_VP9) { |
| 57 | return VideoEncoderVpx::CreateForVP9().Pass(); |
| 58 | } else if (video_config.codec == protocol::ChannelConfig::CODEC_VERBATIM) { |
| 59 | return make_scoped_ptr(new VideoEncoderVerbatim()); |
| 60 | } |
| 61 | |
| 62 | NOTREACHED(); |
| 63 | return nullptr; |
| 64 | } |
| 65 | |
| 66 | scoped_ptr<AudioEncoder> CreateAudioEncoder( |
| 67 | const protocol::SessionConfig& config) { |
| 68 | const protocol::ChannelConfig& audio_config = config.audio_config(); |
| 69 | |
| 70 | if (audio_config.codec == protocol::ChannelConfig::CODEC_VERBATIM) { |
| 71 | return make_scoped_ptr(new AudioEncoderVerbatim()); |
| 72 | } else if (audio_config.codec == protocol::ChannelConfig::CODEC_OPUS) { |
| 73 | return make_scoped_ptr(new AudioEncoderOpus()); |
| 74 | } |
| 75 | |
| 76 | NOTREACHED(); |
| 77 | return nullptr; |
| 78 | } |
| 79 | |
| 80 | } // namespace |
| 81 | |
[email protected] | 44f6076 | 2011-03-23 12:13:35 | [diff] [blame] | 82 | ClientSession::ClientSession( |
| 83 | EventHandler* event_handler, |
[email protected] | 032d9dd | 2012-11-01 17:55:04 | [diff] [blame] | 84 | scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner, |
[email protected] | ce404ca | 2013-01-16 17:23:53 | [diff] [blame] | 85 | scoped_refptr<base::SingleThreadTaskRunner> input_task_runner, |
[email protected] | 16dbfb0e | 2012-11-14 04:11:19 | [diff] [blame] | 86 | scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner, |
| 87 | scoped_refptr<base::SingleThreadTaskRunner> video_encode_task_runner, |
[email protected] | 170cba4 | 2012-09-12 22:28:39 | [diff] [blame] | 88 | scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, |
[email protected] | ce404ca | 2013-01-16 17:23:53 | [diff] [blame] | 89 | scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, |
[email protected] | 3361e1f | 2012-03-20 20:31:44 | [diff] [blame] | 90 | scoped_ptr<protocol::ConnectionToClient> connection, |
[email protected] | 8190479 | 2012-10-18 04:16:28 | [diff] [blame] | 91 | DesktopEnvironmentFactory* desktop_environment_factory, |
[email protected] | 8835692 | 2013-06-04 06:26:01 | [diff] [blame] | 92 | const base::TimeDelta& max_duration, |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 93 | scoped_refptr<protocol::PairingRegistry> pairing_registry, |
| 94 | const std::vector<HostExtension*>& extensions) |
[email protected] | 44f6076 | 2011-03-23 12:13:35 | [diff] [blame] | 95 | : event_handler_(event_handler), |
[email protected] | 3361e1f | 2012-03-20 20:31:44 | [diff] [blame] | 96 | connection_(connection.Pass()), |
| 97 | client_jid_(connection_->session()->jid()), |
[email protected] | 233bb94 | 2013-03-15 07:18:17 | [diff] [blame] | 98 | desktop_environment_factory_(desktop_environment_factory), |
[email protected] | eccc2d4 | 2013-01-10 19:35:14 | [diff] [blame] | 99 | input_tracker_(&host_input_filter_), |
[email protected] | 86cbe6b | 2012-04-03 00:56:18 | [diff] [blame] | 100 | remote_input_filter_(&input_tracker_), |
[email protected] | 58f1ad4 | 2013-01-10 23:49:51 | [diff] [blame] | 101 | mouse_clamping_filter_(&remote_input_filter_), |
[email protected] | eccc2d4 | 2013-01-10 19:35:14 | [diff] [blame] | 102 | disable_input_filter_(mouse_clamping_filter_.input_filter()), |
[email protected] | 750ae6b | 2012-08-20 22:52:40 | [diff] [blame] | 103 | disable_clipboard_filter_(clipboard_echo_filter_.host_filter()), |
[email protected] | 7f44ba4 | 2012-05-31 20:26:29 | [diff] [blame] | 104 | client_clipboard_factory_(clipboard_echo_filter_.client_filter()), |
[email protected] | 170cba4 | 2012-09-12 22:28:39 | [diff] [blame] | 105 | max_duration_(max_duration), |
[email protected] | 032d9dd | 2012-11-01 17:55:04 | [diff] [blame] | 106 | audio_task_runner_(audio_task_runner), |
[email protected] | ce404ca | 2013-01-16 17:23:53 | [diff] [blame] | 107 | input_task_runner_(input_task_runner), |
[email protected] | 16dbfb0e | 2012-11-14 04:11:19 | [diff] [blame] | 108 | video_capture_task_runner_(video_capture_task_runner), |
| 109 | video_encode_task_runner_(video_encode_task_runner), |
[email protected] | 170cba4 | 2012-09-12 22:28:39 | [diff] [blame] | 110 | network_task_runner_(network_task_runner), |
[email protected] | 8835692 | 2013-06-04 06:26:01 | [diff] [blame] | 111 | ui_task_runner_(ui_task_runner), |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 112 | pairing_registry_(pairing_registry), |
sergeyu | 1f8cd50 | 2015-02-13 21:45:53 | [diff] [blame] | 113 | is_authenticated_(false), |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 114 | pause_video_(false), |
| 115 | lossless_video_encode_(false), |
wez | 6e95a37 | 2015-07-08 22:33:31 | [diff] [blame] | 116 | // Note that |lossless_video_color_| defaults to true, but actually only |
| 117 | // controls VP9 video stream color quality. |
| 118 | lossless_video_color_(!base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 119 | kDisableI444SwitchName)), |
kulkarni.a | 933aeaf | 2014-09-20 13:19:17 | [diff] [blame] | 120 | weak_factory_(this) { |
[email protected] | ee910fd | 2011-11-10 18:23:31 | [diff] [blame] | 121 | connection_->SetEventHandler(this); |
| 122 | |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 123 | // Create a manager for the configured extensions, if any. |
| 124 | extension_manager_.reset(new HostExtensionSessionManager(extensions, this)); |
| 125 | |
[email protected] | 2f408489 | 2013-02-25 20:30:20 | [diff] [blame] | 126 | #if defined(OS_WIN) |
| 127 | // LocalInputMonitorWin filters out an echo of the injected input before it |
| 128 | // reaches |remote_input_filter_|. |
| 129 | remote_input_filter_.SetExpectLocalEcho(false); |
| 130 | #endif // defined(OS_WIN) |
[email protected] | 44f6076 | 2011-03-23 12:13:35 | [diff] [blame] | 131 | } |
| 132 | |
[email protected] | f19f09ca | 2013-03-13 11:10:29 | [diff] [blame] | 133 | ClientSession::~ClientSession() { |
| 134 | DCHECK(CalledOnValidThread()); |
sergeyu | 7bc8760 | 2015-02-13 20:33:28 | [diff] [blame] | 135 | DCHECK(!audio_pump_); |
[email protected] | 231316a | 2013-03-25 06:01:12 | [diff] [blame] | 136 | DCHECK(!desktop_environment_); |
[email protected] | b0b72f11 | 2013-03-24 03:42:42 | [diff] [blame] | 137 | DCHECK(!input_injector_); |
[email protected] | 231316a | 2013-03-25 06:01:12 | [diff] [blame] | 138 | DCHECK(!screen_controls_); |
sergeyu | 7bc8760 | 2015-02-13 20:33:28 | [diff] [blame] | 139 | DCHECK(!video_frame_pump_); |
[email protected] | f19f09ca | 2013-03-13 11:10:29 | [diff] [blame] | 140 | |
| 141 | connection_.reset(); |
| 142 | } |
| 143 | |
[email protected] | 48a8ca3 | 2013-02-13 04:31:01 | [diff] [blame] | 144 | void ClientSession::NotifyClientResolution( |
| 145 | const protocol::ClientResolution& resolution) { |
[email protected] | a5d181f | 2013-04-19 14:55:37 | [diff] [blame] | 146 | DCHECK(CalledOnValidThread()); |
| 147 | |
[email protected] | b9ed58f | 2013-05-16 10:45:24 | [diff] [blame] | 148 | // TODO(sergeyu): Move these checks to protocol layer. |
| 149 | if (!resolution.has_dips_width() || !resolution.has_dips_height() || |
| 150 | resolution.dips_width() < 0 || resolution.dips_height() < 0 || |
| 151 | resolution.width() <= 0 || resolution.height() <= 0) { |
| 152 | LOG(ERROR) << "Received invalid ClientResolution message."; |
[email protected] | 739e280 | 2013-03-18 01:03:48 | [diff] [blame] | 153 | return; |
[email protected] | b9ed58f | 2013-05-16 10:45:24 | [diff] [blame] | 154 | } |
[email protected] | 739e280 | 2013-03-18 01:03:48 | [diff] [blame] | 155 | |
| 156 | VLOG(1) << "Received ClientResolution (dips_width=" |
| 157 | << resolution.dips_width() << ", dips_height=" |
| 158 | << resolution.dips_height() << ")"; |
| 159 | |
[email protected] | 231316a | 2013-03-25 06:01:12 | [diff] [blame] | 160 | if (!screen_controls_) |
[email protected] | 739e280 | 2013-03-18 01:03:48 | [diff] [blame] | 161 | return; |
| 162 | |
| 163 | ScreenResolution client_resolution( |
[email protected] | b9ed58f | 2013-05-16 10:45:24 | [diff] [blame] | 164 | webrtc::DesktopSize(resolution.dips_width(), resolution.dips_height()), |
| 165 | webrtc::DesktopVector(kDefaultDPI, kDefaultDPI)); |
[email protected] | 739e280 | 2013-03-18 01:03:48 | [diff] [blame] | 166 | |
| 167 | // Try to match the client's resolution. |
[email protected] | b9ed58f | 2013-05-16 10:45:24 | [diff] [blame] | 168 | screen_controls_->SetScreenResolution(client_resolution); |
[email protected] | 50d71c7 | 2012-05-03 01:28:55 | [diff] [blame] | 169 | } |
| 170 | |
| 171 | void ClientSession::ControlVideo(const protocol::VideoControl& video_control) { |
[email protected] | a5d181f | 2013-04-19 14:55:37 | [diff] [blame] | 172 | DCHECK(CalledOnValidThread()); |
| 173 | |
sergeyu | 10ce97b | 2015-02-05 23:53:14 | [diff] [blame] | 174 | // Note that |video_frame_pump_| may be null, depending upon whether |
| 175 | // extensions choose to wrap or "steal" the video capturer or encoder. |
[email protected] | 50d71c7 | 2012-05-03 01:28:55 | [diff] [blame] | 176 | if (video_control.has_enable()) { |
| 177 | VLOG(1) << "Received VideoControl (enable=" |
| 178 | << video_control.enable() << ")"; |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 179 | pause_video_ = !video_control.enable(); |
sergeyu | 1afb35a1 | 2015-02-13 18:45:30 | [diff] [blame] | 180 | if (video_frame_pump_) |
sergeyu | 10ce97b | 2015-02-05 23:53:14 | [diff] [blame] | 181 | video_frame_pump_->Pause(pause_video_); |
[email protected] | 50d71c7 | 2012-05-03 01:28:55 | [diff] [blame] | 182 | } |
[email protected] | a516f1e | 2014-05-30 07:21:20 | [diff] [blame] | 183 | if (video_control.has_lossless_encode()) { |
| 184 | VLOG(1) << "Received VideoControl (lossless_encode=" |
| 185 | << video_control.lossless_encode() << ")"; |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 186 | lossless_video_encode_ = video_control.lossless_encode(); |
sergeyu | 1afb35a1 | 2015-02-13 18:45:30 | [diff] [blame] | 187 | if (video_frame_pump_) |
sergeyu | 10ce97b | 2015-02-05 23:53:14 | [diff] [blame] | 188 | video_frame_pump_->SetLosslessEncode(lossless_video_encode_); |
[email protected] | a516f1e | 2014-05-30 07:21:20 | [diff] [blame] | 189 | } |
| 190 | if (video_control.has_lossless_color()) { |
| 191 | VLOG(1) << "Received VideoControl (lossless_color=" |
| 192 | << video_control.lossless_color() << ")"; |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 193 | lossless_video_color_ = video_control.lossless_color(); |
sergeyu | 1afb35a1 | 2015-02-13 18:45:30 | [diff] [blame] | 194 | if (video_frame_pump_) |
sergeyu | 10ce97b | 2015-02-05 23:53:14 | [diff] [blame] | 195 | video_frame_pump_->SetLosslessColor(lossless_video_color_); |
[email protected] | a516f1e | 2014-05-30 07:21:20 | [diff] [blame] | 196 | } |
[email protected] | f2b9cf3 | 2012-04-27 00:13:43 | [diff] [blame] | 197 | } |
| 198 | |
[email protected] | f458bed | 2012-10-18 03:27:59 | [diff] [blame] | 199 | void ClientSession::ControlAudio(const protocol::AudioControl& audio_control) { |
[email protected] | a5d181f | 2013-04-19 14:55:37 | [diff] [blame] | 200 | DCHECK(CalledOnValidThread()); |
| 201 | |
[email protected] | f458bed | 2012-10-18 03:27:59 | [diff] [blame] | 202 | if (audio_control.has_enable()) { |
| 203 | VLOG(1) << "Received AudioControl (enable=" |
| 204 | << audio_control.enable() << ")"; |
sergeyu | 7bc8760 | 2015-02-13 20:33:28 | [diff] [blame] | 205 | if (audio_pump_) |
| 206 | audio_pump_->Pause(!audio_control.enable()); |
[email protected] | f458bed | 2012-10-18 03:27:59 | [diff] [blame] | 207 | } |
| 208 | } |
| 209 | |
[email protected] | a5d181f | 2013-04-19 14:55:37 | [diff] [blame] | 210 | void ClientSession::SetCapabilities( |
| 211 | const protocol::Capabilities& capabilities) { |
| 212 | DCHECK(CalledOnValidThread()); |
| 213 | |
[email protected] | a5d181f | 2013-04-19 14:55:37 | [diff] [blame] | 214 | // Ignore all the messages but the 1st one. |
| 215 | if (client_capabilities_) { |
| 216 | LOG(WARNING) << "protocol::Capabilities has been received already."; |
| 217 | return; |
| 218 | } |
| 219 | |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 220 | // Compute the set of capabilities supported by both client and host. |
[email protected] | a5d181f | 2013-04-19 14:55:37 | [diff] [blame] | 221 | client_capabilities_ = make_scoped_ptr(new std::string()); |
| 222 | if (capabilities.has_capabilities()) |
| 223 | *client_capabilities_ = capabilities.capabilities(); |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 224 | capabilities_ = IntersectCapabilities(*client_capabilities_, |
| 225 | host_capabilities_); |
| 226 | extension_manager_->OnNegotiatedCapabilities( |
| 227 | connection_->client_stub(), capabilities_); |
[email protected] | a5d181f | 2013-04-19 14:55:37 | [diff] [blame] | 228 | |
| 229 | VLOG(1) << "Client capabilities: " << *client_capabilities_; |
| 230 | |
| 231 | // Calculate the set of capabilities enabled by both client and host and |
| 232 | // pass it to the desktop environment if it is available. |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 233 | desktop_environment_->SetCapabilities(capabilities_); |
[email protected] | a5d181f | 2013-04-19 14:55:37 | [diff] [blame] | 234 | } |
| 235 | |
[email protected] | 8835692 | 2013-06-04 06:26:01 | [diff] [blame] | 236 | void ClientSession::RequestPairing( |
| 237 | const protocol::PairingRequest& pairing_request) { |
dcheng | ac10e4a8 | 2014-08-26 03:53:44 | [diff] [blame] | 238 | if (pairing_registry_.get() && pairing_request.has_client_name()) { |
[email protected] | 8835692 | 2013-06-04 06:26:01 | [diff] [blame] | 239 | protocol::PairingRegistry::Pairing pairing = |
| 240 | pairing_registry_->CreatePairing(pairing_request.client_name()); |
| 241 | protocol::PairingResponse pairing_response; |
[email protected] | df5189f | 2013-06-18 12:12:05 | [diff] [blame] | 242 | pairing_response.set_client_id(pairing.client_id()); |
| 243 | pairing_response.set_shared_secret(pairing.shared_secret()); |
[email protected] | 8835692 | 2013-06-04 06:26:01 | [diff] [blame] | 244 | connection_->client_stub()->SetPairingResponse(pairing_response); |
| 245 | } |
| 246 | } |
| 247 | |
[email protected] | 09eabd65c | 2013-08-13 00:13:48 | [diff] [blame] | 248 | void ClientSession::DeliverClientMessage( |
| 249 | const protocol::ExtensionMessage& message) { |
[email protected] | af278c4 | 2013-09-02 05:27:00 | [diff] [blame] | 250 | if (message.has_type()) { |
| 251 | if (message.type() == "test-echo") { |
| 252 | protocol::ExtensionMessage reply; |
| 253 | reply.set_type("test-echo-reply"); |
| 254 | if (message.has_data()) |
| 255 | reply.set_data(message.data().substr(0, 16)); |
| 256 | connection_->client_stub()->DeliverHostMessage(reply); |
| 257 | return; |
[email protected] | 3a5b82c | 2014-02-20 13:28:14 | [diff] [blame] | 258 | } else if (message.type() == "gnubby-auth") { |
| 259 | if (gnubby_auth_handler_) { |
| 260 | gnubby_auth_handler_->DeliverClientMessage(message.data()); |
| 261 | } else { |
| 262 | HOST_LOG << "gnubby auth is not enabled"; |
| 263 | } |
| 264 | return; |
[email protected] | 399b4f6 | 2014-05-30 20:13:15 | [diff] [blame] | 265 | } else { |
wez | 73f19de | 2014-10-29 19:06:33 | [diff] [blame] | 266 | if (extension_manager_->OnExtensionMessage(message)) |
wez | 8703699 | 2014-09-05 00:53:43 | [diff] [blame] | 267 | return; |
| 268 | |
| 269 | DLOG(INFO) << "Unexpected message received: " |
| 270 | << message.type() << ": " << message.data(); |
[email protected] | af278c4 | 2013-09-02 05:27:00 | [diff] [blame] | 271 | } |
| 272 | } |
[email protected] | 09eabd65c | 2013-08-13 00:13:48 | [diff] [blame] | 273 | } |
| 274 | |
[email protected] | 064128c | 2014-04-07 22:33:28 | [diff] [blame] | 275 | void ClientSession::OnConnectionAuthenticating( |
| 276 | protocol::ConnectionToClient* connection) { |
| 277 | event_handler_->OnSessionAuthenticating(this); |
| 278 | } |
| 279 | |
[email protected] | cba6f81 | 2012-03-27 01:01:50 | [diff] [blame] | 280 | void ClientSession::OnConnectionAuthenticated( |
[email protected] | ee910fd | 2011-11-10 18:23:31 | [diff] [blame] | 281 | protocol::ConnectionToClient* connection) { |
[email protected] | 86cbe6b | 2012-04-03 00:56:18 | [diff] [blame] | 282 | DCHECK(CalledOnValidThread()); |
| 283 | DCHECK_EQ(connection_.get(), connection); |
sergeyu | 7bc8760 | 2015-02-13 20:33:28 | [diff] [blame] | 284 | DCHECK(!audio_pump_); |
[email protected] | a5d181f | 2013-04-19 14:55:37 | [diff] [blame] | 285 | DCHECK(!desktop_environment_); |
[email protected] | 324b196 | 2013-05-01 19:30:22 | [diff] [blame] | 286 | DCHECK(!input_injector_); |
| 287 | DCHECK(!screen_controls_); |
sergeyu | 7bc8760 | 2015-02-13 20:33:28 | [diff] [blame] | 288 | DCHECK(!video_frame_pump_); |
[email protected] | 34df2ab | 2012-08-19 06:54:23 | [diff] [blame] | 289 | |
sergeyu | 1f8cd50 | 2015-02-13 21:45:53 | [diff] [blame] | 290 | is_authenticated_ = true; |
[email protected] | 34df2ab | 2012-08-19 06:54:23 | [diff] [blame] | 291 | |
[email protected] | 5dc5b12a | 2012-06-23 01:05:14 | [diff] [blame] | 292 | if (max_duration_ > base::TimeDelta()) { |
sergeyu | ec77d854 | 2015-11-03 22:31:00 | [diff] [blame] | 293 | max_duration_timer_.Start( |
| 294 | FROM_HERE, max_duration_, |
| 295 | base::Bind(&ClientSession::DisconnectSession, base::Unretained(this), |
| 296 | protocol::MAX_SESSION_LENGTH)); |
[email protected] | 5dc5b12a | 2012-06-23 01:05:14 | [diff] [blame] | 297 | } |
[email protected] | 34df2ab | 2012-08-19 06:54:23 | [diff] [blame] | 298 | |
[email protected] | 324b196 | 2013-05-01 19:30:22 | [diff] [blame] | 299 | // Disconnect the session if the connection was rejected by the host. |
| 300 | if (!event_handler_->OnSessionAuthenticated(this)) { |
sergeyu | ec77d854 | 2015-11-03 22:31:00 | [diff] [blame] | 301 | DisconnectSession(protocol::SESSION_REJECTED); |
[email protected] | 324b196 | 2013-05-01 19:30:22 | [diff] [blame] | 302 | return; |
| 303 | } |
[email protected] | ce404ca | 2013-01-16 17:23:53 | [diff] [blame] | 304 | |
[email protected] | 96361d0 | 2013-05-08 18:26:18 | [diff] [blame] | 305 | // Create the desktop environment. Drop the connection if it could not be |
| 306 | // created for any reason (for instance the curtain could not initialize). |
[email protected] | 231316a | 2013-03-25 06:01:12 | [diff] [blame] | 307 | desktop_environment_ = |
kulkarni.a | 933aeaf | 2014-09-20 13:19:17 | [diff] [blame] | 308 | desktop_environment_factory_->Create(weak_factory_.GetWeakPtr()); |
[email protected] | 96361d0 | 2013-05-08 18:26:18 | [diff] [blame] | 309 | if (!desktop_environment_) { |
sergeyu | ec77d854 | 2015-11-03 22:31:00 | [diff] [blame] | 310 | DisconnectSession(protocol::HOST_CONFIGURATION_ERROR); |
[email protected] | 96361d0 | 2013-05-08 18:26:18 | [diff] [blame] | 311 | return; |
| 312 | } |
| 313 | |
sergeyu | 1f8cd50 | 2015-02-13 21:45:53 | [diff] [blame] | 314 | // Connect host stub. |
| 315 | connection_->set_host_stub(this); |
| 316 | |
| 317 | // Connect video stub. |
| 318 | mouse_clamping_filter_.set_video_stub(connection_->video_stub()); |
| 319 | |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 320 | // Collate the set of capabilities to offer the client, if it supports them. |
sergeyu | f69b019f | 2014-09-13 20:41:02 | [diff] [blame] | 321 | host_capabilities_ = desktop_environment_->GetCapabilities(); |
| 322 | if (!host_capabilities_.empty()) |
| 323 | host_capabilities_.append(" "); |
| 324 | host_capabilities_.append(extension_manager_->GetCapabilities()); |
[email protected] | 233bb94 | 2013-03-15 07:18:17 | [diff] [blame] | 325 | |
[email protected] | 231316a | 2013-03-25 06:01:12 | [diff] [blame] | 326 | // Create the object that controls the screen resolution. |
| 327 | screen_controls_ = desktop_environment_->CreateScreenControls(); |
[email protected] | 61cfdc5 | 2013-03-09 03:04:56 | [diff] [blame] | 328 | |
[email protected] | 324b196 | 2013-05-01 19:30:22 | [diff] [blame] | 329 | // Create the event executor. |
[email protected] | 231316a | 2013-03-25 06:01:12 | [diff] [blame] | 330 | input_injector_ = desktop_environment_->CreateInputInjector(); |
[email protected] | eccc2d4 | 2013-01-10 19:35:14 | [diff] [blame] | 331 | |
sergeyu | 1f8cd50 | 2015-02-13 21:45:53 | [diff] [blame] | 332 | // Connect the host input stubs. |
| 333 | connection_->set_input_stub(&disable_input_filter_); |
[email protected] | b0b72f11 | 2013-03-24 03:42:42 | [diff] [blame] | 334 | host_input_filter_.set_input_stub(input_injector_.get()); |
sergeyu | 1f8cd50 | 2015-02-13 21:45:53 | [diff] [blame] | 335 | |
| 336 | // Connect the clipboard stubs. |
| 337 | connection_->set_clipboard_stub(&disable_clipboard_filter_); |
[email protected] | b0b72f11 | 2013-03-24 03:42:42 | [diff] [blame] | 338 | clipboard_echo_filter_.set_host_stub(input_injector_.get()); |
sergeyu | 1f8cd50 | 2015-02-13 21:45:53 | [diff] [blame] | 339 | clipboard_echo_filter_.set_client_stub(connection_->client_stub()); |
[email protected] | eccc2d4 | 2013-01-10 19:35:14 | [diff] [blame] | 340 | |
[email protected] | 3a5b82c | 2014-02-20 13:28:14 | [diff] [blame] | 341 | // Create a GnubbyAuthHandler to proxy gnubbyd messages. |
| 342 | gnubby_auth_handler_ = desktop_environment_->CreateGnubbyAuthHandler( |
| 343 | connection_->client_stub()); |
[email protected] | 324b196 | 2013-05-01 19:30:22 | [diff] [blame] | 344 | } |
| 345 | |
| 346 | void ClientSession::OnConnectionChannelsConnected( |
| 347 | protocol::ConnectionToClient* connection) { |
| 348 | DCHECK(CalledOnValidThread()); |
| 349 | DCHECK_EQ(connection_.get(), connection); |
| 350 | |
| 351 | // Negotiate capabilities with the client. |
sergeyu | f69b019f | 2014-09-13 20:41:02 | [diff] [blame] | 352 | VLOG(1) << "Host capabilities: " << host_capabilities_; |
sergeyu | f69b019f | 2014-09-13 20:41:02 | [diff] [blame] | 353 | protocol::Capabilities capabilities; |
| 354 | capabilities.set_capabilities(host_capabilities_); |
| 355 | connection_->client_stub()->SetCapabilities(capabilities); |
[email protected] | 324b196 | 2013-05-01 19:30:22 | [diff] [blame] | 356 | |
| 357 | // Start the event executor. |
| 358 | input_injector_->Start(CreateClipboardProxy()); |
| 359 | SetDisableInputs(false); |
| 360 | |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 361 | // Start recording video. |
| 362 | ResetVideoPipeline(); |
[email protected] | 324b196 | 2013-05-01 19:30:22 | [diff] [blame] | 363 | |
sergeyu | 7bc8760 | 2015-02-13 20:33:28 | [diff] [blame] | 364 | // Create an AudioPump if audio is enabled, to pump audio samples. |
| 365 | if (connection_->session()->config().is_audio_enabled()) { |
| 366 | scoped_ptr<AudioEncoder> audio_encoder = |
| 367 | CreateAudioEncoder(connection_->session()->config()); |
| 368 | audio_pump_.reset(new AudioPump( |
| 369 | audio_task_runner_, desktop_environment_->CreateAudioCapturer(), |
| 370 | audio_encoder.Pass(), connection_->audio_stub())); |
| 371 | } |
[email protected] | 170cba4 | 2012-09-12 22:28:39 | [diff] [blame] | 372 | |
[email protected] | cdd1c9e | 2012-10-26 21:14:04 | [diff] [blame] | 373 | // Notify the event handler that all our channels are now connected. |
[email protected] | cba6f81 | 2012-03-27 01:01:50 | [diff] [blame] | 374 | event_handler_->OnSessionChannelsConnected(this); |
[email protected] | ee910fd | 2011-11-10 18:23:31 | [diff] [blame] | 375 | } |
| 376 | |
[email protected] | cba6f81 | 2012-03-27 01:01:50 | [diff] [blame] | 377 | void ClientSession::OnConnectionClosed( |
[email protected] | 1f249e2 | 2011-11-29 20:19:59 | [diff] [blame] | 378 | protocol::ConnectionToClient* connection, |
[email protected] | 204a9e3 | 2012-03-02 05:42:58 | [diff] [blame] | 379 | protocol::ErrorCode error) { |
[email protected] | ec641187 | 2011-11-11 03:28:55 | [diff] [blame] | 380 | DCHECK(CalledOnValidThread()); |
[email protected] | ee910fd | 2011-11-10 18:23:31 | [diff] [blame] | 381 | DCHECK_EQ(connection_.get(), connection); |
[email protected] | 750ae6b | 2012-08-20 22:52:40 | [diff] [blame] | 382 | |
lukasza | f34d841 | 2015-04-30 23:09:59 | [diff] [blame] | 383 | HOST_LOG << "Client disconnected: " << client_jid_ << "; error = " << error; |
| 384 | |
[email protected] | 231316a | 2013-03-25 06:01:12 | [diff] [blame] | 385 | // Ignore any further callbacks. |
kulkarni.a | 933aeaf | 2014-09-20 13:19:17 | [diff] [blame] | 386 | weak_factory_.InvalidateWeakPtrs(); |
[email protected] | a031c97 | 2012-12-27 20:10:40 | [diff] [blame] | 387 | |
[email protected] | 24a2a9d2 | 2012-12-07 09:06:47 | [diff] [blame] | 388 | // If the client never authenticated then the session failed. |
sergeyu | 1f8cd50 | 2015-02-13 21:45:53 | [diff] [blame] | 389 | if (!is_authenticated_) |
[email protected] | 1f249e2 | 2011-11-29 20:19:59 | [diff] [blame] | 390 | event_handler_->OnSessionAuthenticationFailed(this); |
[email protected] | 750ae6b | 2012-08-20 22:52:40 | [diff] [blame] | 391 | |
[email protected] | 86cbe6b | 2012-04-03 00:56:18 | [diff] [blame] | 392 | // Ensure that any pressed keys or buttons are released. |
| 393 | input_tracker_.ReleaseAll(); |
| 394 | |
[email protected] | 24a2a9d2 | 2012-12-07 09:06:47 | [diff] [blame] | 395 | // Stop components access the client, audio or video stubs, which are no |
| 396 | // longer valid once ConnectionToClient calls OnConnectionClosed(). |
sergeyu | 7bc8760 | 2015-02-13 20:33:28 | [diff] [blame] | 397 | audio_pump_.reset(); |
sergeyu | 1afb35a1 | 2015-02-13 18:45:30 | [diff] [blame] | 398 | video_frame_pump_.reset(); |
| 399 | mouse_shape_pump_.reset(); |
[email protected] | 24a2a9d2 | 2012-12-07 09:06:47 | [diff] [blame] | 400 | client_clipboard_factory_.InvalidateWeakPtrs(); |
[email protected] | b0b72f11 | 2013-03-24 03:42:42 | [diff] [blame] | 401 | input_injector_.reset(); |
[email protected] | 231316a | 2013-03-25 06:01:12 | [diff] [blame] | 402 | screen_controls_.reset(); |
| 403 | desktop_environment_.reset(); |
[email protected] | 24a2a9d2 | 2012-12-07 09:06:47 | [diff] [blame] | 404 | |
| 405 | // Notify the ChromotingHost that this client is disconnected. |
[email protected] | ee910fd | 2011-11-10 18:23:31 | [diff] [blame] | 406 | event_handler_->OnSessionClosed(this); |
[email protected] | ee910fd | 2011-11-10 18:23:31 | [diff] [blame] | 407 | } |
| 408 | |
sergeyu | 752c6e6 | 2015-09-30 06:40:27 | [diff] [blame] | 409 | void ClientSession::OnInputEventReceived( |
| 410 | protocol::ConnectionToClient* connection, |
| 411 | int64_t event_timestamp) { |
[email protected] | ec641187 | 2011-11-11 03:28:55 | [diff] [blame] | 412 | DCHECK(CalledOnValidThread()); |
[email protected] | ee910fd | 2011-11-10 18:23:31 | [diff] [blame] | 413 | DCHECK_EQ(connection_.get(), connection); |
[email protected] | 170cba4 | 2012-09-12 22:28:39 | [diff] [blame] | 414 | |
sergeyu | 10ce97b | 2015-02-05 23:53:14 | [diff] [blame] | 415 | if (video_frame_pump_.get()) |
sergeyu | 752c6e6 | 2015-09-30 06:40:27 | [diff] [blame] | 416 | video_frame_pump_->OnInputEventReceived(event_timestamp); |
[email protected] | ee910fd | 2011-11-10 18:23:31 | [diff] [blame] | 417 | } |
| 418 | |
[email protected] | 17af2ab | 2012-02-02 04:07:52 | [diff] [blame] | 419 | void ClientSession::OnRouteChange( |
| 420 | protocol::ConnectionToClient* connection, |
| 421 | const std::string& channel_name, |
[email protected] | be451c8 | 2012-03-20 22:24:47 | [diff] [blame] | 422 | const protocol::TransportRoute& route) { |
[email protected] | 91e4b7f6 | 2012-01-25 23:23:02 | [diff] [blame] | 423 | DCHECK(CalledOnValidThread()); |
| 424 | DCHECK_EQ(connection_.get(), connection); |
[email protected] | be451c8 | 2012-03-20 22:24:47 | [diff] [blame] | 425 | event_handler_->OnSessionRouteChange(this, channel_name, route); |
[email protected] | 91e4b7f6 | 2012-01-25 23:23:02 | [diff] [blame] | 426 | } |
| 427 | |
[email protected] | 231316a | 2013-03-25 06:01:12 | [diff] [blame] | 428 | const std::string& ClientSession::client_jid() const { |
| 429 | return client_jid_; |
| 430 | } |
| 431 | |
sergeyu | ec77d854 | 2015-11-03 22:31:00 | [diff] [blame] | 432 | void ClientSession::DisconnectSession(protocol::ErrorCode error) { |
[email protected] | ec641187 | 2011-11-11 03:28:55 | [diff] [blame] | 433 | DCHECK(CalledOnValidThread()); |
| 434 | DCHECK(connection_.get()); |
[email protected] | a46bcef | 2011-11-11 01:27:23 | [diff] [blame] | 435 | |
[email protected] | 5dc5b12a | 2012-06-23 01:05:14 | [diff] [blame] | 436 | max_duration_timer_.Stop(); |
[email protected] | 24a2a9d2 | 2012-12-07 09:06:47 | [diff] [blame] | 437 | |
[email protected] | 86cbe6b | 2012-04-03 00:56:18 | [diff] [blame] | 438 | // This triggers OnConnectionClosed(), and the session may be destroyed |
[email protected] | a46bcef | 2011-11-11 01:27:23 | [diff] [blame] | 439 | // as the result, so this call must be the last in this method. |
sergeyu | ec77d854 | 2015-11-03 22:31:00 | [diff] [blame] | 440 | connection_->Disconnect(error); |
[email protected] | 44f6076 | 2011-03-23 12:13:35 | [diff] [blame] | 441 | } |
| 442 | |
[email protected] | 8c83a71c | 2013-12-16 18:02:58 | [diff] [blame] | 443 | void ClientSession::OnLocalMouseMoved(const webrtc::DesktopVector& position) { |
[email protected] | ec641187 | 2011-11-11 03:28:55 | [diff] [blame] | 444 | DCHECK(CalledOnValidThread()); |
[email protected] | 231316a | 2013-03-25 06:01:12 | [diff] [blame] | 445 | remote_input_filter_.LocalMouseMoved(position); |
[email protected] | 86cbe6b | 2012-04-03 00:56:18 | [diff] [blame] | 446 | } |
[email protected] | ec641187 | 2011-11-11 03:28:55 | [diff] [blame] | 447 | |
[email protected] | 86cbe6b | 2012-04-03 00:56:18 | [diff] [blame] | 448 | void ClientSession::SetDisableInputs(bool disable_inputs) { |
| 449 | DCHECK(CalledOnValidThread()); |
| 450 | |
[email protected] | 750ae6b | 2012-08-20 22:52:40 | [diff] [blame] | 451 | if (disable_inputs) |
[email protected] | 86cbe6b | 2012-04-03 00:56:18 | [diff] [blame] | 452 | input_tracker_.ReleaseAll(); |
[email protected] | 750ae6b | 2012-08-20 22:52:40 | [diff] [blame] | 453 | |
| 454 | disable_input_filter_.set_enabled(!disable_inputs); |
| 455 | disable_clipboard_filter_.set_enabled(!disable_inputs); |
[email protected] | c78669c9 | 2011-06-13 22:42:38 | [diff] [blame] | 456 | } |
| 457 | |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 458 | void ClientSession::ResetVideoPipeline() { |
| 459 | DCHECK(CalledOnValidThread()); |
| 460 | |
sergeyu | 1afb35a1 | 2015-02-13 18:45:30 | [diff] [blame] | 461 | mouse_shape_pump_.reset(); |
sergeyu | 97568a816 | 2015-02-24 18:00:55 | [diff] [blame] | 462 | connection_->set_video_feedback_stub(nullptr); |
sergeyu | 1afb35a1 | 2015-02-13 18:45:30 | [diff] [blame] | 463 | video_frame_pump_.reset(); |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 464 | |
[email protected] | 4e719f4 | 2014-08-12 18:04:37 | [diff] [blame] | 465 | // Create VideoEncoder and DesktopCapturer to match the session's video |
| 466 | // channel configuration. |
| 467 | scoped_ptr<webrtc::DesktopCapturer> video_capturer = |
wez | ea12516 | 2014-08-29 01:41:52 | [diff] [blame] | 468 | desktop_environment_->CreateVideoCapturer(); |
| 469 | extension_manager_->OnCreateVideoCapturer(&video_capturer); |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 470 | scoped_ptr<VideoEncoder> video_encoder = |
wez | ea12516 | 2014-08-29 01:41:52 | [diff] [blame] | 471 | CreateVideoEncoder(connection_->session()->config()); |
| 472 | extension_manager_->OnCreateVideoEncoder(&video_encoder); |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 473 | |
sergeyu | 10ce97b | 2015-02-05 23:53:14 | [diff] [blame] | 474 | // Don't start the VideoFramePump if either capturer or encoder are missing. |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 475 | if (!video_capturer || !video_encoder) |
| 476 | return; |
| 477 | |
sergeyu | 1afb35a1 | 2015-02-13 18:45:30 | [diff] [blame] | 478 | // Create MouseShapePump to send mouse cursor shape. |
| 479 | mouse_shape_pump_.reset( |
| 480 | new MouseShapePump(video_capture_task_runner_, |
| 481 | desktop_environment_->CreateMouseCursorMonitor(), |
| 482 | connection_->client_stub())); |
| 483 | |
| 484 | // Create a VideoFramePump to pump frames from the capturer to the client.' |
| 485 | // |
| 486 | // TODO(sergeyu): Move DesktopCapturerProxy creation to DesktopEnvironment. |
| 487 | // When using IpcDesktopCapturer the capture thread is not useful. |
| 488 | scoped_ptr<DesktopCapturerProxy> capturer_proxy(new DesktopCapturerProxy( |
| 489 | video_capture_task_runner_, video_capturer.Pass())); |
| 490 | video_frame_pump_.reset( |
| 491 | new VideoFramePump(video_encode_task_runner_, capturer_proxy.Pass(), |
| 492 | video_encoder.Pass(), &mouse_clamping_filter_)); |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 493 | |
| 494 | // Apply video-control parameters to the new scheduler. |
sergeyu | 10ce97b | 2015-02-05 23:53:14 | [diff] [blame] | 495 | video_frame_pump_->SetLosslessEncode(lossless_video_encode_); |
| 496 | video_frame_pump_->SetLosslessColor(lossless_video_color_); |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 497 | |
sergeyu | 91f93b6 | 2015-02-05 20:14:30 | [diff] [blame] | 498 | // Pause capturing if necessary. |
sergeyu | 10ce97b | 2015-02-05 23:53:14 | [diff] [blame] | 499 | video_frame_pump_->Pause(pause_video_); |
sergeyu | 97568a816 | 2015-02-24 18:00:55 | [diff] [blame] | 500 | |
| 501 | connection_->set_video_feedback_stub( |
| 502 | video_frame_pump_->video_feedback_stub()); |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 503 | } |
| 504 | |
[email protected] | 3a5b82c | 2014-02-20 13:28:14 | [diff] [blame] | 505 | void ClientSession::SetGnubbyAuthHandlerForTesting( |
| 506 | GnubbyAuthHandler* gnubby_auth_handler) { |
[email protected] | 1b478ba | 2014-07-31 12:51:43 | [diff] [blame] | 507 | DCHECK(CalledOnValidThread()); |
[email protected] | 3a5b82c | 2014-02-20 13:28:14 | [diff] [blame] | 508 | gnubby_auth_handler_.reset(gnubby_auth_handler); |
| 509 | } |
| 510 | |
[email protected] | 7f44ba4 | 2012-05-31 20:26:29 | [diff] [blame] | 511 | scoped_ptr<protocol::ClipboardStub> ClientSession::CreateClipboardProxy() { |
| 512 | DCHECK(CalledOnValidThread()); |
| 513 | |
sergeyu | 2d69088 | 2014-10-01 02:36:43 | [diff] [blame] | 514 | return make_scoped_ptr( |
| 515 | new protocol::ClipboardThreadProxy(client_clipboard_factory_.GetWeakPtr(), |
anujk.sharma | bb80293 | 2015-05-05 21:57:29 | [diff] [blame] | 516 | base::ThreadTaskRunnerHandle::Get())); |
[email protected] | 7f44ba4 | 2012-05-31 20:26:29 | [diff] [blame] | 517 | } |
| 518 | |
[email protected] | 44f6076 | 2011-03-23 12:13:35 | [diff] [blame] | 519 | } // namespace remoting |