1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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/desktop_session_proxy.h"
6
7 #include "base/compiler_specific.h"
8 #include "base/logging.h"
9 #include "base/process/process_handle.h"
10 #include "base/memory/shared_memory.h"
11 #include "base/single_thread_task_runner.h"
12 #include "ipc/ipc_channel_proxy.h"
13 #include "ipc/ipc_message_macros.h"
14 #include "remoting/base/capabilities.h"
15 #include "remoting/host/chromoting_messages.h"
16 #include "remoting/host/client_session.h"
17 #include "remoting/host/client_session_control.h"
18 #include "remoting/host/desktop_session_connector.h"
19 #include "remoting/host/ipc_audio_capturer.h"
20 #include "remoting/host/ipc_input_injector.h"
21 #include "remoting/host/ipc_mouse_cursor_monitor.h"
22 #include "remoting/host/ipc_screen_controls.h"
23 #include "remoting/host/ipc_video_frame_capturer.h"
24 #include "remoting/proto/audio.pb.h"
25 #include "remoting/proto/control.pb.h"
26 #include "remoting/proto/event.pb.h"
27 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
28 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
29 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h"
30 #include "third_party/webrtc/modules/desktop_capture/shared_memory.h"
31
32 #if defined(OS_WIN)
33 #include "base/win/scoped_handle.h"
34 #endif // defined(OS_WIN)
35
36 const bool kReadOnly = true;
37 const char kSendInitialResolution[] = "sendInitialResolution";
38 const char kRateLimitResizeRequests[] = "rateLimitResizeRequests";
39
40 namespace remoting {
41
42 class DesktopSessionProxy::IpcSharedBufferCore
43 : public base::RefCountedThreadSafe<IpcSharedBufferCore> {
44 public:
IpcSharedBufferCore(int id,base::SharedMemoryHandle handle,base::ProcessHandle process,size_t size)45 IpcSharedBufferCore(int id,
46 base::SharedMemoryHandle handle,
47 base::ProcessHandle process,
48 size_t size)
49 : id_(id),
50 #if defined(OS_WIN)
51 shared_memory_(handle, kReadOnly, process),
52 #else // !defined(OS_WIN)
53 shared_memory_(handle, kReadOnly),
54 #endif // !defined(OS_WIN)
55 size_(size) {
56 if (!shared_memory_.Map(size)) {
57 LOG(ERROR) << "Failed to map a shared buffer: id=" << id
58 #if defined(OS_WIN)
59 << ", handle=" << handle
60 #else
61 << ", handle.fd=" << handle.fd
62 #endif
63 << ", size=" << size;
64 }
65 }
66
id()67 int id() { return id_; }
size()68 size_t size() { return size_; }
memory()69 void* memory() { return shared_memory_.memory(); }
handle()70 webrtc::SharedMemory::Handle handle() {
71 #if defined(OS_WIN)
72 return shared_memory_.handle();
73 #else
74 return shared_memory_.handle().fd;
75 #endif
76 }
77
78 private:
~IpcSharedBufferCore()79 virtual ~IpcSharedBufferCore() {}
80 friend class base::RefCountedThreadSafe<IpcSharedBufferCore>;
81
82 int id_;
83 base::SharedMemory shared_memory_;
84 size_t size_;
85
86 DISALLOW_COPY_AND_ASSIGN(IpcSharedBufferCore);
87 };
88
89 class DesktopSessionProxy::IpcSharedBuffer : public webrtc::SharedMemory {
90 public:
IpcSharedBuffer(scoped_refptr<IpcSharedBufferCore> core)91 IpcSharedBuffer(scoped_refptr<IpcSharedBufferCore> core)
92 : SharedMemory(core->memory(), core->size(),
93 core->handle(), core->id()),
94 core_(core) {
95 }
96
97 private:
98 scoped_refptr<IpcSharedBufferCore> core_;
99
100 DISALLOW_COPY_AND_ASSIGN(IpcSharedBuffer);
101 };
102
DesktopSessionProxy(scoped_refptr<base::SingleThreadTaskRunner> audio_capture_task_runner,scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner,base::WeakPtr<ClientSessionControl> client_session_control,base::WeakPtr<DesktopSessionConnector> desktop_session_connector,bool virtual_terminal)103 DesktopSessionProxy::DesktopSessionProxy(
104 scoped_refptr<base::SingleThreadTaskRunner> audio_capture_task_runner,
105 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
106 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
107 scoped_refptr<base::SingleThreadTaskRunner> video_capture_task_runner,
108 base::WeakPtr<ClientSessionControl> client_session_control,
109 base::WeakPtr<DesktopSessionConnector> desktop_session_connector,
110 bool virtual_terminal)
111 : audio_capture_task_runner_(audio_capture_task_runner),
112 caller_task_runner_(caller_task_runner),
113 io_task_runner_(io_task_runner),
114 video_capture_task_runner_(video_capture_task_runner),
115 client_session_control_(client_session_control),
116 desktop_session_connector_(desktop_session_connector),
117 desktop_process_(base::kNullProcessHandle),
118 pending_capture_frame_requests_(0),
119 is_desktop_session_connected_(false),
120 virtual_terminal_(virtual_terminal) {
121 DCHECK(caller_task_runner_->BelongsToCurrentThread());
122 }
123
CreateAudioCapturer()124 scoped_ptr<AudioCapturer> DesktopSessionProxy::CreateAudioCapturer() {
125 DCHECK(caller_task_runner_->BelongsToCurrentThread());
126
127 return scoped_ptr<AudioCapturer>(new IpcAudioCapturer(this));
128 }
129
CreateInputInjector()130 scoped_ptr<InputInjector> DesktopSessionProxy::CreateInputInjector() {
131 DCHECK(caller_task_runner_->BelongsToCurrentThread());
132
133 return scoped_ptr<InputInjector>(new IpcInputInjector(this));
134 }
135
CreateScreenControls()136 scoped_ptr<ScreenControls> DesktopSessionProxy::CreateScreenControls() {
137 DCHECK(caller_task_runner_->BelongsToCurrentThread());
138
139 return scoped_ptr<ScreenControls>(new IpcScreenControls(this));
140 }
141
CreateVideoCapturer()142 scoped_ptr<webrtc::DesktopCapturer> DesktopSessionProxy::CreateVideoCapturer() {
143 DCHECK(caller_task_runner_->BelongsToCurrentThread());
144
145 return scoped_ptr<webrtc::DesktopCapturer>(new IpcVideoFrameCapturer(this));
146 }
147
148 scoped_ptr<webrtc::MouseCursorMonitor>
CreateMouseCursorMonitor()149 DesktopSessionProxy::CreateMouseCursorMonitor() {
150 return scoped_ptr<webrtc::MouseCursorMonitor>(
151 new IpcMouseCursorMonitor(this));
152 }
153
GetCapabilities() const154 std::string DesktopSessionProxy::GetCapabilities() const {
155 std::string result = kRateLimitResizeRequests;
156 // Ask the client to send its resolution unconditionally.
157 if (virtual_terminal_)
158 result = result + " " + kSendInitialResolution;
159 return result;
160 }
161
SetCapabilities(const std::string & capabilities)162 void DesktopSessionProxy::SetCapabilities(const std::string& capabilities) {
163 // Delay creation of the desktop session until the client screen resolution is
164 // received if the desktop session requires the initial screen resolution
165 // (when |virtual_terminal_| is true) and the client is expected to
166 // sent its screen resolution (the 'sendInitialResolution' capability is
167 // supported).
168 if (virtual_terminal_ &&
169 HasCapability(capabilities, kSendInitialResolution)) {
170 VLOG(1) << "Waiting for the client screen resolution.";
171 return;
172 }
173
174 // Connect to the desktop session.
175 if (!is_desktop_session_connected_) {
176 is_desktop_session_connected_ = true;
177 if (desktop_session_connector_.get()) {
178 desktop_session_connector_->ConnectTerminal(
179 this, screen_resolution_, virtual_terminal_);
180 }
181 }
182 }
183
OnMessageReceived(const IPC::Message & message)184 bool DesktopSessionProxy::OnMessageReceived(const IPC::Message& message) {
185 DCHECK(caller_task_runner_->BelongsToCurrentThread());
186
187 bool handled = true;
188 IPC_BEGIN_MESSAGE_MAP(DesktopSessionProxy, message)
189 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_AudioPacket,
190 OnAudioPacket)
191 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CaptureCompleted,
192 OnCaptureCompleted)
193 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_MouseCursor,
194 OnMouseCursor)
195 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CreateSharedBuffer,
196 OnCreateSharedBuffer)
197 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_ReleaseSharedBuffer,
198 OnReleaseSharedBuffer)
199 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_InjectClipboardEvent,
200 OnInjectClipboardEvent)
201 IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_DisconnectSession,
202 DisconnectSession);
203 IPC_END_MESSAGE_MAP()
204
205 CHECK(handled) << "Received unexpected IPC type: " << message.type();
206 return handled;
207 }
208
OnChannelConnected(int32 peer_pid)209 void DesktopSessionProxy::OnChannelConnected(int32 peer_pid) {
210 DCHECK(caller_task_runner_->BelongsToCurrentThread());
211
212 VLOG(1) << "IPC: network <- desktop (" << peer_pid << ")";
213 }
214
OnChannelError()215 void DesktopSessionProxy::OnChannelError() {
216 DCHECK(caller_task_runner_->BelongsToCurrentThread());
217
218 DetachFromDesktop();
219 }
220
AttachToDesktop(base::ProcessHandle desktop_process,IPC::PlatformFileForTransit desktop_pipe)221 bool DesktopSessionProxy::AttachToDesktop(
222 base::ProcessHandle desktop_process,
223 IPC::PlatformFileForTransit desktop_pipe) {
224 DCHECK(caller_task_runner_->BelongsToCurrentThread());
225 DCHECK(!desktop_channel_);
226 DCHECK_EQ(desktop_process_, base::kNullProcessHandle);
227
228 // Ignore the attach notification if the client session has been disconnected
229 // already.
230 if (!client_session_control_.get()) {
231 base::CloseProcessHandle(desktop_process);
232 return false;
233 }
234
235 desktop_process_ = desktop_process;
236
237 #if defined(OS_WIN)
238 // On Windows: |desktop_process| is a valid handle, but |desktop_pipe| needs
239 // to be duplicated from the desktop process.
240 HANDLE temp_handle;
241 if (!DuplicateHandle(desktop_process_, desktop_pipe, GetCurrentProcess(),
242 &temp_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
243 PLOG(ERROR) << "Failed to duplicate the desktop-to-network pipe handle";
244
245 desktop_process_ = base::kNullProcessHandle;
246 base::CloseProcessHandle(desktop_process);
247 return false;
248 }
249 base::win::ScopedHandle pipe(temp_handle);
250
251 IPC::ChannelHandle desktop_channel_handle(pipe.Get());
252
253 #elif defined(OS_POSIX)
254 // On posix: |desktop_pipe| is a valid file descriptor.
255 DCHECK(desktop_pipe.auto_close);
256
257 IPC::ChannelHandle desktop_channel_handle(std::string(), desktop_pipe);
258
259 #else
260 #error Unsupported platform.
261 #endif
262
263 // Connect to the desktop process.
264 desktop_channel_ = IPC::ChannelProxy::Create(desktop_channel_handle,
265 IPC::Channel::MODE_CLIENT,
266 this,
267 io_task_runner_.get());
268
269 // Pass ID of the client (which is authenticated at this point) to the desktop
270 // session agent and start the agent.
271 SendToDesktop(new ChromotingNetworkDesktopMsg_StartSessionAgent(
272 client_session_control_->client_jid(),
273 screen_resolution_,
274 virtual_terminal_));
275
276 return true;
277 }
278
DetachFromDesktop()279 void DesktopSessionProxy::DetachFromDesktop() {
280 DCHECK(caller_task_runner_->BelongsToCurrentThread());
281
282 desktop_channel_.reset();
283
284 if (desktop_process_ != base::kNullProcessHandle) {
285 base::CloseProcessHandle(desktop_process_);
286 desktop_process_ = base::kNullProcessHandle;
287 }
288
289 shared_buffers_.clear();
290
291 // Generate fake responses to keep the video capturer in sync.
292 while (pending_capture_frame_requests_) {
293 --pending_capture_frame_requests_;
294 PostCaptureCompleted(scoped_ptr<webrtc::DesktopFrame>());
295 }
296 }
297
SetAudioCapturer(const base::WeakPtr<IpcAudioCapturer> & audio_capturer)298 void DesktopSessionProxy::SetAudioCapturer(
299 const base::WeakPtr<IpcAudioCapturer>& audio_capturer) {
300 DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
301
302 audio_capturer_ = audio_capturer;
303 }
304
CaptureFrame()305 void DesktopSessionProxy::CaptureFrame() {
306 if (!caller_task_runner_->BelongsToCurrentThread()) {
307 caller_task_runner_->PostTask(
308 FROM_HERE, base::Bind(&DesktopSessionProxy::CaptureFrame, this));
309 return;
310 }
311
312 if (desktop_channel_) {
313 ++pending_capture_frame_requests_;
314 SendToDesktop(new ChromotingNetworkDesktopMsg_CaptureFrame());
315 } else {
316 PostCaptureCompleted(scoped_ptr<webrtc::DesktopFrame>());
317 }
318 }
319
SetVideoCapturer(const base::WeakPtr<IpcVideoFrameCapturer> video_capturer)320 void DesktopSessionProxy::SetVideoCapturer(
321 const base::WeakPtr<IpcVideoFrameCapturer> video_capturer) {
322 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
323
324 video_capturer_ = video_capturer;
325 }
326
SetMouseCursorMonitor(const base::WeakPtr<IpcMouseCursorMonitor> & mouse_cursor_monitor)327 void DesktopSessionProxy::SetMouseCursorMonitor(
328 const base::WeakPtr<IpcMouseCursorMonitor>& mouse_cursor_monitor) {
329 DCHECK(video_capture_task_runner_->BelongsToCurrentThread());
330
331 mouse_cursor_monitor_ = mouse_cursor_monitor;
332 }
333
DisconnectSession()334 void DesktopSessionProxy::DisconnectSession() {
335 DCHECK(caller_task_runner_->BelongsToCurrentThread());
336
337 // Disconnect the client session if it hasn't been disconnected yet.
338 if (client_session_control_.get())
339 client_session_control_->DisconnectSession();
340 }
341
InjectClipboardEvent(const protocol::ClipboardEvent & event)342 void DesktopSessionProxy::InjectClipboardEvent(
343 const protocol::ClipboardEvent& event) {
344 DCHECK(caller_task_runner_->BelongsToCurrentThread());
345
346 std::string serialized_event;
347 if (!event.SerializeToString(&serialized_event)) {
348 LOG(ERROR) << "Failed to serialize protocol::ClipboardEvent.";
349 return;
350 }
351
352 SendToDesktop(
353 new ChromotingNetworkDesktopMsg_InjectClipboardEvent(serialized_event));
354 }
355
InjectKeyEvent(const protocol::KeyEvent & event)356 void DesktopSessionProxy::InjectKeyEvent(const protocol::KeyEvent& event) {
357 DCHECK(caller_task_runner_->BelongsToCurrentThread());
358
359 std::string serialized_event;
360 if (!event.SerializeToString(&serialized_event)) {
361 LOG(ERROR) << "Failed to serialize protocol::KeyEvent.";
362 return;
363 }
364
365 SendToDesktop(
366 new ChromotingNetworkDesktopMsg_InjectKeyEvent(serialized_event));
367 }
368
InjectTextEvent(const protocol::TextEvent & event)369 void DesktopSessionProxy::InjectTextEvent(const protocol::TextEvent& event) {
370 DCHECK(caller_task_runner_->BelongsToCurrentThread());
371
372 std::string serialized_event;
373 if (!event.SerializeToString(&serialized_event)) {
374 LOG(ERROR) << "Failed to serialize protocol::TextEvent.";
375 return;
376 }
377
378 SendToDesktop(
379 new ChromotingNetworkDesktopMsg_InjectTextEvent(serialized_event));
380 }
381
InjectMouseEvent(const protocol::MouseEvent & event)382 void DesktopSessionProxy::InjectMouseEvent(const protocol::MouseEvent& event) {
383 DCHECK(caller_task_runner_->BelongsToCurrentThread());
384
385 std::string serialized_event;
386 if (!event.SerializeToString(&serialized_event)) {
387 LOG(ERROR) << "Failed to serialize protocol::MouseEvent.";
388 return;
389 }
390
391 SendToDesktop(
392 new ChromotingNetworkDesktopMsg_InjectMouseEvent(serialized_event));
393 }
394
StartInputInjector(scoped_ptr<protocol::ClipboardStub> client_clipboard)395 void DesktopSessionProxy::StartInputInjector(
396 scoped_ptr<protocol::ClipboardStub> client_clipboard) {
397 DCHECK(caller_task_runner_->BelongsToCurrentThread());
398
399 client_clipboard_ = client_clipboard.Pass();
400 }
401
SetScreenResolution(const ScreenResolution & resolution)402 void DesktopSessionProxy::SetScreenResolution(
403 const ScreenResolution& resolution) {
404 DCHECK(caller_task_runner_->BelongsToCurrentThread());
405
406 if (resolution.IsEmpty())
407 return;
408
409 screen_resolution_ = resolution;
410
411 // Connect to the desktop session if it is not done yet.
412 if (!is_desktop_session_connected_) {
413 is_desktop_session_connected_ = true;
414 if (desktop_session_connector_.get()) {
415 desktop_session_connector_->ConnectTerminal(
416 this, screen_resolution_, virtual_terminal_);
417 }
418 return;
419 }
420
421 // Pass the client's resolution to both daemon and desktop session agent.
422 // Depending on the session kind the screen resolution can be set by either
423 // the daemon (for example RDP sessions on Windows) or by the desktop session
424 // agent (when sharing the physical console).
425 if (desktop_session_connector_.get())
426 desktop_session_connector_->SetScreenResolution(this, screen_resolution_);
427 SendToDesktop(
428 new ChromotingNetworkDesktopMsg_SetScreenResolution(screen_resolution_));
429 }
430
~DesktopSessionProxy()431 DesktopSessionProxy::~DesktopSessionProxy() {
432 DCHECK(caller_task_runner_->BelongsToCurrentThread());
433
434 if (desktop_session_connector_.get() && is_desktop_session_connected_)
435 desktop_session_connector_->DisconnectTerminal(this);
436
437 if (desktop_process_ != base::kNullProcessHandle) {
438 base::CloseProcessHandle(desktop_process_);
439 desktop_process_ = base::kNullProcessHandle;
440 }
441 }
442
443 scoped_refptr<DesktopSessionProxy::IpcSharedBufferCore>
GetSharedBufferCore(int id)444 DesktopSessionProxy::GetSharedBufferCore(int id) {
445 DCHECK(caller_task_runner_->BelongsToCurrentThread());
446
447 SharedBuffers::const_iterator i = shared_buffers_.find(id);
448 if (i != shared_buffers_.end()) {
449 return i->second;
450 } else {
451 LOG(ERROR) << "Failed to find the shared buffer " << id;
452 return NULL;
453 }
454 }
455
OnAudioPacket(const std::string & serialized_packet)456 void DesktopSessionProxy::OnAudioPacket(const std::string& serialized_packet) {
457 DCHECK(caller_task_runner_->BelongsToCurrentThread());
458
459 // Parse a serialized audio packet. No further validation is done since
460 // the message was sent by more privileged process.
461 scoped_ptr<AudioPacket> packet(new AudioPacket());
462 if (!packet->ParseFromString(serialized_packet)) {
463 LOG(ERROR) << "Failed to parse AudioPacket.";
464 return;
465 }
466
467 // Pass a captured audio packet to |audio_capturer_|.
468 audio_capture_task_runner_->PostTask(
469 FROM_HERE, base::Bind(&IpcAudioCapturer::OnAudioPacket, audio_capturer_,
470 base::Passed(&packet)));
471 }
472
OnCreateSharedBuffer(int id,IPC::PlatformFileForTransit handle,uint32 size)473 void DesktopSessionProxy::OnCreateSharedBuffer(
474 int id,
475 IPC::PlatformFileForTransit handle,
476 uint32 size) {
477 DCHECK(caller_task_runner_->BelongsToCurrentThread());
478
479 scoped_refptr<IpcSharedBufferCore> shared_buffer =
480 new IpcSharedBufferCore(id, handle, desktop_process_, size);
481
482 if (shared_buffer->memory() != NULL &&
483 !shared_buffers_.insert(std::make_pair(id, shared_buffer)).second) {
484 LOG(ERROR) << "Duplicate shared buffer id " << id << " encountered";
485 }
486 }
487
OnReleaseSharedBuffer(int id)488 void DesktopSessionProxy::OnReleaseSharedBuffer(int id) {
489 DCHECK(caller_task_runner_->BelongsToCurrentThread());
490
491 // Drop the cached reference to the buffer.
492 shared_buffers_.erase(id);
493 }
494
OnCaptureCompleted(const SerializedDesktopFrame & serialized_frame)495 void DesktopSessionProxy::OnCaptureCompleted(
496 const SerializedDesktopFrame& serialized_frame) {
497 DCHECK(caller_task_runner_->BelongsToCurrentThread());
498
499 // Assume that |serialized_frame| is well-formed because it was received from
500 // a more privileged process.
501 scoped_refptr<IpcSharedBufferCore> shared_buffer_core =
502 GetSharedBufferCore(serialized_frame.shared_buffer_id);
503 CHECK(shared_buffer_core.get());
504
505 scoped_ptr<webrtc::DesktopFrame> frame(
506 new webrtc::SharedMemoryDesktopFrame(
507 serialized_frame.dimensions, serialized_frame.bytes_per_row,
508 new IpcSharedBuffer(shared_buffer_core)));
509 frame->set_capture_time_ms(serialized_frame.capture_time_ms);
510 frame->set_dpi(serialized_frame.dpi);
511
512 for (size_t i = 0; i < serialized_frame.dirty_region.size(); ++i) {
513 frame->mutable_updated_region()->AddRect(serialized_frame.dirty_region[i]);
514 }
515
516 --pending_capture_frame_requests_;
517 PostCaptureCompleted(frame.Pass());
518 }
519
OnMouseCursor(const webrtc::MouseCursor & mouse_cursor)520 void DesktopSessionProxy::OnMouseCursor(
521 const webrtc::MouseCursor& mouse_cursor) {
522 DCHECK(caller_task_runner_->BelongsToCurrentThread());
523 scoped_ptr<webrtc::MouseCursor> cursor(
524 webrtc::MouseCursor::CopyOf(mouse_cursor));
525 PostMouseCursor(cursor.Pass());
526 }
527
OnInjectClipboardEvent(const std::string & serialized_event)528 void DesktopSessionProxy::OnInjectClipboardEvent(
529 const std::string& serialized_event) {
530 DCHECK(caller_task_runner_->BelongsToCurrentThread());
531
532 if (client_clipboard_) {
533 protocol::ClipboardEvent event;
534 if (!event.ParseFromString(serialized_event)) {
535 LOG(ERROR) << "Failed to parse protocol::ClipboardEvent.";
536 return;
537 }
538
539 client_clipboard_->InjectClipboardEvent(event);
540 }
541 }
542
PostCaptureCompleted(scoped_ptr<webrtc::DesktopFrame> frame)543 void DesktopSessionProxy::PostCaptureCompleted(
544 scoped_ptr<webrtc::DesktopFrame> frame) {
545 DCHECK(caller_task_runner_->BelongsToCurrentThread());
546
547 video_capture_task_runner_->PostTask(
548 FROM_HERE,
549 base::Bind(&IpcVideoFrameCapturer::OnCaptureCompleted, video_capturer_,
550 base::Passed(&frame)));
551 }
552
PostMouseCursor(scoped_ptr<webrtc::MouseCursor> mouse_cursor)553 void DesktopSessionProxy::PostMouseCursor(
554 scoped_ptr<webrtc::MouseCursor> mouse_cursor) {
555 DCHECK(caller_task_runner_->BelongsToCurrentThread());
556
557 video_capture_task_runner_->PostTask(
558 FROM_HERE,
559 base::Bind(&IpcMouseCursorMonitor::OnMouseCursor, mouse_cursor_monitor_,
560 base::Passed(&mouse_cursor)));
561 }
562
SendToDesktop(IPC::Message * message)563 void DesktopSessionProxy::SendToDesktop(IPC::Message* message) {
564 DCHECK(caller_task_runner_->BelongsToCurrentThread());
565
566 if (desktop_channel_) {
567 desktop_channel_->Send(message);
568 } else {
569 delete message;
570 }
571 }
572
573 // static
Destruct(const DesktopSessionProxy * desktop_session_proxy)574 void DesktopSessionProxyTraits::Destruct(
575 const DesktopSessionProxy* desktop_session_proxy) {
576 desktop_session_proxy->caller_task_runner_->DeleteSoon(FROM_HERE,
577 desktop_session_proxy);
578 }
579
580 } // namespace remoting
581