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