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