FireBreath  1.4.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Pages
NPObjectAPI.cpp
1 /**********************************************************\
2 Original Author: Richard Bateman (taxilian)
3 
4 Created: Oct 16, 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 #include <boost/lexical_cast.hpp>
16 #include <boost/bind.hpp>
17 #include <boost/scoped_array.hpp>
18 #include "NPObjectAPI.h"
19 #include "NPJavascriptObject.h"
20 #include "NpapiBrowserHost.h"
21 #include "logging.h"
22 #include <cassert>
23 #include "precompiled_headers.h" // On windows, everything above this line in PCH
24 
25 using namespace FB::Npapi;
26 using boost::static_pointer_cast;
27 
28 NPObjectAPI::NPObjectAPI(NPObject *o, const NpapiBrowserHostPtr& h)
29  : JSObject(h), m_browser(h), obj(o), is_JSAPI(false)
30 {
31  assert(!m_browser.expired());
32  if (o != NULL) {
33  getHost()->RetainObject(obj);
34  }
35  FB::JSAPIPtr ptr(getJSAPI());
36  if (ptr) {
37  // This is a JSAPI object wrapped in an IDispatch object; we'll call it
38  // directly(ish)
39  is_JSAPI = true;
40  inner = ptr;
41  }
42 }
43 
44 NPObjectAPI::~NPObjectAPI(void)
45 {
46  // Schedule the NPObject for release on the main thread
47  if (!m_browser.expired())
48  getHost()->deferred_release(obj);
49  obj = NULL;
50 }
51 
52 void NPObjectAPI::getMemberNames(std::vector<std::string> &nameVector) const
53 {
54  if (m_browser.expired())
55  return;
56 
57  NpapiBrowserHostPtr browser(getHost());
58  if (!browser->isMainThread()) {
59  typedef void (FB::JSAPI::*getMemberNamesType)(std::vector<std::string> *nameVector) const;
60  browser->CallOnMainThread(boost::bind((getMemberNamesType)&FB::JSAPI::getMemberNames, this, &nameVector));
61  return;
62  }
63  if (is_JSAPI) {
64  FB::JSAPIPtr tmp = inner.lock();
65  if (tmp)
66  tmp->getMemberNames(nameVector);
67  return;
68  }
69  NPIdentifier *idArray(NULL);
70  uint32_t count;
71 
72  browser->Enumerate(obj, &idArray, &count);
73  for (uint32_t i = 0; i < count; i++) {
74  nameVector.push_back(browser->StringFromIdentifier(idArray[i]));
75  }
76  browser->MemFree(idArray);
77 }
78 
80 {
81  if (m_browser.expired())
82  return 0;
83 
84  NpapiBrowserHostPtr browser(getHost());
85  if (!browser->isMainThread()) {
86  return browser->CallOnMainThread(boost::bind(&NPObjectAPI::getMemberCount, this));
87  }
88  if (is_JSAPI) {
89  FB::JSAPIPtr tmp = inner.lock();
90  if (tmp)
91  return tmp->getMemberCount();
92  else
93  return 0;
94  }
95  NPIdentifier *idArray(NULL);
96  uint32_t count;
97  browser->Enumerate(obj, &idArray, &count);
98  browser->MemFree(idArray);
99  return (size_t)count;
100 }
101 
102 bool NPObjectAPI::HasMethod(const std::string& methodName) const
103 {
104  if (m_browser.expired())
105  return false;
106 
107  NpapiBrowserHostPtr browser(getHost());
108  if (!browser->isMainThread()) {
109  typedef bool (NPObjectAPI::*curtype)(const std::string&) const;
110  return browser->CallOnMainThread(boost::bind((curtype)&NPObjectAPI::HasMethod, this, methodName));
111  }
112  if (is_JSAPI) {
113  FB::JSAPIPtr tmp = inner.lock();
114  if (tmp)
115  return tmp->HasMethod(methodName);
116  else
117  return false;
118  }
119  return browser->HasMethod(obj, browser->GetStringIdentifier(methodName.c_str()));
120 }
121 
122 bool NPObjectAPI::HasProperty(const std::string& propertyName) const
123 {
124  if (m_browser.expired())
125  return false;
126 
127  NpapiBrowserHostPtr browser(getHost());
128  if (!browser->isMainThread()) {
129  typedef bool (NPObjectAPI::*curtype)(const std::string&) const;
130  return browser->CallOnMainThread(boost::bind((curtype)&NPObjectAPI::HasProperty, this, propertyName));
131  }
132  if (is_JSAPI) {
133  FB::JSAPIPtr tmp = inner.lock();
134  if (tmp)
135  return tmp->HasProperty(propertyName);
136  else
137  return false;
138  }
139  return browser->HasProperty(obj, browser->GetStringIdentifier(propertyName.c_str()));
140 }
141 
142 bool NPObjectAPI::HasProperty(int idx) const
143 {
144  if (m_browser.expired())
145  return false;
146 
147  NpapiBrowserHostPtr browser(getHost());
148  if (is_JSAPI) {
149  FB::JSAPIPtr tmp = inner.lock();
150  if (tmp)
151  return tmp->HasProperty(idx);
152  else
153  return false;
154  }
155  return browser->HasProperty(obj, browser->GetIntIdentifier(idx));
156 }
157 
158 // Methods to manage properties on the API
159 FB::variant NPObjectAPI::GetProperty(const std::string& propertyName)
160 {
161  if (m_browser.expired())
162  return FB::FBVoid();
163 
164  NpapiBrowserHostPtr browser(getHost());
165  if (!browser->isMainThread()) {
166  return browser->CallOnMainThread(boost::bind((FB::GetPropertyType)&JSAPI::GetProperty, this, propertyName));
167  }
168  if (is_JSAPI) {
169  FB::JSAPIPtr tmp = inner.lock();
170  if (tmp)
171  return tmp->GetProperty(propertyName);
172  else
173  return false;
174  }
175  NPVariant retVal;
176  if (!browser->GetProperty(obj, browser->GetStringIdentifier(propertyName.c_str()), &retVal)) {
177  browser->ReleaseVariantValue(&retVal);
178  throw script_error(propertyName.c_str());
179  } else {
180  FB::variant ret = browser->getVariant(&retVal);
181  browser->ReleaseVariantValue(&retVal);
182  return ret;
183  }
184 }
185 
186 void NPObjectAPI::SetProperty(const std::string& propertyName, const FB::variant& value)
187 {
188  if (m_browser.expired())
189  return;
190 
191  NpapiBrowserHostPtr browser(getHost());
192  if (!browser->isMainThread()) {
193  browser->CallOnMainThread(boost::bind((FB::SetPropertyType)&JSAPI::SetProperty, this, propertyName, value));
194  return;
195  }
196  if (is_JSAPI) {
197  FB::JSAPIPtr tmp = inner.lock();
198  if (tmp)
199  tmp->SetProperty(propertyName, value);
200  return;
201  }
202  NPVariant val;
203  browser->getNPVariant(&val, value);
204  bool res = browser->SetProperty(obj, browser->GetStringIdentifier(propertyName.c_str()), &val);
205  browser->ReleaseVariantValue(&val);
206  if (!res) {
207  throw script_error(propertyName.c_str());
208  }
209 }
210 
211 void NPObjectAPI::RemoveProperty(const std::string& propertyName)
212 {
213  if (m_browser.expired())
214  return;
215 
216  NpapiBrowserHostPtr browser(getHost());
217  if (!browser->isMainThread()) {
218  return browser->CallOnMainThread(boost::bind((FB::RemovePropertyType)&JSAPI::RemoveProperty, this, propertyName));
219  }
220  if (is_JSAPI) {
221  FB::JSAPIPtr tmp = inner.lock();
222  if (tmp)
223  return tmp->RemoveProperty(propertyName);
224  else
225  return /*false*/;
226  }
227  if (!browser->RemoveProperty(obj, browser->GetStringIdentifier(propertyName.c_str()))) {
228  throw script_error(propertyName.c_str());
229  }
230 }
231 
233 {
234  if (m_browser.expired())
235  return FB::FBVoid();
236 
237  NpapiBrowserHostPtr browser(getHost());
238  std::string strIdx(boost::lexical_cast<std::string>(idx));
239  if (is_JSAPI) {
240  FB::JSAPIPtr tmp = inner.lock();
241  if (tmp)
242  return tmp->GetProperty(idx);
243  }
244  return GetProperty(strIdx);
245 }
246 
247 void NPObjectAPI::SetProperty(int idx, const FB::variant& value)
248 {
249  if (m_browser.expired())
250  return;
251 
252  NpapiBrowserHostPtr browser(getHost());
253  std::string strIdx(boost::lexical_cast<std::string>(idx));
254  if (is_JSAPI) {
255  FB::JSAPIPtr tmp = inner.lock();
256  if (tmp)
257  SetProperty(idx, value);
258  }
259  SetProperty(strIdx, value);
260 }
261 
263 {
264  if (m_browser.expired())
265  return;
266 
267  NpapiBrowserHostPtr browser(getHost());
268  std::string strIdx(boost::lexical_cast<std::string>(idx));
269  if (is_JSAPI) {
270  FB::JSAPIPtr tmp = inner.lock();
271  if (tmp)
272  return tmp->RemoveProperty(idx);
273  }
274  return RemoveProperty(strIdx);
275 }
276 
277 // Methods to manage methods on the API
278 FB::variant NPObjectAPI::Invoke(const std::string& methodName, const std::vector<FB::variant>& args)
279 {
280  if (m_browser.expired())
281  return false;
282 
283  NpapiBrowserHostPtr browser(getHost());
284  if (!browser->isMainThread()) {
285  return browser->CallOnMainThread(boost::bind((FB::InvokeType)&NPObjectAPI::Invoke, this, methodName, args));
286  }
287  if (is_JSAPI) {
288  FB::JSAPIPtr tmp = inner.lock();
289  if (tmp)
290  return tmp->Invoke(methodName, args);
291  else
292  return false;
293  }
294  NPVariant retVal;
295 
296  // Convert the arguments to NPVariants
297  boost::scoped_array<NPVariant> npargs(new NPVariant[args.size()]);
298  for (unsigned int i = 0; i < args.size(); i++) {
299  browser->getNPVariant(&npargs[i], args[i]);
300  }
301 
302  bool res = false;
303  // Invoke the method ("" means invoke default method)
304  if (methodName.size() > 0) {
305  res = browser->Invoke(obj, browser->GetStringIdentifier(methodName.c_str()), npargs.get(), args.size(), &retVal);
306  } else {
307  res = browser->InvokeDefault(obj, npargs.get(), args.size(), &retVal);
308  }
309 
310  // Free the NPVariants that we earlier allocated
311  for (unsigned int i = 0; i < args.size(); i++) {
312  browser->ReleaseVariantValue(&npargs[i]);
313  }
314 
315  if (!res) { // If the method call failed, throw an exception
316  browser->ReleaseVariantValue(&retVal); // Always release the return value!
317  throw script_error(methodName.c_str());
318  } else {
319  FB::variant ret = browser->getVariant(&retVal);
320  browser->ReleaseVariantValue(&retVal); // Always release the return value!
321  return ret;
322  }
323 }
324 
325 void FB::Npapi::NPObjectAPI::callMultipleFunctions( const std::string& name, const FB::VariantList& args, const std::vector<JSObjectPtr>& direct, const std::vector<JSObjectPtr>& ifaces )
326 {
327  if (!isValid())
328  throw FB::script_error("Error calling handlers");
329 
330  NpapiBrowserHostPtr browser(getHost());
331  if (!browser->isMainThread()) {
332  return browser->ScheduleOnMainThread(shared_from_this(), boost::bind(&NPObjectAPI::callMultipleFunctions, this, name, args, direct, ifaces));
333  }
334  NPVariant retVal;
335 
336  // We make these calls through a delegate javascript function that is injected into
337  // the page on startup. The reason we do this is to prevent some weird reentrance bugs
338  // particularly in FF4.
339 
340  NPObjectAPIPtr d = static_pointer_cast<NPObjectAPI>(browser->getDelayedInvokeDelegate());
341  if (!d) {
342  throw FB::script_error("Error calling handlers (delegate disappeared)");
343  }
344  NPObject* delegate(d->getNPObject());
345 
346  // Allocate the arguments
347  boost::scoped_array<NPVariant> npargs(new NPVariant[4]);
348  browser->getNPVariant(&npargs[0], 0);
349  browser->getNPVariant(&npargs[2], args);
350  browser->getNPVariant(&npargs[3], name);
351 
352  bool res = false;
353  std::vector<JSObjectPtr>::const_iterator it(direct.begin());
354  std::vector<JSObjectPtr>::const_iterator end(direct.end());
355  for (; it != end; ++it) {
356  NPObjectAPIPtr ptr(boost::static_pointer_cast<NPObjectAPI>(*it));
357  if (ptr->is_JSAPI) {
358  FB::JSAPIPtr tmp = ptr->inner.lock();
359  if (tmp) {
360  tmp->Invoke("", args);
361  continue;
362  }
363  }
364  browser->getNPVariant(&npargs[1], ptr);
365  res = browser->InvokeDefault(delegate, npargs.get(), 3, &retVal);
366  browser->ReleaseVariantValue(&retVal);
367  browser->ReleaseVariantValue(&npargs[1]);
368  }
369 
370  it = ifaces.begin();
371  end = ifaces.end();
372  for (; it != end; ++it) {
373  NPObjectAPIPtr ptr(boost::static_pointer_cast<NPObjectAPI>(*it));
374  if (ptr->is_JSAPI) {
375  FB::JSAPIPtr tmp = ptr->inner.lock();
376  if (tmp) {
377  tmp->Invoke("", args);
378  continue;
379  }
380  }
381  browser->getNPVariant(&npargs[1], ptr);
382  res = browser->InvokeDefault(delegate, npargs.get(), 4, &retVal);
383  browser->ReleaseVariantValue(&retVal);
384  browser->ReleaseVariantValue(&npargs[1]);
385  }
386  browser->ReleaseVariantValue(&npargs[2]);
387  browser->ReleaseVariantValue(&npargs[3]);
388 }
389 
391 {
392  if (m_browser.expired())
393  return false;
394 
395  NpapiBrowserHostPtr browser(getHost());
396  if (!browser->isMainThread()) {
397  return browser->CallOnMainThread(boost::bind((FB::ConstructType)&NPObjectAPI::Construct, this, args));
398  }
399  if (is_JSAPI) {
400  FB::JSAPIPtr tmp = inner.lock();
401  if (tmp)
402  return tmp->Construct(args);
403  else
404  return false;
405  }
406  NPVariant retVal;
407 
408  // Convert the arguments to NPVariants
409  boost::scoped_array<NPVariant> npargs(new NPVariant[args.size()]);
410  for (unsigned int i = 0; i < args.size(); i++) {
411  browser->getNPVariant(&npargs[i], args[i]);
412  }
413 
414  bool res = false;
415  // construct
416  res = browser->Construct(obj, npargs.get(), args.size(), &retVal);
417 
418  // Free the NPVariants that we earlier allocated
419  for (unsigned int i = 0; i < args.size(); i++) {
420  browser->ReleaseVariantValue(&npargs[i]);
421  }
422 
423  if (!res) { // If the method call failed, throw an exception
424  throw script_error("constructor");
425  } else {
426  FB::variant ret = browser->getVariant(&retVal);
427  browser->ReleaseVariantValue(&retVal); // Always release the return value!
428  return ret;
429  }
430 }
431 
433 {
434  if (!obj) {
435  return JSAPIPtr();
436  }
437 
438  if (!NPJavascriptObject::isNPJavaScriptObject(obj)) {
439  return JSAPIPtr();
440  }
441 
442  return static_cast<NPJavascriptObject*>(obj)->getAPI();
443 }
444 
variant(JSAPI::* InvokeType)(const std::string &, const std::vector< variant > &)
Defines an alias representing a function pointer to JSAPI::Invoke.
Definition: APITypes.h:172
virtual void SetProperty(const std::wstring &propertyName, const variant &value)
Definition: JSAPI.h:405
virtual variant GetProperty(const std::wstring &propertyName)
Definition: JSAPI.h:386
variant GetProperty(const std::string &propertyName)
Gets a property value.
void SetProperty(const std::string &propertyName, const variant &value)
Sets the value of a property.
variant Invoke(const std::string &methodName, const std::vector< variant > &args)
Called by the browser to invoke a method on the JSAPI object.
void RemoveProperty(const std::string &propertyName)
Removes a property.
virtual void RemoveProperty(const std::wstring &propertyName)
Definition: JSAPI.h:455
Accepts any datatype, used in all interactions with javascript. Provides tools for getting back out t...
Definition: variant.h:198
variant(JSAPI::* GetPropertyType)(const std::string &)
Defines an alias representing a function pointer to JSAPI::GetProperty.
Definition: APITypes.h:178
virtual JSAPIPtr getJSAPI() const
Get associated FB::JSAPI.
std::vector< variant > VariantList
Defines an alias representing list of variants.
Definition: APITypes.h:64
bool HasMethod(const std::string &methodName) const
Query if the JSAPI object has the 'methodName' method.
variant(JSAPI::* ConstructType)(const std::vector< variant > &)
Defines an alias representing a function pointer to JSAPI::Invoke.
Definition: APITypes.h:174
variant Construct(const std::vector< variant > &args)
Called by the browser to construct the JSAPI object.
size_t getMemberCount() const
Gets the member count.
Definition: NPObjectAPI.cpp:79
boost::shared_ptr< FB::JSAPI > JSAPIPtr
Defines an alias for a JSAPI shared_ptr (you should never use a JSAPI* directly)
Definition: APITypes.h:94
Exception type; when thrown in a JSAPI method, a javascript exception will be thrown.
Definition: JSExceptions.h:28
void(JSAPI::* RemovePropertyType)(const std::string &)
Defines an alias representing a function pointer to JSAPI::GetProperty.
Definition: APITypes.h:180
Provides a FB::JSObject implementation that wraps a NPObject*.
Definition: NPObjectAPI.h:32
void getMemberNames(std::vector< std::string > &nameVector) const
Called by the browser to enumerate the members of this JSAPI object.
Definition: NPObjectAPI.cpp:52
void(JSAPI::* SetPropertyType)(const std::string &, const variant &)
Defines an alias representing a function pointer to JSAPI::SetProperty.
Definition: APITypes.h:176
bool HasProperty(const std::string &propertyName) const
Query if 'propertyName' is a valid property.
JavaScript API class – provides a javascript interface that can be exposed to the browser...
Definition: JSAPI.h:56