1 /* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * Copyright (C) 2009 Pawel Hajdan (phajdan.jr (at) 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: 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 } 351