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/client/plugin/chromoting_instance.h" 6 7 #include <algorithm> 8 #include <string> 9 #include <vector> 10 11 #if defined(OS_NACL) 12 #include <sys/mount.h> 13 #include <nacl_io/nacl_io.h> 14 #endif 15 16 #include "base/bind.h" 17 #include "base/callback.h" 18 #include "base/json/json_reader.h" 19 #include "base/json/json_writer.h" 20 #include "base/lazy_instance.h" 21 #include "base/logging.h" 22 #include "base/strings/string_split.h" 23 #include "base/strings/stringprintf.h" 24 #include "base/synchronization/lock.h" 25 #include "base/threading/thread.h" 26 #include "base/values.h" 27 #include "crypto/random.h" 28 #include "jingle/glue/thread_wrapper.h" 29 #include "media/base/yuv_convert.h" 30 #include "net/socket/ssl_server_socket.h" 31 #include "ppapi/cpp/completion_callback.h" 32 #include "ppapi/cpp/dev/url_util_dev.h" 33 #include "ppapi/cpp/image_data.h" 34 #include "ppapi/cpp/input_event.h" 35 #include "ppapi/cpp/rect.h" 36 #include "ppapi/cpp/var_array_buffer.h" 37 #include "ppapi/cpp/var_dictionary.h" 38 #include "remoting/base/constants.h" 39 #include "remoting/base/util.h" 40 #include "remoting/client/chromoting_client.h" 41 #include "remoting/client/client_config.h" 42 #include "remoting/client/frame_consumer_proxy.h" 43 #include "remoting/client/plugin/delegating_signal_strategy.h" 44 #include "remoting/client/plugin/media_source_video_renderer.h" 45 #include "remoting/client/plugin/normalizing_input_filter_cros.h" 46 #include "remoting/client/plugin/normalizing_input_filter_mac.h" 47 #include "remoting/client/plugin/pepper_audio_player.h" 48 #include "remoting/client/plugin/pepper_input_handler.h" 49 #include "remoting/client/plugin/pepper_port_allocator.h" 50 #include "remoting/client/plugin/pepper_view.h" 51 #include "remoting/client/software_video_renderer.h" 52 #include "remoting/client/token_fetcher_proxy.h" 53 #include "remoting/protocol/connection_to_host.h" 54 #include "remoting/protocol/host_stub.h" 55 #include "remoting/protocol/libjingle_transport_factory.h" 56 #include "third_party/libjingle/source/talk/base/helpers.h" 57 #include "third_party/libjingle/source/talk/base/ssladapter.h" 58 #include "url/gurl.h" 59 60 // Windows defines 'PostMessage', so we have to undef it. 61 #if defined(PostMessage) 62 #undef PostMessage 63 #endif 64 65 namespace remoting { 66 67 namespace { 68 69 // 32-bit BGRA is 4 bytes per pixel. 70 const int kBytesPerPixel = 4; 71 72 #if defined(ARCH_CPU_LITTLE_ENDIAN) 73 const uint32_t kPixelAlphaMask = 0xff000000; 74 #else // !defined(ARCH_CPU_LITTLE_ENDIAN) 75 const uint32_t kPixelAlphaMask = 0x000000ff; 76 #endif // !defined(ARCH_CPU_LITTLE_ENDIAN) 77 78 // Default DPI to assume for old clients that use notifyClientResolution. 79 const int kDefaultDPI = 96; 80 81 // Interval at which to sample performance statistics. 82 const int kPerfStatsIntervalMs = 1000; 83 84 // URL scheme used by Chrome apps and extensions. 85 const char kChromeExtensionUrlScheme[] = "chrome-extension"; 86 87 // Maximum width and height of a mouse cursor supported by PPAPI. 88 const int kMaxCursorWidth = 32; 89 const int kMaxCursorHeight = 32; 90 91 #if defined(USE_OPENSSL) 92 // Size of the random seed blob used to initialize RNG in libjingle. Libjingle 93 // uses the seed only for OpenSSL builds. OpenSSL needs at least 32 bytes of 94 // entropy (see http://wiki.openssl.org/index.php/Random_Numbers), but stores 95 // 1039 bytes of state, so we initialize it with 1k or random data. 96 const int kRandomSeedSize = 1024; 97 #endif // defined(USE_OPENSSL) 98 99 std::string ConnectionStateToString(protocol::ConnectionToHost::State state) { 100 // Values returned by this function must match the 101 // remoting.ClientSession.State enum in JS code. 102 switch (state) { 103 case protocol::ConnectionToHost::INITIALIZING: 104 return "INITIALIZING"; 105 case protocol::ConnectionToHost::CONNECTING: 106 return "CONNECTING"; 107 case protocol::ConnectionToHost::AUTHENTICATED: 108 // Report the authenticated state as 'CONNECTING' to avoid changing 109 // the interface between the plugin and webapp. 110 return "CONNECTING"; 111 case protocol::ConnectionToHost::CONNECTED: 112 return "CONNECTED"; 113 case protocol::ConnectionToHost::CLOSED: 114 return "CLOSED"; 115 case protocol::ConnectionToHost::FAILED: 116 return "FAILED"; 117 } 118 NOTREACHED(); 119 return std::string(); 120 } 121 122 // TODO(sergeyu): Ideally we should just pass ErrorCode to the webapp 123 // and let it handle it, but it would be hard to fix it now because 124 // client plugin and webapp versions may not be in sync. It should be 125 // easy to do after we are finished moving the client plugin to NaCl. 126 std::string ConnectionErrorToString(protocol::ErrorCode error) { 127 // Values returned by this function must match the 128 // remoting.ClientSession.Error enum in JS code. 129 switch (error) { 130 case protocol::OK: 131 return "NONE"; 132 133 case protocol::PEER_IS_OFFLINE: 134 return "HOST_IS_OFFLINE"; 135 136 case protocol::SESSION_REJECTED: 137 case protocol::AUTHENTICATION_FAILED: 138 return "SESSION_REJECTED"; 139 140 case protocol::INCOMPATIBLE_PROTOCOL: 141 return "INCOMPATIBLE_PROTOCOL"; 142 143 case protocol::HOST_OVERLOAD: 144 return "HOST_OVERLOAD"; 145 146 case protocol::CHANNEL_CONNECTION_ERROR: 147 case protocol::SIGNALING_ERROR: 148 case protocol::SIGNALING_TIMEOUT: 149 case protocol::UNKNOWN_ERROR: 150 return "NETWORK_FAILURE"; 151 } 152 DLOG(FATAL) << "Unknown error code" << error; 153 return std::string(); 154 } 155 156 // Returns true if |pixel| is not completely transparent. 157 bool IsVisiblePixel(uint32_t pixel) { 158 return (pixel & kPixelAlphaMask) != 0; 159 } 160 161 // Returns true if there is at least one visible pixel in the given range. 162 bool IsVisibleRow(const uint32_t* begin, const uint32_t* end) { 163 return std::find_if(begin, end, &IsVisiblePixel) != end; 164 } 165 166 // This flag blocks LOGs to the UI if we're already in the middle of logging 167 // to the UI. This prevents a potential infinite loop if we encounter an error 168 // while sending the log message to the UI. 169 bool g_logging_to_plugin = false; 170 bool g_has_logging_instance = false; 171 base::LazyInstance<scoped_refptr<base::SingleThreadTaskRunner> >::Leaky 172 g_logging_task_runner = LAZY_INSTANCE_INITIALIZER; 173 base::LazyInstance<base::WeakPtr<ChromotingInstance> >::Leaky 174 g_logging_instance = LAZY_INSTANCE_INITIALIZER; 175 base::LazyInstance<base::Lock>::Leaky 176 g_logging_lock = LAZY_INSTANCE_INITIALIZER; 177 logging::LogMessageHandlerFunction g_logging_old_handler = NULL; 178 179 } // namespace 180 181 // String sent in the "hello" message to the webapp to describe features. 182 const char ChromotingInstance::kApiFeatures[] = 183 "highQualityScaling injectKeyEvent sendClipboardItem remapKey trapKey " 184 "notifyClientResolution pauseVideo pauseAudio asyncPin thirdPartyAuth " 185 "pinlessAuth extensionMessage allowMouseLock mediaSourceRendering " 186 "videoControl"; 187 188 const char ChromotingInstance::kRequestedCapabilities[] = ""; 189 const char ChromotingInstance::kSupportedCapabilities[] = "desktopShape"; 190 191 bool ChromotingInstance::ParseAuthMethods(const std::string& auth_methods_str, 192 ClientConfig* config) { 193 std::vector<std::string> auth_methods; 194 base::SplitString(auth_methods_str, ',', &auth_methods); 195 for (std::vector<std::string>::iterator it = auth_methods.begin(); 196 it != auth_methods.end(); ++it) { 197 protocol::AuthenticationMethod authentication_method = 198 protocol::AuthenticationMethod::FromString(*it); 199 if (authentication_method.is_valid()) 200 config->authentication_methods.push_back(authentication_method); 201 } 202 if (config->authentication_methods.empty()) { 203 LOG(ERROR) << "No valid authentication methods specified."; 204 return false; 205 } 206 207 return true; 208 } 209 210 ChromotingInstance::ChromotingInstance(PP_Instance pp_instance) 211 : pp::Instance(pp_instance), 212 initialized_(false), 213 plugin_task_runner_(new PluginThreadTaskRunner(&plugin_thread_delegate_)), 214 context_(plugin_task_runner_.get()), 215 input_tracker_(&mouse_input_filter_), 216 key_mapper_(&input_tracker_), 217 input_handler_(this), 218 use_async_pin_dialog_(false), 219 use_media_source_rendering_(false), 220 delegate_large_cursors_(false), 221 weak_factory_(this) { 222 #if defined(OS_NACL) 223 // In NaCl global resources need to be initialized differently because they 224 // are not shared with Chrome. 225 thread_task_runner_handle_.reset( 226 new base::ThreadTaskRunnerHandle(plugin_task_runner_)); 227 thread_wrapper_.reset( 228 new jingle_glue::JingleThreadWrapper(plugin_task_runner_)); 229 media::InitializeCPUSpecificYUVConversions(); 230 #else 231 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop(); 232 #endif 233 234 #if defined(OS_NACL) 235 nacl_io_init_ppapi(pp_instance, pp::Module::Get()->get_browser_interface()); 236 mount("", "/etc", "memfs", 0, ""); 237 mount("", "/usr", "memfs", 0, ""); 238 #endif 239 240 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL); 241 RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); 242 243 // Resister this instance to handle debug log messsages. 244 RegisterLoggingInstance(); 245 246 #if defined(USE_OPENSSL) 247 // Initialize random seed for libjingle. It's necessary only with OpenSSL. 248 char random_seed[kRandomSeedSize]; 249 crypto::RandBytes(random_seed, sizeof(random_seed)); 250 talk_base::InitRandom(random_seed, sizeof(random_seed)); 251 #else 252 // Libjingle's SSL implementation is not really used, but it has to be 253 // initialized for NSS builds to make sure that RNG is initialized in NSS, 254 // because libjingle uses it. 255 talk_base::InitializeSSL(); 256 #endif // !defined(USE_OPENSSL) 257 258 // Send hello message. 259 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 260 data->SetInteger("apiVersion", kApiVersion); 261 data->SetString("apiFeatures", kApiFeatures); 262 data->SetInteger("apiMinVersion", kApiMinMessagingVersion); 263 data->SetString("requestedCapabilities", kRequestedCapabilities); 264 data->SetString("supportedCapabilities", kSupportedCapabilities); 265 266 PostLegacyJsonMessage("hello", data.Pass()); 267 } 268 269 ChromotingInstance::~ChromotingInstance() { 270 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 271 272 // Unregister this instance so that debug log messages will no longer be sent 273 // to it. This will stop all logging in all Chromoting instances. 274 UnregisterLoggingInstance(); 275 276 // PepperView must be destroyed before the client. 277 view_weak_factory_.reset(); 278 view_.reset(); 279 280 client_.reset(); 281 282 plugin_task_runner_->Quit(); 283 284 // Ensure that nothing touches the plugin thread delegate after this point. 285 plugin_task_runner_->DetachAndRunShutdownLoop(); 286 287 // Stopping the context shuts down all chromoting threads. 288 context_.Stop(); 289 } 290 291 bool ChromotingInstance::Init(uint32_t argc, 292 const char* argn[], 293 const char* argv[]) { 294 CHECK(!initialized_); 295 initialized_ = true; 296 297 VLOG(1) << "Started ChromotingInstance::Init"; 298 299 // Check that the calling content is part of an app or extension. This is only 300 // necessary for non-PNaCl version of the plugin. Also PPB_URLUtil_Dev doesn't 301 // work in NaCl at the moment so the check fails in NaCl builds. 302 #if !defined(OS_NACL) 303 if (!IsCallerAppOrExtension()) { 304 LOG(ERROR) << "Not an app or extension"; 305 return false; 306 } 307 #endif 308 309 // Start all the threads. 310 context_.Start(); 311 312 return true; 313 } 314 315 void ChromotingInstance::HandleMessage(const pp::Var& message) { 316 if (!message.is_string()) { 317 LOG(ERROR) << "Received a message that is not a string."; 318 return; 319 } 320 321 scoped_ptr<base::Value> json( 322 base::JSONReader::Read(message.AsString(), 323 base::JSON_ALLOW_TRAILING_COMMAS)); 324 base::DictionaryValue* message_dict = NULL; 325 std::string method; 326 base::DictionaryValue* data = NULL; 327 if (!json.get() || 328 !json->GetAsDictionary(&message_dict) || 329 !message_dict->GetString("method", &method) || 330 !message_dict->GetDictionary("data", &data)) { 331 LOG(ERROR) << "Received invalid message:" << message.AsString(); 332 return; 333 } 334 335 if (method == "connect") { 336 HandleConnect(*data); 337 } else if (method == "disconnect") { 338 HandleDisconnect(*data); 339 } else if (method == "incomingIq") { 340 HandleOnIncomingIq(*data); 341 } else if (method == "releaseAllKeys") { 342 HandleReleaseAllKeys(*data); 343 } else if (method == "injectKeyEvent") { 344 HandleInjectKeyEvent(*data); 345 } else if (method == "remapKey") { 346 HandleRemapKey(*data); 347 } else if (method == "trapKey") { 348 HandleTrapKey(*data); 349 } else if (method == "sendClipboardItem") { 350 HandleSendClipboardItem(*data); 351 } else if (method == "notifyClientResolution") { 352 HandleNotifyClientResolution(*data); 353 } else if (method == "pauseVideo") { 354 HandlePauseVideo(*data); 355 } else if (method == "videoControl") { 356 HandleVideoControl(*data); 357 } else if (method == "pauseAudio") { 358 HandlePauseAudio(*data); 359 } else if (method == "useAsyncPinDialog") { 360 use_async_pin_dialog_ = true; 361 } else if (method == "onPinFetched") { 362 HandleOnPinFetched(*data); 363 } else if (method == "onThirdPartyTokenFetched") { 364 HandleOnThirdPartyTokenFetched(*data); 365 } else if (method == "requestPairing") { 366 HandleRequestPairing(*data); 367 } else if (method == "extensionMessage") { 368 HandleExtensionMessage(*data); 369 } else if (method == "allowMouseLock") { 370 HandleAllowMouseLockMessage(); 371 } else if (method == "enableMediaSourceRendering") { 372 HandleEnableMediaSourceRendering(); 373 } else if (method == "sendMouseInputWhenUnfocused") { 374 HandleSendMouseInputWhenUnfocused(); 375 } else if (method == "delegateLargeCursors") { 376 HandleDelegateLargeCursors(); 377 } 378 } 379 380 void ChromotingInstance::DidChangeFocus(bool has_focus) { 381 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 382 383 if (!IsConnected()) 384 return; 385 386 input_handler_.DidChangeFocus(has_focus); 387 } 388 389 void ChromotingInstance::DidChangeView(const pp::View& view) { 390 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 391 392 plugin_view_ = view; 393 mouse_input_filter_.set_input_size( 394 webrtc::DesktopSize(view.GetRect().width(), view.GetRect().height())); 395 396 if (view_) 397 view_->SetView(view); 398 } 399 400 bool ChromotingInstance::HandleInputEvent(const pp::InputEvent& event) { 401 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 402 403 if (!IsConnected()) 404 return false; 405 406 return input_handler_.HandleInputEvent(event); 407 } 408 409 void ChromotingInstance::SetDesktopSize(const webrtc::DesktopSize& size, 410 const webrtc::DesktopVector& dpi) { 411 mouse_input_filter_.set_output_size(size); 412 413 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 414 data->SetInteger("width", size.width()); 415 data->SetInteger("height", size.height()); 416 if (dpi.x()) 417 data->SetInteger("x_dpi", dpi.x()); 418 if (dpi.y()) 419 data->SetInteger("y_dpi", dpi.y()); 420 PostLegacyJsonMessage("onDesktopSize", data.Pass()); 421 } 422 423 void ChromotingInstance::SetDesktopShape(const webrtc::DesktopRegion& shape) { 424 if (desktop_shape_ && shape.Equals(*desktop_shape_)) 425 return; 426 427 desktop_shape_.reset(new webrtc::DesktopRegion(shape)); 428 429 scoped_ptr<base::ListValue> rects_value(new base::ListValue()); 430 for (webrtc::DesktopRegion::Iterator i(shape); !i.IsAtEnd(); i.Advance()) { 431 const webrtc::DesktopRect& rect = i.rect(); 432 scoped_ptr<base::ListValue> rect_value(new base::ListValue()); 433 rect_value->AppendInteger(rect.left()); 434 rect_value->AppendInteger(rect.top()); 435 rect_value->AppendInteger(rect.width()); 436 rect_value->AppendInteger(rect.height()); 437 rects_value->Append(rect_value.release()); 438 } 439 440 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 441 data->Set("rects", rects_value.release()); 442 PostLegacyJsonMessage("onDesktopShape", data.Pass()); 443 } 444 445 void ChromotingInstance::OnConnectionState( 446 protocol::ConnectionToHost::State state, 447 protocol::ErrorCode error) { 448 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 449 data->SetString("state", ConnectionStateToString(state)); 450 data->SetString("error", ConnectionErrorToString(error)); 451 PostLegacyJsonMessage("onConnectionStatus", data.Pass()); 452 } 453 454 void ChromotingInstance::FetchThirdPartyToken( 455 const GURL& token_url, 456 const std::string& host_public_key, 457 const std::string& scope, 458 base::WeakPtr<TokenFetcherProxy> token_fetcher_proxy) { 459 // Once the Session object calls this function, it won't continue the 460 // authentication until the callback is called (or connection is canceled). 461 // So, it's impossible to reach this with a callback already registered. 462 DCHECK(!token_fetcher_proxy_.get()); 463 token_fetcher_proxy_ = token_fetcher_proxy; 464 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 465 data->SetString("tokenUrl", token_url.spec()); 466 data->SetString("hostPublicKey", host_public_key); 467 data->SetString("scope", scope); 468 PostLegacyJsonMessage("fetchThirdPartyToken", data.Pass()); 469 } 470 471 void ChromotingInstance::OnConnectionReady(bool ready) { 472 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 473 data->SetBoolean("ready", ready); 474 PostLegacyJsonMessage("onConnectionReady", data.Pass()); 475 } 476 477 void ChromotingInstance::OnRouteChanged(const std::string& channel_name, 478 const protocol::TransportRoute& route) { 479 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 480 std::string message = "Channel " + channel_name + " using " + 481 protocol::TransportRoute::GetTypeString(route.type) + " connection."; 482 data->SetString("message", message); 483 PostLegacyJsonMessage("logDebugMessage", data.Pass()); 484 } 485 486 void ChromotingInstance::SetCapabilities(const std::string& capabilities) { 487 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 488 data->SetString("capabilities", capabilities); 489 PostLegacyJsonMessage("setCapabilities", data.Pass()); 490 } 491 492 void ChromotingInstance::SetPairingResponse( 493 const protocol::PairingResponse& pairing_response) { 494 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 495 data->SetString("clientId", pairing_response.client_id()); 496 data->SetString("sharedSecret", pairing_response.shared_secret()); 497 PostLegacyJsonMessage("pairingResponse", data.Pass()); 498 } 499 500 void ChromotingInstance::DeliverHostMessage( 501 const protocol::ExtensionMessage& message) { 502 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 503 data->SetString("type", message.type()); 504 data->SetString("data", message.data()); 505 PostLegacyJsonMessage("extensionMessage", data.Pass()); 506 } 507 508 void ChromotingInstance::FetchSecretFromDialog( 509 bool pairing_supported, 510 const protocol::SecretFetchedCallback& secret_fetched_callback) { 511 // Once the Session object calls this function, it won't continue the 512 // authentication until the callback is called (or connection is canceled). 513 // So, it's impossible to reach this with a callback already registered. 514 DCHECK(secret_fetched_callback_.is_null()); 515 secret_fetched_callback_ = secret_fetched_callback; 516 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 517 data->SetBoolean("pairingSupported", pairing_supported); 518 PostLegacyJsonMessage("fetchPin", data.Pass()); 519 } 520 521 void ChromotingInstance::FetchSecretFromString( 522 const std::string& shared_secret, 523 bool pairing_supported, 524 const protocol::SecretFetchedCallback& secret_fetched_callback) { 525 secret_fetched_callback.Run(shared_secret); 526 } 527 528 protocol::ClipboardStub* ChromotingInstance::GetClipboardStub() { 529 // TODO(sergeyu): Move clipboard handling to a separate class. 530 // crbug.com/138108 531 return this; 532 } 533 534 protocol::CursorShapeStub* ChromotingInstance::GetCursorShapeStub() { 535 // TODO(sergeyu): Move cursor shape code to a separate class. 536 // crbug.com/138108 537 return this; 538 } 539 540 scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher> 541 ChromotingInstance::GetTokenFetcher(const std::string& host_public_key) { 542 return scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>( 543 new TokenFetcherProxy( 544 base::Bind(&ChromotingInstance::FetchThirdPartyToken, 545 weak_factory_.GetWeakPtr()), 546 host_public_key)); 547 } 548 549 void ChromotingInstance::InjectClipboardEvent( 550 const protocol::ClipboardEvent& event) { 551 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 552 data->SetString("mimeType", event.mime_type()); 553 data->SetString("item", event.data()); 554 PostLegacyJsonMessage("injectClipboardItem", data.Pass()); 555 } 556 557 void ChromotingInstance::SetCursorShape( 558 const protocol::CursorShapeInfo& cursor_shape) { 559 COMPILE_ASSERT(sizeof(uint32_t) == kBytesPerPixel, rgba_pixels_are_32bit); 560 561 // pp::MouseCursor requires image to be in the native format. 562 if (pp::ImageData::GetNativeImageDataFormat() != 563 PP_IMAGEDATAFORMAT_BGRA_PREMUL) { 564 LOG(WARNING) << "Unable to set cursor shape - native image format is not" 565 " premultiplied BGRA"; 566 return; 567 } 568 569 int width = cursor_shape.width(); 570 int height = cursor_shape.height(); 571 572 int hotspot_x = cursor_shape.hotspot_x(); 573 int hotspot_y = cursor_shape.hotspot_y(); 574 int bytes_per_row = width * kBytesPerPixel; 575 int src_stride = width; 576 const uint32_t* src_row_data = reinterpret_cast<const uint32_t*>( 577 cursor_shape.data().data()); 578 const uint32_t* src_row_data_end = src_row_data + src_stride * height; 579 580 scoped_ptr<pp::ImageData> cursor_image; 581 pp::Point cursor_hotspot; 582 583 // Check if the cursor is visible. 584 if (IsVisibleRow(src_row_data, src_row_data_end)) { 585 // If the cursor exceeds the size permitted by PPAPI then crop it, keeping 586 // the hotspot as close to the center of the new cursor shape as possible. 587 if (height > kMaxCursorHeight && !delegate_large_cursors_) { 588 int y = hotspot_y - (kMaxCursorHeight / 2); 589 y = std::max(y, 0); 590 y = std::min(y, height - kMaxCursorHeight); 591 592 src_row_data += src_stride * y; 593 height = kMaxCursorHeight; 594 hotspot_y -= y; 595 } 596 if (width > kMaxCursorWidth && !delegate_large_cursors_) { 597 int x = hotspot_x - (kMaxCursorWidth / 2); 598 x = std::max(x, 0); 599 x = std::min(x, height - kMaxCursorWidth); 600 601 src_row_data += x; 602 width = kMaxCursorWidth; 603 bytes_per_row = width * kBytesPerPixel; 604 hotspot_x -= x; 605 } 606 607 cursor_image.reset(new pp::ImageData(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, 608 pp::Size(width, height), false)); 609 cursor_hotspot = pp::Point(hotspot_x, hotspot_y); 610 611 uint8* dst_row_data = reinterpret_cast<uint8*>(cursor_image->data()); 612 for (int row = 0; row < height; row++) { 613 memcpy(dst_row_data, src_row_data, bytes_per_row); 614 src_row_data += src_stride; 615 dst_row_data += cursor_image->stride(); 616 } 617 } 618 619 if (height > kMaxCursorHeight || width > kMaxCursorWidth) { 620 DCHECK(delegate_large_cursors_); 621 size_t buffer_size = height * bytes_per_row; 622 pp::VarArrayBuffer array_buffer(buffer_size); 623 void* dst = array_buffer.Map(); 624 memcpy(dst, cursor_image->data(), buffer_size); 625 array_buffer.Unmap(); 626 pp::VarDictionary dictionary; 627 dictionary.Set(pp::Var("width"), width); 628 dictionary.Set(pp::Var("height"), height); 629 dictionary.Set(pp::Var("hotspotX"), cursor_hotspot.x()); 630 dictionary.Set(pp::Var("hotspotY"), cursor_hotspot.y()); 631 dictionary.Set(pp::Var("data"), array_buffer); 632 PostChromotingMessage("setCursorShape", dictionary); 633 input_handler_.SetMouseCursor(scoped_ptr<pp::ImageData>(), cursor_hotspot); 634 } else { 635 if (delegate_large_cursors_) { 636 pp::VarDictionary dictionary; 637 PostChromotingMessage("unsetCursorShape", dictionary); 638 } 639 input_handler_.SetMouseCursor(cursor_image.Pass(), cursor_hotspot); 640 } 641 } 642 643 void ChromotingInstance::OnFirstFrameReceived() { 644 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 645 PostLegacyJsonMessage("onFirstFrameReceived", data.Pass()); 646 } 647 648 void ChromotingInstance::HandleConnect(const base::DictionaryValue& data) { 649 ClientConfig config; 650 std::string local_jid; 651 std::string auth_methods; 652 if (!data.GetString("hostJid", &config.host_jid) || 653 !data.GetString("hostPublicKey", &config.host_public_key) || 654 !data.GetString("localJid", &local_jid) || 655 !data.GetString("authenticationMethods", &auth_methods) || 656 !ParseAuthMethods(auth_methods, &config) || 657 !data.GetString("authenticationTag", &config.authentication_tag)) { 658 LOG(ERROR) << "Invalid connect() data."; 659 return; 660 } 661 data.GetString("clientPairingId", &config.client_pairing_id); 662 data.GetString("clientPairedSecret", &config.client_paired_secret); 663 if (use_async_pin_dialog_) { 664 config.fetch_secret_callback = 665 base::Bind(&ChromotingInstance::FetchSecretFromDialog, 666 weak_factory_.GetWeakPtr()); 667 } else { 668 std::string shared_secret; 669 if (!data.GetString("sharedSecret", &shared_secret)) { 670 LOG(ERROR) << "sharedSecret not specified in connect()."; 671 return; 672 } 673 config.fetch_secret_callback = 674 base::Bind(&ChromotingInstance::FetchSecretFromString, shared_secret); 675 } 676 677 // Read the list of capabilities, if any. 678 if (data.HasKey("capabilities")) { 679 if (!data.GetString("capabilities", &config.capabilities)) { 680 LOG(ERROR) << "Invalid connect() data."; 681 return; 682 } 683 } 684 685 #if defined(OS_NACL) 686 std::string key_filter; 687 if (!data.GetString("keyFilter", &key_filter)) { 688 NOTREACHED(); 689 normalizing_input_filter_.reset(new protocol::InputFilter(&key_mapper_)); 690 } else if (key_filter == "mac") { 691 normalizing_input_filter_.reset( 692 new NormalizingInputFilterMac(&key_mapper_)); 693 } else if (key_filter == "cros") { 694 normalizing_input_filter_.reset( 695 new NormalizingInputFilterCros(&key_mapper_)); 696 } else { 697 DCHECK(key_filter.empty()); 698 normalizing_input_filter_.reset(new protocol::InputFilter(&key_mapper_)); 699 } 700 #elif defined(OS_MACOSX) 701 normalizing_input_filter_.reset(new NormalizingInputFilterMac(&key_mapper_)); 702 #elif defined(OS_CHROMEOS) 703 normalizing_input_filter_.reset(new NormalizingInputFilterCros(&key_mapper_)); 704 #else 705 normalizing_input_filter_.reset(new protocol::InputFilter(&key_mapper_)); 706 #endif 707 input_handler_.set_input_stub(normalizing_input_filter_.get()); 708 709 ConnectWithConfig(config, local_jid); 710 } 711 712 void ChromotingInstance::ConnectWithConfig(const ClientConfig& config, 713 const std::string& local_jid) { 714 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 715 716 if (use_media_source_rendering_) { 717 video_renderer_.reset(new MediaSourceVideoRenderer(this)); 718 } else { 719 view_.reset(new PepperView(this, &context_)); 720 view_weak_factory_.reset( 721 new base::WeakPtrFactory<FrameConsumer>(view_.get())); 722 723 // SoftwareVideoRenderer runs on a separate thread so for now we wrap 724 // PepperView with a ref-counted proxy object. 725 scoped_refptr<FrameConsumerProxy> consumer_proxy = 726 new FrameConsumerProxy(plugin_task_runner_, 727 view_weak_factory_->GetWeakPtr()); 728 729 SoftwareVideoRenderer* renderer = 730 new SoftwareVideoRenderer(context_.main_task_runner(), 731 context_.decode_task_runner(), 732 consumer_proxy); 733 view_->Initialize(renderer); 734 if (!plugin_view_.is_null()) 735 view_->SetView(plugin_view_); 736 video_renderer_.reset(renderer); 737 } 738 739 host_connection_.reset(new protocol::ConnectionToHost(true)); 740 scoped_ptr<AudioPlayer> audio_player(new PepperAudioPlayer(this)); 741 client_.reset(new ChromotingClient(config, &context_, host_connection_.get(), 742 this, video_renderer_.get(), 743 audio_player.Pass())); 744 745 // Connect the input pipeline to the protocol stub & initialize components. 746 mouse_input_filter_.set_input_stub(host_connection_->input_stub()); 747 if (!plugin_view_.is_null()) { 748 mouse_input_filter_.set_input_size(webrtc::DesktopSize( 749 plugin_view_.GetRect().width(), plugin_view_.GetRect().height())); 750 } 751 752 VLOG(0) << "Connecting to " << config.host_jid 753 << ". Local jid: " << local_jid << "."; 754 755 // Setup the signal strategy. 756 signal_strategy_.reset(new DelegatingSignalStrategy( 757 local_jid, base::Bind(&ChromotingInstance::SendOutgoingIq, 758 weak_factory_.GetWeakPtr()))); 759 760 scoped_ptr<cricket::HttpPortAllocatorBase> port_allocator( 761 PepperPortAllocator::Create(this)); 762 scoped_ptr<protocol::TransportFactory> transport_factory( 763 new protocol::LibjingleTransportFactory( 764 signal_strategy_.get(), port_allocator.Pass(), 765 NetworkSettings(NetworkSettings::NAT_TRAVERSAL_FULL))); 766 767 // Kick off the connection. 768 client_->Start(signal_strategy_.get(), transport_factory.Pass()); 769 770 // Start timer that periodically sends perf stats. 771 plugin_task_runner_->PostDelayedTask( 772 FROM_HERE, base::Bind(&ChromotingInstance::SendPerfStats, 773 weak_factory_.GetWeakPtr()), 774 base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs)); 775 } 776 777 void ChromotingInstance::HandleDisconnect(const base::DictionaryValue& data) { 778 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 779 780 // PepperView must be destroyed before the client. 781 view_weak_factory_.reset(); 782 view_.reset(); 783 784 VLOG(0) << "Disconnecting from host."; 785 786 client_.reset(); 787 788 // Disconnect the input pipeline and teardown the connection. 789 mouse_input_filter_.set_input_stub(NULL); 790 host_connection_.reset(); 791 } 792 793 void ChromotingInstance::HandleOnIncomingIq(const base::DictionaryValue& data) { 794 std::string iq; 795 if (!data.GetString("iq", &iq)) { 796 LOG(ERROR) << "Invalid incomingIq() data."; 797 return; 798 } 799 800 // Just ignore the message if it's received before Connect() is called. It's 801 // likely to be a leftover from a previous session, so it's safe to ignore it. 802 if (signal_strategy_) 803 signal_strategy_->OnIncomingMessage(iq); 804 } 805 806 void ChromotingInstance::HandleReleaseAllKeys( 807 const base::DictionaryValue& data) { 808 if (IsConnected()) 809 input_tracker_.ReleaseAll(); 810 } 811 812 void ChromotingInstance::HandleInjectKeyEvent( 813 const base::DictionaryValue& data) { 814 int usb_keycode = 0; 815 bool is_pressed = false; 816 if (!data.GetInteger("usbKeycode", &usb_keycode) || 817 !data.GetBoolean("pressed", &is_pressed)) { 818 LOG(ERROR) << "Invalid injectKeyEvent."; 819 return; 820 } 821 822 protocol::KeyEvent event; 823 event.set_usb_keycode(usb_keycode); 824 event.set_pressed(is_pressed); 825 826 // Inject after the KeyEventMapper, so the event won't get mapped or trapped. 827 if (IsConnected()) 828 input_tracker_.InjectKeyEvent(event); 829 } 830 831 void ChromotingInstance::HandleRemapKey(const base::DictionaryValue& data) { 832 int from_keycode = 0; 833 int to_keycode = 0; 834 if (!data.GetInteger("fromKeycode", &from_keycode) || 835 !data.GetInteger("toKeycode", &to_keycode)) { 836 LOG(ERROR) << "Invalid remapKey."; 837 return; 838 } 839 840 key_mapper_.RemapKey(from_keycode, to_keycode); 841 } 842 843 void ChromotingInstance::HandleTrapKey(const base::DictionaryValue& data) { 844 int keycode = 0; 845 bool trap = false; 846 if (!data.GetInteger("keycode", &keycode) || 847 !data.GetBoolean("trap", &trap)) { 848 LOG(ERROR) << "Invalid trapKey."; 849 return; 850 } 851 852 key_mapper_.TrapKey(keycode, trap); 853 } 854 855 void ChromotingInstance::HandleSendClipboardItem( 856 const base::DictionaryValue& data) { 857 std::string mime_type; 858 std::string item; 859 if (!data.GetString("mimeType", &mime_type) || 860 !data.GetString("item", &item)) { 861 LOG(ERROR) << "Invalid sendClipboardItem data."; 862 return; 863 } 864 if (!IsConnected()) { 865 return; 866 } 867 protocol::ClipboardEvent event; 868 event.set_mime_type(mime_type); 869 event.set_data(item); 870 host_connection_->clipboard_forwarder()->InjectClipboardEvent(event); 871 } 872 873 void ChromotingInstance::HandleNotifyClientResolution( 874 const base::DictionaryValue& data) { 875 int width = 0; 876 int height = 0; 877 int x_dpi = kDefaultDPI; 878 int y_dpi = kDefaultDPI; 879 if (!data.GetInteger("width", &width) || 880 !data.GetInteger("height", &height) || 881 !data.GetInteger("x_dpi", &x_dpi) || 882 !data.GetInteger("y_dpi", &y_dpi) || 883 width <= 0 || height <= 0 || 884 x_dpi <= 0 || y_dpi <= 0) { 885 LOG(ERROR) << "Invalid notifyClientResolution."; 886 return; 887 } 888 889 if (!IsConnected()) { 890 return; 891 } 892 893 protocol::ClientResolution client_resolution; 894 client_resolution.set_width(width); 895 client_resolution.set_height(height); 896 client_resolution.set_x_dpi(x_dpi); 897 client_resolution.set_y_dpi(y_dpi); 898 899 // Include the legacy width & height in DIPs for use by older hosts. 900 client_resolution.set_dips_width((width * kDefaultDPI) / x_dpi); 901 client_resolution.set_dips_height((height * kDefaultDPI) / y_dpi); 902 903 host_connection_->host_stub()->NotifyClientResolution(client_resolution); 904 } 905 906 void ChromotingInstance::HandlePauseVideo(const base::DictionaryValue& data) { 907 if (!data.HasKey("pause")) { 908 LOG(ERROR) << "Invalid pauseVideo."; 909 return; 910 } 911 HandleVideoControl(data); 912 } 913 914 void ChromotingInstance::HandleVideoControl(const base::DictionaryValue& data) { 915 protocol::VideoControl video_control; 916 bool pause_video = false; 917 if (data.GetBoolean("pause", &pause_video)) { 918 video_control.set_enable(!pause_video); 919 } 920 bool lossless_encode = false; 921 if (data.GetBoolean("losslessEncode", &lossless_encode)) { 922 video_control.set_lossless_encode(lossless_encode); 923 } 924 bool lossless_color = false; 925 if (data.GetBoolean("losslessColor", &lossless_color)) { 926 video_control.set_lossless_color(lossless_color); 927 } 928 if (!IsConnected()) { 929 return; 930 } 931 host_connection_->host_stub()->ControlVideo(video_control); 932 } 933 934 void ChromotingInstance::HandlePauseAudio(const base::DictionaryValue& data) { 935 bool pause = false; 936 if (!data.GetBoolean("pause", &pause)) { 937 LOG(ERROR) << "Invalid pauseAudio."; 938 return; 939 } 940 if (!IsConnected()) { 941 return; 942 } 943 protocol::AudioControl audio_control; 944 audio_control.set_enable(!pause); 945 host_connection_->host_stub()->ControlAudio(audio_control); 946 } 947 void ChromotingInstance::HandleOnPinFetched(const base::DictionaryValue& data) { 948 std::string pin; 949 if (!data.GetString("pin", &pin)) { 950 LOG(ERROR) << "Invalid onPinFetched."; 951 return; 952 } 953 if (!secret_fetched_callback_.is_null()) { 954 secret_fetched_callback_.Run(pin); 955 secret_fetched_callback_.Reset(); 956 } else { 957 LOG(WARNING) << "Ignored OnPinFetched received without a pending fetch."; 958 } 959 } 960 961 void ChromotingInstance::HandleOnThirdPartyTokenFetched( 962 const base::DictionaryValue& data) { 963 std::string token; 964 std::string shared_secret; 965 if (!data.GetString("token", &token) || 966 !data.GetString("sharedSecret", &shared_secret)) { 967 LOG(ERROR) << "Invalid onThirdPartyTokenFetched data."; 968 return; 969 } 970 if (token_fetcher_proxy_.get()) { 971 token_fetcher_proxy_->OnTokenFetched(token, shared_secret); 972 token_fetcher_proxy_.reset(); 973 } else { 974 LOG(WARNING) << "Ignored OnThirdPartyTokenFetched without a pending fetch."; 975 } 976 } 977 978 void ChromotingInstance::HandleRequestPairing( 979 const base::DictionaryValue& data) { 980 std::string client_name; 981 if (!data.GetString("clientName", &client_name)) { 982 LOG(ERROR) << "Invalid requestPairing"; 983 return; 984 } 985 if (!IsConnected()) { 986 return; 987 } 988 protocol::PairingRequest pairing_request; 989 pairing_request.set_client_name(client_name); 990 host_connection_->host_stub()->RequestPairing(pairing_request); 991 } 992 993 void ChromotingInstance::HandleExtensionMessage( 994 const base::DictionaryValue& data) { 995 std::string type; 996 std::string message_data; 997 if (!data.GetString("type", &type) || 998 !data.GetString("data", &message_data)) { 999 LOG(ERROR) << "Invalid extensionMessage."; 1000 return; 1001 } 1002 if (!IsConnected()) { 1003 return; 1004 } 1005 protocol::ExtensionMessage message; 1006 message.set_type(type); 1007 message.set_data(message_data); 1008 host_connection_->host_stub()->DeliverClientMessage(message); 1009 } 1010 1011 void ChromotingInstance::HandleAllowMouseLockMessage() { 1012 input_handler_.AllowMouseLock(); 1013 } 1014 1015 void ChromotingInstance::HandleEnableMediaSourceRendering() { 1016 use_media_source_rendering_ = true; 1017 } 1018 1019 void ChromotingInstance::HandleSendMouseInputWhenUnfocused() { 1020 input_handler_.set_send_mouse_input_when_unfocused(true); 1021 } 1022 1023 void ChromotingInstance::HandleDelegateLargeCursors() { 1024 delegate_large_cursors_ = true; 1025 } 1026 1027 ChromotingStats* ChromotingInstance::GetStats() { 1028 if (!video_renderer_.get()) 1029 return NULL; 1030 return video_renderer_->GetStats(); 1031 } 1032 1033 void ChromotingInstance::PostChromotingMessage(const std::string& method, 1034 const pp::VarDictionary& data) { 1035 pp::VarDictionary message; 1036 message.Set(pp::Var("method"), pp::Var(method)); 1037 message.Set(pp::Var("data"), data); 1038 PostMessage(message); 1039 } 1040 1041 void ChromotingInstance::PostLegacyJsonMessage( 1042 const std::string& method, 1043 scoped_ptr<base::DictionaryValue> data) { 1044 scoped_ptr<base::DictionaryValue> message(new base::DictionaryValue()); 1045 message->SetString("method", method); 1046 message->Set("data", data.release()); 1047 1048 std::string message_json; 1049 base::JSONWriter::Write(message.get(), &message_json); 1050 PostMessage(pp::Var(message_json)); 1051 } 1052 1053 void ChromotingInstance::SendTrappedKey(uint32 usb_keycode, bool pressed) { 1054 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 1055 data->SetInteger("usbKeycode", usb_keycode); 1056 data->SetBoolean("pressed", pressed); 1057 PostLegacyJsonMessage("trappedKeyEvent", data.Pass()); 1058 } 1059 1060 void ChromotingInstance::SendOutgoingIq(const std::string& iq) { 1061 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 1062 data->SetString("iq", iq); 1063 PostLegacyJsonMessage("sendOutgoingIq", data.Pass()); 1064 } 1065 1066 void ChromotingInstance::SendPerfStats() { 1067 if (!video_renderer_.get()) { 1068 return; 1069 } 1070 1071 plugin_task_runner_->PostDelayedTask( 1072 FROM_HERE, base::Bind(&ChromotingInstance::SendPerfStats, 1073 weak_factory_.GetWeakPtr()), 1074 base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs)); 1075 1076 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 1077 ChromotingStats* stats = video_renderer_->GetStats(); 1078 data->SetDouble("videoBandwidth", stats->video_bandwidth()->Rate()); 1079 data->SetDouble("videoFrameRate", stats->video_frame_rate()->Rate()); 1080 data->SetDouble("captureLatency", stats->video_capture_ms()->Average()); 1081 data->SetDouble("encodeLatency", stats->video_encode_ms()->Average()); 1082 data->SetDouble("decodeLatency", stats->video_decode_ms()->Average()); 1083 data->SetDouble("renderLatency", stats->video_paint_ms()->Average()); 1084 data->SetDouble("roundtripLatency", stats->round_trip_ms()->Average()); 1085 PostLegacyJsonMessage("onPerfStats", data.Pass()); 1086 } 1087 1088 // static 1089 void ChromotingInstance::RegisterLogMessageHandler() { 1090 base::AutoLock lock(g_logging_lock.Get()); 1091 1092 VLOG(1) << "Registering global log handler"; 1093 1094 // Record previous handler so we can call it in a chain. 1095 g_logging_old_handler = logging::GetLogMessageHandler(); 1096 1097 // Set up log message handler. 1098 // This is not thread-safe so we need it within our lock. 1099 logging::SetLogMessageHandler(&LogToUI); 1100 } 1101 1102 void ChromotingInstance::RegisterLoggingInstance() { 1103 base::AutoLock lock(g_logging_lock.Get()); 1104 1105 // Register this instance as the one that will handle all logging calls 1106 // and display them to the user. 1107 // If multiple plugins are run, then the last one registered will handle all 1108 // logging for all instances. 1109 g_logging_instance.Get() = weak_factory_.GetWeakPtr(); 1110 g_logging_task_runner.Get() = plugin_task_runner_; 1111 g_has_logging_instance = true; 1112 } 1113 1114 void ChromotingInstance::UnregisterLoggingInstance() { 1115 base::AutoLock lock(g_logging_lock.Get()); 1116 1117 // Don't unregister unless we're the currently registered instance. 1118 if (this != g_logging_instance.Get().get()) 1119 return; 1120 1121 // Unregister this instance for logging. 1122 g_has_logging_instance = false; 1123 g_logging_instance.Get().reset(); 1124 g_logging_task_runner.Get() = NULL; 1125 1126 VLOG(1) << "Unregistering global log handler"; 1127 } 1128 1129 // static 1130 bool ChromotingInstance::LogToUI(int severity, const char* file, int line, 1131 size_t message_start, 1132 const std::string& str) { 1133 // Note that we're reading |g_has_logging_instance| outside of a lock. 1134 // This lockless read is done so that we don't needlessly slow down global 1135 // logging with a lock for each log message. 1136 // 1137 // This lockless read is safe because: 1138 // 1139 // Misreading a false value (when it should be true) means that we'll simply 1140 // skip processing a few log messages. 1141 // 1142 // Misreading a true value (when it should be false) means that we'll take 1143 // the lock and check |g_logging_instance| unnecessarily. This is not 1144 // problematic because we always set |g_logging_instance| inside a lock. 1145 if (g_has_logging_instance) { 1146 scoped_refptr<base::SingleThreadTaskRunner> logging_task_runner; 1147 base::WeakPtr<ChromotingInstance> logging_instance; 1148 1149 { 1150 base::AutoLock lock(g_logging_lock.Get()); 1151 // If we're on the logging thread and |g_logging_to_plugin| is set then 1152 // this LOG message came from handling a previous LOG message and we 1153 // should skip it to avoid an infinite loop of LOG messages. 1154 if (!g_logging_task_runner.Get()->BelongsToCurrentThread() || 1155 !g_logging_to_plugin) { 1156 logging_task_runner = g_logging_task_runner.Get(); 1157 logging_instance = g_logging_instance.Get(); 1158 } 1159 } 1160 1161 if (logging_task_runner.get()) { 1162 std::string message = remoting::GetTimestampString(); 1163 message += (str.c_str() + message_start); 1164 1165 logging_task_runner->PostTask( 1166 FROM_HERE, base::Bind(&ChromotingInstance::ProcessLogToUI, 1167 logging_instance, message)); 1168 } 1169 } 1170 1171 if (g_logging_old_handler) 1172 return (g_logging_old_handler)(severity, file, line, message_start, str); 1173 return false; 1174 } 1175 1176 void ChromotingInstance::ProcessLogToUI(const std::string& message) { 1177 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1178 1179 // This flag (which is set only here) is used to prevent LogToUI from posting 1180 // new tasks while we're in the middle of servicing a LOG call. This can 1181 // happen if the call to LogDebugInfo tries to LOG anything. 1182 // Since it is read on the plugin thread, we don't need to lock to set it. 1183 g_logging_to_plugin = true; 1184 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 1185 data->SetString("message", message); 1186 PostLegacyJsonMessage("logDebugMessage", data.Pass()); 1187 g_logging_to_plugin = false; 1188 } 1189 1190 bool ChromotingInstance::IsCallerAppOrExtension() { 1191 const pp::URLUtil_Dev* url_util = pp::URLUtil_Dev::Get(); 1192 if (!url_util) 1193 return false; 1194 1195 PP_URLComponents_Dev url_components; 1196 pp::Var url_var = url_util->GetDocumentURL(this, &url_components); 1197 if (!url_var.is_string()) 1198 return false; 1199 1200 std::string url = url_var.AsString(); 1201 std::string url_scheme = url.substr(url_components.scheme.begin, 1202 url_components.scheme.len); 1203 return url_scheme == kChromeExtensionUrlScheme; 1204 } 1205 1206 bool ChromotingInstance::IsConnected() { 1207 return host_connection_.get() && 1208 (host_connection_->state() == protocol::ConnectionToHost::CONNECTED); 1209 } 1210 1211 void ChromotingInstance::OnMediaSourceSize(const webrtc::DesktopSize& size, 1212 const webrtc::DesktopVector& dpi) { 1213 SetDesktopSize(size, dpi); 1214 } 1215 1216 void ChromotingInstance::OnMediaSourceShape( 1217 const webrtc::DesktopRegion& shape) { 1218 SetDesktopShape(shape); 1219 } 1220 1221 void ChromotingInstance::OnMediaSourceReset(const std::string& format) { 1222 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 1223 data->SetString("format", format); 1224 PostLegacyJsonMessage("mediaSourceReset", data.Pass()); 1225 } 1226 1227 void ChromotingInstance::OnMediaSourceData(uint8_t* buffer, size_t buffer_size, 1228 bool keyframe) { 1229 pp::VarArrayBuffer array_buffer(buffer_size); 1230 void* data_ptr = array_buffer.Map(); 1231 memcpy(data_ptr, buffer, buffer_size); 1232 array_buffer.Unmap(); 1233 pp::VarDictionary data_dictionary; 1234 data_dictionary.Set(pp::Var("buffer"), array_buffer); 1235 data_dictionary.Set(pp::Var("keyframe"), keyframe); 1236 PostChromotingMessage("mediaSourceData", data_dictionary); 1237 } 1238 1239 } // namespace remoting 1240