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. 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 gfx::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