FireBreath  1.4.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Pages
JSAPIAuto.cpp
1 /**********************************************************\
2 Original Author: Georg Fritzsche
3 
4 Created: November 7, 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 Georg Fritzsche, Firebreath development team
13 \**********************************************************/
14 
15 #include "utf8_tools.h"
16 #include "boost/thread/mutex.hpp"
17 #include "boost/make_shared.hpp"
18 #include "JSFunction.h"
19 #include "JSEvent.h"
20 #include <cassert>
21 #include "precompiled_headers.h" // On windows, everything above this line in PCH
22 
23 #include "JSAPIAuto.h"
24 
25 bool FB::JSAPIAuto::s_allowDynamicAttributes = true;
26 bool FB::JSAPIAuto::s_allowRemoveProperties = false;
27 bool FB::JSAPIAuto::s_allowMethodObjects = true;
28 
29 FB::JSAPIAuto::JSAPIAuto(const std::string& description)
30  : FB::JSAPIImpl(SecurityScope_Public),
31  m_description(description),
32  m_allowDynamicAttributes(FB::JSAPIAuto::s_allowDynamicAttributes),
33  m_allowRemoveProperties(FB::JSAPIAuto::s_allowRemoveProperties),
34  m_allowMethodObjects(FB::JSAPIAuto::s_allowMethodObjects)
35 {
36  init();
37 }
38 
39 FB::JSAPIAuto::JSAPIAuto( const SecurityZone& securityLevel, const std::string& description /*= "<JSAPI-Auto Secure Javascript Object>"*/ )
40  : FB::JSAPIImpl(securityLevel),
41  m_description(description),
42  m_allowDynamicAttributes(FB::JSAPIAuto::s_allowDynamicAttributes),
43  m_allowRemoveProperties(FB::JSAPIAuto::s_allowRemoveProperties),
44  m_allowMethodObjects(FB::JSAPIAuto::s_allowMethodObjects)
45 {
46  init();
47 }
48 
49 void FB::JSAPIAuto::init( )
50 {
51  {
52  scoped_zonelock _l(this, SecurityScope_Public);
53  registerMethod("toString", make_method(this, &JSAPIAuto::ToString));
54  registerMethod("getAttribute", make_method(this, &JSAPIAuto::getAttribute));
55  registerMethod("setAttribute", make_method(this, &JSAPIAuto::setAttribute));
56 
57  registerProperty("value", make_property(this, &JSAPIAuto::ToString));
58  registerProperty("valid", make_property(this, &JSAPIAuto::get_valid));
59  }
60 
61  setReserved("offsetWidth");
62  setReserved("offsetHeight");
63  setReserved("width");
64  setReserved("height");
65  setReserved("attributes");
66  setReserved("nodeType");
67  setReserved("namespaceURI");
68  setReserved("localName");
69  setReserved("wrappedJSObject");
70  setReserved("prototype");
71  setReserved("style");
72  setReserved("id");
73  setReserved("constructor");
74  setReserved("nodeName");
75  setReserved("hasAttribute");
76 }
77 
78 FB::JSAPIAuto::~JSAPIAuto()
79 {
80 
81 }
82 
83 void FB::JSAPIAuto::registerMethod(const std::string& name, const CallMethodFunctor& func)
84 {
85  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
86  m_methodFunctorMap[name] = func;
87  m_zoneMap[name] = getZone();
88 }
89 
90 void FB::JSAPIAuto::unregisterMethod( const std::string& name )
91 {
92  FB::MethodFunctorMap::iterator fnd = m_methodFunctorMap.find(name);
93  if (fnd != m_methodFunctorMap.end()) {
94  m_methodFunctorMap.erase(name);
95  m_zoneMap.erase(name);
96  }
97 }
98 
99 void FB::JSAPIAuto::registerProperty(const std::wstring& name, const PropertyFunctors& func)
100 {
101  registerProperty(FB::wstring_to_utf8(name), func);
102 }
103 
104 void FB::JSAPIAuto::registerProperty(const std::string& name, const PropertyFunctors& propFuncs)
105 {
106  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
107  m_propertyFunctorsMap[name] = propFuncs;
108  m_zoneMap[name] = getZone();
109 }
110 
111 void FB::JSAPIAuto::unregisterProperty( const std::wstring& name )
112 {
113  unregisterProperty(FB::wstring_to_utf8(name));
114 }
115 
116 void FB::JSAPIAuto::unregisterProperty( const std::string& name )
117 {
118  FB::PropertyFunctorsMap::iterator fnd = m_propertyFunctorsMap.find(name);
119  if (fnd != m_propertyFunctorsMap.end()) {
120  m_propertyFunctorsMap.erase(name);
121  m_zoneMap.erase(name);
122  }
123 }
124 
125 void FB::JSAPIAuto::getMemberNames(std::vector<std::string> &nameVector) const
126 {
127  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
128  nameVector.clear();
129  for (ZoneMap::const_iterator it = m_zoneMap.begin(); it != m_zoneMap.end(); ++it) {
130  if (getZone() >= it->second)
131  nameVector.push_back(it->first);
132  }
133 }
134 
136 {
137  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
138  size_t count = 0;
139  for (ZoneMap::const_iterator it = m_zoneMap.begin(); it != m_zoneMap.end(); ++it) {
140  if (getZone() >= it->second)
141  ++count;
142  }
143  return count;
144 }
145 
146 bool FB::JSAPIAuto::HasMethod(const std::string& methodName) const
147 {
148  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
149  if(!m_valid)
150  return false;
151 
152  return (m_methodFunctorMap.find(methodName) != m_methodFunctorMap.end()) && memberAccessible(m_zoneMap.find(methodName));
153 }
154 
155 bool FB::JSAPIAuto::HasMethodObject( const std::string& methodObjName ) const
156 {
157  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
158 
159  return m_allowMethodObjects && HasMethod(methodObjName);
160 }
161 
162 bool FB::JSAPIAuto::HasProperty(const std::string& propertyName) const
163 {
164  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
165  if(!m_valid)
166  return false;
167 
168  // To be able to set dynamic properties, we have to respond true always
169  if (m_allowDynamicAttributes && !HasMethod(propertyName) && !isReserved(propertyName))
170  return true;
171  else if (m_allowMethodObjects && HasMethod(propertyName) && memberAccessible(m_zoneMap.find(propertyName)))
172  return true;
173 
174  return m_propertyFunctorsMap.find(propertyName) != m_propertyFunctorsMap.end()
175  || m_attributes.find(propertyName) != m_attributes.end();
176 }
177 
178 bool FB::JSAPIAuto::HasProperty(int idx) const
179 {
180  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
181  if(!m_valid)
182  return false;
183 
184  // To be able to set dynamic properties, we have to respond true always
185  if (m_allowDynamicAttributes)
186  return true;
187 
188  return m_attributes.find(boost::lexical_cast<std::string>(idx)) != m_attributes.end();
189 }
190 
191 FB::variant FB::JSAPIAuto::GetProperty(const std::string& propertyName)
192 {
193  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
194  if(!m_valid)
195  throw object_invalidated();
196 
197  ZoneMap::const_iterator zoneName = m_zoneMap.find(propertyName);
198  PropertyFunctorsMap::const_iterator it = m_propertyFunctorsMap.find(propertyName);
199  if(it != m_propertyFunctorsMap.end() && memberAccessible(zoneName)) {
200  return it->second.get();
201  } else if (memberAccessible(zoneName)) {
202  if (HasMethodObject(propertyName))
203  return GetMethodObject(propertyName);
204 
205  AttributeMap::iterator fnd = m_attributes.find(propertyName);
206  if (fnd != m_attributes.end())
207  return fnd->second.value;
208  else if (m_allowDynamicAttributes) {
209  return FB::FBVoid(); // If we allow dynamic attributes then we need to
210  // return void if the property doesn't exist;
211  // otherwise checking a property will throw an exception
212  } else {
213  throw invalid_member(propertyName);
214  }
215  } else {
216  if (m_allowDynamicAttributes) {
217  return FB::FBVoid();
218  } else {
219  throw invalid_member(propertyName);
220  }
221  }
222 }
223 
224 void FB::JSAPIAuto::SetProperty(const std::string& propertyName, const variant& value)
225 {
226  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
227  if(!m_valid)
228  throw object_invalidated();
229 
230  PropertyFunctorsMap::iterator it = m_propertyFunctorsMap.find(propertyName);
231  // Note that if an explicit property exists but is not accessible in the current security context,
232  // we throw an exception.
233  if(it != m_propertyFunctorsMap.end()) {
234  if (memberAccessible(m_zoneMap.find(propertyName))) {
235  try {
236  it->second.set(value);
237  } catch (const FB::bad_variant_cast& ex) {
238  std::string errorMsg("Could not convert from ");
239  errorMsg += ex.from;
240  errorMsg += " to ";
241  errorMsg += ex.to;
242  throw FB::invalid_arguments(errorMsg);
243  }
244  } else {
245  throw invalid_member(propertyName);
246  }
247  } else if (m_allowDynamicAttributes || (m_attributes.find(propertyName) != m_attributes.end() && !m_attributes[propertyName].readonly)) {
248  registerAttribute(propertyName, value);
249  } else {
250  throw invalid_member(propertyName);
251  }
252 }
253 
254 void FB::JSAPIAuto::RemoveProperty(const std::string& propertyName)
255 {
256  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
257  if(!m_valid)
258  throw object_invalidated();
259 
260  // If there is nothing with this name available in the current security context,
261  // we throw an exception -- whether or not a real property exists
262  if (!memberAccessible(m_zoneMap.find(propertyName)))
263  throw invalid_member(propertyName);
264 
265  if(m_allowRemoveProperties && m_propertyFunctorsMap.find(propertyName) != m_propertyFunctorsMap.end()) {
266  unregisterProperty(propertyName);
267  } else if (m_allowDynamicAttributes && m_attributes.find(propertyName) != m_attributes.end()
268  && !m_attributes[propertyName].readonly) {
269  unregisterAttribute(propertyName);
270  }
271 
272  // If nothing is found matching, we'll just let it slide -- no sense causing exceptions
273  // when the end goal is reached already.
274 }
275 
277 {
278  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
279  if(!m_valid)
280  throw object_invalidated();
281 
282  std::string id = boost::lexical_cast<std::string>(idx);
283  AttributeMap::iterator fnd = m_attributes.find(id);
284  if (fnd != m_attributes.end() && memberAccessible(m_zoneMap.find(id)))
285  return fnd->second.value;
286  else if (m_allowDynamicAttributes) {
287  return FB::FBVoid(); // If we allow dynamic attributes then we need to
288  // return void if the property doesn't exist;
289  // otherwise checking a property will throw an exception
290  } else {
291  throw invalid_member(boost::lexical_cast<std::string>(idx));
292  }
293 
294  // This method should be overridden to access properties in an array style from javascript,
295  // i.e. var value = pluginObj[45]; would call GetProperty(45)
296  // Default is to throw "invalid member" unless m_attributes has something matching
297 }
298 
299 void FB::JSAPIAuto::SetProperty(int idx, const variant& value)
300 {
301  if (!m_valid)
302  throw object_invalidated();
303 
304  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
305 
306  std::string id(boost::lexical_cast<std::string>(idx));
307  if (m_allowDynamicAttributes || (m_attributes.find(id) != m_attributes.end() && !m_attributes[id].readonly)) {
308  registerAttribute(id, value);
309  } else {
310  throw invalid_member(FB::variant(idx).convert_cast<std::string>());
311  }
312 }
313 
315 {
316  if (!m_valid)
317  throw object_invalidated();
318 
319  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
320 
321  std::string id(boost::lexical_cast<std::string>(idx));
322  if (m_allowDynamicAttributes && m_attributes.find(id) != m_attributes.end() && !m_attributes[id].readonly) {
323  unregisterAttribute(id);
324  } else {
325  throw invalid_member(FB::variant(idx).convert_cast<std::string>());
326  }
327 }
328 
329 FB::variant FB::JSAPIAuto::Invoke(const std::string& methodName, const std::vector<variant> &args)
330 {
331  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
332  if(!m_valid)
333  throw object_invalidated();
334 
335  if (memberAccessible(m_zoneMap.find(methodName))) {
336  try {
337  MethodFunctorMap::iterator it = m_methodFunctorMap.find(methodName);
338  if(it == m_methodFunctorMap.end())
339  throw invalid_member(methodName);
340 
341  return it->second.call(args);
342  } catch (const FB::bad_variant_cast& ex) {
343  std::string errorMsg("Could not convert from ");
344  errorMsg += ex.from;
345  errorMsg += " to ";
346  errorMsg += ex.to;
347  throw FB::invalid_arguments(errorMsg);
348  }
349  } else {
350  throw invalid_member(methodName);
351  }
352 }
353 
354 FB::variant FB::JSAPIAuto::Construct(const std::vector<variant> &args)
355 {
356  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
357  if(!m_valid)
358  throw object_invalidated();
359 
360  throw invalid_member("constructor");
361 }
362 
363 FB::JSAPIPtr FB::JSAPIAuto::GetMethodObject( const std::string& methodObjName )
364 {
365  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
366  if(!m_valid)
367  throw object_invalidated();
368 
369  if (memberAccessible(m_zoneMap.find(methodObjName)) && HasMethod(methodObjName)) {
370  MethodObjectMap::const_iterator fnd = m_methodObjectMap.find(boost::make_tuple(methodObjName, getZone()));
371  if (fnd != m_methodObjectMap.end()) {
372  return fnd->second;
373  } else {
374  FB::JSFunctionPtr ptr(boost::make_shared<FB::JSFunction>(shared_from_this(), methodObjName, getZone()));
375  m_methodObjectMap[boost::make_tuple(methodObjName, getZone())] = ptr;
376  return ptr;
377  }
378  } else {
379  throw invalid_member(methodObjName);
380  }
381 }
382 
383 void FB::JSAPIAuto::registerAttribute( const std::string &name, const FB::variant& value, bool readonly /*= false*/ )
384 {
385  boost::recursive_mutex::scoped_lock lock(m_zoneMutex);
386  Attribute attr = {value, readonly};
387  m_attributes[name] = attr;
388  m_zoneMap[name] = getZone();
389 }
390 
391 void FB::JSAPIAuto::unregisterAttribute( const std::string& name )
392 {
393  AttributeMap::iterator fnd = m_attributes.find(name);
394  if ( fnd != m_attributes.end() ) {
395  if (fnd->second.readonly ) {
396  throw FB::script_error("Cannot remove read-only property " + name);
397  } else {
398  m_attributes.erase(fnd);
399  m_zoneMap.erase(name);
400  }
401  } else {
402  return; // No attribute of that name? success!
403  }
404 }
405 
406 FB::variant FB::JSAPIAuto::getAttribute( const std::string& name )
407 {
408  if (m_attributes.find(name) != m_attributes.end()) {
409  return m_attributes[name].value;
410  }
411  return FB::FBVoid();
412 }
413 
414 void FB::JSAPIAuto::setAttribute( const std::string& name, const FB::variant& value )
415 {
416  AttributeMap::iterator fnd = m_attributes.find(name);
417  if (fnd == m_attributes.end() || !fnd->second.readonly) {
418  Attribute attr = {value, false};
419  m_attributes[name] = attr;
420  m_zoneMap[name] = getZone();
421  } else {
422  throw FB::script_error("Cannot set read-only property " + name);
423  }
424 }
425 
426 void FB::JSAPIAuto::FireJSEvent( const std::string& eventName, const FB::VariantMap &members, const FB::VariantList &arguments )
427 {
428  JSAPIImpl::FireJSEvent(eventName, members, arguments);
429  FB::variant evt(getAttribute(eventName));
430  if (evt.is_of_type<FB::JSObjectPtr>()) {
431  VariantList args;
432  args.push_back(FB::CreateEvent(shared_from_this(), eventName, members, arguments));
433  try {
434  evt.cast<JSObjectPtr>()->InvokeAsync("", args);
435  } catch (...) {
436  // Apparently either this isn't really an event handler or something failed.
437  }
438  }
439 }
440 
441 void FB::JSAPIAuto::fireAsyncEvent( const std::string& eventName, const std::vector<variant>& args )
442 {
443  JSAPIImpl::fireAsyncEvent(eventName, args);
444  FB::variant evt(getAttribute(eventName));
445  if (evt.is_of_type<FB::JSObjectPtr>()) {
446  try {
447  FB::JSObjectPtr handler(evt.cast<JSObjectPtr>());
448  if (handler) {
449  handler->InvokeAsync("", args);
450  }
451  } catch (...) {
452  // Apparently either this isn't really an event handler or something failed.
453  }
454  }
455 }
boost::function< variant(const std::vector< variant > &)> CallMethodFunctor
Defines an alias representing a method functor used by FB::JSAPIAuto, created by FB::make_method().
Definition: APITypes.h:286
JavaScript API base class implementation – provides basic functionality for C++ JSAPI objects...
Definition: JSAPIImpl.h:49
virtual void RemoveProperty(const std::string &propertyName)
Removes a property.
Definition: JSAPIAuto.cpp:254
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
virtual std::string ToString()
Default method called when a string value is requested for the scriptable object. ...
Definition: JSAPIAuto.h:293
PropertyFunctors make_property(C *instance, F1 getter, F2 setter)
Generate read-write property functors for use with registerProperty() of FB::JSAPIAuto.
virtual void registerAttribute(const std::string &name, const FB::variant &value, bool readonly=false)
Registers an attribute name and sets the value to _value. Optionally read-only.
Definition: JSAPIAuto.cpp:383
Accepts any datatype, used in all interactions with javascript. Provides tools for getting back out t...
Definition: variant.h:198
virtual void FireJSEvent(const std::string &eventName, const FB::VariantMap &members, const FB::VariantList &arguments)
Fires an event into javascript asynchronously using a W3C-compliant event parameter.
Definition: JSAPIAuto.cpp:426
Thrown by a JSAPI object when the argument(s) provided to a SetProperty or Invoke call are found to b...
Definition: JSExceptions.h:47
Thrown by a JSAPI object when a call is made on it after the object has been invalidated.
Definition: JSExceptions.h:69
virtual bool HasMethodObject(const std::string &methodObjName) const
Query if 'methodObjName' is a valid methodObj.
Definition: JSAPIAuto.cpp:155
virtual void FireJSEvent(const std::string &eventName, const FB::VariantMap &members, const FB::VariantList &arguments)
Fires an event into javascript asynchronously using a W3C-compliant event parameter.
Definition: JSAPIImpl.cpp:185
std::vector< variant > VariantList
Defines an alias representing list of variants.
Definition: APITypes.h:64
int SecurityZone
Used to set a SecurityZone for a method or property – used by JSAPIAuto.
Definition: APITypes.h:275
Thrown when variant::cast<type> or variant::convert_cast<type> fails because the type of the value st...
Definition: variant.h:133
virtual void getMemberNames(std::vector< std::string > &nameVector) const
Called by the browser to enumerate the members of this JSAPI object.
Definition: JSAPIAuto.cpp:125
virtual bool HasProperty(const std::string &propertyName) const
Query if 'propertyName' is a valid property.
Definition: JSAPIAuto.cpp:162
virtual bool HasMethod(const std::string &methodName) const
Query if the JSAPI object has the 'methodName' method.
Definition: JSAPIAuto.cpp:146
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
std::string wstring_to_utf8(const std::wstring &src)
Accepts a std::wstring and returns a UTF8-encoded std::string.
Definition: utf8_tools.cpp:37
virtual void SetProperty(const std::string &propertyName, const variant &value)
Sets the value of a property.
Definition: JSAPIAuto.cpp:224
virtual variant Invoke(const std::string &methodName, const std::vector< variant > &args)
Called by the browser to invoke a method on the JSAPI object.
Definition: JSAPIAuto.cpp:329
virtual JSAPIPtr GetMethodObject(const std::string &methodObjName)
Gets a method object (JSAPI object that has a default method)
Definition: JSAPIAuto.cpp:363
virtual FB::variant getAttribute(const std::string &name)
Returns the attribute with the given name, empty variant if none.
Definition: JSAPIAuto.cpp:406
virtual variant GetProperty(const std::string &propertyName)
Gets a property value.
Definition: JSAPIAuto.cpp:191
virtual bool get_valid()
Property exposed by default to javascript useful for checking to make sure that the JSAPI is working...
Definition: JSAPIAuto.h:306
virtual variant Construct(const std::vector< variant > &args)
Called by the browser to construct the JSAPI object.
Definition: JSAPIAuto.cpp:354
used by FB::JSAPIAuto to store property implementation details, created by FB::make_property().
Definition: APITypes.h:311
Thrown when an Invoke, SetProperty, or GetProperty call is made for a member that is invalid (does no...
Definition: JSExceptions.h:83
std::map< std::string, variant > VariantMap
Defines an alias representing a string -> variant map.
Definition: APITypes.h:72
T cast() const
returns the value cast as the given type; throws bad_variant_type if that type is not the type of the...
Definition: variant.h:370
JSAPIAuto(const std::string &description="<JSAPI-Auto Javascript Object>")
Description is used by ToString().
Definition: JSAPIAuto.cpp:29
virtual void setAttribute(const std::string &name, const FB::variant &value)
Assigns a value to the specified attribute, if it is not reserved or read-only.
Definition: JSAPIAuto.cpp:414
virtual size_t getMemberCount() const
Gets the member count.
Definition: JSAPIAuto.cpp:135
bool is_of_type() const
Query if this object is of a particular type.
Definition: variant.h:355
JSAPI class with automatic argument type enforcement.
Definition: JSAPIAuto.h:94