FireBreath  1.4.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Pages
NpapiPluginModule_NPP.cpp
1 /**********************************************************\
2 Original Author: Richard Bateman (taxilian)
3 
4 Created: Oct 19, 2009
5 License: Dual license model; choose one of two:
6  New BSD License
7  http://www.opensource.org/licenses/bsd-license.php
8  - or -
9  GNU Lesser General Public License, version 2.1
10  http://www.gnu.org/licenses/lgpl-2.1.html
11 
12 Copyright 2009 Richard Bateman, Firebreath development team
13 \**********************************************************/
14 
15 #ifdef FB_MACOSX
16 #include <boost/type_traits.hpp>
17 #include <dlfcn.h>
18 #endif
19 #include <cstdio>
20 #include "NpapiPlugin.h"
21 #include "FactoryBase.h"
22 #include "NpapiBrowserHost.h"
23 #include <boost/shared_ptr.hpp>
24 #include "precompiled_headers.h" // On windows, everything above this line in PCH
25 #include "AsyncFunctionCall.h"
26 #include "PluginInfo.h"
27 #include "SafeQueue.h"
28 #include "NpapiPluginModule.h"
29 
30 #if FB_WIN
31 # include "Win/NpapiBrowserHostAsyncWin.h"
32 #elif FB_MACOSX
33 # include "Mac/OneShotManager.h"
34 #endif
35 
36 using namespace FB::Npapi;
37 
38 namespace
39 {
40  bool needAsyncCallsWorkaround(NPP npp, NPNetscapeFuncs* funcs)
41  {
42  bool result(false);
43  // work-around detection here
44 #if FB_WIN
45  const char* const cstrUserAgent = funcs->uagent(npp);
46  if(cstrUserAgent) {
47  const std::string userAgent(cstrUserAgent);
48  // Safari 5.1 NPN_PluginThreadAsyncCall doesn't seem to work anymore; use the workaround
49  result = userAgent.find("Safari") != std::string::npos;
50  }
51 #endif
52  return result || (funcs->version < NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL);
53  }
54 
55  bool asyncCallsWorkaround(NPP npp, NPNetscapeFuncs* funcs = 0)
56  {
57  static const bool useWorkaround = (funcs) ? needAsyncCallsWorkaround(npp, funcs) : false;
58  //static const bool useWorkaround = false;
59  return useWorkaround;
60  }
61 
62  NpapiBrowserHostPtr createBrowserHost(NpapiPluginModule* module, NPP npp)
63  {
64  try {
65  NpapiBrowserHostPtr host;
66  NPNetscapeFuncs& npnFuncs = module->NPNFuncs;
67 
68  if(asyncCallsWorkaround(npp, &npnFuncs)) {
69  npnFuncs.pluginthreadasynccall = NULL;
70  #if FB_WIN
71  NpapiBrowserHostPtr host(boost::make_shared<NpapiBrowserHostAsyncWin>(module, npp));
72  return host;
73  #else
74  // no work-around for this platform
75  NpapiBrowserHostPtr host(boost::make_shared<NpapiBrowserHost>(module, npp));
76  return host;
77  #endif
78  } else {
79  NpapiBrowserHostPtr host(boost::make_shared<NpapiBrowserHost>(module, npp));
80  return host;
81  }
82  } catch (...) {
83  // This function must not return an exception
84  return NpapiBrowserHostPtr();
85  }
86  }
87 
88  class NpapiPDataHolder
89  {
90  private:
91  NpapiBrowserHostPtr m_host;
92  boost::shared_ptr<NpapiPlugin> m_plugin;
93 
94  public:
95  NpapiPDataHolder(NpapiBrowserHostPtr host, boost::shared_ptr<NpapiPlugin> plugin)
96  : m_host(host), m_plugin(plugin)
97  {
98 #ifdef FB_MACOSX
99  FB::OneShotManager::getInstance().npp_register(m_host->getContextID());
100 #endif
101  }
102  ~NpapiPDataHolder() {
103 #ifdef FB_MACOSX
104  FB::OneShotManager::getInstance().npp_unregister(m_host->getContextID());
105 #endif
106  }
107 
108  NpapiBrowserHostPtr getHost() const {
109  return m_host;
110  }
111  NpapiPluginPtr getPlugin() const {
112  return m_plugin;
113  }
114  };
115 
116  bool validInstance(NPP instance)
117  {
118  return instance != NULL && instance->pdata != NULL;
119  }
120 
121  NpapiPDataHolder* getHolder(NPP instance)
122  {
123  if (validInstance(instance))
124  return static_cast<NpapiPDataHolder*>(instance->pdata);
125  return NULL;
126  }
127 }
128 
129 NpapiPluginPtr FB::Npapi::getPlugin(NPP instance)
130 {
131  if (NpapiPDataHolder* holder = getHolder(instance))
132  return holder->getPlugin();
133  return NpapiPluginPtr();
134 }
135 
136 #ifdef FB_MACOSX
137 // This is used on mac snow leopard safari
138 void NpapiPluginModule::scheduleAsyncCallback(NPP instance, void (*func)(void *), void *userData)
139 {
140  FB::OneShotManager::getInstance().npp_scheduleAsyncCallback(instance, func, userData);
141 }
142 #endif
143 
144 // These are the static NPP_ functions; NPP_New and NPP_Destroy create and destroy the
145 // plugin, the rest are wrappers that dereference NPP->pdata to get at the plugin object
146 // and proxy the call to there.
147 NPError NpapiPluginModule::NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc,
148  char* argn[], char* argv[], NPSavedData* saved)
149 {
150  FBLOG_INFO("NPAPI", "NPP_New: " << (void*) instance);
151  if (instance == NULL) {
152  return NPERR_INVALID_INSTANCE_ERROR;
153  }
154 
155  try
156  {
157 #ifdef FB_MACOSX
158  // Helps with certain weird embedding cases
159  Dl_info info;
160 
161  dladdr(__builtin_return_address(0), &info);
162 
163  NpapiPluginModule *module = NpapiPluginModule::GetModule(info.dli_fbase);
164 #else
165  NpapiPluginModule *module = NpapiPluginModule::GetModule(0);
166 #endif
167  //printf("%p NpapiPluginModule::%s()\n", module, __func__);
168  NPNetscapeFuncs& npnFuncs = module->NPNFuncs;
169 
170  NpapiBrowserHostPtr host(createBrowserHost(module, instance));
171 
172  host->setBrowserFuncs(&(npnFuncs));
173 
174  // TODO: We should probably change this and pass the MIMEType into _getNpapiPlugin instead
175  // of into init later so that we can optionally return a different plugin type depending
176  // on the specific mimetype
177  NpapiPluginPtr plugin(getFactoryInstance()->createNpapiPlugin(host, pluginType));
178  if (!plugin) {
179  return NPERR_OUT_OF_MEMORY_ERROR;
180  }
181 
182  NpapiPDataHolder* holder = new NpapiPDataHolder(host, plugin);
183  instance->pdata = static_cast<void*>(holder);
184 
185  plugin->init(pluginType, argc, argn, argv);
186  }
187  catch (const PluginCreateError &e)
188  {
189  printf("%s\n", e.what());
190  return NPERR_INCOMPATIBLE_VERSION_ERROR;
191  }
192  catch (const std::bad_alloc& e)
193  {
194  printf("%s\n", e.what());
195  return NPERR_OUT_OF_MEMORY_ERROR;
196  }
197  catch (const std::exception& e)
198  {
199  printf("%s\n", e.what());
200  return NPERR_GENERIC_ERROR;
201  }
202 
203  return NPERR_NO_ERROR;
204 }
205 
206 NPError NpapiPluginModule::NPP_Destroy(NPP instance, NPSavedData** save)
207 {
208  FBLOG_INFO("NPAPI", "NPP_Destroy: " << (void*) instance);
209  if (!validInstance(instance)) {
210  return NPERR_INVALID_INSTANCE_ERROR;
211  }
212  NpapiBrowserHostWeakPtr weakHost;
213 
214  if (NpapiPDataHolder* holder = getHolder(instance)) {
215  NpapiBrowserHostPtr host(holder->getHost());
216  weakHost = host;
217  if (host)
218  host->shutdown();
219 
220  if (NpapiPluginPtr plugin = holder->getPlugin())
221  plugin->shutdown();
222 
223  instance->pdata = NULL;
224  delete holder; // Destroy plugin
225  // host should be destroyed when it goes out of scope here
226  } else {
227  return NPERR_GENERIC_ERROR;
228  }
229  // If this assertion fails, you probably have a circular reference
230  // to your BrowserHost object somewhere -- the host should be gone
231  // by this point. This assertion is warning you of a bug.
232  assert(weakHost.expired());
233 
234  return NPERR_NO_ERROR;
235 }
236 
237 NPError NpapiPluginModule::NPP_SetWindow(NPP instance, NPWindow* window)
238 {
239  FBLOG_TRACE("NPAPI", (void*) instance);
240  if (!validInstance(instance)) {
241  return NPERR_INVALID_INSTANCE_ERROR;
242  }
243 
244  if (pluginGuiEnabled())
245  if (NpapiPluginPtr plugin = getPlugin(instance)) {
246  return plugin->SetWindow(window);
247  }
248 
249  return NPERR_NO_ERROR;
250 }
251 
252 NPError NpapiPluginModule::NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
253  NPBool seekable, uint16_t* stype)
254 {
255  FBLOG_INFO("NPAPI", (void*) instance);
256  if (!validInstance(instance)) {
257  return NPERR_INVALID_INSTANCE_ERROR;
258  }
259 
260  if (NpapiPluginPtr plugin = getPlugin(instance)) {
261  return plugin->NewStream(type, stream, seekable, stype);
262  } else {
263  return NPERR_GENERIC_ERROR;
264  }
265 }
266 
267 NPError NpapiPluginModule::NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason)
268 {
269  FBLOG_INFO("NPAPI", (void*) instance);
270  if (!validInstance(instance)) {
271  return NPERR_INVALID_INSTANCE_ERROR;
272  }
273 
274  if (NpapiPluginPtr plugin = getPlugin(instance)) {
275  return plugin->DestroyStream(stream, reason);
276  } else {
277  return NPERR_GENERIC_ERROR;
278  }
279 }
280 
281 int32_t NpapiPluginModule::NPP_WriteReady(NPP instance, NPStream* stream)
282 {
283  FBLOG_INFO("NPAPI",(void*) instance);
284  if (!validInstance(instance)) {
285  return NPERR_INVALID_INSTANCE_ERROR;
286  }
287 
288  if (NpapiPluginPtr plugin = getPlugin(instance)) {
289  return plugin->WriteReady(stream);
290  } else {
291  return NPERR_GENERIC_ERROR;
292  }
293 }
294 
295 int32_t NpapiPluginModule::NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len,
296  void* buffer)
297 {
298  FBLOG_INFO("NPAPI", (void*) instance);
299  if (!validInstance(instance)) {
300  return NPERR_INVALID_INSTANCE_ERROR;
301  }
302 
303  if (NpapiPluginPtr plugin = getPlugin(instance)) {
304  return plugin->Write(stream, offset, len, buffer);
305  } else {
306  return NPERR_GENERIC_ERROR;
307  }
308 }
309 
310 void NpapiPluginModule::NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
311 {
312  FBLOG_INFO("NPAPI", (void*) instance);
313  if (!validInstance(instance)) {
314  return;
315  }
316 
317  if (NpapiPluginPtr plugin = getPlugin(instance)) {
318  plugin->StreamAsFile(stream, fname);
319  }
320 }
321 
322 void NpapiPluginModule::NPP_Print(NPP instance, NPPrint* platformPrint)
323 {
324  FBLOG_INFO("NPAPI", (void*) instance);
325  if (!validInstance(instance)) {
326  return;
327  }
328 
329  if (NpapiPluginPtr plugin = getPlugin(instance)) {
330  plugin->Print(platformPrint);
331  }
332 }
333 
334 int16_t NpapiPluginModule::NPP_HandleEvent(NPP instance, void* event)
335 {
336  FBLOG_TRACE("NPAPI", (void*) instance);
337  if (!validInstance(instance)) {
338  return 0;
339  }
340 
341  if (NpapiPluginPtr plugin = getPlugin(instance)) {
342  return plugin->HandleEvent(event);
343  } else {
344  return 0;
345  }
346 }
347 
348 void NpapiPluginModule::NPP_URLNotify(NPP instance, const char* url, NPReason reason,
349  void* notifyData)
350 {
351  FBLOG_INFO("NPAPI", (void*) instance);
352  if (!validInstance(instance)) {
353  return;
354  }
355 
356  if (NpapiPluginPtr plugin = getPlugin(instance)) {
357  plugin->URLNotify(url, reason, notifyData);
358  }
359 }
360 
361 NPError NpapiPluginModule::NPP_GetValue(NPP instance, NPPVariable variable, void *value)
362 {
363  FBLOG_TRACE("NPAPI", (void*) instance);
364  // These values may depend on the mimetype of the plugin
365  if (!validInstance(instance)) {
366  switch (variable) {
367  case NPPVpluginNameString: {
368  static const std::string pluginName = getFactoryInstance()->getPluginName("");
369  *((const char **)value) = pluginName.c_str();
370  break; }
371  case NPPVpluginDescriptionString: {
372  // If nothing is instantiated, use the first description found
373  static const std::string pluginDesc = getFactoryInstance()->getPluginDescription("");
374  *((const char **)value) = pluginDesc.c_str();
375  break; }
376  default:
377  return NPERR_GENERIC_ERROR;
378  }
379  return NPERR_NO_ERROR;
380  }
381 
382  if (NpapiPluginPtr plugin = getPlugin(instance)) {
383  return plugin->GetValue(variable, value);
384  }
385 
386  return NPERR_NO_ERROR;
387 }
388 
389 NPError NpapiPluginModule::NPP_SetValue(NPP instance, NPNVariable variable, void *value)
390 {
391  FBLOG_TRACE("NPAPI", (void*) instance << "variable: " << (void*)variable);
392  if (!validInstance(instance)) {
393  return NPERR_INVALID_INSTANCE_ERROR;
394  }
395 
396  if (NpapiPluginPtr plugin = getPlugin(instance)) {
397  return plugin->SetValue(variable, value);
398  } else {
399  return NPERR_GENERIC_ERROR;
400  }
401 }
402