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