• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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