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 <stdio.h>
6#include <string.h>
7
8#include <string>
9#include <vector>
10
11#include "base/at_exit.h"
12#include "base/basictypes.h"
13#include "base/command_line.h"
14#include "base/logging.h"
15#include "base/strings/stringize_macros.h"
16#include "net/socket/ssl_server_socket.h"
17#include "remoting/base/plugin_thread_task_runner.h"
18#include "remoting/base/resources.h"
19#include "remoting/base/string_resources.h"
20#include "remoting/host/plugin/host_log_handler.h"
21#include "remoting/host/plugin/host_plugin_utils.h"
22#include "remoting/host/plugin/host_script_object.h"
23#if defined(OS_WIN)
24#include "ui/gfx/win/dpi.h"
25#endif
26#include "third_party/npapi/bindings/npapi.h"
27#include "third_party/npapi/bindings/npfunctions.h"
28#include "third_party/npapi/bindings/npruntime.h"
29#include "ui/base/l10n/l10n_util.h"
30
31// Symbol export is handled with a separate def file on Windows.
32#if defined (__GNUC__) && __GNUC__ >= 4
33#define EXPORT __attribute__((visibility("default")))
34#else
35#define EXPORT
36#endif
37
38#if defined(OS_WIN)
39// TODO(wez): libvpx expects these 64-bit division functions to be provided
40// by libgcc.a, which we aren't linked against.  These implementations can
41// be removed once we have native MSVC libvpx builds for Windows.
42extern "C" {
43
44int64_t __cdecl __divdi3(int64_t a, int64_t b) {
45  return a / b;
46}
47uint64_t __cdecl __udivdi3(uint64_t a, uint64_t b) {
48  return a / b;
49}
50
51}
52#endif
53
54using remoting::g_npnetscape_funcs;
55using remoting::HostLogHandler;
56using remoting::HostNPScriptObject;
57using remoting::StringFromNPIdentifier;
58
59namespace {
60
61bool g_initialized = false;
62
63base::AtExitManager* g_at_exit_manager = NULL;
64
65// The plugin name and description returned by GetValue().
66std::string* g_ui_name = NULL;
67std::string* g_ui_description = NULL;
68
69// NPAPI plugin implementation for remoting host.
70// Documentation for most of the calls in this class can be found here:
71// https://developer.mozilla.org/en/Gecko_Plugin_API_Reference/Scripting_plugins
72class HostNPPlugin : public remoting::PluginThreadTaskRunner::Delegate {
73 public:
74  // |mode| is the display mode of plug-in. Values:
75  // NP_EMBED: (1) Instance was created by an EMBED tag and shares the browser
76  //               window with other content.
77  // NP_FULL: (2) Instance was created by a separate file and is the primary
78  //              content in the window.
79  HostNPPlugin(NPP instance, uint16 mode)
80      : instance_(instance),
81        scriptable_object_(NULL) {
82    plugin_task_runner_ = new remoting::PluginThreadTaskRunner(this);
83    plugin_auto_task_runner_ =
84        new remoting::AutoThreadTaskRunner(
85            plugin_task_runner_,
86            base::Bind(&remoting::PluginThreadTaskRunner::Quit,
87                       plugin_task_runner_));
88  }
89
90  virtual ~HostNPPlugin() {
91    if (scriptable_object_) {
92      DCHECK_EQ(scriptable_object_->referenceCount, 1UL);
93      g_npnetscape_funcs->releaseobject(scriptable_object_);
94      scriptable_object_ = NULL;
95    }
96
97    // Process tasks on |plugin_task_runner_| until all references to
98    // |plugin_auto_task_runner_| have been dropped. This requires that the
99    // browser has dropped any script object references - see above.
100    plugin_auto_task_runner_ = NULL;
101    plugin_task_runner_->DetachAndRunShutdownLoop();
102  }
103
104  bool Init(int16 argc, char** argn, char** argv, NPSavedData* saved) {
105#if defined(OS_MACOSX)
106    // Use the modern CoreGraphics and Cocoa models when available, since
107    // QuickDraw and Carbon are deprecated.
108    // The drawing and event models don't change anything for this plugin, since
109    // none of the functions affected by the models actually do anything.
110    // This does however keep the plugin from breaking when Chromium eventually
111    // drops support for QuickDraw and Carbon, and it also keeps the browser
112    // from sending Null Events once a second to support old Carbon based
113    // timers.
114    // Chromium should always be supporting the newer models.
115
116    // Sanity check to see if Chromium supports the CoreGraphics drawing model.
117    NPBool supports_core_graphics = false;
118    NPError err = g_npnetscape_funcs->getvalue(instance_,
119                                               NPNVsupportsCoreGraphicsBool,
120                                               &supports_core_graphics);
121    if (err == NPERR_NO_ERROR && supports_core_graphics) {
122      // Switch to CoreGraphics drawing model.
123      g_npnetscape_funcs->setvalue(instance_, NPPVpluginDrawingModel,
124          reinterpret_cast<void*>(NPDrawingModelCoreGraphics));
125    } else {
126      LOG(ERROR) << "No Core Graphics support";
127      return false;
128    }
129
130    // Sanity check to see if Chromium supports the Cocoa event model.
131    NPBool supports_cocoa = false;
132    err = g_npnetscape_funcs->getvalue(instance_, NPNVsupportsCocoaBool,
133                                       &supports_cocoa);
134    if (err == NPERR_NO_ERROR && supports_cocoa) {
135      // Switch to Cocoa event model.
136      g_npnetscape_funcs->setvalue(instance_, NPPVpluginEventModel,
137          reinterpret_cast<void*>(NPEventModelCocoa));
138    } else {
139      LOG(ERROR) << "No Cocoa Event Model support";
140      return false;
141    }
142#endif  // OS_MACOSX
143    net::EnableSSLServerSockets();
144    return true;
145  }
146
147  bool Save(NPSavedData** saved) {
148    return true;
149  }
150
151  NPObject* GetScriptableObject() {
152    if (!scriptable_object_) {
153      // Must be static. If it is a temporary, objects created by this
154      // method will fail in weird and wonderful ways later.
155      static NPClass npc_ref_object = {
156        NP_CLASS_STRUCT_VERSION,
157        &Allocate,
158        &Deallocate,
159        &Invalidate,
160        &HasMethod,
161        &Invoke,
162        &InvokeDefault,
163        &HasProperty,
164        &GetProperty,
165        &SetProperty,
166        &RemoveProperty,
167        &Enumerate,
168        NULL
169      };
170      scriptable_object_ = g_npnetscape_funcs->createobject(instance_,
171                                                            &npc_ref_object);
172    }
173    return scriptable_object_;
174  }
175
176  // PluginThreadTaskRunner::Delegate implementation.
177  virtual bool RunOnPluginThread(
178      base::TimeDelta delay, void(function)(void*), void* data) OVERRIDE {
179    if (delay == base::TimeDelta()) {
180      g_npnetscape_funcs->pluginthreadasynccall(instance_, function, data);
181    } else {
182      base::AutoLock auto_lock(timers_lock_);
183      uint32_t timer_id = g_npnetscape_funcs->scheduletimer(
184          instance_, delay.InMilliseconds(), false, &NPDelayedTaskSpringboard);
185      DelayedTask task = {function, data};
186      timers_[timer_id] = task;
187    }
188    return true;
189  }
190
191  void SetWindow(NPWindow* np_window) {
192    if (scriptable_object_) {
193      ScriptableFromObject(scriptable_object_)->SetWindow(np_window);
194    }
195  }
196
197  static void NPDelayedTaskSpringboard(NPP npp, uint32_t timer_id) {
198    HostNPPlugin* self = reinterpret_cast<HostNPPlugin*>(npp->pdata);
199    DelayedTask task;
200    {
201      base::AutoLock auto_lock(self->timers_lock_);
202      std::map<uint32_t, DelayedTask>::iterator it =
203          self->timers_.find(timer_id);
204      CHECK(it != self->timers_.end());
205      task = it->second;
206      self->timers_.erase(it);
207    }
208    task.function(task.data);
209  }
210
211 private:
212  struct ScriptableNPObject : public NPObject {
213    HostNPScriptObject* scriptable_object;
214  };
215
216  struct DelayedTask {
217    void (*function)(void*);
218    void* data;
219  };
220
221  static HostNPScriptObject* ScriptableFromObject(NPObject* obj) {
222    return reinterpret_cast<ScriptableNPObject*>(obj)->scriptable_object;
223  }
224
225  static NPObject* Allocate(NPP npp, NPClass* aClass) {
226    VLOG(2) << "static Allocate";
227    ScriptableNPObject* object =
228        reinterpret_cast<ScriptableNPObject*>(
229            g_npnetscape_funcs->memalloc(sizeof(ScriptableNPObject)));
230    HostNPPlugin* plugin = reinterpret_cast<HostNPPlugin*>(npp->pdata);
231
232    object->_class = aClass;
233    object->referenceCount = 1;
234    object->scriptable_object =
235        new HostNPScriptObject(npp, object, plugin->plugin_auto_task_runner_);
236    return object;
237  }
238
239  static void Deallocate(NPObject* npobj) {
240    VLOG(2) << "static Deallocate";
241    if (npobj) {
242      Invalidate(npobj);
243      g_npnetscape_funcs->memfree(npobj);
244    }
245  }
246
247  static void Invalidate(NPObject* npobj) {
248    if (npobj) {
249      ScriptableNPObject* object = reinterpret_cast<ScriptableNPObject*>(npobj);
250      if (object->scriptable_object) {
251        delete object->scriptable_object;
252        object->scriptable_object = NULL;
253      }
254    }
255  }
256
257  static bool HasMethod(NPObject* obj, NPIdentifier method_name) {
258    VLOG(2) << "static HasMethod";
259    HostNPScriptObject* scriptable = ScriptableFromObject(obj);
260    if (!scriptable) return false;
261    std::string method_name_string = StringFromNPIdentifier(method_name);
262    if (method_name_string.empty())
263      return false;
264    return scriptable->HasMethod(method_name_string);
265  }
266
267  static bool InvokeDefault(NPObject* obj,
268                            const NPVariant* args,
269                            uint32_t argCount,
270                            NPVariant* result) {
271    VLOG(2) << "static InvokeDefault";
272    HostNPScriptObject* scriptable = ScriptableFromObject(obj);
273    if (!scriptable) return false;
274    return scriptable->InvokeDefault(args, argCount, result);
275  }
276
277  static bool Invoke(NPObject* obj,
278                     NPIdentifier method_name,
279                     const NPVariant* args,
280                     uint32_t argCount,
281                     NPVariant* result) {
282    VLOG(2) << "static Invoke";
283    HostNPScriptObject* scriptable = ScriptableFromObject(obj);
284    if (!scriptable)
285      return false;
286    std::string method_name_string = StringFromNPIdentifier(method_name);
287    if (method_name_string.empty())
288      return false;
289    return scriptable->Invoke(method_name_string, args, argCount, result);
290  }
291
292  static bool HasProperty(NPObject* obj, NPIdentifier property_name) {
293    VLOG(2) << "static HasProperty";
294    HostNPScriptObject* scriptable = ScriptableFromObject(obj);
295    if (!scriptable) return false;
296    std::string property_name_string = StringFromNPIdentifier(property_name);
297    if (property_name_string.empty())
298      return false;
299    return scriptable->HasProperty(property_name_string);
300  }
301
302  static bool GetProperty(NPObject* obj,
303                          NPIdentifier property_name,
304                          NPVariant* result) {
305    VLOG(2) << "static GetProperty";
306    HostNPScriptObject* scriptable = ScriptableFromObject(obj);
307    if (!scriptable) return false;
308    std::string property_name_string = StringFromNPIdentifier(property_name);
309    if (property_name_string.empty())
310      return false;
311    return scriptable->GetProperty(property_name_string, result);
312  }
313
314  static bool SetProperty(NPObject* obj,
315                          NPIdentifier property_name,
316                          const NPVariant* value) {
317    VLOG(2) << "static SetProperty";
318    HostNPScriptObject* scriptable = ScriptableFromObject(obj);
319    if (!scriptable) return false;
320    std::string property_name_string = StringFromNPIdentifier(property_name);
321    if (property_name_string.empty())
322      return false;
323    return scriptable->SetProperty(property_name_string, value);
324  }
325
326  static bool RemoveProperty(NPObject* obj, NPIdentifier property_name) {
327    VLOG(2) << "static RemoveProperty";
328    HostNPScriptObject* scriptable = ScriptableFromObject(obj);
329    if (!scriptable) return false;
330    std::string property_name_string = StringFromNPIdentifier(property_name);
331    if (property_name_string.empty())
332      return false;
333    return scriptable->RemoveProperty(property_name_string);
334  }
335
336  static bool Enumerate(NPObject* obj,
337                        NPIdentifier** value,
338                        uint32_t* count) {
339    VLOG(2) << "static Enumerate";
340    HostNPScriptObject* scriptable = ScriptableFromObject(obj);
341    if (!scriptable) return false;
342    std::vector<std::string> values;
343    bool is_good = scriptable->Enumerate(&values);
344    if (is_good) {
345      *count = values.size();
346      *value = reinterpret_cast<NPIdentifier*>(
347          g_npnetscape_funcs->memalloc(sizeof(NPIdentifier) * (*count)));
348      for (uint32_t i = 0; i < *count; ++i) {
349        (*value)[i] =
350            g_npnetscape_funcs->getstringidentifier(values[i].c_str());
351      }
352    }
353    return is_good;
354  }
355
356  NPP instance_;
357  NPObject* scriptable_object_;
358
359  scoped_refptr<remoting::PluginThreadTaskRunner> plugin_task_runner_;
360  scoped_refptr<remoting::AutoThreadTaskRunner> plugin_auto_task_runner_;
361
362  std::map<uint32_t, DelayedTask> timers_;
363  base::Lock timers_lock_;
364};
365
366void InitializePlugin() {
367  if (g_initialized)
368    return;
369
370  g_initialized = true;
371  g_at_exit_manager = new base::AtExitManager;
372
373  // Init an empty command line for common objects that use it.
374  CommandLine::Init(0, NULL);
375
376  if (remoting::LoadResources("")) {
377    g_ui_name = new std::string(
378        l10n_util::GetStringUTF8(IDR_REMOTING_HOST_PLUGIN_NAME));
379    g_ui_description = new std::string(
380        l10n_util::GetStringUTF8(IDR_REMOTING_HOST_PLUGIN_DESCRIPTION));
381  } else {
382    g_ui_name = new std::string();
383    g_ui_description = new std::string();
384  }
385}
386
387void ShutdownPlugin() {
388  delete g_ui_name;
389  delete g_ui_description;
390
391  remoting::UnloadResources();
392
393  delete g_at_exit_manager;
394}
395
396// Utility functions to map NPAPI Entry Points to C++ Objects.
397HostNPPlugin* PluginFromInstance(NPP instance) {
398  return reinterpret_cast<HostNPPlugin*>(instance->pdata);
399}
400
401NPError CreatePlugin(NPMIMEType pluginType,
402                     NPP instance,
403                     uint16 mode,
404                     int16 argc,
405                     char** argn,
406                     char** argv,
407                     NPSavedData* saved) {
408  VLOG(2) << "CreatePlugin";
409
410  // Register a global log handler.
411  // The LogMessage registration code is not thread-safe, so we need to perform
412  // this while we're running in a single thread.
413  HostLogHandler::RegisterLogMessageHandler();
414
415  HostNPPlugin* plugin = new HostNPPlugin(instance, mode);
416  instance->pdata = plugin;
417  if (!plugin->Init(argc, argn, argv, saved)) {
418    delete plugin;
419    instance->pdata = NULL;
420    return NPERR_INVALID_PLUGIN_ERROR;
421  } else {
422    return NPERR_NO_ERROR;
423  }
424}
425
426NPError DestroyPlugin(NPP instance,
427                      NPSavedData** save) {
428  VLOG(2) << "DestroyPlugin";
429
430  // Normally, we would unregister the global log handler that we registered
431  // in CreatePlugin. However, the LogHandler registration code is not thread-
432  // safe so we could crash if we update (register or unregister) the
433  // LogHandler while it's being read on another thread.
434  // At this point, all our threads should be shutdown, but it's safer to leave
435  // the handler registered until we're completely destroyed.
436
437  HostNPPlugin* plugin = PluginFromInstance(instance);
438  if (plugin) {
439    plugin->Save(save);
440    delete plugin;
441    instance->pdata = NULL;
442    return NPERR_NO_ERROR;
443  } else {
444    return NPERR_INVALID_PLUGIN_ERROR;
445  }
446}
447
448NPError GetValue(NPP instance, NPPVariable variable, void* value) {
449  // NP_GetValue() can be called before NP_Initialize().
450  InitializePlugin();
451
452  switch(variable) {
453  default:
454    VLOG(2) << "GetValue - default " << variable;
455    return NPERR_GENERIC_ERROR;
456  case NPPVpluginNameString:
457    VLOG(2) << "GetValue - name string";
458    *reinterpret_cast<const char**>(value) = g_ui_name->c_str();
459    break;
460  case NPPVpluginDescriptionString:
461    VLOG(2) << "GetValue - description string";
462    *reinterpret_cast<const char**>(value) = g_ui_description->c_str();
463    break;
464  case NPPVpluginNeedsXEmbed:
465    VLOG(2) << "GetValue - NeedsXEmbed";
466    *(static_cast<NPBool*>(value)) = true;
467    break;
468  case NPPVpluginScriptableNPObject:
469    VLOG(2) << "GetValue - scriptable object";
470    HostNPPlugin* plugin = PluginFromInstance(instance);
471    if (!plugin)
472      return NPERR_INVALID_PLUGIN_ERROR;
473    NPObject* scriptable_object = plugin->GetScriptableObject();
474    g_npnetscape_funcs->retainobject(scriptable_object);
475    *reinterpret_cast<NPObject**>(value) = scriptable_object;
476    break;
477  }
478  return NPERR_NO_ERROR;
479}
480
481NPError HandleEvent(NPP instance, void* ev) {
482  VLOG(2) << "HandleEvent";
483  return NPERR_NO_ERROR;
484}
485
486NPError SetWindow(NPP instance, NPWindow* pNPWindow) {
487  VLOG(2) << "SetWindow";
488  HostNPPlugin* plugin = PluginFromInstance(instance);
489  if (plugin) {
490    plugin->SetWindow(pNPWindow);
491  }
492  return NPERR_NO_ERROR;
493}
494
495}  // namespace
496
497#if defined(OS_WIN)
498BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) {
499  switch (dwReason) {
500    case DLL_PROCESS_ATTACH:
501      DisableThreadLibraryCalls(hModule);
502      break;
503    case DLL_PROCESS_DETACH:
504    case DLL_THREAD_ATTACH:
505    case DLL_THREAD_DETACH:
506      break;
507  }
508  return TRUE;
509}
510#endif
511
512// The actual required NPAPI Entry points
513
514extern "C" {
515
516EXPORT NPError API_CALL NP_GetEntryPoints(NPPluginFuncs* nppfuncs) {
517  VLOG(2) << "NP_GetEntryPoints";
518  nppfuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
519  nppfuncs->newp = &CreatePlugin;
520  nppfuncs->destroy = &DestroyPlugin;
521  nppfuncs->getvalue = &GetValue;
522  nppfuncs->event = &HandleEvent;
523  nppfuncs->setwindow = &SetWindow;
524
525  return NPERR_NO_ERROR;
526}
527
528EXPORT NPError API_CALL NP_Initialize(NPNetscapeFuncs* npnetscape_funcs
529#if defined(OS_POSIX) && !defined(OS_MACOSX)
530                            , NPPluginFuncs* nppfuncs
531#endif
532                            ) {
533  VLOG(2) << "NP_Initialize";
534  InitializePlugin();
535
536  if(npnetscape_funcs == NULL)
537    return NPERR_INVALID_FUNCTABLE_ERROR;
538
539  if(((npnetscape_funcs->version & 0xff00) >> 8) > NP_VERSION_MAJOR)
540    return NPERR_INCOMPATIBLE_VERSION_ERROR;
541
542  g_npnetscape_funcs = npnetscape_funcs;
543#if defined(OS_POSIX) && !defined(OS_MACOSX)
544  NP_GetEntryPoints(nppfuncs);
545#endif
546
547#if defined(OS_WIN)
548  gfx::EnableHighDPISupport();
549#endif
550
551  return NPERR_NO_ERROR;
552}
553
554EXPORT NPError API_CALL NP_Shutdown() {
555  VLOG(2) << "NP_Shutdown";
556  ShutdownPlugin();
557
558  return NPERR_NO_ERROR;
559}
560
561#if defined(OS_POSIX) && !defined(OS_MACOSX)
562EXPORT const char* API_CALL NP_GetMIMEDescription(void) {
563  VLOG(2) << "NP_GetMIMEDescription";
564  return STRINGIZE(HOST_PLUGIN_MIME_TYPE) "::";
565}
566
567EXPORT NPError API_CALL NP_GetValue(void* npp,
568                                    NPPVariable variable,
569                                    void* value) {
570  return GetValue((NPP)npp, variable, value);
571}
572#endif
573
574}  // extern "C"
575