1 // Copyright (c) 2012 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 "webkit/renderer/cpp_bound_class.h" 16 17 #include "base/compiler_specific.h" 18 #include "base/logging.h" 19 #include "base/stl_util.h" 20 #include "base/strings/utf_string_conversions.h" 21 #include "third_party/WebKit/public/web/WebBindings.h" 22 #include "third_party/WebKit/public/web/WebFrame.h" 23 #include "third_party/WebKit/public/platform/WebString.h" 24 25 using blink::WebBindings; 26 using blink::WebFrame; 27 28 namespace webkit_glue { 29 30 namespace { 31 32 class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback { 33 public: 34 CppVariantPropertyCallback(CppVariant* value) : value_(value) { } 35 36 virtual bool GetValue(CppVariant* value) OVERRIDE { 37 value->Set(*value_); 38 return true; 39 } 40 virtual bool SetValue(const CppVariant& value) OVERRIDE { 41 value_->Set(value); 42 return true; 43 } 44 45 private: 46 CppVariant* value_; 47 }; 48 49 class GetterPropertyCallback : public CppBoundClass::PropertyCallback { 50 public: 51 GetterPropertyCallback(const CppBoundClass::GetterCallback& callback) 52 : callback_(callback) { } 53 54 virtual bool GetValue(CppVariant* value) OVERRIDE { 55 callback_.Run(value); 56 return true; 57 } 58 59 virtual bool SetValue(const CppVariant& value) OVERRIDE { 60 return false; 61 } 62 63 private: 64 CppBoundClass::GetterCallback callback_; 65 }; 66 67 } 68 69 // Our special NPObject type. We extend an NPObject with a pointer to a 70 // CppBoundClass, which is just a C++ interface that we forward all NPObject 71 // callbacks to. 72 struct CppNPObject { 73 NPObject parent; // This must be the first field in the struct. 74 CppBoundClass* bound_class; 75 76 // 77 // All following objects and functions are static, and just used to interface 78 // with NPObject/NPClass. 79 // 80 81 // An NPClass associates static functions of CppNPObject with the 82 // function pointers used by the JS runtime. 83 static NPClass np_class_; 84 85 // Allocate a new NPObject with the specified class. 86 static NPObject* allocate(NPP npp, NPClass* aClass); 87 88 // Free an object. 89 static void deallocate(NPObject* obj); 90 91 // Returns true if the C++ class associated with this NPObject exposes the 92 // given property. Called by the JS runtime. 93 static bool hasProperty(NPObject *obj, NPIdentifier ident); 94 95 // Returns true if the C++ class associated with this NPObject exposes the 96 // given method. Called by the JS runtime. 97 static bool hasMethod(NPObject *obj, NPIdentifier ident); 98 99 // If the given method is exposed by the C++ class associated with this 100 // NPObject, invokes it with the given args and returns a result. Otherwise, 101 // returns "undefined" (in the JavaScript sense). Called by the JS runtime. 102 static bool invoke(NPObject *obj, NPIdentifier ident, 103 const NPVariant *args, uint32_t arg_count, 104 NPVariant *result); 105 106 // If the given property is exposed by the C++ class associated with this 107 // NPObject, returns its value. Otherwise, returns "undefined" (in the 108 // JavaScript sense). Called by the JS runtime. 109 static bool getProperty(NPObject *obj, NPIdentifier ident, 110 NPVariant *result); 111 112 // If the given property is exposed by the C++ class associated with this 113 // NPObject, sets its value. Otherwise, does nothing. Called by the JS 114 // runtime. 115 static bool setProperty(NPObject *obj, NPIdentifier ident, 116 const NPVariant *value); 117 }; 118 119 // Build CppNPObject's static function pointers into an NPClass, for use 120 // in constructing NPObjects for the C++ classes. 121 NPClass CppNPObject::np_class_ = { 122 NP_CLASS_STRUCT_VERSION, 123 CppNPObject::allocate, 124 CppNPObject::deallocate, 125 /* NPInvalidateFunctionPtr */ NULL, 126 CppNPObject::hasMethod, 127 CppNPObject::invoke, 128 /* NPInvokeDefaultFunctionPtr */ NULL, 129 CppNPObject::hasProperty, 130 CppNPObject::getProperty, 131 CppNPObject::setProperty, 132 /* NPRemovePropertyFunctionPtr */ NULL 133 }; 134 135 /* static */ NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass) { 136 CppNPObject* obj = new CppNPObject; 137 // obj->parent will be initialized by the NPObject code calling this. 138 obj->bound_class = NULL; 139 return &obj->parent; 140 } 141 142 /* static */ void CppNPObject::deallocate(NPObject* np_obj) { 143 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 144 delete obj; 145 } 146 147 /* static */ bool CppNPObject::hasMethod(NPObject* np_obj, 148 NPIdentifier ident) { 149 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 150 return obj->bound_class->HasMethod(ident); 151 } 152 153 /* static */ bool CppNPObject::hasProperty(NPObject* np_obj, 154 NPIdentifier ident) { 155 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 156 return obj->bound_class->HasProperty(ident); 157 } 158 159 /* static */ bool CppNPObject::invoke(NPObject* np_obj, NPIdentifier ident, 160 const NPVariant* args, uint32_t arg_count, 161 NPVariant* result) { 162 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 163 return obj->bound_class->Invoke(ident, args, arg_count, result); 164 } 165 166 /* static */ bool CppNPObject::getProperty(NPObject* np_obj, 167 NPIdentifier ident, 168 NPVariant* result) { 169 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 170 return obj->bound_class->GetProperty(ident, result); 171 } 172 173 /* static */ bool CppNPObject::setProperty(NPObject* np_obj, 174 NPIdentifier ident, 175 const NPVariant* value) { 176 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 177 return obj->bound_class->SetProperty(ident, value); 178 } 179 180 CppBoundClass::CppBoundClass() : bound_to_frame_(false), npp_(new NPP_t) { 181 WebBindings::registerObjectOwner(npp_.get()); 182 } 183 184 CppBoundClass::~CppBoundClass() { 185 STLDeleteValues(&properties_); 186 187 // TODO(wez): Remove once crrev.com/14019005 lands. 188 if (bound_to_frame_) 189 WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(self_variant_)); 190 191 WebBindings::unregisterObjectOwner(npp_.get()); 192 } 193 194 bool CppBoundClass::HasMethod(NPIdentifier ident) const { 195 return (methods_.find(ident) != methods_.end()); 196 } 197 198 bool CppBoundClass::HasProperty(NPIdentifier ident) const { 199 return (properties_.find(ident) != properties_.end()); 200 } 201 202 bool 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_.is_null()) { 210 callback = fallback_callback_; 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 231 bool 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 245 bool 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 256 void CppBoundClass::BindCallback(const std::string& name, 257 const Callback& callback) { 258 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); 259 if (callback.is_null()) { 260 methods_.erase(ident); 261 return; 262 } 263 264 methods_[ident] = callback; 265 } 266 267 void CppBoundClass::BindGetterCallback(const std::string& name, 268 const GetterCallback& callback) { 269 PropertyCallback* property_callback = callback.is_null() ? 270 NULL : new GetterPropertyCallback(callback); 271 272 BindProperty(name, property_callback); 273 } 274 275 void CppBoundClass::BindProperty(const std::string& name, CppVariant* prop) { 276 PropertyCallback* property_callback = prop == NULL ? 277 NULL : new CppVariantPropertyCallback(prop); 278 279 BindProperty(name, property_callback); 280 } 281 282 void CppBoundClass::BindProperty(const std::string& name, 283 PropertyCallback* callback) { 284 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); 285 PropertyList::iterator old_callback = properties_.find(ident); 286 if (old_callback != properties_.end()) { 287 delete old_callback->second; 288 if (callback == NULL) { 289 properties_.erase(old_callback); 290 return; 291 } 292 } 293 294 properties_[ident] = callback; 295 } 296 297 bool CppBoundClass::IsMethodRegistered(const std::string& name) const { 298 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); 299 MethodList::const_iterator callback = methods_.find(ident); 300 return (callback != methods_.end()); 301 } 302 303 CppVariant* CppBoundClass::GetAsCppVariant() { 304 if (!self_variant_.isObject()) { 305 // Create an NPObject using our static NPClass. The first argument has type 306 // NPP, but is only used to track object ownership, so passing this is fine. 307 NPObject* np_obj = WebBindings::createObject( 308 npp_.get(), &CppNPObject::np_class_); 309 CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); 310 obj->bound_class = this; 311 self_variant_.Set(np_obj); 312 WebBindings::releaseObject(np_obj); // CppVariant takes the reference. 313 } 314 DCHECK(self_variant_.isObject()); 315 return &self_variant_; 316 } 317 318 void CppBoundClass::BindToJavascript(WebFrame* frame, 319 const std::string& classname) { 320 // BindToWindowObject will take its own reference to the NPObject, and clean 321 // up after itself. It will also (indirectly) register the object with V8, 322 // against an owner pointer we supply, so we must register that as an owner, 323 // and unregister when we teardown. 324 frame->bindToWindowObject(ASCIIToUTF16(classname), 325 NPVARIANT_TO_OBJECT(*GetAsCppVariant())); 326 bound_to_frame_ = true; 327 } 328 329 } // namespace webkit_glue 330