FireBreath  1.4.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Pages
JSAPIImpl.cpp
1 /**********************************************************\
2 Original Author: Richard Bateman (taxilian)
3 
4 Created: April 8, 2011
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/bind.hpp>
16 #include "BrowserHost.h"
17 #include "JSObject.h"
18 #include "utf8_tools.h"
19 #include "JSEvent.h"
20 #include "precompiled_headers.h" // On windows, everything above this line in PCH
21 
22 #include "JSAPIImpl.h"
23 
24 using namespace FB;
25 
26 JSAPIImpl::JSAPIImpl(void) : m_valid(true)
27 {
28  m_zoneStack.push_back(SecurityScope_Public);
29  registerEvent("onload");
30 }
31 
32 JSAPIImpl::JSAPIImpl( const SecurityZone& securityLevel ) : m_valid(true)
33 {
34  m_zoneStack.push_back(securityLevel);
35  registerEvent("onload");
36 }
37 
39 {
40 }
41 
43 {
44  m_valid = false;
45 }
46 
47 VariantMap proxyProcessMap( const VariantMap &args, const JSAPIImplPtr& self, const JSAPIImplPtr& proxy );
48 VariantList proxyProcessList( const VariantList &args, const JSAPIImplPtr& self, const JSAPIImplPtr& proxy )
49 {
50  VariantList newArgs;
51  for (VariantList::const_iterator it = args.begin();
52  it != args.end(); ++it) {
53  if (it->is_of_type<JSAPIPtr>() && it->convert_cast<JSAPIPtr>() == self) {
54  newArgs.push_back(proxy);
55  } else if (it->is_of_type<VariantList>()) {
56  newArgs.push_back(proxyProcessList(it->convert_cast<VariantList>(), self, proxy));
57  } else if (it->is_of_type<VariantMap>()) {
58  newArgs.push_back(proxyProcessMap(it->convert_cast<VariantMap>(), self, proxy));
59  } else {
60  newArgs.push_back(*it);
61  }
62  }
63  return newArgs;
64 }
65 
66 VariantMap proxyProcessMap( const VariantMap &args, const JSAPIImplPtr& self, const JSAPIImplPtr& proxy )
67 {
68  VariantMap newMap;
69  for (VariantMap::const_iterator it = args.begin();
70  it != args.end(); ++it) {
71  if (it->second.is_of_type<JSAPIPtr>() && it->second.convert_cast<JSAPIPtr>() == self) {
72  newMap[it->first] = proxy;
73  } else if (it->second.is_of_type<VariantList>()) {
74  newMap[it->first] = proxyProcessList(it->second.convert_cast<VariantList>(), self, proxy);
75  } else if (it->second.is_of_type<VariantMap>()) {
76  newMap[it->first] = proxyProcessMap(it->second.convert_cast<VariantMap>(), self, proxy);
77  } else {
78  newMap[it->first] = it->second;
79  }
80  }
81  return newMap;
82 }
83 
84 void JSAPIImpl::fireAsyncEvent( const std::string& eventName, const std::vector<variant>& args )
85 {
86  EventContextMap eventMap;
87  EventIfaceContextMap evtIfaces;
88  {
89  boost::recursive_mutex::scoped_lock _l(m_eventMutex);
90  eventMap = m_eventMap;
91  evtIfaces = m_evtIfaces;
92  }
93 
94  std::set<void*> contexts;
95  {
96  EventContextMap::iterator it(eventMap.begin());
97  EventContextMap::iterator end(eventMap.end());
98  for (; it != end; ++it) {
99  bool first(true);
100  std::pair<EventMultiMap::iterator, EventMultiMap::iterator> range = it->second.equal_range(eventName);
101  for (EventMultiMap::const_iterator eventIt = range.first; eventIt != range.second; ++eventIt) {
102  if (first && eventIt->second->isValid() && eventIt->second->supportsOptimizedCalls()) {
103  contexts.insert(it->first);
104  std::vector<FB::JSObjectPtr> handlers;
105  std::vector<FB::JSObjectPtr> ifaces;
106  for (EventMultiMap::const_iterator inIt(range.first); inIt != range.second; ++inIt) {
107  handlers.push_back(inIt->second);
108  }
109  EventIfaceContextMap::iterator ifCtx(evtIfaces.find(it->first));
110  if (ifCtx != evtIfaces.end()) {
111  for (EventIFaceMap::const_iterator ifaceIt(ifCtx->second.begin()); ifaceIt != ifCtx->second.end(); ++ifaceIt) {
112  ifaces.push_back(ifaceIt->second);
113  }
114  }
115  eventIt->second->callMultipleFunctions(eventName, args, handlers, ifaces);
116  break;
117  } else {
118  if (eventIt->second->isValid()) {
119  first = false;
120  eventIt->second->InvokeAsync("", args);
121  }
122  }
123  }
124  }
125  }
126 
127  // Some events are registered as a jsapi object with a method of the same name as the event
128  {
129  EventIfaceContextMap::iterator it(evtIfaces.begin());
130  EventIfaceContextMap::iterator end(evtIfaces.end());
131  for (; it != end; ++it) {
132  if (contexts.find(it->first) != contexts.end())
133  continue; // We've already handled these
134 
135  for (EventIFaceMap::const_iterator ifaceIt = it->second.begin(); ifaceIt != it->second.end(); ++ifaceIt) {
136  if (ifaceIt->second->isValid() && ifaceIt->second->supportsOptimizedCalls()) {
137  std::vector<FB::JSObjectPtr> handlers;
138  std::vector<FB::JSObjectPtr> ifaces;
139  for (EventIFaceMap::const_iterator inIt = it->second.begin(); inIt != it->second.end(); ++inIt) {
140  ifaces.push_back(inIt->second);
141  }
142  ifaceIt->second->callMultipleFunctions(eventName, args, handlers, ifaces);
143  break;
144  }
145  ifaceIt->second->InvokeAsync(eventName, args);
146  }
147  }
148  }
149 }
150 
151 void JSAPIImpl::FireEvent(const std::string& eventName, const std::vector<variant>& args)
152 {
153  if (!m_valid) // When invalidated, do nothing more
154  return;
155 
156  {
157  JSAPIImplPtr self(shared_from_this());
158  boost::recursive_mutex::scoped_lock _l(m_proxyMutex);
159  ProxyList::iterator proxyIt = m_proxies.begin();
160  while (proxyIt != m_proxies.end()) {
161  JSAPIImplPtr proxy(proxyIt->lock());
162  if (!proxy) {
163  // Since you can't use a shared_ptr in a destructor, there
164  // is no way for the proxy object to let us know when it goes
165  // away; thus when we find them, we remove them for efficiency
166  proxyIt = m_proxies.erase(proxyIt);
167  continue;
168  }
169 
170  VariantList newArgs = proxyProcessList(args, self, proxy);
171 
172  proxy->FireEvent(eventName, newArgs);
173  ++proxyIt;
174  }
175  }
176 
177  try {
178  fireAsyncEvent(eventName, args);
179  } catch (const FB::script_error&) {
180  // a script_error can be fired during shutdown when this is called
181  // from another thread; this should not be an error
182  }
183 }
184 
185 void JSAPIImpl::FireJSEvent( const std::string& eventName, const VariantMap &members, const VariantList &arguments )
186 {
187  if (!m_valid) // When invalidated, do nothing more
188  return;
189 
190  {
191  JSAPIImplPtr self(shared_from_this());
192  boost::recursive_mutex::scoped_lock _l(m_proxyMutex);
193  ProxyList::iterator proxyIt = m_proxies.begin();
194  while (proxyIt != m_proxies.end()) {
195  JSAPIImplPtr proxy(proxyIt->lock());
196  if (!proxy) {
197  // Since you can't use a shared_ptr in a destructor, there
198  // is no way for the proxy object to let us know when it goes
199  // away; thus when we find them, we remove them for efficiency
200  proxyIt = m_proxies.erase(proxyIt);
201  continue;
202  }
203 
204  VariantList newArgs = proxyProcessList(arguments, self, proxy);
205  VariantMap newMap = proxyProcessMap(members, self, proxy);
206 
207  proxy->FireJSEvent(eventName, newMap, newArgs);
208  ++proxyIt;
209  }
210  }
211 
212  VariantList args;
213  args.push_back(CreateEvent(shared_from_this(), eventName, members, arguments));
214 
215  {
216  EventContextMap eventMap;
217  {
218  boost::recursive_mutex::scoped_lock _l(m_eventMutex);
219  eventMap = m_eventMap;
220  }
221 
222  EventContextMap::iterator it(eventMap.begin());
223  while (it != eventMap.end()) {
224  std::pair<EventMultiMap::iterator, EventMultiMap::iterator> range = it->second.equal_range(eventName);
225  for (EventMultiMap::const_iterator eventIt = range.first; eventIt != range.second; ++eventIt) {
226  eventIt->second->InvokeAsync("", args);
227  }
228  ++it;
229  }
230  }
231 
232  // Some events are registered as a jsapi object with a method of the same name as the event
233  {
234  EventIfaceContextMap evtIfaces;
235  {
236  boost::recursive_mutex::scoped_lock _l(m_eventMutex);
237  evtIfaces = m_evtIfaces;
238  }
239 
240  EventIfaceContextMap::iterator it(evtIfaces.begin());
241  while (it != evtIfaces.end()) {
242  for (EventIFaceMap::const_iterator ifaceIt = it->second.begin(); ifaceIt != it->second.end(); ++ifaceIt) {
243  ifaceIt->second->InvokeAsync(eventName, args);
244  }
245  }
246  }
247 }
248 
249 void JSAPIImpl::registerEventMethod(const std::string& name, JSObjectPtr &event)
250 {
251  if (!event)
252  throw invalid_arguments();
253 
254  boost::recursive_mutex::scoped_lock _l(m_eventMutex);
255  std::pair<EventMultiMap::iterator, EventMultiMap::iterator> range = m_eventMap[event->getEventContext()].equal_range(name);
256 
257  for (EventMultiMap::iterator it = range.first; it != range.second; ++it) {
258  if (it->second->getEventId() == event->getEventId()) {
259  return; // Already registered
260  }
261  }
262  m_eventMap[event->getEventContext()].insert(EventPair(name, event));
263 }
264 
265 
266 void JSAPIImpl::unregisterEventMethod(const std::string& name, JSObjectPtr &event)
267 {
268  if (!event)
269  throw invalid_arguments();
270 
271  boost::recursive_mutex::scoped_lock _l(m_eventMutex);
272  std::pair<EventMultiMap::iterator, EventMultiMap::iterator> range = m_eventMap[event->getEventContext()].equal_range(name);
273 
274  for (EventMultiMap::iterator it = range.first; it != range.second; ++it) {
275  if (it->second->getEventId() == event->getEventId()) {
276  m_eventMap[event->getEventContext()].erase(it);
277  return;
278  }
279  }
280 }
281 
282 void JSAPIImpl::registerProxy( const JSAPIImplWeakPtr &ptr ) const
283 {
284  boost::recursive_mutex::scoped_lock _l(m_proxyMutex);
285  m_proxies.push_back(ptr);
286 }
287 
288 void JSAPIImpl::unregisterProxy( const JSAPIImplPtr& ptr ) const
289 {
290  boost::recursive_mutex::scoped_lock _l(m_proxyMutex);
291  ProxyList::iterator it = m_proxies.begin();
292  while (it != m_proxies.end()) {
293  JSAPIPtr cur(it->lock());
294  if (!cur || ptr == cur)
295  it = m_proxies.erase(it);
296  else
297  ++it;
298  }
299 }
virtual void unregisterEventMethod(const std::string &name, JSObjectPtr &event)
Called by the browser to unregister an event handler method.
Definition: JSAPIImpl.cpp:266
JSAPIImpl(void)
Default constructor.
Definition: JSAPIImpl.cpp:26
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
void invalidate()
Invalidates this object.
Definition: JSAPIImpl.cpp:42
Thrown by a JSAPI object when the argument(s) provided to a SetProperty or Invoke call are found to b...
Definition: JSExceptions.h:47
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
virtual void registerEvent(const std::string &name)
Register event so that event listeners can be added/attached from javascript.
Definition: JSAPIImpl.h:253
int SecurityZone
Used to set a SecurityZone for a method or property – used by JSAPIAuto.
Definition: APITypes.h:275
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
virtual ~JSAPIImpl(void)
Finaliser.
Definition: JSAPIImpl.cpp:38
std::map< std::string, variant > VariantMap
Defines an alias representing a string -> variant map.
Definition: APITypes.h:72
virtual void FireEvent(const std::wstring &eventName, const std::vector< variant > &args)
Definition: JSAPIImpl.h:92
virtual void registerEventMethod(const std::string &name, JSObjectPtr &event)
Called by the browser to register an event handler method.
Definition: JSAPIImpl.cpp:249