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 "CppBoundClass.h"
43
44 #include "TestCommon.h"
45 #include "public/platform/WebString.h"
46 #include "public/web/WebBindings.h"
47 #include "public/web/WebFrame.h"
48
49 using namespace blink;
50 using namespace std;
51
52 namespace WebTestRunner {
53
54 namespace {
55
56 class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback {
57 public:
CppVariantPropertyCallback(CppVariant * value)58 CppVariantPropertyCallback(CppVariant* value) : m_value(value) { }
59
getValue(CppVariant * value)60 virtual bool getValue(CppVariant* value)
61 {
62 value->set(*m_value);
63 return true;
64 }
65
setValue(const CppVariant & value)66 virtual bool setValue(const CppVariant& value)
67 {
68 m_value->set(value);
69 return true;
70 }
71
72 private:
73 CppVariant* m_value;
74 };
75
76 class GetterPropertyCallback : public CppBoundClass::PropertyCallback {
77 public:
GetterPropertyCallback(WebScopedPtr<CppBoundClass::GetterCallback> callback)78 GetterPropertyCallback(WebScopedPtr<CppBoundClass::GetterCallback> callback)
79 : m_callback(callback)
80 {
81 }
82
getValue(CppVariant * value)83 virtual bool getValue(CppVariant* value)
84 {
85 m_callback->run(value);
86 return true;
87 }
88
setValue(const CppVariant & value)89 virtual bool setValue(const CppVariant& value) { return false; }
90
91 private:
92 WebScopedPtr<CppBoundClass::GetterCallback> m_callback;
93 };
94
95 }
96
97 // Our special NPObject type. We extend an NPObject with a pointer to a
98 // CppBoundClass, which is just a C++ interface that we forward all NPObject
99 // callbacks to.
100 struct CppNPObject {
101 NPObject parent; // This must be the first field in the struct.
102 CppBoundClass* boundClass;
103
104 //
105 // All following objects and functions are static, and just used to interface
106 // with NPObject/NPClass.
107 //
108
109 // An NPClass associates static functions of CppNPObject with the
110 // function pointers used by the JS runtime.
111 static NPClass npClass;
112
113 // Allocate a new NPObject with the specified class.
114 static NPObject* allocate(NPP, NPClass*);
115
116 // Free an object.
117 static void deallocate(NPObject*);
118
119 // Returns true if the C++ class associated with this NPObject exposes the
120 // given property. Called by the JS runtime.
121 static bool hasProperty(NPObject*, NPIdentifier);
122
123 // Returns true if the C++ class associated with this NPObject exposes the
124 // given method. Called by the JS runtime.
125 static bool hasMethod(NPObject*, NPIdentifier);
126
127 // If the given method is exposed by the C++ class associated with this
128 // NPObject, invokes it with the given arguments and returns a result. Otherwise,
129 // returns "undefined" (in the JavaScript sense). Called by the JS runtime.
130 static bool invoke(NPObject*, NPIdentifier,
131 const NPVariant* arguments, uint32_t argumentCount,
132 NPVariant* result);
133
134 // If the given property is exposed by the C++ class associated with this
135 // NPObject, returns its value. Otherwise, returns "undefined" (in the
136 // JavaScript sense). Called by the JS runtime.
137 static bool getProperty(NPObject*, NPIdentifier, NPVariant* result);
138
139 // If the given property is exposed by the C++ class associated with this
140 // NPObject, sets its value. Otherwise, does nothing. Called by the JS
141 // runtime.
142 static bool setProperty(NPObject*, NPIdentifier, const NPVariant* value);
143 };
144
145 // Build CppNPObject's static function pointers into an NPClass, for use
146 // in constructing NPObjects for the C++ classes.
147 NPClass CppNPObject::npClass = {
148 NP_CLASS_STRUCT_VERSION,
149 CppNPObject::allocate,
150 CppNPObject::deallocate,
151 /* NPInvalidateFunctionPtr */ 0,
152 CppNPObject::hasMethod,
153 CppNPObject::invoke,
154 /* NPInvokeDefaultFunctionPtr */ 0,
155 CppNPObject::hasProperty,
156 CppNPObject::getProperty,
157 CppNPObject::setProperty,
158 /* NPRemovePropertyFunctionPtr */ 0
159 };
160
allocate(NPP npp,NPClass * aClass)161 NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass)
162 {
163 CppNPObject* obj = new CppNPObject;
164 // obj->parent will be initialized by the NPObject code calling this.
165 obj->boundClass = 0;
166 return &obj->parent;
167 }
168
deallocate(NPObject * npObj)169 void CppNPObject::deallocate(NPObject* npObj)
170 {
171 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
172 delete obj;
173 }
174
hasMethod(NPObject * npObj,NPIdentifier ident)175 bool CppNPObject::hasMethod(NPObject* npObj, NPIdentifier ident)
176 {
177 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
178 return obj->boundClass->hasMethod(ident);
179 }
180
hasProperty(NPObject * npObj,NPIdentifier ident)181 bool CppNPObject::hasProperty(NPObject* npObj, NPIdentifier ident)
182 {
183 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
184 return obj->boundClass->hasProperty(ident);
185 }
186
invoke(NPObject * npObj,NPIdentifier ident,const NPVariant * arguments,uint32_t argumentCount,NPVariant * result)187 bool CppNPObject::invoke(NPObject* npObj, NPIdentifier ident,
188 const NPVariant* arguments, uint32_t argumentCount,
189 NPVariant* result)
190 {
191 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
192 return obj->boundClass->invoke(ident, arguments, argumentCount, result);
193 }
194
getProperty(NPObject * npObj,NPIdentifier ident,NPVariant * result)195 bool CppNPObject::getProperty(NPObject* npObj, NPIdentifier ident, NPVariant* result)
196 {
197 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
198 return obj->boundClass->getProperty(ident, result);
199 }
200
setProperty(NPObject * npObj,NPIdentifier ident,const NPVariant * value)201 bool CppNPObject::setProperty(NPObject* npObj, NPIdentifier ident, const NPVariant* value)
202 {
203 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
204 return obj->boundClass->setProperty(ident, value);
205 }
206
~CppBoundClass()207 CppBoundClass::~CppBoundClass()
208 {
209 for (MethodList::iterator i = m_methods.begin(); i != m_methods.end(); ++i)
210 delete i->second;
211
212 for (PropertyList::iterator i = m_properties.begin(); i != m_properties.end(); ++i)
213 delete i->second;
214
215 // Unregister ourselves if we were bound to a frame.
216 if (m_boundToFrame)
217 WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(m_selfVariant));
218 }
219
hasMethod(NPIdentifier ident) const220 bool CppBoundClass::hasMethod(NPIdentifier ident) const
221 {
222 return m_methods.find(ident) != m_methods.end();
223 }
224
hasProperty(NPIdentifier ident) const225 bool CppBoundClass::hasProperty(NPIdentifier ident) const
226 {
227 return m_properties.find(ident) != m_properties.end();
228 }
229
invoke(NPIdentifier ident,const NPVariant * arguments,size_t argumentCount,NPVariant * result)230 bool CppBoundClass::invoke(NPIdentifier ident,
231 const NPVariant* arguments,
232 size_t argumentCount,
233 NPVariant* result) {
234 MethodList::const_iterator end = m_methods.end();
235 MethodList::const_iterator method = m_methods.find(ident);
236 Callback* callback;
237 if (method == end) {
238 if (!m_fallbackCallback.get()) {
239 VOID_TO_NPVARIANT(*result);
240 return false;
241 }
242 callback = m_fallbackCallback.get();
243 } else
244 callback = (*method).second;
245
246 // Build a CppArgumentList argument vector from the NPVariants coming in.
247 CppArgumentList cppArguments(argumentCount);
248 for (size_t i = 0; i < argumentCount; i++)
249 cppArguments[i].set(arguments[i]);
250
251 CppVariant cppResult;
252 callback->run(cppArguments, &cppResult);
253
254 cppResult.copyToNPVariant(result);
255 return true;
256 }
257
getProperty(NPIdentifier ident,NPVariant * result) const258 bool CppBoundClass::getProperty(NPIdentifier ident, NPVariant* result) const
259 {
260 PropertyList::const_iterator callback = m_properties.find(ident);
261 if (callback == m_properties.end()) {
262 VOID_TO_NPVARIANT(*result);
263 return false;
264 }
265
266 CppVariant cppValue;
267 if (!callback->second->getValue(&cppValue))
268 return false;
269 cppValue.copyToNPVariant(result);
270 return true;
271 }
272
setProperty(NPIdentifier ident,const NPVariant * value)273 bool CppBoundClass::setProperty(NPIdentifier ident, const NPVariant* value)
274 {
275 PropertyList::iterator callback = m_properties.find(ident);
276 if (callback == m_properties.end())
277 return false;
278
279 CppVariant cppValue;
280 cppValue.set(*value);
281 return (*callback).second->setValue(cppValue);
282 }
283
bindCallback(const string & name,Callback * callback)284 void CppBoundClass::bindCallback(const string& name, Callback* callback)
285 {
286 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
287 MethodList::iterator oldCallback = m_methods.find(ident);
288 if (oldCallback != m_methods.end()) {
289 delete oldCallback->second;
290 if (!callback) {
291 m_methods.erase(oldCallback);
292 return;
293 }
294 }
295
296 m_methods[ident] = callback;
297 }
298
bindGetterCallback(const string & name,WebScopedPtr<GetterCallback> callback)299 void CppBoundClass::bindGetterCallback(const string& name, WebScopedPtr<GetterCallback> callback)
300 {
301 PropertyCallback* propertyCallback = callback.get() ? new GetterPropertyCallback(callback) : 0;
302 bindProperty(name, propertyCallback);
303 }
304
bindProperty(const string & name,CppVariant * prop)305 void CppBoundClass::bindProperty(const string& name, CppVariant* prop)
306 {
307 PropertyCallback* propertyCallback = prop ? new CppVariantPropertyCallback(prop) : 0;
308 bindProperty(name, propertyCallback);
309 }
310
bindProperty(const string & name,PropertyCallback * callback)311 void CppBoundClass::bindProperty(const string& name, PropertyCallback* callback)
312 {
313 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
314 PropertyList::iterator oldCallback = m_properties.find(ident);
315 if (oldCallback != m_properties.end()) {
316 delete oldCallback->second;
317 if (!callback) {
318 m_properties.erase(oldCallback);
319 return;
320 }
321 }
322
323 m_properties[ident] = callback;
324 }
325
isMethodRegistered(const string & name) const326 bool CppBoundClass::isMethodRegistered(const string& name) const
327 {
328 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
329 MethodList::const_iterator callback = m_methods.find(ident);
330 return callback != m_methods.end();
331 }
332
getAsCppVariant()333 CppVariant* CppBoundClass::getAsCppVariant()
334 {
335 if (!m_selfVariant.isObject()) {
336 // Create an NPObject using our static NPClass. The first argument (a
337 // plugin's instance handle) is passed through to the allocate function
338 // directly, and we don't use it, so it's ok to be 0.
339 NPObject* npObj = WebBindings::createObject(0, &CppNPObject::npClass);
340 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
341 obj->boundClass = this;
342 m_selfVariant.set(npObj);
343 WebBindings::releaseObject(npObj); // CppVariant takes the reference.
344 }
345 BLINK_ASSERT(m_selfVariant.isObject());
346 return &m_selfVariant;
347 }
348
bindToJavascript(WebFrame * frame,const WebString & classname)349 void CppBoundClass::bindToJavascript(WebFrame* frame, const WebString& classname)
350 {
351 // BindToWindowObject will take its own reference to the NPObject, and clean
352 // up after itself. It will also (indirectly) register the object with V8,
353 // so we must remember this so we can unregister it when we're destroyed.
354 frame->bindToWindowObject(classname, NPVARIANT_TO_OBJECT(*getAsCppVariant()), 0);
355 m_boundToFrame = true;
356 }
357
358 }
359