OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/client/plugin/chromoting_instance.h" | 5 #include "remoting/client/plugin/chromoting_instance.h" |
6 | 6 |
| 7 #include <algorithm> |
7 #include <string> | 8 #include <string> |
8 #include <vector> | 9 #include <vector> |
9 | 10 |
10 #include "base/bind.h" | 11 #include "base/bind.h" |
11 #include "base/callback.h" | 12 #include "base/callback.h" |
12 #include "base/json/json_reader.h" | 13 #include "base/json/json_reader.h" |
13 #include "base/json/json_writer.h" | 14 #include "base/json/json_writer.h" |
14 #include "base/lazy_instance.h" | 15 #include "base/lazy_instance.h" |
15 #include "base/logging.h" | 16 #include "base/logging.h" |
16 #include "base/strings/string_split.h" | 17 #include "base/strings/string_split.h" |
17 #include "base/strings/stringprintf.h" | 18 #include "base/strings/stringprintf.h" |
18 #include "base/synchronization/lock.h" | 19 #include "base/synchronization/lock.h" |
19 #include "base/threading/thread.h" | 20 #include "base/threading/thread.h" |
20 #include "base/values.h" | 21 #include "base/values.h" |
21 #include "jingle/glue/thread_wrapper.h" | 22 #include "jingle/glue/thread_wrapper.h" |
22 #include "media/base/media.h" | 23 #include "media/base/media.h" |
23 #include "net/socket/ssl_server_socket.h" | 24 #include "net/socket/ssl_server_socket.h" |
24 #include "ppapi/cpp/completion_callback.h" | 25 #include "ppapi/cpp/completion_callback.h" |
25 #include "ppapi/cpp/dev/url_util_dev.h" | 26 #include "ppapi/cpp/dev/url_util_dev.h" |
| 27 #include "ppapi/cpp/image_data.h" |
26 #include "ppapi/cpp/input_event.h" | 28 #include "ppapi/cpp/input_event.h" |
27 #include "ppapi/cpp/mouse_cursor.h" | |
28 #include "ppapi/cpp/rect.h" | 29 #include "ppapi/cpp/rect.h" |
29 #include "remoting/base/constants.h" | 30 #include "remoting/base/constants.h" |
30 #include "remoting/base/util.h" | 31 #include "remoting/base/util.h" |
31 #include "remoting/client/chromoting_client.h" | 32 #include "remoting/client/chromoting_client.h" |
32 #include "remoting/client/client_config.h" | 33 #include "remoting/client/client_config.h" |
33 #include "remoting/client/frame_consumer_proxy.h" | 34 #include "remoting/client/frame_consumer_proxy.h" |
34 #include "remoting/client/plugin/delegating_signal_strategy.h" | 35 #include "remoting/client/plugin/delegating_signal_strategy.h" |
35 #include "remoting/client/plugin/pepper_audio_player.h" | 36 #include "remoting/client/plugin/pepper_audio_player.h" |
36 #include "remoting/client/plugin/pepper_input_handler.h" | 37 #include "remoting/client/plugin/pepper_input_handler.h" |
37 #include "remoting/client/plugin/pepper_port_allocator.h" | 38 #include "remoting/client/plugin/pepper_port_allocator.h" |
(...skipping 10 matching lines...) Expand all Loading... |
48 #undef PostMessage | 49 #undef PostMessage |
49 #endif | 50 #endif |
50 | 51 |
51 namespace remoting { | 52 namespace remoting { |
52 | 53 |
53 namespace { | 54 namespace { |
54 | 55 |
55 // 32-bit BGRA is 4 bytes per pixel. | 56 // 32-bit BGRA is 4 bytes per pixel. |
56 const int kBytesPerPixel = 4; | 57 const int kBytesPerPixel = 4; |
57 | 58 |
| 59 #if defined(ARCH_CPU_LITTLE_ENDIAN) |
| 60 const uint32_t kPixelAlphaMask = 0xff000000; |
| 61 #else // !defined(ARCH_CPU_LITTLE_ENDIAN) |
| 62 const uint32_t kPixelAlphaMask = 0x000000ff; |
| 63 #endif // !defined(ARCH_CPU_LITTLE_ENDIAN) |
| 64 |
58 // Default DPI to assume for old clients that use notifyClientResolution. | 65 // Default DPI to assume for old clients that use notifyClientResolution. |
59 const int kDefaultDPI = 96; | 66 const int kDefaultDPI = 96; |
60 | 67 |
61 // Interval at which to sample performance statistics. | 68 // Interval at which to sample performance statistics. |
62 const int kPerfStatsIntervalMs = 1000; | 69 const int kPerfStatsIntervalMs = 1000; |
63 | 70 |
64 // URL scheme used by Chrome apps and extensions. | 71 // URL scheme used by Chrome apps and extensions. |
65 const char kChromeExtensionUrlScheme[] = "chrome-extension"; | 72 const char kChromeExtensionUrlScheme[] = "chrome-extension"; |
66 | 73 |
67 // Maximum width and height of a mouse cursor supported by PPAPI. | 74 // Maximum width and height of a mouse cursor supported by PPAPI. |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
118 case protocol::CHANNEL_CONNECTION_ERROR: | 125 case protocol::CHANNEL_CONNECTION_ERROR: |
119 case protocol::SIGNALING_ERROR: | 126 case protocol::SIGNALING_ERROR: |
120 case protocol::SIGNALING_TIMEOUT: | 127 case protocol::SIGNALING_TIMEOUT: |
121 case protocol::UNKNOWN_ERROR: | 128 case protocol::UNKNOWN_ERROR: |
122 return "NETWORK_FAILURE"; | 129 return "NETWORK_FAILURE"; |
123 } | 130 } |
124 DLOG(FATAL) << "Unknown error code" << error; | 131 DLOG(FATAL) << "Unknown error code" << error; |
125 return std::string(); | 132 return std::string(); |
126 } | 133 } |
127 | 134 |
| 135 // Returns true if |pixel| is not completely transparent. |
| 136 bool IsVisiblePixel(uint32_t pixel) { |
| 137 return (pixel & kPixelAlphaMask) != 0; |
| 138 } |
| 139 |
| 140 // Returns true if there is at least one visible pixel in the given range. |
| 141 bool IsVisibleRow(const uint32_t* begin, const uint32_t* end) { |
| 142 return std::find_if(begin, end, &IsVisiblePixel) != end; |
| 143 } |
| 144 |
128 // This flag blocks LOGs to the UI if we're already in the middle of logging | 145 // This flag blocks LOGs to the UI if we're already in the middle of logging |
129 // to the UI. This prevents a potential infinite loop if we encounter an error | 146 // to the UI. This prevents a potential infinite loop if we encounter an error |
130 // while sending the log message to the UI. | 147 // while sending the log message to the UI. |
131 bool g_logging_to_plugin = false; | 148 bool g_logging_to_plugin = false; |
132 bool g_has_logging_instance = false; | 149 bool g_has_logging_instance = false; |
133 base::LazyInstance<scoped_refptr<base::SingleThreadTaskRunner> >::Leaky | 150 base::LazyInstance<scoped_refptr<base::SingleThreadTaskRunner> >::Leaky |
134 g_logging_task_runner = LAZY_INSTANCE_INITIALIZER; | 151 g_logging_task_runner = LAZY_INSTANCE_INITIALIZER; |
135 base::LazyInstance<base::WeakPtr<ChromotingInstance> >::Leaky | 152 base::LazyInstance<base::WeakPtr<ChromotingInstance> >::Leaky |
136 g_logging_instance = LAZY_INSTANCE_INITIALIZER; | 153 g_logging_instance = LAZY_INSTANCE_INITIALIZER; |
137 base::LazyInstance<base::Lock>::Leaky | 154 base::LazyInstance<base::Lock>::Leaky |
138 g_logging_lock = LAZY_INSTANCE_INITIALIZER; | 155 g_logging_lock = LAZY_INSTANCE_INITIALIZER; |
139 logging::LogMessageHandlerFunction g_logging_old_handler = NULL; | 156 logging::LogMessageHandlerFunction g_logging_old_handler = NULL; |
140 | 157 |
141 } // namespace | 158 } // namespace |
142 | 159 |
143 // String sent in the "hello" message to the webapp to describe features. | 160 // String sent in the "hello" message to the webapp to describe features. |
144 const char ChromotingInstance::kApiFeatures[] = | 161 const char ChromotingInstance::kApiFeatures[] = |
145 "highQualityScaling injectKeyEvent sendClipboardItem remapKey trapKey " | 162 "highQualityScaling injectKeyEvent sendClipboardItem remapKey trapKey " |
146 "notifyClientResolution pauseVideo pauseAudio asyncPin thirdPartyAuth " | 163 "notifyClientResolution pauseVideo pauseAudio asyncPin thirdPartyAuth " |
147 "pinlessAuth extensionMessage"; | 164 "pinlessAuth extensionMessage allowMouseLock"; |
148 | 165 |
149 const char ChromotingInstance::kRequestedCapabilities[] = ""; | 166 const char ChromotingInstance::kRequestedCapabilities[] = ""; |
150 const char ChromotingInstance::kSupportedCapabilities[] = "desktopShape"; | 167 const char ChromotingInstance::kSupportedCapabilities[] = "desktopShape"; |
151 | 168 |
152 bool ChromotingInstance::ParseAuthMethods(const std::string& auth_methods_str, | 169 bool ChromotingInstance::ParseAuthMethods(const std::string& auth_methods_str, |
153 ClientConfig* config) { | 170 ClientConfig* config) { |
154 std::vector<std::string> auth_methods; | 171 std::vector<std::string> auth_methods; |
155 base::SplitString(auth_methods_str, ',', &auth_methods); | 172 base::SplitString(auth_methods_str, ',', &auth_methods); |
156 for (std::vector<std::string>::iterator it = auth_methods.begin(); | 173 for (std::vector<std::string>::iterator it = auth_methods.begin(); |
157 it != auth_methods.end(); ++it) { | 174 it != auth_methods.end(); ++it) { |
(...skipping 11 matching lines...) Expand all Loading... |
169 } | 186 } |
170 | 187 |
171 ChromotingInstance::ChromotingInstance(PP_Instance pp_instance) | 188 ChromotingInstance::ChromotingInstance(PP_Instance pp_instance) |
172 : pp::Instance(pp_instance), | 189 : pp::Instance(pp_instance), |
173 initialized_(false), | 190 initialized_(false), |
174 plugin_task_runner_(new PluginThreadTaskRunner(&plugin_thread_delegate_)), | 191 plugin_task_runner_(new PluginThreadTaskRunner(&plugin_thread_delegate_)), |
175 context_(plugin_task_runner_.get()), | 192 context_(plugin_task_runner_.get()), |
176 input_tracker_(&mouse_input_filter_), | 193 input_tracker_(&mouse_input_filter_), |
177 key_mapper_(&input_tracker_), | 194 key_mapper_(&input_tracker_), |
178 normalizing_input_filter_(CreateNormalizingInputFilter(&key_mapper_)), | 195 normalizing_input_filter_(CreateNormalizingInputFilter(&key_mapper_)), |
179 input_handler_(normalizing_input_filter_.get()), | 196 input_handler_(this, normalizing_input_filter_.get()), |
180 use_async_pin_dialog_(false), | 197 use_async_pin_dialog_(false), |
181 weak_factory_(this) { | 198 weak_factory_(this) { |
182 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL); | 199 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL); |
183 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); | 200 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); |
184 | 201 |
185 // Resister this instance to handle debug log messsages. | 202 // Resister this instance to handle debug log messsages. |
186 RegisterLoggingInstance(); | 203 RegisterLoggingInstance(); |
187 | 204 |
188 // Send hello message. | 205 // Send hello message. |
189 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); | 206 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
297 } else if (method == "useAsyncPinDialog") { | 314 } else if (method == "useAsyncPinDialog") { |
298 use_async_pin_dialog_ = true; | 315 use_async_pin_dialog_ = true; |
299 } else if (method == "onPinFetched") { | 316 } else if (method == "onPinFetched") { |
300 HandleOnPinFetched(*data); | 317 HandleOnPinFetched(*data); |
301 } else if (method == "onThirdPartyTokenFetched") { | 318 } else if (method == "onThirdPartyTokenFetched") { |
302 HandleOnThirdPartyTokenFetched(*data); | 319 HandleOnThirdPartyTokenFetched(*data); |
303 } else if (method == "requestPairing") { | 320 } else if (method == "requestPairing") { |
304 HandleRequestPairing(*data); | 321 HandleRequestPairing(*data); |
305 } else if (method == "extensionMessage") { | 322 } else if (method == "extensionMessage") { |
306 HandleExtensionMessage(*data); | 323 HandleExtensionMessage(*data); |
| 324 } else if (method == "allowMouseLock") { |
| 325 HandleAllowMouseLockMessage(); |
307 } | 326 } |
308 } | 327 } |
309 | 328 |
| 329 void ChromotingInstance::DidChangeFocus(bool has_focus) { |
| 330 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); |
| 331 |
| 332 input_handler_.DidChangeFocus(has_focus); |
| 333 } |
| 334 |
310 void ChromotingInstance::DidChangeView(const pp::View& view) { | 335 void ChromotingInstance::DidChangeView(const pp::View& view) { |
311 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); | 336 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); |
312 | 337 |
313 plugin_view_ = view; | 338 plugin_view_ = view; |
314 if (view_) { | 339 if (view_) { |
315 view_->SetView(view); | 340 view_->SetView(view); |
316 mouse_input_filter_.set_input_size(view_->get_view_size_dips()); | 341 mouse_input_filter_.set_input_size(view_->get_view_size_dips()); |
317 } | 342 } |
318 } | 343 } |
319 | 344 |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
457 void ChromotingInstance::InjectClipboardEvent( | 482 void ChromotingInstance::InjectClipboardEvent( |
458 const protocol::ClipboardEvent& event) { | 483 const protocol::ClipboardEvent& event) { |
459 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); | 484 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); |
460 data->SetString("mimeType", event.mime_type()); | 485 data->SetString("mimeType", event.mime_type()); |
461 data->SetString("item", event.data()); | 486 data->SetString("item", event.data()); |
462 PostChromotingMessage("injectClipboardItem", data.Pass()); | 487 PostChromotingMessage("injectClipboardItem", data.Pass()); |
463 } | 488 } |
464 | 489 |
465 void ChromotingInstance::SetCursorShape( | 490 void ChromotingInstance::SetCursorShape( |
466 const protocol::CursorShapeInfo& cursor_shape) { | 491 const protocol::CursorShapeInfo& cursor_shape) { |
| 492 COMPILE_ASSERT(sizeof(uint32_t) == kBytesPerPixel, rgba_pixels_are_32bit); |
| 493 |
467 if (!cursor_shape.has_data() || | 494 if (!cursor_shape.has_data() || |
468 !cursor_shape.has_width() || | 495 !cursor_shape.has_width() || |
469 !cursor_shape.has_height() || | 496 !cursor_shape.has_height() || |
470 !cursor_shape.has_hotspot_x() || | 497 !cursor_shape.has_hotspot_x() || |
471 !cursor_shape.has_hotspot_y()) { | 498 !cursor_shape.has_hotspot_y()) { |
472 return; | 499 return; |
473 } | 500 } |
474 | 501 |
475 int width = cursor_shape.width(); | 502 int width = cursor_shape.width(); |
476 int height = cursor_shape.height(); | 503 int height = cursor_shape.height(); |
(...skipping 16 matching lines...) Expand all Loading... |
493 } | 520 } |
494 | 521 |
495 if (pp::ImageData::GetNativeImageDataFormat() != | 522 if (pp::ImageData::GetNativeImageDataFormat() != |
496 PP_IMAGEDATAFORMAT_BGRA_PREMUL) { | 523 PP_IMAGEDATAFORMAT_BGRA_PREMUL) { |
497 VLOG(2) << "Unable to set cursor shape - non-native image format"; | 524 VLOG(2) << "Unable to set cursor shape - non-native image format"; |
498 return; | 525 return; |
499 } | 526 } |
500 | 527 |
501 int hotspot_x = cursor_shape.hotspot_x(); | 528 int hotspot_x = cursor_shape.hotspot_x(); |
502 int hotspot_y = cursor_shape.hotspot_y(); | 529 int hotspot_y = cursor_shape.hotspot_y(); |
| 530 int bytes_per_row = width * kBytesPerPixel; |
| 531 int src_stride = width; |
| 532 const uint32_t* src_row_data = reinterpret_cast<const uint32_t*>( |
| 533 cursor_shape.data().data()); |
| 534 const uint32_t* src_row_data_end = src_row_data + src_stride * height; |
503 | 535 |
504 int bytes_per_row = width * kBytesPerPixel; | 536 scoped_ptr<pp::ImageData> cursor_image; |
505 const uint8* src_row_data = reinterpret_cast<const uint8*>( | 537 pp::Point cursor_hotspot; |
506 cursor_shape.data().data()); | |
507 int stride = bytes_per_row; | |
508 | 538 |
509 // If the cursor exceeds the size permitted by PPAPI then crop it, keeping | 539 // Check if the cursor is visible. |
510 // the hotspot as close to the center of the new cursor shape as possible. | 540 if (IsVisibleRow(src_row_data, src_row_data_end)) { |
511 if (height > kMaxCursorHeight) { | 541 // If the cursor exceeds the size permitted by PPAPI then crop it, keeping |
512 int y = hotspot_y - (kMaxCursorHeight / 2); | 542 // the hotspot as close to the center of the new cursor shape as possible. |
513 y = std::max(y, 0); | 543 if (height > kMaxCursorHeight) { |
514 y = std::min(y, height - kMaxCursorHeight); | 544 int y = hotspot_y - (kMaxCursorHeight / 2); |
| 545 y = std::max(y, 0); |
| 546 y = std::min(y, height - kMaxCursorHeight); |
515 | 547 |
516 src_row_data += stride * y; | 548 src_row_data += src_stride * y; |
517 height = kMaxCursorHeight; | 549 height = kMaxCursorHeight; |
518 hotspot_y -= y; | 550 hotspot_y -= y; |
519 } | 551 } |
520 if (width > kMaxCursorWidth) { | 552 if (width > kMaxCursorWidth) { |
521 int x = hotspot_x - (kMaxCursorWidth / 2); | 553 int x = hotspot_x - (kMaxCursorWidth / 2); |
522 x = std::max(x, 0); | 554 x = std::max(x, 0); |
523 x = std::min(x, height - kMaxCursorWidth); | 555 x = std::min(x, height - kMaxCursorWidth); |
524 | 556 |
525 src_row_data += x * kBytesPerPixel; | 557 src_row_data += x; |
526 width = kMaxCursorWidth; | 558 width = kMaxCursorWidth; |
527 bytes_per_row = width * kBytesPerPixel; | 559 bytes_per_row = width * kBytesPerPixel; |
528 hotspot_x -= x; | 560 hotspot_x -= x; |
| 561 } |
| 562 |
| 563 cursor_image.reset(new pp::ImageData(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, |
| 564 pp::Size(width, height), false)); |
| 565 cursor_hotspot = pp::Point(hotspot_x, hotspot_y); |
| 566 |
| 567 uint8* dst_row_data = reinterpret_cast<uint8*>(cursor_image->data()); |
| 568 for (int row = 0; row < height; row++) { |
| 569 memcpy(dst_row_data, src_row_data, bytes_per_row); |
| 570 src_row_data += src_stride; |
| 571 dst_row_data += cursor_image->stride(); |
| 572 } |
529 } | 573 } |
530 | 574 |
531 pp::ImageData cursor_image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, | 575 input_handler_.SetMouseCursor(cursor_image.Pass(), cursor_hotspot); |
532 pp::Size(width, height), false); | |
533 | |
534 uint8* dst_row_data = reinterpret_cast<uint8*>(cursor_image.data()); | |
535 for (int row = 0; row < height; row++) { | |
536 memcpy(dst_row_data, src_row_data, bytes_per_row); | |
537 src_row_data += stride; | |
538 dst_row_data += cursor_image.stride(); | |
539 } | |
540 | |
541 pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, | |
542 cursor_image, | |
543 pp::Point(hotspot_x, hotspot_y)); | |
544 } | 576 } |
545 | 577 |
546 void ChromotingInstance::OnFirstFrameReceived() { | 578 void ChromotingInstance::OnFirstFrameReceived() { |
547 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); | 579 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); |
548 PostChromotingMessage("onFirstFrameReceived", data.Pass()); | 580 PostChromotingMessage("onFirstFrameReceived", data.Pass()); |
549 } | 581 } |
550 | 582 |
551 void ChromotingInstance::HandleConnect(const base::DictionaryValue& data) { | 583 void ChromotingInstance::HandleConnect(const base::DictionaryValue& data) { |
552 ClientConfig config; | 584 ClientConfig config; |
553 std::string local_jid; | 585 std::string local_jid; |
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
853 } | 885 } |
854 if (!IsConnected()) { | 886 if (!IsConnected()) { |
855 return; | 887 return; |
856 } | 888 } |
857 protocol::ExtensionMessage message; | 889 protocol::ExtensionMessage message; |
858 message.set_type(type); | 890 message.set_type(type); |
859 message.set_data(message_data); | 891 message.set_data(message_data); |
860 host_connection_->host_stub()->DeliverClientMessage(message); | 892 host_connection_->host_stub()->DeliverClientMessage(message); |
861 } | 893 } |
862 | 894 |
| 895 void ChromotingInstance::HandleAllowMouseLockMessage() { |
| 896 input_handler_.AllowMouseLock(); |
| 897 } |
| 898 |
863 ChromotingStats* ChromotingInstance::GetStats() { | 899 ChromotingStats* ChromotingInstance::GetStats() { |
864 if (!client_.get()) | 900 if (!client_.get()) |
865 return NULL; | 901 return NULL; |
866 return client_->GetStats(); | 902 return client_->GetStats(); |
867 } | 903 } |
868 | 904 |
869 void ChromotingInstance::PostChromotingMessage( | 905 void ChromotingInstance::PostChromotingMessage( |
870 const std::string& method, | 906 const std::string& method, |
871 scoped_ptr<base::DictionaryValue> data) { | 907 scoped_ptr<base::DictionaryValue> data) { |
872 scoped_ptr<base::DictionaryValue> message(new base::DictionaryValue()); | 908 scoped_ptr<base::DictionaryValue> message(new base::DictionaryValue()); |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1030 url_components.scheme.len); | 1066 url_components.scheme.len); |
1031 return url_scheme == kChromeExtensionUrlScheme; | 1067 return url_scheme == kChromeExtensionUrlScheme; |
1032 } | 1068 } |
1033 | 1069 |
1034 bool ChromotingInstance::IsConnected() { | 1070 bool ChromotingInstance::IsConnected() { |
1035 return host_connection_.get() && | 1071 return host_connection_.get() && |
1036 (host_connection_->state() == protocol::ConnectionToHost::CONNECTED); | 1072 (host_connection_->state() == protocol::ConnectionToHost::CONNECTED); |
1037 } | 1073 } |
1038 | 1074 |
1039 } // namespace remoting | 1075 } // namespace remoting |
OLD | NEW |