Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / remoting / client / plugin / chromoting_instance.cc
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
55 namespace remoting {
56
57 namespace {
58
59 // 32-bit BGRA is 4 bytes per pixel.
60 const int kBytesPerPixel = 4;
61
62 #if defined(ARCH_CPU_LITTLE_ENDIAN)
63 const uint32_t kPixelAlphaMask = 0xff000000;
64 #else  // !defined(ARCH_CPU_LITTLE_ENDIAN)
65 const uint32_t kPixelAlphaMask = 0x000000ff;
66 #endif  // !defined(ARCH_CPU_LITTLE_ENDIAN)
67
68 // Default DPI to assume for old clients that use notifyClientResolution.
69 const int kDefaultDPI = 96;
70
71 // Interval at which to sample performance statistics.
72 const int kPerfStatsIntervalMs = 1000;
73
74 // URL scheme used by Chrome apps and extensions.
75 const char kChromeExtensionUrlScheme[] = "chrome-extension";
76
77 // Maximum width and height of a mouse cursor supported by PPAPI.
78 const int kMaxCursorWidth = 32;
79 const 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.
86 const int kRandomSeedSize = 1024;
87 #endif  // defined(USE_OPENSSL)
88
89 std::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.
116 std::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.
147 bool 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.
152 bool 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.
159 bool g_logging_to_plugin = false;
160 bool g_has_logging_instance = false;
161 base::LazyInstance<scoped_refptr<base::SingleThreadTaskRunner> >::Leaky
162     g_logging_task_runner = LAZY_INSTANCE_INITIALIZER;
163 base::LazyInstance<base::WeakPtr<ChromotingInstance> >::Leaky
164     g_logging_instance = LAZY_INSTANCE_INITIALIZER;
165 base::LazyInstance<base::Lock>::Leaky
166     g_logging_lock = LAZY_INSTANCE_INITIALIZER;
167 logging::LogMessageHandlerFunction g_logging_old_handler = NULL;
168
169 }  // namespace
170
171 // String sent in the "hello" message to the webapp to describe features.
172 const char ChromotingInstance::kApiFeatures[] =
173     "highQualityScaling injectKeyEvent sendClipboardItem remapKey trapKey "
174     "notifyClientResolution pauseVideo pauseAudio asyncPin thirdPartyAuth "
175     "pinlessAuth extensionMessage allowMouseLock mediaSourceRendering";
176
177 const char ChromotingInstance::kRequestedCapabilities[] = "";
178 const char ChromotingInstance::kSupportedCapabilities[] = "desktopShape";
179
180 bool 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
199 ChromotingInstance::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
235 ChromotingInstance::~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
257 bool 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
291 void 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
350 void ChromotingInstance::DidChangeFocus(bool has_focus) {
351   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
352
353   input_handler_.DidChangeFocus(has_focus);
354 }
355
356 void 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
367 bool 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
376 void 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
390 void 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
412 void 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
421 void 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
438 void 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
444 void 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
453 void 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
459 void 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
467 void 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
475 void 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
488 void 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
495 protocol::ClipboardStub* ChromotingInstance::GetClipboardStub() {
496   // TODO(sergeyu): Move clipboard handling to a separate class.
497   // crbug.com/138108
498   return this;
499 }
500
501 protocol::CursorShapeStub* ChromotingInstance::GetCursorShapeStub() {
502   // TODO(sergeyu): Move cursor shape code to a separate class.
503   // crbug.com/138108
504   return this;
505 }
506
507 scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher>
508 ChromotingInstance::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
513 void 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
521 void 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
586 void ChromotingInstance::OnFirstFrameReceived() {
587   scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue());
588   PostLegacyJsonMessage("onFirstFrameReceived", data.Pass());
589 }
590
591 void 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
631 void 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
698 void 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
714 void 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
727 void ChromotingInstance::HandleReleaseAllKeys(
728     const base::DictionaryValue& data) {
729   if (IsConnected())
730     input_tracker_.ReleaseAll();
731 }
732
733 void 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
752 void 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
764 void 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
776 void 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
794 void 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
827 void 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
841 void 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 }
854 void 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
868 void 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
885 void 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
900 void 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
918 void ChromotingInstance::HandleAllowMouseLockMessage() {
919   input_handler_.AllowMouseLock();
920 }
921
922 void ChromotingInstance::HandleEnableMediaSourceRendering() {
923   use_media_source_rendering_ = true;
924 }
925
926 ChromotingStats* ChromotingInstance::GetStats() {
927   if (!video_renderer_.get())
928     return NULL;
929   return video_renderer_->GetStats();
930 }
931
932 void 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
940 void 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
952 void 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
959 void 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
965 void 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
988 void 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
1001 void 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
1013 void 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
1029 bool 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
1075 void 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
1089 bool 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
1105 bool ChromotingInstance::IsConnected() {
1106   return host_connection_.get() &&
1107     (host_connection_->state() == protocol::ConnectionToHost::CONNECTED);
1108 }
1109
1110 void ChromotingInstance::OnMediaSourceSize(const webrtc::DesktopSize& size,
1111                                            const webrtc::DesktopVector& dpi) {
1112   SetDesktopSize(size, dpi);
1113 }
1114
1115 void ChromotingInstance::OnMediaSourceShape(
1116     const webrtc::DesktopRegion& shape) {
1117   SetDesktopShape(shape);
1118 }
1119
1120 void 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
1126 void 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