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