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 |