|
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 "public/WebBindings.h" |
|
46 #include "public/WebFrame.h" |
|
47 #include "public/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: |
|
56 CppVariantPropertyCallback(CppVariant* value) : m_value(value) { } |
|
57 |
|
58 virtual bool getValue(CppVariant* value) |
|
59 { |
|
60 value->set(*m_value); |
|
61 return true; |
|
62 } |
|
63 |
|
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: |
|
76 GetterPropertyCallback(CppBoundClass::GetterCallback* callback) |
|
77 : m_callback(callback) { } |
|
78 |
|
79 virtual bool getValue(CppVariant* value) |
|
80 { |
|
81 m_callback->run(value); |
|
82 return true; |
|
83 } |
|
84 |
|
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 |
|
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 |
|
163 void CppNPObject::deallocate(NPObject* npObj) |
|
164 { |
|
165 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); |
|
166 delete obj; |
|
167 } |
|
168 |
|
169 bool CppNPObject::hasMethod(NPObject* npObj, NPIdentifier ident) |
|
170 { |
|
171 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); |
|
172 return obj->boundClass->hasMethod(ident); |
|
173 } |
|
174 |
|
175 bool CppNPObject::hasProperty(NPObject* npObj, NPIdentifier ident) |
|
176 { |
|
177 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); |
|
178 return obj->boundClass->hasProperty(ident); |
|
179 } |
|
180 |
|
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 |
|
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 |
|
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 |
|
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 |
|
214 bool CppBoundClass::hasMethod(NPIdentifier ident) const |
|
215 { |
|
216 return m_methods.find(ident) != m_methods.end(); |
|
217 } |
|
218 |
|
219 bool CppBoundClass::hasProperty(NPIdentifier ident) const |
|
220 { |
|
221 return m_properties.find(ident) != m_properties.end(); |
|
222 } |
|
223 |
|
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 |
|
252 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 |
|
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 |
|
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 |
|
293 void CppBoundClass::bindGetterCallback(const string& name, GetterCallback* callback) |
|
294 { |
|
295 PropertyCallback* propertyCallback = callback ? new GetterPropertyCallback(callback) : 0; |
|
296 bindProperty(name, propertyCallback); |
|
297 } |
|
298 |
|
299 void CppBoundClass::bindProperty(const string& name, CppVariant* prop) |
|
300 { |
|
301 PropertyCallback* propertyCallback = prop ? new CppVariantPropertyCallback(prop) : 0; |
|
302 bindProperty(name, propertyCallback); |
|
303 } |
|
304 |
|
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 |
|
320 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 |
|
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 |
|
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 } |