Home | History | Annotate | Download | only in plugin
      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/base/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.
     42 extern "C" {
     43 
     44 int64_t __cdecl __divdi3(int64_t a, int64_t b) {
     45   return a / b;
     46 }
     47 uint64_t __cdecl __udivdi3(uint64_t a, uint64_t b) {
     48   return a / b;
     49 }
     50 
     51 }
     52 #endif
     53 
     54 using remoting::g_npnetscape_funcs;
     55 using remoting::HostLogHandler;
     56 using remoting::HostNPScriptObject;
     57 using remoting::StringFromNPIdentifier;
     58 
     59 namespace {
     60 
     61 bool g_initialized = false;
     62 
     63 base::AtExitManager* g_at_exit_manager = NULL;
     64 
     65 // The plugin name and description returned by GetValue().
     66 std::string* g_ui_name = NULL;
     67 std::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
     72 class 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 
    366 void 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 
    387 void 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.
    397 HostNPPlugin* PluginFromInstance(NPP instance) {
    398   return reinterpret_cast<HostNPPlugin*>(instance->pdata);
    399 }
    400 
    401 NPError 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 
    426 NPError 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 
    448 NPError 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 
    481 NPError HandleEvent(NPP instance, void* ev) {
    482   VLOG(2) << "HandleEvent";
    483   return NPERR_NO_ERROR;
    484 }
    485 
    486 NPError 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)
    498 BOOL 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 
    514 extern "C" {
    515 
    516 EXPORT 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 
    528 EXPORT 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   ui::EnableHighDPISupport();
    549 #endif
    550 
    551   return NPERR_NO_ERROR;
    552 }
    553 
    554 EXPORT 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)
    562 EXPORT const char* API_CALL NP_GetMIMEDescription(void) {
    563   VLOG(2) << "NP_GetMIMEDescription";
    564   return STRINGIZE(HOST_PLUGIN_MIME_TYPE) "::";
    565 }
    566 
    567 EXPORT 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