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