1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2009 Pawel Hajdan (phajdan.jr@chromium.org)
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 // This file contains definitions for CppBoundClass
33
34 // Here's the control flow of a JS method getting forwarded to a class.
35 // - Something calls our NPObject with a function like "Invoke".
36 // - CppNPObject's static invoke() function forwards it to its attached
37 // CppBoundClass's invoke() method.
38 // - CppBoundClass has then overridden invoke() to look up the function
39 // name in its internal map of methods, and then calls the appropriate
40 // method.
41
42 #include "config.h"
43 #include "CppBoundClass.h"
44
45 #include "WebBindings.h"
46 #include "WebFrame.h"
47 #include "WebString.h"
48 #include <wtf/Assertions.h>
49 #include <wtf/OwnPtr.h>
50
51 using namespace WebKit;
52 using namespace std;
53
54 class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback {
55 public:
CppVariantPropertyCallback(CppVariant * value)56 CppVariantPropertyCallback(CppVariant* value) : m_value(value) { }
57
getValue(CppVariant * value)58 virtual bool getValue(CppVariant* value)
59 {
60 value->set(*m_value);
61 return true;
62 }
63
setValue(const CppVariant & value)64 virtual bool setValue(const CppVariant& value)
65 {
66 m_value->set(value);
67 return true;
68 }
69
70 private:
71 CppVariant* m_value;
72 };
73
74 class GetterPropertyCallback : public CppBoundClass::PropertyCallback {
75 public:
GetterPropertyCallback(CppBoundClass::GetterCallback * callback)76 GetterPropertyCallback(CppBoundClass::GetterCallback* callback)
77 : m_callback(callback) { }
78
getValue(CppVariant * value)79 virtual bool getValue(CppVariant* value)
80 {
81 m_callback->run(value);
82 return true;
83 }
84
setValue(const CppVariant & value)85 virtual bool setValue(const CppVariant& value) { return false; }
86
87 private:
88 OwnPtr<CppBoundClass::GetterCallback> m_callback;
89 };
90
91 // Our special NPObject type. We extend an NPObject with a pointer to a
92 // CppBoundClass, which is just a C++ interface that we forward all NPObject
93 // callbacks to.
94 struct CppNPObject {
95 NPObject parent; // This must be the first field in the struct.
96 CppBoundClass* boundClass;
97
98 //
99 // All following objects and functions are static, and just used to interface
100 // with NPObject/NPClass.
101 //
102
103 // An NPClass associates static functions of CppNPObject with the
104 // function pointers used by the JS runtime.
105 static NPClass npClass;
106
107 // Allocate a new NPObject with the specified class.
108 static NPObject* allocate(NPP, NPClass*);
109
110 // Free an object.
111 static void deallocate(NPObject*);
112
113 // Returns true if the C++ class associated with this NPObject exposes the
114 // given property. Called by the JS runtime.
115 static bool hasProperty(NPObject*, NPIdentifier);
116
117 // Returns true if the C++ class associated with this NPObject exposes the
118 // given method. Called by the JS runtime.
119 static bool hasMethod(NPObject*, NPIdentifier);
120
121 // If the given method is exposed by the C++ class associated with this
122 // NPObject, invokes it with the given arguments and returns a result. Otherwise,
123 // returns "undefined" (in the JavaScript sense). Called by the JS runtime.
124 static bool invoke(NPObject*, NPIdentifier,
125 const NPVariant* arguments, uint32_t argumentCount,
126 NPVariant* result);
127
128 // If the given property is exposed by the C++ class associated with this
129 // NPObject, returns its value. Otherwise, returns "undefined" (in the
130 // JavaScript sense). Called by the JS runtime.
131 static bool getProperty(NPObject*, NPIdentifier, NPVariant* result);
132
133 // If the given property is exposed by the C++ class associated with this
134 // NPObject, sets its value. Otherwise, does nothing. Called by the JS
135 // runtime.
136 static bool setProperty(NPObject*, NPIdentifier, const NPVariant* value);
137 };
138
139 // Build CppNPObject's static function pointers into an NPClass, for use
140 // in constructing NPObjects for the C++ classes.
141 NPClass CppNPObject::npClass = {
142 NP_CLASS_STRUCT_VERSION,
143 CppNPObject::allocate,
144 CppNPObject::deallocate,
145 /* NPInvalidateFunctionPtr */ 0,
146 CppNPObject::hasMethod,
147 CppNPObject::invoke,
148 /* NPInvokeDefaultFunctionPtr */ 0,
149 CppNPObject::hasProperty,
150 CppNPObject::getProperty,
151 CppNPObject::setProperty,
152 /* NPRemovePropertyFunctionPtr */ 0
153 };
154
allocate(NPP npp,NPClass * aClass)155 NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass)
156 {
157 CppNPObject* obj = new CppNPObject;
158 // obj->parent will be initialized by the NPObject code calling this.
159 obj->boundClass = 0;
160 return &obj->parent;
161 }
162
deallocate(NPObject * npObj)163 void CppNPObject::deallocate(NPObject* npObj)
164 {
165 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
166 delete obj;
167 }
168
hasMethod(NPObject * npObj,NPIdentifier ident)169 bool CppNPObject::hasMethod(NPObject* npObj, NPIdentifier ident)
170 {
171 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
172 return obj->boundClass->hasMethod(ident);
173 }
174
hasProperty(NPObject * npObj,NPIdentifier ident)175 bool CppNPObject::hasProperty(NPObject* npObj, NPIdentifier ident)
176 {
177 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
178 return obj->boundClass->hasProperty(ident);
179 }
180
invoke(NPObject * npObj,NPIdentifier ident,const NPVariant * arguments,uint32_t argumentCount,NPVariant * result)181 bool CppNPObject::invoke(NPObject* npObj, NPIdentifier ident,
182 const NPVariant* arguments, uint32_t argumentCount,
183 NPVariant* result)
184 {
185 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
186 return obj->boundClass->invoke(ident, arguments, argumentCount, result);
187 }
188
getProperty(NPObject * npObj,NPIdentifier ident,NPVariant * result)189 bool CppNPObject::getProperty(NPObject* npObj, NPIdentifier ident, NPVariant* result)
190 {
191 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
192 return obj->boundClass->getProperty(ident, result);
193 }
194
setProperty(NPObject * npObj,NPIdentifier ident,const NPVariant * value)195 bool CppNPObject::setProperty(NPObject* npObj, NPIdentifier ident, const NPVariant* value)
196 {
197 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
198 return obj->boundClass->setProperty(ident, value);
199 }
200
~CppBoundClass()201 CppBoundClass::~CppBoundClass()
202 {
203 for (MethodList::iterator i = m_methods.begin(); i != m_methods.end(); ++i)
204 delete i->second;
205
206 for (PropertyList::iterator i = m_properties.begin(); i != m_properties.end(); ++i)
207 delete i->second;
208
209 // Unregister ourselves if we were bound to a frame.
210 if (m_boundToFrame)
211 WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(m_selfVariant));
212 }
213
hasMethod(NPIdentifier ident) const214 bool CppBoundClass::hasMethod(NPIdentifier ident) const
215 {
216 return m_methods.find(ident) != m_methods.end();
217 }
218
hasProperty(NPIdentifier ident) const219 bool CppBoundClass::hasProperty(NPIdentifier ident) const
220 {
221 return m_properties.find(ident) != m_properties.end();
222 }
223
invoke(NPIdentifier ident,const NPVariant * arguments,size_t argumentCount,NPVariant * result)224 bool CppBoundClass::invoke(NPIdentifier ident,
225 const NPVariant* arguments,
226 size_t argumentCount,
227 NPVariant* result) {
228 MethodList::const_iterator end = m_methods.end();
229 MethodList::const_iterator method = m_methods.find(ident);
230 Callback* callback;
231 if (method == end) {
232 if (!m_fallbackCallback.get()) {
233 VOID_TO_NPVARIANT(*result);
234 return false;
235 }
236 callback = m_fallbackCallback.get();
237 } else
238 callback = (*method).second;
239
240 // Build a CppArgumentList argument vector from the NPVariants coming in.
241 CppArgumentList cppArguments(argumentCount);
242 for (size_t i = 0; i < argumentCount; i++)
243 cppArguments[i].set(arguments[i]);
244
245 CppVariant cppResult;
246 callback->run(cppArguments, &cppResult);
247
248 cppResult.copyToNPVariant(result);
249 return true;
250 }
251
getProperty(NPIdentifier ident,NPVariant * result) const252 bool CppBoundClass::getProperty(NPIdentifier ident, NPVariant* result) const
253 {
254 PropertyList::const_iterator callback = m_properties.find(ident);
255 if (callback == m_properties.end()) {
256 VOID_TO_NPVARIANT(*result);
257 return false;
258 }
259
260 CppVariant cppValue;
261 if (!callback->second->getValue(&cppValue))
262 return false;
263 cppValue.copyToNPVariant(result);
264 return true;
265 }
266
setProperty(NPIdentifier ident,const NPVariant * value)267 bool CppBoundClass::setProperty(NPIdentifier ident, const NPVariant* value)
268 {
269 PropertyList::iterator callback = m_properties.find(ident);
270 if (callback == m_properties.end())
271 return false;
272
273 CppVariant cppValue;
274 cppValue.set(*value);
275 return (*callback).second->setValue(cppValue);
276 }
277
bindCallback(const string & name,Callback * callback)278 void CppBoundClass::bindCallback(const string& name, Callback* callback)
279 {
280 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
281 MethodList::iterator oldCallback = m_methods.find(ident);
282 if (oldCallback != m_methods.end()) {
283 delete oldCallback->second;
284 if (!callback) {
285 m_methods.remove(oldCallback);
286 return;
287 }
288 }
289
290 m_methods.set(ident, callback);
291 }
292
bindGetterCallback(const string & name,GetterCallback * callback)293 void CppBoundClass::bindGetterCallback(const string& name, GetterCallback* callback)
294 {
295 PropertyCallback* propertyCallback = callback ? new GetterPropertyCallback(callback) : 0;
296 bindProperty(name, propertyCallback);
297 }
298
bindProperty(const string & name,CppVariant * prop)299 void CppBoundClass::bindProperty(const string& name, CppVariant* prop)
300 {
301 PropertyCallback* propertyCallback = prop ? new CppVariantPropertyCallback(prop) : 0;
302 bindProperty(name, propertyCallback);
303 }
304
bindProperty(const string & name,PropertyCallback * callback)305 void CppBoundClass::bindProperty(const string& name, PropertyCallback* callback)
306 {
307 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
308 PropertyList::iterator oldCallback = m_properties.find(ident);
309 if (oldCallback != m_properties.end()) {
310 delete oldCallback->second;
311 if (!callback) {
312 m_properties.remove(oldCallback);
313 return;
314 }
315 }
316
317 m_properties.set(ident, callback);
318 }
319
isMethodRegistered(const string & name) const320 bool CppBoundClass::isMethodRegistered(const string& name) const
321 {
322 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
323 MethodList::const_iterator callback = m_methods.find(ident);
324 return callback != m_methods.end();
325 }
326
getAsCppVariant()327 CppVariant* CppBoundClass::getAsCppVariant()
328 {
329 if (!m_selfVariant.isObject()) {
330 // Create an NPObject using our static NPClass. The first argument (a
331 // plugin's instance handle) is passed through to the allocate function
332 // directly, and we don't use it, so it's ok to be 0.
333 NPObject* npObj = WebBindings::createObject(0, &CppNPObject::npClass);
334 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
335 obj->boundClass = this;
336 m_selfVariant.set(npObj);
337 WebBindings::releaseObject(npObj); // CppVariant takes the reference.
338 }
339 ASSERT(m_selfVariant.isObject());
340 return &m_selfVariant;
341 }
342
bindToJavascript(WebFrame * frame,const WebString & classname)343 void CppBoundClass::bindToJavascript(WebFrame* frame, const WebString& classname)
344 {
345 // BindToWindowObject will take its own reference to the NPObject, and clean
346 // up after itself. It will also (indirectly) register the object with V8,
347 // so we must remember this so we can unregister it when we're destroyed.
348 frame->bindToWindowObject(classname, NPVARIANT_TO_OBJECT(*getAsCppVariant()));
349 m_boundToFrame = true;
350 }
351