FireBreath  1.4.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Pages
NPJavascriptObject.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 <typeinfo>
16 #include "JSObject.h"
17 
18 #include "precompiled_headers.h" // On windows, everything above this line in PCH
19 #include "NPJavascriptObject.h"
20 
21 using namespace FB::Npapi;
22 
23 NPJavascriptObject *NPJavascriptObject::NewObject(const NpapiBrowserHostPtr& host, const FB::JSAPIWeakPtr& api, bool auto_release/* = false*/)
24 {
25  NPJavascriptObject *obj = static_cast<NPJavascriptObject *>(host->CreateObject(&NPJavascriptObjectClass));
26 
27  if (obj) { // There are certain cases where this may have failed;
28  // If obj is null, return null; it's probably during shutdown
29  obj->setAPI(api, host);
30  obj->m_autoRelease = auto_release;
31  if (auto_release) {
32  // If we're autoreleasing it we need to autoretain it
33  FB::JSAPIPtr api_strong(api.lock());
34  if (api_strong) {
35  host->retainJSAPIPtr(api_strong);
36  }
37  }
38  }
39  return obj;
40 }
41 
42 bool NPJavascriptObject::isNPJavaScriptObject(const NPObject* const npo)
43 {
44  return npo->_class == &NPJavascriptObjectClass;
45 }
46 
47 NPJavascriptObject::NPJavascriptObject(NPP npp)
48  : m_valid(true), m_autoRelease(false), m_addEventFunc(boost::make_shared<NPO_addEventListener>(this)),
49  m_removeEventFunc(boost::make_shared<NPO_removeEventListener>(this)),
50  m_getLastExceptionFunc(boost::make_shared<NPO_getLastException>(this))
51 {
52  m_sharedRef = boost::make_shared<FB::ShareableReference<NPJavascriptObject> >(this);
53 }
54 
55 NPJavascriptObject::~NPJavascriptObject(void)
56 {
57  if (m_autoRelease && !m_browser.expired() && !m_api.expired()) {
58  // If the JSAPI object is still around and we're set to autorelease, tell the BrowserHost
59  // that we're done with it. Otherwise it's either gone or we don't control its lifecycle
60  getHost()->releaseJSAPIPtr(m_api.lock());
61  }
62 }
63 
64 void NPJavascriptObject::setAPI(const FB::JSAPIWeakPtr& api, const NpapiBrowserHostPtr& host)
65 {
66  m_api = api;
67  m_browser = host;
68 }
69 
70 void NPJavascriptObject::Invalidate()
71 {
72  m_valid = false;
73  try {
74  if (!m_api.expired())
75  getAPI()->invalidate();
76  } catch(const std::bad_cast&) {
77  }
78 }
79 
80 bool NPJavascriptObject::HasMethod(NPIdentifier name)
81 {
82  if (!isValid()) return false;
83  try {
84  std::string mName = getHost()->StringFromIdentifier(name);
85  if (mName == "toString") return true;
86  return !getAPI()->HasMethodObject(mName) && getAPI()->HasMethod(mName);
87  } catch (const std::bad_cast&) {
88  return false; // invalid object
89  } catch (const script_error& e) {
90  if (!m_browser.expired())
91  getHost()->SetException(this, e.what());
92  m_getLastExceptionFunc->setMessage(e.what());
93  return false;
94  }
95 }
96 FB::variant FB::Npapi::NPJavascriptObject::NPO_addEventListener::exec( const std::vector<variant>& args )
97 {
98  if (obj->isValid() && args.size() > 1 && args.size() < 4) {
99  try {
100  std::string evtName = "on" + args[0].convert_cast<std::string>();
101  FB::JSObjectPtr method(args[1].convert_cast<FB::JSObjectPtr>());
102  obj->getAPI()->registerEventMethod(evtName, method);
103  return FB::variant();
104  } catch (const std::bad_cast& e) {
105  throw FB::invalid_arguments(e.what());
106  }
107  } else {
108  throw FB::invalid_arguments();
109  }
110 }
111 
112 FB::variant FB::Npapi::NPJavascriptObject::NPO_removeEventListener::exec( const std::vector<variant>& args )
113 {
114  if (obj->isValid() && args.size() > 1 && args.size() < 4) {
115  try {
116  std::string evtName = "on" + args[0].convert_cast<std::string>();
117  FB::JSObjectPtr method(args[1].convert_cast<FB::JSObjectPtr>());
118  obj->getAPI()->unregisterEventMethod(evtName, method);
119  return FB::variant();
120  } catch (const std::bad_cast& e) {
121  throw FB::invalid_arguments(e.what());
122  }
123  } else {
124  throw FB::invalid_arguments();
125  }
126 }
127 
128 FB::variant FB::Npapi::NPJavascriptObject::NPO_getLastException::m_msg;
129 
130 bool NPJavascriptObject::Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
131 {
132  VOID_TO_NPVARIANT(*result);
133  if (!isValid()) return false;
134  try {
135  std::string mName;
136  NpapiBrowserHostPtr browser(getHost());
137  if (name != NULL) {
138  mName = browser->StringFromIdentifier(name);
139  }
140  std::vector<FB::variant> vArgs;
141  for (unsigned int i = 0; i < argCount; i++) {
142  vArgs.push_back(browser->getVariant(&args[i]));
143  }
144 
145  // Default method call
146  FB::variant ret = getAPI()->Invoke(mName, vArgs);
147  browser->getNPVariant(result, ret);
148  return true;
149  } catch (const std::bad_cast&) {
150  return false; // invalid object
151  } catch (const script_error& e) {
152  if (!m_browser.expired())
153  getHost()->SetException(this, e.what());
154  m_getLastExceptionFunc->setMessage(e.what());
155  return false;
156  }
157 }
158 
159 bool NPJavascriptObject::InvokeDefault(const NPVariant *args, uint32_t argCount, NPVariant *result)
160 {
161  return Invoke(NULL, args, argCount, result);
162 }
163 
164 bool NPJavascriptObject::HasProperty(NPIdentifier name)
165 {
166  if (!isValid()) return false;
167  try {
168  NpapiBrowserHostPtr browser(getHost());
169  // Handle numeric identifiers
170  if(!browser->IdentifierIsString(name)) {
171  int32_t sIdx = browser->IntFromIdentifier(name);
172  return getAPI()->HasProperty(sIdx);
173  }
174 
175  std::string sName(browser->StringFromIdentifier(name));
176  // We check for events of that name as well in order to allow setting of an event handler in the
177  // old javascript style, i.e. plugin.onload = function() .....;
178 
179  if (sName == "addEventListener" || sName == "removeEventListener" || sName == "getLastException") {
180  return true;
181  } else if (sName != "toString" && getAPI()->HasMethodObject(sName))
182  return true;
183  else
184  return !HasMethod(name) && getAPI()->HasProperty(sName);
185  } catch (const std::bad_cast&) {
186  return false; // invalid object
187  } catch (const script_error& e) {
188  if (!m_browser.expired())
189  getHost()->SetException(this, e.what());
190  m_getLastExceptionFunc->setMessage(e.what());
191  return false;
192  }
193 }
194 
195 bool NPJavascriptObject::GetProperty(NPIdentifier name, NPVariant *result)
196 {
197  if (!isValid()) return false;
198  try {
199  NpapiBrowserHostPtr browser(getHost());
200  FB::variant res;
201  if (browser->IdentifierIsString(name)) {
202  std::string sName(browser->StringFromIdentifier(name));
203  if (sName == "addEventListener") {
204  res = m_addEventFunc;
205  } else if (sName == "removeEventListener") {
206  res = m_removeEventFunc;
207  } else if (sName == "getLastException") {
208  res = m_getLastExceptionFunc;
209  } else if (getAPI()->HasMethodObject(sName)) {
210  res = getAPI()->GetMethodObject(sName);
211  } else {
212  res = getAPI()->GetProperty(sName);
213  }
214  } else {
215  res = getAPI()->GetProperty(browser->IntFromIdentifier(name));
216  }
217 
218  browser->getNPVariant(result, res);
219  return true;
220  } catch (const std::bad_cast&) {
221  return false; // invalid object
222  } catch (const script_error& e) {
223  if (!m_browser.expired())
224  getHost()->SetException(this, e.what());
225  m_getLastExceptionFunc->setMessage(e.what());
226  return false;
227  }
228 }
229 
230 bool NPJavascriptObject::SetProperty(NPIdentifier name, const NPVariant *value)
231 {
232  if (!isValid()) return false;
233  try {
234  NpapiBrowserHostPtr browser(getHost());
235  FB::variant arg = browser->getVariant(value);
236  if (browser->IdentifierIsString(name)) {
237  std::string sName(browser->StringFromIdentifier(name));
238  if (getAPI()->HasMethodObject(sName)) {
239  throw FB::script_error("This property cannot be changed");
240  } else {
241  getAPI()->SetProperty(sName, arg);
242  }
243  } else {
244  getAPI()->SetProperty(browser->IntFromIdentifier(name), arg);
245  }
246  return true;
247  } catch (const std::bad_cast&) {
248  return false; // invalid object
249  } catch(const script_error& e) {
250  if (!m_browser.expired())
251  getHost()->SetException(this, e.what());
252  m_getLastExceptionFunc->setMessage(e.what());
253  return false;
254  }
255 }
256 
257 bool NPJavascriptObject::RemoveProperty(NPIdentifier name)
258 {
259  if (!isValid()) return false;
260  try {
261  NpapiBrowserHostPtr browser(getHost());
262  if (browser->IdentifierIsString(name)) {
263  std::string sName(browser->StringFromIdentifier(name));
264  getAPI()->RemoveProperty(sName);
265  } else {
266  getAPI()->RemoveProperty(browser->IntFromIdentifier(name));
267  }
268  return true;
269  } catch (const std::bad_cast&) {
270  return false; // invalid object
271  } catch(const script_error& e) {
272  if (!m_browser.expired())
273  getHost()->SetException(this, e.what());
274  m_getLastExceptionFunc->setMessage(e.what());
275  return false;
276  }
277 }
278 
279 bool NPJavascriptObject::Enumeration(NPIdentifier **value, uint32_t *count)
280 {
281  if (!isValid()) return false;
282  try {
283  typedef std::vector<std::string> StringArray;
284  StringArray memberList;
285  getAPI()->getMemberNames(memberList);
286  *count = memberList.size() + 3;
287  NPIdentifier *outList(NULL);
288 
289  NpapiBrowserHostPtr browser(getHost());
290  outList = (NPIdentifier*)browser->MemAlloc((uint32_t)(sizeof(NPIdentifier) * *count));
291 
292  for (uint32_t i = 0; i < memberList.size(); i++) {
293  outList[i] = browser->GetStringIdentifier(memberList.at(i).c_str());
294  }
295  outList[memberList.size()] = browser->GetStringIdentifier("addEventListener");
296  outList[memberList.size() + 1] = browser->GetStringIdentifier("removeEventListener");
297  outList[memberList.size() + 2] = browser->GetStringIdentifier("getLastException");
298  *value = outList;
299  return true;
300  } catch (const std::bad_cast&) {
301  *count = 0;
302  return false; // invalid object
303  } catch (const script_error& e) {
304  *count = 0;
305  if (!m_browser.expired())
306  getHost()->SetException(this, e.what());
307  m_getLastExceptionFunc->setMessage(e.what());
308  return false;
309  }
310 }
311 
312 bool NPJavascriptObject::Construct(const NPVariant *args, uint32_t argCount, NPVariant *result)
313 {
314  VOID_TO_NPVARIANT(*result);
315  if (!isValid()) return false;
316  try {
317  NpapiBrowserHostPtr browser(getHost());
318 
319  std::vector<FB::variant> vArgs;
320  for (unsigned int i = 0; i < argCount; i++) {
321  vArgs.push_back(browser->getVariant(&args[i]));
322  }
323  // Default method call
324  FB::variant ret = getAPI()->Construct(vArgs);
325  browser->getNPVariant(result, ret);
326  return true;
327  } catch (const std::bad_cast&) {
328  return false; // invalid object
329  } catch (const script_error& e) {
330  if (!m_browser.expired())
331  getHost()->SetException(this, e.what());
332  m_getLastExceptionFunc->setMessage(e.what());
333  return false;
334  }
335 }
336 
337 
338 /****************************************************************************\
339  These are the static functions given to the browser in the NPClass struct.
340  You might look at these as the "entry points" for the NPJavascriptObject
341 \****************************************************************************/
342 
343 NPObject *NPJavascriptObject::_Allocate(NPP npp, NPClass *aClass)
344 {
345  return (NPObject *)new NPJavascriptObject(npp);
346 }
347 
348 void NPJavascriptObject::_Deallocate(NPObject *npobj)
349 {
350  delete static_cast<NPJavascriptObject *>(npobj);
351 }
352 
353 void NPJavascriptObject::_Invalidate(NPObject *npobj)
354 {
355  NPJavascriptObject *obj = static_cast<NPJavascriptObject *>(npobj);
356  obj->Invalidate();
357 }
358 
359 bool NPJavascriptObject::_HasMethod(NPObject *npobj, NPIdentifier name)
360 {
361  NPJavascriptObject *obj = static_cast<NPJavascriptObject *>(npobj);
362  return obj->HasMethod(name);
363 }
364 
365 bool NPJavascriptObject::_Invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
366 {
367  NPJavascriptObject *obj = static_cast<NPJavascriptObject *>(npobj);
368  return obj->Invoke(name, args, argCount, result);
369 }
370 
371 bool NPJavascriptObject::_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result)
372 {
373  NPJavascriptObject *obj = static_cast<NPJavascriptObject *>(npobj);
374  return obj->InvokeDefault(args, argCount, result);
375 }
376 
377 bool NPJavascriptObject::_HasProperty(NPObject *npobj, NPIdentifier name)
378 {
379  NPJavascriptObject *obj = static_cast<NPJavascriptObject *>(npobj);
380  return obj->HasProperty(name);
381 }
382 
383 bool NPJavascriptObject::_GetProperty(NPObject *npobj, NPIdentifier name, NPVariant *result)
384 {
385  NPJavascriptObject *obj = static_cast<NPJavascriptObject *>(npobj);
386  return obj->GetProperty(name, result);
387 }
388 
389 bool NPJavascriptObject::_SetProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value)
390 {
391  NPJavascriptObject *obj = static_cast<NPJavascriptObject *>(npobj);
392  return obj->SetProperty(name, value);
393 }
394 
395 bool NPJavascriptObject::_RemoveProperty(NPObject *npobj, NPIdentifier name)
396 {
397  NPJavascriptObject *obj = static_cast<NPJavascriptObject *>(npobj);
398  return obj->RemoveProperty(name);
399 }
400 
401 bool NPJavascriptObject::_Enumeration(NPObject *npobj, NPIdentifier **value, uint32_t *count)
402 {
403  NPJavascriptObject *obj = static_cast<NPJavascriptObject *>(npobj);
404  return obj->Enumeration(value, count);
405 }
406 
407 bool NPJavascriptObject::_Construct(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result)
408 {
409  NPJavascriptObject *obj = static_cast<NPJavascriptObject *>(npobj);
410  return obj->Construct(args, argCount, result);
411 }
412 
413 // This defines the "entry points"; it's how the browser knows how to create the object
414 // when you call NPN_CreateObject, and how it knows how to call functions on it
415 NPClass NPJavascriptObject::NPJavascriptObjectClass = {
416  NP_CLASS_STRUCT_VERSION_CTOR,
417  NPJavascriptObject::_Allocate,
418  NPJavascriptObject::_Deallocate,
419  NPJavascriptObject::_Invalidate,
420  NPJavascriptObject::_HasMethod,
421  NPJavascriptObject::_Invoke,
422  NPJavascriptObject::_InvokeDefault,
423  NPJavascriptObject::_HasProperty,
424  NPJavascriptObject::_GetProperty,
425  NPJavascriptObject::_SetProperty,
426  NPJavascriptObject::_RemoveProperty,
427  NPJavascriptObject::_Enumeration,
428  NPJavascriptObject::_Construct
429 };
boost::shared_ptr< FB::JSObject > JSObjectPtr
Defines an alias representing a JSObject shared_ptr (you should never use a JSObject* directly) ...
Definition: APITypes.h:109
Accepts any datatype, used in all interactions with javascript. Provides tools for getting back out t...
Definition: variant.h:198
Thrown by a JSAPI object when the argument(s) provided to a SetProperty or Invoke call are found to b...
Definition: JSExceptions.h:47
boost::weak_ptr< FB::JSAPI > JSAPIWeakPtr
Defines an alias for a JSAPI weak_ptr (you should never use a JSAPI* directly)
Definition: APITypes.h:88
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