Home | History | Annotate | Download | only in glue
      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 
     23 using WebKit::WebBindings;
     24 using WebKit::WebFrame;
     25 
     26 namespace {
     27 
     28 class 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 
     45 class GetterPropertyCallback : public CppBoundClass::PropertyCallback {
     46 public:
     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 
     59 private:
     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.
     68 struct 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.
    117 NPClass 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 
    176 CppBoundClass::CppBoundClass()
    177     : bound_to_frame_(false) {
    178 }
    179 
    180 CppBoundClass::~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 
    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_.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 
    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, 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 
    270 void 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 
    278 void 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 
    285 void 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 
    300 bool 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 
    306 CppVariant* 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 
    321 void 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