1// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// This file contains definitions for CppBoundClass 6 7// Here's the control flow of a JS method getting forwarded to a class. 8// - Something calls our NPObject with a function like "Invoke". 9// - CppNPObject's static invoke() function forwards it to its attached 10// CppBoundClass's Invoke() method. 11// - CppBoundClass has then overridden Invoke() to look up the function 12// name in its internal map of methods, and then calls the appropriate 13// method. 14 15#include "base/compiler_specific.h" 16#include "base/logging.h" 17#include "base/utf_string_conversions.h" 18#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h" 19#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" 20#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" 21#include "webkit/glue/cpp_bound_class.h" 22 23using WebKit::WebBindings; 24using WebKit::WebFrame; 25 26namespace { 27 28class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback { 29 public: 30 CppVariantPropertyCallback(CppVariant* value) : value_(value) { } 31 32 virtual bool GetValue(CppVariant* value) { 33 value->Set(*value_); 34 return true; 35 } 36 virtual bool SetValue(const CppVariant& value) { 37 value_->Set(value); 38 return true; 39 } 40 41 private: 42 CppVariant* value_; 43}; 44 45class GetterPropertyCallback : public CppBoundClass::PropertyCallback { 46public: 47 GetterPropertyCallback(CppBoundClass::GetterCallback* callback) 48 : callback_(callback) { } 49 50 virtual bool GetValue(CppVariant* value) { 51 callback_->Run(value); 52 return true; 53 } 54 55 virtual bool SetValue(const CppVariant& value) { 56 return false; 57 } 58 59private: 60 scoped_ptr<CppBoundClass::GetterCallback> callback_; 61}; 62 63} 64 65// Our special NPObject type. We extend an NPObject with a pointer to a 66// CppBoundClass, which is just a C++ interface that we forward all NPObject 67// callbacks to. 68struct CppNPObject { 69 NPObject parent; // This must be the first field in the struct. 70 CppBoundClass* bound_class; 71 72 // 73 // All following objects and functions are static, and just used to interface 74 // with NPObject/NPClass. 75 // 76 77 // An NPClass associates static functions of CppNPObject with the 78 // function pointers used by the JS runtime. 79 static NPClass np_class_; 80 81 // Allocate a new NPObject with the specified class. 82 static NPObject* allocate(NPP npp, NPClass* aClass); 83 84 // Free an object. 85 static void deallocate(NPObject* obj); 86 87 // Returns true if the C++ class associated with this NPObject exposes the 88 // given property. Called by the JS runtime. 89 static bool hasProperty(NPObject *obj, NPIdentifier ident); 90 91 // Returns true if the C++ class associated with this NPObject exposes the 92 // given method. Called by the JS runtime. 93 static bool hasMethod(NPObject *obj, NPIdentifier ident); 94 95 // If the given method is exposed by the C++ class associated with this 96 // NPObject, invokes it with the given args and returns a result. Otherwise, 97 // returns "undefined" (in the JavaScript sense). Called by the JS runtime. 98 static bool invoke(NPObject *obj, NPIdentifier ident, 99 const NPVariant *args, uint32_t arg_count, 100 NPVariant *result); 101 102 // If the given property is exposed by the C++ class associated with this 103 // NPObject, returns its value. Otherwise, returns "undefined" (in the 104 // JavaScript sense). Called by the JS runtime. 105 static bool getProperty(NPObject *obj, NPIdentifier ident, 106 NPVariant *result); 107 108 // If the given property is exposed by the C++ class associated with this 109 // NPObject, sets its value. Otherwise, does nothing. Called by the JS 110 // runtime. 111 static bool setProperty(NPObject *obj, NPIdentifier ident, 112 const NPVariant *value); 113}; 114 115// Build CppNPObject's static function pointers into an NPClass, for use 116// in constructing NPObjects for the C++ classes. 117NPClass CppNPObject::np_class_ = { 118 NP_CLASS_STRUCT_VERSION, 119 CppNPObject::allocate, 120 CppNPObject::deallocate, 121 /* NPInvalidateFunctionPtr */ NULL, 122 CppNPObject::hasMethod, 123 CppNPObject::invoke, 124 /* NPInvokeDefaultFunctionPtr */ NULL, 125 CppNPObject::hasProperty, 126 CppNPObject::getProperty, 127 CppNPObject::setProperty, 128 /* NPRemovePropertyFunctionPtr */ NULL 129}; 130 131/* static */ NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass) { 132 CppNPObject* obj = new CppNPObject; 133 // obj->parent will be initialized by the NPObject code calling this. 134 obj->bound_class = NULL; 135 return &obj->parent; 136} 137 138/* static */ void CppNPObject::deallocate(NPObject* np_obj) { 139 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 140 delete obj; 141} 142 143/* static */ bool CppNPObject::hasMethod(NPObject* np_obj, 144 NPIdentifier ident) { 145 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 146 return obj->bound_class->HasMethod(ident); 147} 148 149/* static */ bool CppNPObject::hasProperty(NPObject* np_obj, 150 NPIdentifier ident) { 151 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 152 return obj->bound_class->HasProperty(ident); 153} 154 155/* static */ bool CppNPObject::invoke(NPObject* np_obj, NPIdentifier ident, 156 const NPVariant* args, uint32_t arg_count, 157 NPVariant* result) { 158 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 159 return obj->bound_class->Invoke(ident, args, arg_count, result); 160} 161 162/* static */ bool CppNPObject::getProperty(NPObject* np_obj, 163 NPIdentifier ident, 164 NPVariant* result) { 165 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 166 return obj->bound_class->GetProperty(ident, result); 167} 168 169/* static */ bool CppNPObject::setProperty(NPObject* np_obj, 170 NPIdentifier ident, 171 const NPVariant* value) { 172 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 173 return obj->bound_class->SetProperty(ident, value); 174} 175 176CppBoundClass::CppBoundClass() 177 : bound_to_frame_(false) { 178} 179 180CppBoundClass::~CppBoundClass() { 181 for (MethodList::iterator i = methods_.begin(); i != methods_.end(); ++i) 182 delete i->second; 183 184 for (PropertyList::iterator i = properties_.begin(); i != properties_.end(); 185 ++i) { 186 delete i->second; 187 } 188 189 // Unregister ourselves if we were bound to a frame. 190 if (bound_to_frame_) 191 WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(self_variant_)); 192} 193 194bool CppBoundClass::HasMethod(NPIdentifier ident) const { 195 return (methods_.find(ident) != methods_.end()); 196} 197 198bool CppBoundClass::HasProperty(NPIdentifier ident) const { 199 return (properties_.find(ident) != properties_.end()); 200} 201 202bool CppBoundClass::Invoke(NPIdentifier ident, 203 const NPVariant* args, 204 size_t arg_count, 205 NPVariant* result) { 206 MethodList::const_iterator method = methods_.find(ident); 207 Callback* callback; 208 if (method == methods_.end()) { 209 if (fallback_callback_.get()) { 210 callback = fallback_callback_.get(); 211 } else { 212 VOID_TO_NPVARIANT(*result); 213 return false; 214 } 215 } else { 216 callback = (*method).second; 217 } 218 219 // Build a CppArgumentList argument vector from the NPVariants coming in. 220 CppArgumentList cpp_args(arg_count); 221 for (size_t i = 0; i < arg_count; i++) 222 cpp_args[i].Set(args[i]); 223 224 CppVariant cpp_result; 225 callback->Run(cpp_args, &cpp_result); 226 227 cpp_result.CopyToNPVariant(result); 228 return true; 229} 230 231bool CppBoundClass::GetProperty(NPIdentifier ident, NPVariant* result) const { 232 PropertyList::const_iterator callback = properties_.find(ident); 233 if (callback == properties_.end()) { 234 VOID_TO_NPVARIANT(*result); 235 return false; 236 } 237 238 CppVariant cpp_value; 239 if (!callback->second->GetValue(&cpp_value)) 240 return false; 241 cpp_value.CopyToNPVariant(result); 242 return true; 243} 244 245bool CppBoundClass::SetProperty(NPIdentifier ident, 246 const NPVariant* value) { 247 PropertyList::iterator callback = properties_.find(ident); 248 if (callback == properties_.end()) 249 return false; 250 251 CppVariant cpp_value; 252 cpp_value.Set(*value); 253 return (*callback).second->SetValue(cpp_value); 254} 255 256void CppBoundClass::BindCallback(const std::string& name, Callback* callback) { 257 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); 258 MethodList::iterator old_callback = methods_.find(ident); 259 if (old_callback != methods_.end()) { 260 delete old_callback->second; 261 if (callback == NULL) { 262 methods_.erase(old_callback); 263 return; 264 } 265 } 266 267 methods_[ident] = callback; 268} 269 270void CppBoundClass::BindGetterCallback(const std::string& name, 271 GetterCallback* callback) { 272 PropertyCallback* property_callback = callback == NULL ? 273 NULL : new GetterPropertyCallback(callback); 274 275 BindProperty(name, property_callback); 276} 277 278void CppBoundClass::BindProperty(const std::string& name, CppVariant* prop) { 279 PropertyCallback* property_callback = prop == NULL ? 280 NULL : new CppVariantPropertyCallback(prop); 281 282 BindProperty(name, property_callback); 283} 284 285void CppBoundClass::BindProperty(const std::string& name, 286 PropertyCallback* callback) { 287 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); 288 PropertyList::iterator old_callback = properties_.find(ident); 289 if (old_callback != properties_.end()) { 290 delete old_callback->second; 291 if (callback == NULL) { 292 properties_.erase(old_callback); 293 return; 294 } 295 } 296 297 properties_[ident] = callback; 298} 299 300bool CppBoundClass::IsMethodRegistered(const std::string& name) const { 301 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); 302 MethodList::const_iterator callback = methods_.find(ident); 303 return (callback != methods_.end()); 304} 305 306CppVariant* CppBoundClass::GetAsCppVariant() { 307 if (!self_variant_.isObject()) { 308 // Create an NPObject using our static NPClass. The first argument (a 309 // plugin's instance handle) is passed through to the allocate function 310 // directly, and we don't use it, so it's ok to be 0. 311 NPObject* np_obj = WebBindings::createObject(0, &CppNPObject::np_class_); 312 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 313 obj->bound_class = this; 314 self_variant_.Set(np_obj); 315 WebBindings::releaseObject(np_obj); // CppVariant takes the reference. 316 } 317 DCHECK(self_variant_.isObject()); 318 return &self_variant_; 319} 320 321void CppBoundClass::BindToJavascript(WebFrame* frame, 322 const std::string& classname) { 323#if WEBKIT_USING_JSC 324#error "This is not going to work anymore...but it's not clear what the solution is...or if it's still necessary." 325 JSC::JSLock lock(false); 326#endif 327 328 // BindToWindowObject will take its own reference to the NPObject, and clean 329 // up after itself. It will also (indirectly) register the object with V8, 330 // so we must remember this so we can unregister it when we're destroyed. 331 frame->bindToWindowObject(ASCIIToUTF16(classname), 332 NPVARIANT_TO_OBJECT(*GetAsCppVariant())); 333 bound_to_frame_ = true; 334} 335