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
54namespace remoting {
55
56namespace {
57
58// 32-bit BGRA is 4 bytes per pixel.
59const int kBytesPerPixel = 4;
60
61#if defined(ARCH_CPU_LITTLE_ENDIAN)
62const uint32_t kPixelAlphaMask = 0xff000000;
63#else  // !defined(ARCH_CPU_LITTLE_ENDIAN)
64const uint32_t kPixelAlphaMask = 0x000000ff;
65#endif  // !defined(ARCH_CPU_LITTLE_ENDIAN)
66
67// Default DPI to assume for old clients that use notifyClientResolution.
68const int kDefaultDPI = 96;
69
70// Interval at which to sample performance statistics.
71const int kPerfStatsIntervalMs = 1000;
72
73// URL scheme used by Chrome apps and extensions.
74const char kChromeExtensionUrlScheme[] = "chrome-extension";
75
76// Maximum width and height of a mouse cursor supported by PPAPI.
77const int kMaxCursorWidth = 32;
78const 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.
85const int kRandomSeedSize = 1024;
86#endif  // defined(USE_OPENSSL)
87
88std::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.
115std::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.
146bool 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.
151bool 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.
158bool g_logging_to_plugin = false;
159bool g_has_logging_instance = false;
160base::LazyInstance<scoped_refptr<base::SingleThreadTaskRunner> >::Leaky
161    g_logging_task_runner = LAZY_INSTANCE_INITIALIZER;
162base::LazyInstance<base::WeakPtr<ChromotingInstance> >::Leaky
163    g_logging_instance = LAZY_INSTANCE_INITIALIZER;
164base::LazyInstance<base::Lock>::Leaky
165    g_logging_lock = LAZY_INSTANCE_INITIALIZER;
166logging::LogMessageHandlerFunction g_logging_old_handler = NULL;
167
168}  // namespace
169
170// String sent in the "hello" message to the webapp to describe features.
171const char ChromotingInstance::kApiFeatures[] =
172    "highQualityScaling injectKeyEvent sendClipboardItem remapKey trapKey "
173    "notifyClientResolution pauseVideo pauseAudio asyncPin thirdPartyAuth "
174    "pinlessAuth extensionMessage allowMouseLock";
175
176const char ChromotingInstance::kRequestedCapabilities[] = "";
177const char ChromotingInstance::kSupportedCapabilities[] = "desktopShape";
178
179bool 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
198ChromotingInstance::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
233ChromotingInstance::~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
255bool 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
289void 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
346void ChromotingInstance::DidChangeFocus(bool has_focus) {
347  DCHECK(plugin_task_runner_->BelongsToCurrentThread());
348
349  input_handler_.DidChangeFocus(has_focus);
350}
351
352void 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
362bool 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
371void 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
385void 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
407void 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
416void 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
433void 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
439void 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
445void 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
453void 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
461void 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
474void 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
481protocol::ClipboardStub* ChromotingInstance::GetClipboardStub() {
482  // TODO(sergeyu): Move clipboard handling to a separate class.
483  // crbug.com/138108
484  return this;
485}
486
487protocol::CursorShapeStub* ChromotingInstance::GetCursorShapeStub() {
488  // TODO(sergeyu): Move cursor shape code to a separate class.
489  // crbug.com/138108
490  return this;
491}
492
493scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>
494ChromotingInstance::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
499void 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
507void 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
572void ChromotingInstance::OnFirstFrameReceived() {
573  scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue());
574  PostChromotingMessage("onFirstFrameReceived", data.Pass());
575}
576
577void 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
617void 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
675void 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
691void 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
704void ChromotingInstance::HandleReleaseAllKeys(
705    const base::DictionaryValue& data) {
706  if (IsConnected())
707    input_tracker_.ReleaseAll();
708}
709
710void 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
729void 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
741void 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
753void 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
771void 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
804void 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
818void 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}
831void 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
845void 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
862void 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
877void 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
895void ChromotingInstance::HandleAllowMouseLockMessage() {
896  input_handler_.AllowMouseLock();
897}
898
899ChromotingStats* ChromotingInstance::GetStats() {
900  if (!client_.get())
901    return NULL;
902  return client_->GetStats();
903}
904
905void 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
917void 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
924void 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
930void 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
953void 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
966void 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
978void 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
994bool 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
1040void 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
1054bool 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
1070bool ChromotingInstance::IsConnected() {
1071  return host_connection_.get() &&
1072    (host_connection_->state() == protocol::ConnectionToHost::CONNECTED);
1073}
1074
1075}  // namespace remoting
1076