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