-
Notifications
You must be signed in to change notification settings - Fork 291
/
JSAPIImpl.h
401 lines (365 loc) · 18.3 KB
/
JSAPIImpl.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
/**********************************************************\
Original Author: Richard Bateman (taxilian)
Created: April 8, 2011
License: Dual license model; choose one of two:
New BSD License
http://www.opensource.org/licenses/bsd-license.php
- or -
GNU Lesser General Public License, version 2.1
http://www.gnu.org/licenses/lgpl-2.1.html
Copyright 2009 Richard Bateman, Firebreath development team
\**********************************************************/
#pragma once
#ifndef H_FB_JSAPIImpl
#define H_FB_JSAPIImpl
#include "APITypes.h"
#include <list>
#include <deque>
#include <boost/noncopyable.hpp>
#include "JSExceptions.h"
#include "JSAPI.h"
namespace FB
{
FB_FORWARD_PTR(JSAPIImpl);
/// @brief Defines an alias representing a method functor used by FB::JSAPIAuto, created by FB::make_method().
using CallMethodFunctor = std::function < variantPromise(const std::vector<variant>&) >;
struct MethodFunctors
{
FB::CallMethodFunctor call;
SecurityZone zone;
MethodFunctors() : call() {}
MethodFunctors(const CallMethodFunctor& call) : call(call) {}
MethodFunctors(const SecurityZone& zone, const CallMethodFunctor& call) : call(call), zone(zone) {}
MethodFunctors(const MethodFunctors& m) : call(m.call) {}
MethodFunctors& operator=(const MethodFunctors& rhs) {
call = rhs.call;
zone = rhs.zone;
return *this;
}
};
/// @brief Defines an alias representing a map of method functors used by FB::JSAPIAuto
using MethodFunctorMap = std::map < std::string, MethodFunctors >;
// new style JSAPI properties
/// @brief Defines an alias representing a property getter functor used by FB::JSAPIAuto
using GetPropFunctor = std::function < variantPromise() >;
/// @brief Defines an alias representing a property setter functor used by FB::JSAPIAuto
using SetPropFunctor = std::function < void(const FB::variant&) >;
/// @brief used by FB::JSAPIAuto to store property implementation details, created by FB::make_property().
struct PropertyFunctors
{
GetPropFunctor get;
SetPropFunctor set;
PropertyFunctors() : get(), set() {}
PropertyFunctors(const GetPropFunctor& get, const SetPropFunctor& set)
: get(get), set(set) {}
PropertyFunctors(const PropertyFunctors& p)
: get(p.get), set(p.set) {}
PropertyFunctors& operator=(const PropertyFunctors& rhs) {
get = rhs.get;
set = rhs.set;
return *this;
}
};
/// @brief Defines an alias representing a map of property functors used by FB::JSAPIAuto
using PropertyFunctorsMap = std::map < std::string, PropertyFunctors >;
// JSAPI event handlers
using EventPair = std::pair < std::string, FB::JSObjectPtr >;
using EventMultiMap = std::multimap < std::string, FB::JSObjectPtr >;
using EventIFaceMap = std::map < void*, FB::JSObjectPtr >;
using EventSingleMap = std::map < std::string, FB::JSObjectPtr >;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @class JSAPIImpl
///
/// @brief JavaScript API base class implementation -- provides basic functionality for C++ JSAPI
/// objects.
///
/// JSAPIImpl is the base class for C++ objects that interact with Javascript. It provides some basic
/// functionality that is shared with most C++ JSAPI implementations. If you want to provide an
/// interface to something else, such as an object from Javascript, you probably want to extend
/// JSAPI instead so as not to get the extra (unused) functionality from this class
///
/// @author Richard Bateman
/// @see FB::JSAPI
/// @see FB::JSAPIAuto
/// @since 1.5
////////////////////////////////////////////////////////////////////////////////////////////////////
class JSAPIImpl : public JSAPI
{
public:
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn JSAPIImpl(void)
///
/// @brief Default constructor.
////////////////////////////////////////////////////////////////////////////////////////////////////
JSAPIImpl(void);
JSAPIImpl( const SecurityZone& securityLevel );
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn virtual ~JSAPIImpl(void)
///
/// @brief Finaliser.
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual ~JSAPIImpl(void);
public:
JSAPIImplPtr shared_from_this() { return std::static_pointer_cast<JSAPIImpl>(JSAPI::shared_from_this()); }
std::shared_ptr<const JSAPIImpl> shared_from_this() const { return std::static_pointer_cast<const JSAPIImpl>(JSAPI::shared_from_this()); }
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn void invalidate()
///
/// @brief Invalidates this object.
////////////////////////////////////////////////////////////////////////////////////////////////////
void invalidate();
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn void shutdown()
///
/// @brief Called to notify the object that the plugin is shutting down
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void shutdown() {};
protected:
virtual void fireAsyncEvent( std::string eventName, const std::vector<variant>& args );
protected:
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @overload virtual void FireEvent(const std::wstring& eventName, const std::vector<variant> &args)
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void FireEvent(const std::wstring& eventName, const std::vector<variant> &args)
{
FireEvent(wstring_to_utf8(eventName), args);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn virtual void FireEvent(std::string eventName, const std::vector<variant> &args)
///
/// @brief Fires an event into javascript asynchronously
///
/// This fires an event to all handlers attached to the given event in javascript.
///
/// IE:
/// @code
/// document.getElementByID("plugin").attachEvent("onload", function() { alert("loaded!"); });
/// @endcode
/// Firefox/Safari/Chrome/Opera:
/// @code
/// // Note that the convention used by these browsers is that "on" is implied
/// document.getElementByID("plugin").addEventListener("load", function() { alert("loaded!"); }, false);;/.
/// @endcode
///
/// You can then fire the event -- from any thread -- from the JSAPI object like so:
/// @code
/// FireEvent("onload", FB::VariantList{"param1", 2, 3.0});
/// @endcode
///
/// Also note that registerEvent must be called from the constructor to register the event.
/// @code
/// registerEvent("onload");
/// @endcode
///
/// @param eventName Name of the event. This event must start with "on"
/// @param args The arguments that should be sent to each attached event handler
///
/// @see registerEvent
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void FireEvent(std::string eventName, const std::vector<variant> &args);
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief Fires an event into javascript asynchronously using a W3C-compliant event parameter
///
/// This fires an event to all handlers attached to the given event in javascript. With a
/// W3C-compliant event parameter
///
/// IE:
/// @code
/// document.getElementByID("plugin").attachEvent("onload", function() { alert("loaded!"); });
/// @endcode
/// Firefox/Safari/Chrome/Opera:
/// @code
/// // Note that the convention used by these browsers is that "on" is implied
/// document.getElementByID("plugin").addEventListener("load", function() { alert("loaded!"); }, false);;/.
/// @endcode
///
/// You can then fire the event -- from any thread -- from the JSAPI object like so:
/// @code
/// FireEvent("onload", FB::VariantList{"param1", 2, 3.0});
/// @endcode
///
/// Also note that registerEvent must be called from the constructor to register the event.
/// @code
/// registerEvent("onload");
/// @endcode
///
/// @param eventName Name of the event. This event must start with "on"
/// @param members
/// @param arguments The arguments that should be sent to each attached event handler
///
/// @see registerEvent
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void FireJSEvent(std::string eventName, const FB::VariantMap &members, const FB::VariantList &arguments);
/// @overload
virtual void FireJSEvent(std::string eventName, const FB::VariantMap ¶ms)
{
FireJSEvent(eventName, params, FB::VariantList());
}
/// @overload
virtual void FireJSEvent(std::string eventName, const FB::VariantList &arguments)
{
FireJSEvent(eventName, FB::VariantMap(), arguments);
}
public:
virtual void pushZone(const SecurityZone& securityLevel)
{
m_zoneMutex.lock();
m_zoneStack.push_back(securityLevel);
}
virtual void popZone()
{
m_zoneStack.pop_back();
m_zoneMutex.unlock();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn public void setDefaultZone(const SecurityZone& securityLevel)
///
/// @brief Sets the default zone (the zone the class operates on before a push)
///
/// @returns void
/// @since 1.4a3
/// @see FB::scoped_zonelock
/// @see pushZone
/// @see popZone
/// @see getDefaultZone
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void setDefaultZone(const SecurityZone& securityLevel)
{
std::unique_lock<std::recursive_mutex> lock(m_zoneMutex);
assert(!m_zoneStack.empty());
m_zoneStack.pop_front();
m_zoneStack.push_front(securityLevel);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn public virtual SecurityZone getDefaultZone() const
///
/// @brief Gets the default zone (the zone the class operates on before a push)
///
/// @returns SecurityZone the default zone
/// @since 1.4a3
/// @see FB::scoped_zonelock
/// @see pushZone
/// @see popZone
/// @see getDefaultZone
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual SecurityZone getDefaultZone() const
{
std::unique_lock<std::recursive_mutex> lock(m_zoneMutex);
assert(!m_zoneStack.empty());
return m_zoneStack.front();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn public SecurityZone getZone() const
///
/// @brief Gets the currently active zone
///
/// @returns SecurityZone the current zone
/// @since 1.4a3
/// @see FB::scoped_zonelock
/// @see pushZone
/// @see popZone
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual SecurityZone getZone() const
{
assert(!m_zoneStack.empty());
std::unique_lock<std::recursive_mutex> lock(m_zoneMutex);
return m_zoneStack.back();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn virtual void registerEvent(std::string name)
///
/// @brief Register event so that event listeners can be added/attached from javascript
///
/// @param name The name of the event to register. This event must start with "on"
/// @deprecated 1.5
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void registerEvent(std::string name) {}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @overload virtual void registerEvent(const std::wstring& name)
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void registerEvent(const std::wstring& name)
{
registerEvent(wstring_to_utf8(name));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn virtual void registerEventMethod(std::string name, JSObjectPtr& event)
///
/// @brief Called by the browser to register an event handler method
///
/// @param name The name.
/// @param event The event handler method.
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void registerEventMethod(std::string name, JSObjectPtr& event);
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @overload virtual void registerEventMethod(const std::wstring& name, JSObjectPtr& event)
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void registerEventMethod(const std::wstring& name, JSObjectPtr& event)
{
registerEventMethod(wstring_to_utf8(name), event);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn virtual void unregisterEventMethod(std::string name, JSObjectPtr& event)
///
/// @brief Called by the browser to unregister an event handler method
///
/// @param name The name.
/// @param event The event handler method to unregister.
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void unregisterEventMethod(std::string name, JSObjectPtr& event);
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @overload virtual void unregisterEventMethod(const std::wstring& name, JSObjectPtr& event)
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void unregisterEventMethod(const std::wstring& name, JSObjectPtr& event)
{
unregisterEventMethod(wstring_to_utf8(name), event);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn virtual void registerEventInterface(const JSObjectPtr& event)
///
/// @brief Called by the browser to register a JSObject interface that handles events. This is
/// primarily used by IE. Objects provided to this method are called when events are fired
/// by calling a method of the event name on the event interface
///
/// @param event The JSAPI interface
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void registerEventInterface(const JSObjectPtr& event)
{
std::unique_lock<std::recursive_mutex> _l(m_eventMutex);
m_evtIfaces[event->getEventContext()][static_cast<void*>(event.get())] = event;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn virtual void unregisterEventInterface(const JSObjectPtr& event)
///
/// @brief Called by the browser to unregister a JSObject interface that handles events.
///
/// @param event The JSAPI interface
////////////////////////////////////////////////////////////////////////////////////////////////////
virtual void unregisterEventInterface(const JSObjectPtr& event)
{
std::unique_lock<std::recursive_mutex> _l(m_eventMutex);
EventIFaceMap::iterator fnd = m_evtIfaces[event->getEventContext()].find(static_cast<void*>(event.get()));
m_evtIfaces[event->getEventContext()].erase(fnd);
}
public:
virtual void registerProxy(const JSAPIImplWeakPtr &ptr) const;
virtual void unregisterProxy(const FB::JSAPIImplPtr& ptr) const;
protected:
using ZoneStack = std::deque < SecurityZone > ;
using EventContextMap = std::map < void*, EventMultiMap > ;
// Stores event handlers
EventContextMap m_eventMap;
using EventIfaceContextMap = std::map < void*, EventIFaceMap > ;
// Stores event interfaces
EventIfaceContextMap m_evtIfaces;
using ProxyList = std::vector < JSAPIImplWeakPtr > ;
mutable ProxyList m_proxies;
mutable std::recursive_mutex m_eventMutex;
mutable std::recursive_mutex m_proxyMutex;
mutable std::recursive_mutex m_zoneMutex;
ZoneStack m_zoneStack;
bool m_valid; // Tracks if this object has been invalidated
};
};
// There are important conversion routines that require JSObject and JSAPI to both be loaded
#include "JSObject.h"
#endif