chromoting_instance.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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 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 rtc::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 rtc::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 269ChromotingInstance::~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 291bool 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 315void 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 380void 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 389void 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 400bool 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 409void 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 423void 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 445void 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 454void 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 471void 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 477void 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 486void 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 492void 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 500void 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 508void 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 521void 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 528protocol::ClipboardStub* ChromotingInstance::GetClipboardStub() { 529 // TODO(sergeyu): Move clipboard handling to a separate class. 530 // crbug.com/138108 531 return this; 532} 533 534protocol::CursorShapeStub* ChromotingInstance::GetCursorShapeStub() { 535 // TODO(sergeyu): Move cursor shape code to a separate class. 536 // crbug.com/138108 537 return this; 538} 539 540void ChromotingInstance::InjectClipboardEvent( 541 const protocol::ClipboardEvent& event) { 542 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 543 data->SetString("mimeType", event.mime_type()); 544 data->SetString("item", event.data()); 545 PostLegacyJsonMessage("injectClipboardItem", data.Pass()); 546} 547 548void ChromotingInstance::SetCursorShape( 549 const protocol::CursorShapeInfo& cursor_shape) { 550 COMPILE_ASSERT(sizeof(uint32_t) == kBytesPerPixel, rgba_pixels_are_32bit); 551 552 // pp::MouseCursor requires image to be in the native format. 553 if (pp::ImageData::GetNativeImageDataFormat() != 554 PP_IMAGEDATAFORMAT_BGRA_PREMUL) { 555 LOG(WARNING) << "Unable to set cursor shape - native image format is not" 556 " premultiplied BGRA"; 557 return; 558 } 559 560 int width = cursor_shape.width(); 561 int height = cursor_shape.height(); 562 563 int hotspot_x = cursor_shape.hotspot_x(); 564 int hotspot_y = cursor_shape.hotspot_y(); 565 int bytes_per_row = width * kBytesPerPixel; 566 int src_stride = width; 567 const uint32_t* src_row_data = reinterpret_cast<const uint32_t*>( 568 cursor_shape.data().data()); 569 const uint32_t* src_row_data_end = src_row_data + src_stride * height; 570 571 scoped_ptr<pp::ImageData> cursor_image; 572 pp::Point cursor_hotspot; 573 574 // Check if the cursor is visible. 575 if (IsVisibleRow(src_row_data, src_row_data_end)) { 576 // If the cursor exceeds the size permitted by PPAPI then crop it, keeping 577 // the hotspot as close to the center of the new cursor shape as possible. 578 if (height > kMaxCursorHeight && !delegate_large_cursors_) { 579 int y = hotspot_y - (kMaxCursorHeight / 2); 580 y = std::max(y, 0); 581 y = std::min(y, height - kMaxCursorHeight); 582 583 src_row_data += src_stride * y; 584 height = kMaxCursorHeight; 585 hotspot_y -= y; 586 } 587 if (width > kMaxCursorWidth && !delegate_large_cursors_) { 588 int x = hotspot_x - (kMaxCursorWidth / 2); 589 x = std::max(x, 0); 590 x = std::min(x, height - kMaxCursorWidth); 591 592 src_row_data += x; 593 width = kMaxCursorWidth; 594 bytes_per_row = width * kBytesPerPixel; 595 hotspot_x -= x; 596 } 597 598 cursor_image.reset(new pp::ImageData(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, 599 pp::Size(width, height), false)); 600 cursor_hotspot = pp::Point(hotspot_x, hotspot_y); 601 602 uint8* dst_row_data = reinterpret_cast<uint8*>(cursor_image->data()); 603 for (int row = 0; row < height; row++) { 604 memcpy(dst_row_data, src_row_data, bytes_per_row); 605 src_row_data += src_stride; 606 dst_row_data += cursor_image->stride(); 607 } 608 } 609 610 if (height > kMaxCursorHeight || width > kMaxCursorWidth) { 611 DCHECK(delegate_large_cursors_); 612 size_t buffer_size = height * bytes_per_row; 613 pp::VarArrayBuffer array_buffer(buffer_size); 614 void* dst = array_buffer.Map(); 615 memcpy(dst, cursor_image->data(), buffer_size); 616 array_buffer.Unmap(); 617 pp::VarDictionary dictionary; 618 dictionary.Set(pp::Var("width"), width); 619 dictionary.Set(pp::Var("height"), height); 620 dictionary.Set(pp::Var("hotspotX"), cursor_hotspot.x()); 621 dictionary.Set(pp::Var("hotspotY"), cursor_hotspot.y()); 622 dictionary.Set(pp::Var("data"), array_buffer); 623 PostChromotingMessage("setCursorShape", dictionary); 624 input_handler_.SetMouseCursor(scoped_ptr<pp::ImageData>(), cursor_hotspot); 625 } else { 626 if (delegate_large_cursors_) { 627 pp::VarDictionary dictionary; 628 PostChromotingMessage("unsetCursorShape", dictionary); 629 } 630 input_handler_.SetMouseCursor(cursor_image.Pass(), cursor_hotspot); 631 } 632} 633 634void ChromotingInstance::OnFirstFrameReceived() { 635 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 636 PostLegacyJsonMessage("onFirstFrameReceived", data.Pass()); 637} 638 639void ChromotingInstance::HandleConnect(const base::DictionaryValue& data) { 640 std::string local_jid; 641 std::string host_jid; 642 std::string host_public_key; 643 std::string auth_methods_str; 644 std::string authentication_tag; 645 std::vector<protocol::AuthenticationMethod> auth_methods; 646 if (!data.GetString("hostJid", &host_jid) || 647 !data.GetString("hostPublicKey", &host_public_key) || 648 !data.GetString("localJid", &local_jid) || 649 !data.GetString("authenticationMethods", &auth_methods_str) || 650 !ParseAuthMethods(auth_methods_str, &auth_methods) || 651 !data.GetString("authenticationTag", &authentication_tag)) { 652 LOG(ERROR) << "Invalid connect() data."; 653 return; 654 } 655 656 std::string client_pairing_id; 657 data.GetString("clientPairingId", &client_pairing_id); 658 std::string client_paired_secret; 659 data.GetString("clientPairedSecret", &client_paired_secret); 660 661 protocol::FetchSecretCallback fetch_secret_callback; 662 if (use_async_pin_dialog_) { 663 fetch_secret_callback = base::Bind( 664 &ChromotingInstance::FetchSecretFromDialog, weak_factory_.GetWeakPtr()); 665 } else { 666 std::string shared_secret; 667 if (!data.GetString("sharedSecret", &shared_secret)) { 668 LOG(ERROR) << "sharedSecret not specified in connect()."; 669 return; 670 } 671 fetch_secret_callback = 672 base::Bind(&ChromotingInstance::FetchSecretFromString, shared_secret); 673 } 674 675 // Read the list of capabilities, if any. 676 std::string capabilities; 677 if (data.HasKey("capabilities")) { 678 if (!data.GetString("capabilities", &capabilities)) { 679 LOG(ERROR) << "Invalid connect() data."; 680 return; 681 } 682 } 683 684 VLOG(0) << "Connecting to " << host_jid 685 << ". Local jid: " << local_jid << "."; 686 687#if defined(OS_NACL) 688 std::string key_filter; 689 if (!data.GetString("keyFilter", &key_filter)) { 690 NOTREACHED(); 691 normalizing_input_filter_.reset(new protocol::InputFilter(&key_mapper_)); 692 } else if (key_filter == "mac") { 693 normalizing_input_filter_.reset( 694 new NormalizingInputFilterMac(&key_mapper_)); 695 } else if (key_filter == "cros") { 696 normalizing_input_filter_.reset( 697 new NormalizingInputFilterCros(&key_mapper_)); 698 } else { 699 DCHECK(key_filter.empty()); 700 normalizing_input_filter_.reset(new protocol::InputFilter(&key_mapper_)); 701 } 702#elif defined(OS_MACOSX) 703 normalizing_input_filter_.reset(new NormalizingInputFilterMac(&key_mapper_)); 704#elif defined(OS_CHROMEOS) 705 normalizing_input_filter_.reset(new NormalizingInputFilterCros(&key_mapper_)); 706#else 707 normalizing_input_filter_.reset(new protocol::InputFilter(&key_mapper_)); 708#endif 709 input_handler_.set_input_stub(normalizing_input_filter_.get()); 710 711 if (use_media_source_rendering_) { 712 video_renderer_.reset(new MediaSourceVideoRenderer(this)); 713 } else { 714 view_.reset(new PepperView(this, &context_)); 715 view_weak_factory_.reset( 716 new base::WeakPtrFactory<FrameConsumer>(view_.get())); 717 718 // SoftwareVideoRenderer runs on a separate thread so for now we wrap 719 // PepperView with a ref-counted proxy object. 720 scoped_refptr<FrameConsumerProxy> consumer_proxy = 721 new FrameConsumerProxy(plugin_task_runner_, 722 view_weak_factory_->GetWeakPtr()); 723 724 SoftwareVideoRenderer* renderer = 725 new SoftwareVideoRenderer(context_.main_task_runner(), 726 context_.decode_task_runner(), 727 consumer_proxy); 728 view_->Initialize(renderer); 729 if (!plugin_view_.is_null()) 730 view_->SetView(plugin_view_); 731 video_renderer_.reset(renderer); 732 } 733 734 scoped_ptr<AudioPlayer> audio_player(new PepperAudioPlayer(this)); 735 client_.reset(new ChromotingClient(&context_, this, video_renderer_.get(), 736 audio_player.Pass())); 737 738 // Connect the input pipeline to the protocol stub & initialize components. 739 mouse_input_filter_.set_input_stub(client_->input_stub()); 740 if (!plugin_view_.is_null()) { 741 mouse_input_filter_.set_input_size(webrtc::DesktopSize( 742 plugin_view_.GetRect().width(), plugin_view_.GetRect().height())); 743 } 744 745 // Setup the signal strategy. 746 signal_strategy_.reset(new DelegatingSignalStrategy( 747 local_jid, base::Bind(&ChromotingInstance::SendOutgoingIq, 748 weak_factory_.GetWeakPtr()))); 749 750 // Create TransportFactory. 751 scoped_ptr<protocol::TransportFactory> transport_factory( 752 new protocol::LibjingleTransportFactory( 753 signal_strategy_.get(), 754 PepperPortAllocator::Create(this) 755 .PassAs<cricket::HttpPortAllocatorBase>(), 756 protocol::NetworkSettings( 757 protocol::NetworkSettings::NAT_TRAVERSAL_FULL))); 758 759 // Create Authenticator. 760 scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher> 761 token_fetcher(new TokenFetcherProxy( 762 base::Bind(&ChromotingInstance::FetchThirdPartyToken, 763 weak_factory_.GetWeakPtr()), 764 host_public_key)); 765 scoped_ptr<protocol::Authenticator> authenticator( 766 new protocol::NegotiatingClientAuthenticator( 767 client_pairing_id, client_paired_secret, authentication_tag, 768 fetch_secret_callback, token_fetcher.Pass(), auth_methods)); 769 770 // Kick off the connection. 771 client_->Start(signal_strategy_.get(), authenticator.Pass(), 772 transport_factory.Pass(), host_jid, capabilities); 773 774 // Start timer that periodically sends perf stats. 775 plugin_task_runner_->PostDelayedTask( 776 FROM_HERE, base::Bind(&ChromotingInstance::SendPerfStats, 777 weak_factory_.GetWeakPtr()), 778 base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs)); 779} 780 781void ChromotingInstance::HandleDisconnect(const base::DictionaryValue& data) { 782 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 783 784 // PepperView must be destroyed before the client. 785 view_weak_factory_.reset(); 786 view_.reset(); 787 788 VLOG(0) << "Disconnecting from host."; 789 790 // Disconnect the input pipeline and teardown the connection. 791 mouse_input_filter_.set_input_stub(NULL); 792 client_.reset(); 793} 794 795void ChromotingInstance::HandleOnIncomingIq(const base::DictionaryValue& data) { 796 std::string iq; 797 if (!data.GetString("iq", &iq)) { 798 LOG(ERROR) << "Invalid incomingIq() data."; 799 return; 800 } 801 802 // Just ignore the message if it's received before Connect() is called. It's 803 // likely to be a leftover from a previous session, so it's safe to ignore it. 804 if (signal_strategy_) 805 signal_strategy_->OnIncomingMessage(iq); 806} 807 808void ChromotingInstance::HandleReleaseAllKeys( 809 const base::DictionaryValue& data) { 810 if (IsConnected()) 811 input_tracker_.ReleaseAll(); 812} 813 814void ChromotingInstance::HandleInjectKeyEvent( 815 const base::DictionaryValue& data) { 816 int usb_keycode = 0; 817 bool is_pressed = false; 818 if (!data.GetInteger("usbKeycode", &usb_keycode) || 819 !data.GetBoolean("pressed", &is_pressed)) { 820 LOG(ERROR) << "Invalid injectKeyEvent."; 821 return; 822 } 823 824 protocol::KeyEvent event; 825 event.set_usb_keycode(usb_keycode); 826 event.set_pressed(is_pressed); 827 828 // Inject after the KeyEventMapper, so the event won't get mapped or trapped. 829 if (IsConnected()) 830 input_tracker_.InjectKeyEvent(event); 831} 832 833void ChromotingInstance::HandleRemapKey(const base::DictionaryValue& data) { 834 int from_keycode = 0; 835 int to_keycode = 0; 836 if (!data.GetInteger("fromKeycode", &from_keycode) || 837 !data.GetInteger("toKeycode", &to_keycode)) { 838 LOG(ERROR) << "Invalid remapKey."; 839 return; 840 } 841 842 key_mapper_.RemapKey(from_keycode, to_keycode); 843} 844 845void ChromotingInstance::HandleTrapKey(const base::DictionaryValue& data) { 846 int keycode = 0; 847 bool trap = false; 848 if (!data.GetInteger("keycode", &keycode) || 849 !data.GetBoolean("trap", &trap)) { 850 LOG(ERROR) << "Invalid trapKey."; 851 return; 852 } 853 854 key_mapper_.TrapKey(keycode, trap); 855} 856 857void ChromotingInstance::HandleSendClipboardItem( 858 const base::DictionaryValue& data) { 859 std::string mime_type; 860 std::string item; 861 if (!data.GetString("mimeType", &mime_type) || 862 !data.GetString("item", &item)) { 863 LOG(ERROR) << "Invalid sendClipboardItem data."; 864 return; 865 } 866 if (!IsConnected()) { 867 return; 868 } 869 protocol::ClipboardEvent event; 870 event.set_mime_type(mime_type); 871 event.set_data(item); 872 client_->clipboard_forwarder()->InjectClipboardEvent(event); 873} 874 875void ChromotingInstance::HandleNotifyClientResolution( 876 const base::DictionaryValue& data) { 877 int width = 0; 878 int height = 0; 879 int x_dpi = kDefaultDPI; 880 int y_dpi = kDefaultDPI; 881 if (!data.GetInteger("width", &width) || 882 !data.GetInteger("height", &height) || 883 !data.GetInteger("x_dpi", &x_dpi) || 884 !data.GetInteger("y_dpi", &y_dpi) || 885 width <= 0 || height <= 0 || 886 x_dpi <= 0 || y_dpi <= 0) { 887 LOG(ERROR) << "Invalid notifyClientResolution."; 888 return; 889 } 890 891 if (!IsConnected()) { 892 return; 893 } 894 895 protocol::ClientResolution client_resolution; 896 client_resolution.set_width(width); 897 client_resolution.set_height(height); 898 client_resolution.set_x_dpi(x_dpi); 899 client_resolution.set_y_dpi(y_dpi); 900 901 // Include the legacy width & height in DIPs for use by older hosts. 902 client_resolution.set_dips_width((width * kDefaultDPI) / x_dpi); 903 client_resolution.set_dips_height((height * kDefaultDPI) / y_dpi); 904 905 client_->host_stub()->NotifyClientResolution(client_resolution); 906} 907 908void ChromotingInstance::HandlePauseVideo(const base::DictionaryValue& data) { 909 if (!data.HasKey("pause")) { 910 LOG(ERROR) << "Invalid pauseVideo."; 911 return; 912 } 913 HandleVideoControl(data); 914} 915 916void ChromotingInstance::HandleVideoControl(const base::DictionaryValue& data) { 917 protocol::VideoControl video_control; 918 bool pause_video = false; 919 if (data.GetBoolean("pause", &pause_video)) { 920 video_control.set_enable(!pause_video); 921 } 922 bool lossless_encode = false; 923 if (data.GetBoolean("losslessEncode", &lossless_encode)) { 924 video_control.set_lossless_encode(lossless_encode); 925 } 926 bool lossless_color = false; 927 if (data.GetBoolean("losslessColor", &lossless_color)) { 928 video_control.set_lossless_color(lossless_color); 929 } 930 if (!IsConnected()) { 931 return; 932 } 933 client_->host_stub()->ControlVideo(video_control); 934} 935 936void ChromotingInstance::HandlePauseAudio(const base::DictionaryValue& data) { 937 bool pause = false; 938 if (!data.GetBoolean("pause", &pause)) { 939 LOG(ERROR) << "Invalid pauseAudio."; 940 return; 941 } 942 if (!IsConnected()) { 943 return; 944 } 945 protocol::AudioControl audio_control; 946 audio_control.set_enable(!pause); 947 client_->host_stub()->ControlAudio(audio_control); 948} 949void ChromotingInstance::HandleOnPinFetched(const base::DictionaryValue& data) { 950 std::string pin; 951 if (!data.GetString("pin", &pin)) { 952 LOG(ERROR) << "Invalid onPinFetched."; 953 return; 954 } 955 if (!secret_fetched_callback_.is_null()) { 956 secret_fetched_callback_.Run(pin); 957 secret_fetched_callback_.Reset(); 958 } else { 959 LOG(WARNING) << "Ignored OnPinFetched received without a pending fetch."; 960 } 961} 962 963void ChromotingInstance::HandleOnThirdPartyTokenFetched( 964 const base::DictionaryValue& data) { 965 std::string token; 966 std::string shared_secret; 967 if (!data.GetString("token", &token) || 968 !data.GetString("sharedSecret", &shared_secret)) { 969 LOG(ERROR) << "Invalid onThirdPartyTokenFetched data."; 970 return; 971 } 972 if (token_fetcher_proxy_.get()) { 973 token_fetcher_proxy_->OnTokenFetched(token, shared_secret); 974 token_fetcher_proxy_.reset(); 975 } else { 976 LOG(WARNING) << "Ignored OnThirdPartyTokenFetched without a pending fetch."; 977 } 978} 979 980void ChromotingInstance::HandleRequestPairing( 981 const base::DictionaryValue& data) { 982 std::string client_name; 983 if (!data.GetString("clientName", &client_name)) { 984 LOG(ERROR) << "Invalid requestPairing"; 985 return; 986 } 987 if (!IsConnected()) { 988 return; 989 } 990 protocol::PairingRequest pairing_request; 991 pairing_request.set_client_name(client_name); 992 client_->host_stub()->RequestPairing(pairing_request); 993} 994 995void ChromotingInstance::HandleExtensionMessage( 996 const base::DictionaryValue& data) { 997 std::string type; 998 std::string message_data; 999 if (!data.GetString("type", &type) || 1000 !data.GetString("data", &message_data)) { 1001 LOG(ERROR) << "Invalid extensionMessage."; 1002 return; 1003 } 1004 if (!IsConnected()) { 1005 return; 1006 } 1007 protocol::ExtensionMessage message; 1008 message.set_type(type); 1009 message.set_data(message_data); 1010 client_->host_stub()->DeliverClientMessage(message); 1011} 1012 1013void ChromotingInstance::HandleAllowMouseLockMessage() { 1014 input_handler_.AllowMouseLock(); 1015} 1016 1017void ChromotingInstance::HandleEnableMediaSourceRendering() { 1018 use_media_source_rendering_ = true; 1019} 1020 1021void ChromotingInstance::HandleSendMouseInputWhenUnfocused() { 1022 input_handler_.set_send_mouse_input_when_unfocused(true); 1023} 1024 1025void ChromotingInstance::HandleDelegateLargeCursors() { 1026 delegate_large_cursors_ = true; 1027} 1028 1029ChromotingStats* ChromotingInstance::GetStats() { 1030 if (!video_renderer_.get()) 1031 return NULL; 1032 return video_renderer_->GetStats(); 1033} 1034 1035void ChromotingInstance::PostChromotingMessage(const std::string& method, 1036 const pp::VarDictionary& data) { 1037 pp::VarDictionary message; 1038 message.Set(pp::Var("method"), pp::Var(method)); 1039 message.Set(pp::Var("data"), data); 1040 PostMessage(message); 1041} 1042 1043void ChromotingInstance::PostLegacyJsonMessage( 1044 const std::string& method, 1045 scoped_ptr<base::DictionaryValue> data) { 1046 scoped_ptr<base::DictionaryValue> message(new base::DictionaryValue()); 1047 message->SetString("method", method); 1048 message->Set("data", data.release()); 1049 1050 std::string message_json; 1051 base::JSONWriter::Write(message.get(), &message_json); 1052 PostMessage(pp::Var(message_json)); 1053} 1054 1055void ChromotingInstance::SendTrappedKey(uint32 usb_keycode, bool pressed) { 1056 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 1057 data->SetInteger("usbKeycode", usb_keycode); 1058 data->SetBoolean("pressed", pressed); 1059 PostLegacyJsonMessage("trappedKeyEvent", data.Pass()); 1060} 1061 1062void ChromotingInstance::SendOutgoingIq(const std::string& iq) { 1063 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 1064 data->SetString("iq", iq); 1065 PostLegacyJsonMessage("sendOutgoingIq", data.Pass()); 1066} 1067 1068void ChromotingInstance::SendPerfStats() { 1069 if (!video_renderer_.get()) { 1070 return; 1071 } 1072 1073 plugin_task_runner_->PostDelayedTask( 1074 FROM_HERE, base::Bind(&ChromotingInstance::SendPerfStats, 1075 weak_factory_.GetWeakPtr()), 1076 base::TimeDelta::FromMilliseconds(kPerfStatsIntervalMs)); 1077 1078 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 1079 ChromotingStats* stats = video_renderer_->GetStats(); 1080 data->SetDouble("videoBandwidth", stats->video_bandwidth()->Rate()); 1081 data->SetDouble("videoFrameRate", stats->video_frame_rate()->Rate()); 1082 data->SetDouble("captureLatency", stats->video_capture_ms()->Average()); 1083 data->SetDouble("encodeLatency", stats->video_encode_ms()->Average()); 1084 data->SetDouble("decodeLatency", stats->video_decode_ms()->Average()); 1085 data->SetDouble("renderLatency", stats->video_paint_ms()->Average()); 1086 data->SetDouble("roundtripLatency", stats->round_trip_ms()->Average()); 1087 PostLegacyJsonMessage("onPerfStats", data.Pass()); 1088} 1089 1090// static 1091void ChromotingInstance::RegisterLogMessageHandler() { 1092 base::AutoLock lock(g_logging_lock.Get()); 1093 1094 VLOG(1) << "Registering global log handler"; 1095 1096 // Record previous handler so we can call it in a chain. 1097 g_logging_old_handler = logging::GetLogMessageHandler(); 1098 1099 // Set up log message handler. 1100 // This is not thread-safe so we need it within our lock. 1101 logging::SetLogMessageHandler(&LogToUI); 1102} 1103 1104void ChromotingInstance::RegisterLoggingInstance() { 1105 base::AutoLock lock(g_logging_lock.Get()); 1106 1107 // Register this instance as the one that will handle all logging calls 1108 // and display them to the user. 1109 // If multiple plugins are run, then the last one registered will handle all 1110 // logging for all instances. 1111 g_logging_instance.Get() = weak_factory_.GetWeakPtr(); 1112 g_logging_task_runner.Get() = plugin_task_runner_; 1113 g_has_logging_instance = true; 1114} 1115 1116void ChromotingInstance::UnregisterLoggingInstance() { 1117 base::AutoLock lock(g_logging_lock.Get()); 1118 1119 // Don't unregister unless we're the currently registered instance. 1120 if (this != g_logging_instance.Get().get()) 1121 return; 1122 1123 // Unregister this instance for logging. 1124 g_has_logging_instance = false; 1125 g_logging_instance.Get().reset(); 1126 g_logging_task_runner.Get() = NULL; 1127 1128 VLOG(1) << "Unregistering global log handler"; 1129} 1130 1131// static 1132bool ChromotingInstance::LogToUI(int severity, const char* file, int line, 1133 size_t message_start, 1134 const std::string& str) { 1135 // Note that we're reading |g_has_logging_instance| outside of a lock. 1136 // This lockless read is done so that we don't needlessly slow down global 1137 // logging with a lock for each log message. 1138 // 1139 // This lockless read is safe because: 1140 // 1141 // Misreading a false value (when it should be true) means that we'll simply 1142 // skip processing a few log messages. 1143 // 1144 // Misreading a true value (when it should be false) means that we'll take 1145 // the lock and check |g_logging_instance| unnecessarily. This is not 1146 // problematic because we always set |g_logging_instance| inside a lock. 1147 if (g_has_logging_instance) { 1148 scoped_refptr<base::SingleThreadTaskRunner> logging_task_runner; 1149 base::WeakPtr<ChromotingInstance> logging_instance; 1150 1151 { 1152 base::AutoLock lock(g_logging_lock.Get()); 1153 // If we're on the logging thread and |g_logging_to_plugin| is set then 1154 // this LOG message came from handling a previous LOG message and we 1155 // should skip it to avoid an infinite loop of LOG messages. 1156 if (!g_logging_task_runner.Get()->BelongsToCurrentThread() || 1157 !g_logging_to_plugin) { 1158 logging_task_runner = g_logging_task_runner.Get(); 1159 logging_instance = g_logging_instance.Get(); 1160 } 1161 } 1162 1163 if (logging_task_runner.get()) { 1164 std::string message = remoting::GetTimestampString(); 1165 message += (str.c_str() + message_start); 1166 1167 logging_task_runner->PostTask( 1168 FROM_HERE, base::Bind(&ChromotingInstance::ProcessLogToUI, 1169 logging_instance, message)); 1170 } 1171 } 1172 1173 if (g_logging_old_handler) 1174 return (g_logging_old_handler)(severity, file, line, message_start, str); 1175 return false; 1176} 1177 1178void ChromotingInstance::ProcessLogToUI(const std::string& message) { 1179 DCHECK(plugin_task_runner_->BelongsToCurrentThread()); 1180 1181 // This flag (which is set only here) is used to prevent LogToUI from posting 1182 // new tasks while we're in the middle of servicing a LOG call. This can 1183 // happen if the call to LogDebugInfo tries to LOG anything. 1184 // Since it is read on the plugin thread, we don't need to lock to set it. 1185 g_logging_to_plugin = true; 1186 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 1187 data->SetString("message", message); 1188 PostLegacyJsonMessage("logDebugMessage", data.Pass()); 1189 g_logging_to_plugin = false; 1190} 1191 1192bool ChromotingInstance::IsCallerAppOrExtension() { 1193 const pp::URLUtil_Dev* url_util = pp::URLUtil_Dev::Get(); 1194 if (!url_util) 1195 return false; 1196 1197 PP_URLComponents_Dev url_components; 1198 pp::Var url_var = url_util->GetDocumentURL(this, &url_components); 1199 if (!url_var.is_string()) 1200 return false; 1201 1202 std::string url = url_var.AsString(); 1203 std::string url_scheme = url.substr(url_components.scheme.begin, 1204 url_components.scheme.len); 1205 return url_scheme == kChromeExtensionUrlScheme; 1206} 1207 1208bool ChromotingInstance::IsConnected() { 1209 return client_ && 1210 (client_->connection_state() == protocol::ConnectionToHost::CONNECTED); 1211} 1212 1213void ChromotingInstance::OnMediaSourceSize(const webrtc::DesktopSize& size, 1214 const webrtc::DesktopVector& dpi) { 1215 SetDesktopSize(size, dpi); 1216} 1217 1218void ChromotingInstance::OnMediaSourceShape( 1219 const webrtc::DesktopRegion& shape) { 1220 SetDesktopShape(shape); 1221} 1222 1223void ChromotingInstance::OnMediaSourceReset(const std::string& format) { 1224 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 1225 data->SetString("format", format); 1226 PostLegacyJsonMessage("mediaSourceReset", data.Pass()); 1227} 1228 1229void ChromotingInstance::OnMediaSourceData(uint8_t* buffer, size_t buffer_size, 1230 bool keyframe) { 1231 pp::VarArrayBuffer array_buffer(buffer_size); 1232 void* data_ptr = array_buffer.Map(); 1233 memcpy(data_ptr, buffer, buffer_size); 1234 array_buffer.Unmap(); 1235 pp::VarDictionary data_dictionary; 1236 data_dictionary.Set(pp::Var("buffer"), array_buffer); 1237 data_dictionary.Set(pp::Var("keyframe"), keyframe); 1238 PostChromotingMessage("mediaSourceData", data_dictionary); 1239} 1240 1241} // namespace remoting 1242