• 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 "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