Home | History | Annotate | Download | only in plugin
      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 
     64 namespace remoting {
     65 
     66 namespace {
     67 
     68 // 32-bit BGRA is 4 bytes per pixel.
     69 const int kBytesPerPixel = 4;
     70 
     71 #if defined(ARCH_CPU_LITTLE_ENDIAN)
     72 const uint32_t kPixelAlphaMask = 0xff000000;
     73 #else  // !defined(ARCH_CPU_LITTLE_ENDIAN)
     74 const uint32_t kPixelAlphaMask = 0x000000ff;
     75 #endif  // !defined(ARCH_CPU_LITTLE_ENDIAN)
     76 
     77 // Default DPI to assume for old clients that use notifyClientResolution.
     78 const int kDefaultDPI = 96;
     79 
     80 // Interval at which to sample performance statistics.
     81 const int kPerfStatsIntervalMs = 1000;
     82 
     83 // URL scheme used by Chrome apps and extensions.
     84 const char kChromeExtensionUrlScheme[] = "chrome-extension";
     85 
     86 // Maximum width and height of a mouse cursor supported by PPAPI.
     87 const int kMaxCursorWidth = 32;
     88 const 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.
     95 const int kRandomSeedSize = 1024;
     96 #endif  // defined(USE_OPENSSL)
     97 
     98 std::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.
    125 std::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.
    156 bool 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.
    161 bool IsVisibleRow(const uint32_t* begin, const uint32_t* end) {
    162   return std::find_if(begin, end, &IsVisiblePixel) != end;
    163 }
    164 
    165 bool 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.
    188 bool g_logging_to_plugin = false;
    189 bool g_has_logging_instance = false;
    190 base::LazyInstance<scoped_refptr<base::SingleThreadTaskRunner> >::Leaky
    191     g_logging_task_runner = LAZY_INSTANCE_INITIALIZER;
    192 base::LazyInstance<base::WeakPtr<ChromotingInstance> >::Leaky
    193     g_logging_instance = LAZY_INSTANCE_INITIALIZER;
    194 base::LazyInstance<base::Lock>::Leaky
    195     g_logging_lock = LAZY_INSTANCE_INITIALIZER;
    196 logging::LogMessageHandlerFunction g_logging_old_handler = NULL;
    197 
    198 }  // namespace
    199 
    200 // String sent in the "hello" message to the webapp to describe features.
    201 const char ChromotingInstance::kApiFeatures[] =
    202     "highQualityScaling injectKeyEvent sendClipboardItem remapKey trapKey "
    203     "notifyClientResolution pauseVideo pauseAudio asyncPin thirdPartyAuth "
    204     "pinlessAuth extensionMessage allowMouseLock mediaSourceRendering "
    205     "videoControl";
    206 
    207 const char ChromotingInstance::kRequestedCapabilities[] = "";
    208 const char ChromotingInstance::kSupportedCapabilities[] = "desktopShape";
    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       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 
    277 ChromotingInstance::~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 
    299 bool 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 
    323 void 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 
    388 void 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 
    397 void 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 
    408 bool 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 
    417 void 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 
    431 void 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 
    453 void 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 
    462 void 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 
    479 void 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 
    485 void 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 
    494 void 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 
    500 void 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 
    508 void 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 
    516 void 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 
    529 void 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 
    536 protocol::ClipboardStub* ChromotingInstance::GetClipboardStub() {
    537   // TODO(sergeyu): Move clipboard handling to a separate class.
    538   // crbug.com/138108
    539   return this;
    540 }
    541 
    542 protocol::CursorShapeStub* ChromotingInstance::GetCursorShapeStub() {
    543   // TODO(sergeyu): Move cursor shape code to a separate class.
    544   // crbug.com/138108
    545   return this;
    546 }
    547 
    548 void 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 
    556 void 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 
    642 void ChromotingInstance::OnFirstFrameReceived() {
    643   scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue());
    644   PostLegacyJsonMessage("onFirstFrameReceived", data.Pass());
    645 }
    646 
    647 void 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 
    789 void 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 
    803 void 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 
    816 void ChromotingInstance::HandleReleaseAllKeys(
    817     const base::DictionaryValue& data) {
    818   if (IsConnected())
    819     input_tracker_.ReleaseAll();
    820 }
    821 
    822 void 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 
    841 void 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 
    853 void 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 
    865 void 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 
    883 void 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 
    916 void ChromotingInstance::HandlePauseVideo(const base::DictionaryValue& data) {
    917   if (!data.HasKey("pause")) {
    918     LOG(ERROR) << "Invalid pauseVideo.";
    919     return;
    920   }
    921   HandleVideoControl(data);
    922 }
    923 
    924 void 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 
    944 void 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 }
    957 void 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 
    971 void 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 
    988 void 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 
   1003 void 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 
   1021 void ChromotingInstance::HandleAllowMouseLockMessage() {
   1022   input_handler_.AllowMouseLock();
   1023 }
   1024 
   1025 void ChromotingInstance::HandleEnableMediaSourceRendering() {
   1026   use_media_source_rendering_ = true;
   1027 }
   1028 
   1029 void ChromotingInstance::HandleSendMouseInputWhenUnfocused() {
   1030   input_handler_.set_send_mouse_input_when_unfocused(true);
   1031 }
   1032 
   1033 void ChromotingInstance::HandleDelegateLargeCursors() {
   1034   delegate_large_cursors_ = true;
   1035 }
   1036 
   1037 ChromotingStats* ChromotingInstance::GetStats() {
   1038   if (!video_renderer_.get())
   1039     return NULL;
   1040   return video_renderer_->GetStats();
   1041 }
   1042 
   1043 void 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 
   1051 void 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 
   1063 void 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 
   1070 void 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 
   1076 void 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
   1099 void 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 
   1112 void 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 
   1124 void 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
   1140 bool 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 
   1186 void 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 
   1200 bool 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 
   1216 bool ChromotingInstance::IsConnected() {
   1217   return client_ &&
   1218          (client_->connection_state() == protocol::ConnectionToHost::CONNECTED);
   1219 }
   1220 
   1221 void ChromotingInstance::OnMediaSourceSize(const webrtc::DesktopSize& size,
   1222                                            const webrtc::DesktopVector& dpi) {
   1223   SetDesktopSize(size, dpi);
   1224 }
   1225 
   1226 void ChromotingInstance::OnMediaSourceShape(
   1227     const webrtc::DesktopRegion& shape) {
   1228   SetDesktopShape(shape);
   1229 }
   1230 
   1231 void 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 
   1237 void 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