OLD | NEW |
(Empty) | |
| 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 base::CommandLine::Init(0, NULL); |
| 375 |
| 376 if (remoting::LoadResources("")) { |
| 377 g_ui_name = new std::string( |
| 378 l10n_util::GetStringUTF8(IDS_REMOTING_HOST_PLUGIN_NAME)); |
| 379 g_ui_description = new std::string( |
| 380 l10n_util::GetStringUTF8(IDS_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" |
OLD | NEW |