| OLD | NEW |
| (Empty) |
| 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 WebKit::WebBindings; | |
| 26 using WebKit::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 | |
| OLD | NEW |