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