Home | History | Annotate | Download | only in renderer
      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