• 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 #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 
ConnectionStateToString(protocol::ConnectionToHost::State state)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.
ConnectionErrorToString(protocol::ErrorCode error)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.
IsVisiblePixel(uint32_t pixel)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.
IsVisibleRow(const uint32_t * begin,const uint32_t * end)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 
ParseAuthMethods(const std::string & auth_methods_str,ClientConfig * config)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 
ChromotingInstance(PP_Instance pp_instance)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 
~ChromotingInstance()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 
Init(uint32_t argc,const char * argn[],const char * argv[])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 
HandleMessage(const pp::Var & message)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 
DidChangeFocus(bool has_focus)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 
DidChangeView(const pp::View & view)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 
HandleInputEvent(const pp::InputEvent & event)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 
SetDesktopSize(const webrtc::DesktopSize & size,const webrtc::DesktopVector & dpi)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 
SetDesktopShape(const webrtc::DesktopRegion & shape)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 
OnConnectionState(protocol::ConnectionToHost::State state,protocol::ErrorCode error)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 
FetchThirdPartyToken(const GURL & token_url,const std::string & host_public_key,const std::string & scope,base::WeakPtr<TokenFetcherProxy> token_fetcher_proxy)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 
OnConnectionReady(bool ready)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 
OnRouteChanged(const std::string & channel_name,const protocol::TransportRoute & route)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 
SetCapabilities(const std::string & capabilities)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 
SetPairingResponse(const protocol::PairingResponse & pairing_response)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 
DeliverHostMessage(const protocol::ExtensionMessage & message)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 
FetchSecretFromDialog(bool pairing_supported,const protocol::SecretFetchedCallback & secret_fetched_callback)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 
FetchSecretFromString(const std::string & shared_secret,bool pairing_supported,const protocol::SecretFetchedCallback & secret_fetched_callback)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 
GetClipboardStub()528 protocol::ClipboardStub* ChromotingInstance::GetClipboardStub() {
529   // TODO(sergeyu): Move clipboard handling to a separate class.
530   // crbug.com/138108
531   return this;
532 }
533 
GetCursorShapeStub()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>
GetTokenFetcher(const std::string & host_public_key)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 
InjectClipboardEvent(const protocol::ClipboardEvent & event)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 
SetCursorShape(const protocol::CursorShapeInfo & cursor_shape)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 
OnFirstFrameReceived()643 void ChromotingInstance::OnFirstFrameReceived() {
644   scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue());
645   PostLegacyJsonMessage("onFirstFrameReceived", data.Pass());
646 }
647 
HandleConnect(const base::DictionaryValue & data)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 
ConnectWithConfig(const ClientConfig & config,const std::string & local_jid)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 
HandleDisconnect(const base::DictionaryValue & data)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 
HandleOnIncomingIq(const base::DictionaryValue & data)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 
HandleReleaseAllKeys(const base::DictionaryValue & data)806 void ChromotingInstance::HandleReleaseAllKeys(
807     const base::DictionaryValue& data) {
808   if (IsConnected())
809     input_tracker_.ReleaseAll();
810 }
811 
HandleInjectKeyEvent(const base::DictionaryValue & data)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 
HandleRemapKey(const base::DictionaryValue & data)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 
HandleTrapKey(const base::DictionaryValue & data)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 
HandleSendClipboardItem(const base::DictionaryValue & data)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 
HandleNotifyClientResolution(const base::DictionaryValue & data)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 
HandlePauseVideo(const base::DictionaryValue & data)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 
HandleVideoControl(const base::DictionaryValue & data)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 
HandlePauseAudio(const base::DictionaryValue & data)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 }
HandleOnPinFetched(const base::DictionaryValue & data)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 
HandleOnThirdPartyTokenFetched(const base::DictionaryValue & data)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 
HandleRequestPairing(const base::DictionaryValue & data)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 
HandleExtensionMessage(const base::DictionaryValue & data)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 
HandleAllowMouseLockMessage()1011 void ChromotingInstance::HandleAllowMouseLockMessage() {
1012   input_handler_.AllowMouseLock();
1013 }
1014 
HandleEnableMediaSourceRendering()1015 void ChromotingInstance::HandleEnableMediaSourceRendering() {
1016   use_media_source_rendering_ = true;
1017 }
1018 
HandleSendMouseInputWhenUnfocused()1019 void ChromotingInstance::HandleSendMouseInputWhenUnfocused() {
1020   input_handler_.set_send_mouse_input_when_unfocused(true);
1021 }
1022 
HandleDelegateLargeCursors()1023 void ChromotingInstance::HandleDelegateLargeCursors() {
1024   delegate_large_cursors_ = true;
1025 }
1026 
GetStats()1027 ChromotingStats* ChromotingInstance::GetStats() {
1028   if (!video_renderer_.get())
1029     return NULL;
1030   return video_renderer_->GetStats();
1031 }
1032 
PostChromotingMessage(const std::string & method,const pp::VarDictionary & data)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 
PostLegacyJsonMessage(const std::string & method,scoped_ptr<base::DictionaryValue> data)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 
SendTrappedKey(uint32 usb_keycode,bool pressed)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 
SendOutgoingIq(const std::string & iq)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 
SendPerfStats()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
RegisterLogMessageHandler()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 
RegisterLoggingInstance()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 
UnregisterLoggingInstance()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
LogToUI(int severity,const char * file,int line,size_t message_start,const std::string & str)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 
ProcessLogToUI(const std::string & message)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 
IsCallerAppOrExtension()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 
IsConnected()1206 bool ChromotingInstance::IsConnected() {
1207   return host_connection_.get() &&
1208     (host_connection_->state() == protocol::ConnectionToHost::CONNECTED);
1209 }
1210 
OnMediaSourceSize(const webrtc::DesktopSize & size,const webrtc::DesktopVector & dpi)1211 void ChromotingInstance::OnMediaSourceSize(const webrtc::DesktopSize& size,
1212                                            const webrtc::DesktopVector& dpi) {
1213   SetDesktopSize(size, dpi);
1214 }
1215 
OnMediaSourceShape(const webrtc::DesktopRegion & shape)1216 void ChromotingInstance::OnMediaSourceShape(
1217     const webrtc::DesktopRegion& shape) {
1218   SetDesktopShape(shape);
1219 }
1220 
OnMediaSourceReset(const std::string & format)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 
OnMediaSourceData(uint8_t * buffer,size_t buffer_size,bool keyframe)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