Home | History | Annotate | Download | only in chromium
      1 /*
      2  * Copyright (C) 2010 Google Inc. All rights reserved.
      3  * Copyright (C) 2009 Pawel Hajdan (phajdan.jr (at) chromium.org)
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 // This file contains definitions for CppBoundClass
     33 
     34 // Here's the control flow of a JS method getting forwarded to a class.
     35 // - Something calls our NPObject with a function like "Invoke".
     36 // - CppNPObject's static invoke() function forwards it to its attached
     37 //   CppBoundClass's invoke() method.
     38 // - CppBoundClass has then overridden invoke() to look up the function
     39 //   name in its internal map of methods, and then calls the appropriate
     40 //   method.
     41 
     42 #include "config.h"
     43 #include "CppBoundClass.h"
     44 
     45 #include "WebBindings.h"
     46 #include "WebFrame.h"
     47 #include "WebString.h"
     48 #include <wtf/Assertions.h>
     49 #include <wtf/OwnPtr.h>
     50 
     51 using namespace WebKit;
     52 using namespace std;
     53 
     54 class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback {
     55 public:
     56     CppVariantPropertyCallback(CppVariant* value) : m_value(value) { }
     57 
     58     virtual bool getValue(CppVariant* value)
     59     {
     60         value->set(*m_value);
     61         return true;
     62     }
     63 
     64     virtual bool setValue(const CppVariant& value)
     65     {
     66         m_value->set(value);
     67         return true;
     68     }
     69 
     70 private:
     71     CppVariant* m_value;
     72 };
     73 
     74 class GetterPropertyCallback : public CppBoundClass::PropertyCallback {
     75 public:
     76     GetterPropertyCallback(CppBoundClass::GetterCallback* callback)
     77         : m_callback(callback) { }
     78 
     79     virtual bool getValue(CppVariant* value)
     80     {
     81         m_callback->run(value);
     82         return true;
     83     }
     84 
     85     virtual bool setValue(const CppVariant& value) { return false; }
     86 
     87 private:
     88     OwnPtr<CppBoundClass::GetterCallback> m_callback;
     89 };
     90 
     91 // Our special NPObject type.  We extend an NPObject with a pointer to a
     92 // CppBoundClass, which is just a C++ interface that we forward all NPObject
     93 // callbacks to.
     94 struct CppNPObject {
     95     NPObject parent; // This must be the first field in the struct.
     96     CppBoundClass* boundClass;
     97 
     98     //
     99     // All following objects and functions are static, and just used to interface
    100     // with NPObject/NPClass.
    101     //
    102 
    103     // An NPClass associates static functions of CppNPObject with the
    104     // function pointers used by the JS runtime.
    105     static NPClass npClass;
    106 
    107     // Allocate a new NPObject with the specified class.
    108     static NPObject* allocate(NPP, NPClass*);
    109 
    110     // Free an object.
    111     static void deallocate(NPObject*);
    112 
    113     // Returns true if the C++ class associated with this NPObject exposes the
    114     // given property.  Called by the JS runtime.
    115     static bool hasProperty(NPObject*, NPIdentifier);
    116 
    117     // Returns true if the C++ class associated with this NPObject exposes the
    118     // given method.  Called by the JS runtime.
    119     static bool hasMethod(NPObject*, NPIdentifier);
    120 
    121     // If the given method is exposed by the C++ class associated with this
    122     // NPObject, invokes it with the given arguments and returns a result.  Otherwise,
    123     // returns "undefined" (in the JavaScript sense).  Called by the JS runtime.
    124     static bool invoke(NPObject*, NPIdentifier,
    125                        const NPVariant* arguments, uint32_t argumentCount,
    126                        NPVariant* result);
    127 
    128     // If the given property is exposed by the C++ class associated with this
    129     // NPObject, returns its value.  Otherwise, returns "undefined" (in the
    130     // JavaScript sense).  Called by the JS runtime.
    131     static bool getProperty(NPObject*, NPIdentifier, NPVariant* result);
    132 
    133     // If the given property is exposed by the C++ class associated with this
    134     // NPObject, sets its value.  Otherwise, does nothing. Called by the JS
    135     // runtime.
    136     static bool setProperty(NPObject*, NPIdentifier, const NPVariant* value);
    137 };
    138 
    139 // Build CppNPObject's static function pointers into an NPClass, for use
    140 // in constructing NPObjects for the C++ classes.
    141 NPClass CppNPObject::npClass = {
    142     NP_CLASS_STRUCT_VERSION,
    143     CppNPObject::allocate,
    144     CppNPObject::deallocate,
    145     /* NPInvalidateFunctionPtr */ 0,
    146     CppNPObject::hasMethod,
    147     CppNPObject::invoke,
    148     /* NPInvokeDefaultFunctionPtr */ 0,
    149     CppNPObject::hasProperty,
    150     CppNPObject::getProperty,
    151     CppNPObject::setProperty,
    152     /* NPRemovePropertyFunctionPtr */ 0
    153 };
    154 
    155 NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass)
    156 {
    157     CppNPObject* obj = new CppNPObject;
    158     // obj->parent will be initialized by the NPObject code calling this.
    159     obj->boundClass = 0;
    160     return &obj->parent;
    161 }
    162 
    163 void CppNPObject::deallocate(NPObject* npObj)
    164 {
    165     CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    166     delete obj;
    167 }
    168 
    169 bool CppNPObject::hasMethod(NPObject* npObj, NPIdentifier ident)
    170 {
    171     CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    172     return obj->boundClass->hasMethod(ident);
    173 }
    174 
    175 bool CppNPObject::hasProperty(NPObject* npObj, NPIdentifier ident)
    176 {
    177     CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    178     return obj->boundClass->hasProperty(ident);
    179 }
    180 
    181 bool CppNPObject::invoke(NPObject* npObj, NPIdentifier ident,
    182                          const NPVariant* arguments, uint32_t argumentCount,
    183                          NPVariant* result)
    184 {
    185     CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    186     return obj->boundClass->invoke(ident, arguments, argumentCount, result);
    187 }
    188 
    189 bool CppNPObject::getProperty(NPObject* npObj, NPIdentifier ident, NPVariant* result)
    190 {
    191     CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    192     return obj->boundClass->getProperty(ident, result);
    193 }
    194 
    195 bool CppNPObject::setProperty(NPObject* npObj, NPIdentifier ident, const NPVariant* value)
    196 {
    197     CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    198     return obj->boundClass->setProperty(ident, value);
    199 }
    200 
    201 CppBoundClass::~CppBoundClass()
    202 {
    203     for (MethodList::iterator i = m_methods.begin(); i != m_methods.end(); ++i)
    204         delete i->second;
    205 
    206     for (PropertyList::iterator i = m_properties.begin(); i != m_properties.end(); ++i)
    207         delete i->second;
    208 
    209     // Unregister ourselves if we were bound to a frame.
    210     if (m_boundToFrame)
    211         WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(m_selfVariant));
    212 }
    213 
    214 bool CppBoundClass::hasMethod(NPIdentifier ident) const
    215 {
    216     return m_methods.find(ident) != m_methods.end();
    217 }
    218 
    219 bool CppBoundClass::hasProperty(NPIdentifier ident) const
    220 {
    221     return m_properties.find(ident) != m_properties.end();
    222 }
    223 
    224 bool CppBoundClass::invoke(NPIdentifier ident,
    225                            const NPVariant* arguments,
    226                            size_t argumentCount,
    227                            NPVariant* result) {
    228     MethodList::const_iterator end = m_methods.end();
    229     MethodList::const_iterator method = m_methods.find(ident);
    230     Callback* callback;
    231     if (method == end) {
    232         if (!m_fallbackCallback.get()) {
    233             VOID_TO_NPVARIANT(*result);
    234             return false;
    235         }
    236         callback = m_fallbackCallback.get();
    237     } else
    238         callback = (*method).second;
    239 
    240     // Build a CppArgumentList argument vector from the NPVariants coming in.
    241     CppArgumentList cppArguments(argumentCount);
    242     for (size_t i = 0; i < argumentCount; i++)
    243         cppArguments[i].set(arguments[i]);
    244 
    245     CppVariant cppResult;
    246     callback->run(cppArguments, &cppResult);
    247 
    248     cppResult.copyToNPVariant(result);
    249     return true;
    250 }
    251 
    252 bool CppBoundClass::getProperty(NPIdentifier ident, NPVariant* result) const
    253 {
    254     PropertyList::const_iterator callback = m_properties.find(ident);
    255     if (callback == m_properties.end()) {
    256         VOID_TO_NPVARIANT(*result);
    257         return false;
    258     }
    259 
    260     CppVariant cppValue;
    261     if (!callback->second->getValue(&cppValue))
    262         return false;
    263     cppValue.copyToNPVariant(result);
    264     return true;
    265 }
    266 
    267 bool CppBoundClass::setProperty(NPIdentifier ident, const NPVariant* value)
    268 {
    269     PropertyList::iterator callback = m_properties.find(ident);
    270     if (callback == m_properties.end())
    271         return false;
    272 
    273     CppVariant cppValue;
    274     cppValue.set(*value);
    275     return (*callback).second->setValue(cppValue);
    276 }
    277 
    278 void CppBoundClass::bindCallback(const string& name, Callback* callback)
    279 {
    280     NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
    281     MethodList::iterator oldCallback = m_methods.find(ident);
    282     if (oldCallback != m_methods.end()) {
    283         delete oldCallback->second;
    284         if (!callback) {
    285             m_methods.remove(oldCallback);
    286             return;
    287         }
    288     }
    289 
    290     m_methods.set(ident, callback);
    291 }
    292 
    293 void CppBoundClass::bindGetterCallback(const string& name, GetterCallback* callback)
    294 {
    295     PropertyCallback* propertyCallback = callback ? new GetterPropertyCallback(callback) : 0;
    296     bindProperty(name, propertyCallback);
    297 }
    298 
    299 void CppBoundClass::bindProperty(const string& name, CppVariant* prop)
    300 {
    301     PropertyCallback* propertyCallback = prop ? new CppVariantPropertyCallback(prop) : 0;
    302     bindProperty(name, propertyCallback);
    303 }
    304 
    305 void CppBoundClass::bindProperty(const string& name, PropertyCallback* callback)
    306 {
    307     NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
    308     PropertyList::iterator oldCallback = m_properties.find(ident);
    309     if (oldCallback != m_properties.end()) {
    310         delete oldCallback->second;
    311         if (!callback) {
    312             m_properties.remove(oldCallback);
    313             return;
    314         }
    315     }
    316 
    317     m_properties.set(ident, callback);
    318 }
    319 
    320 bool CppBoundClass::isMethodRegistered(const string& name) const
    321 {
    322     NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
    323     MethodList::const_iterator callback = m_methods.find(ident);
    324     return callback != m_methods.end();
    325 }
    326 
    327 CppVariant* CppBoundClass::getAsCppVariant()
    328 {
    329     if (!m_selfVariant.isObject()) {
    330         // Create an NPObject using our static NPClass.  The first argument (a
    331         // plugin's instance handle) is passed through to the allocate function
    332         // directly, and we don't use it, so it's ok to be 0.
    333         NPObject* npObj = WebBindings::createObject(0, &CppNPObject::npClass);
    334         CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    335         obj->boundClass = this;
    336         m_selfVariant.set(npObj);
    337         WebBindings::releaseObject(npObj); // CppVariant takes the reference.
    338     }
    339     ASSERT(m_selfVariant.isObject());
    340     return &m_selfVariant;
    341 }
    342 
    343 void CppBoundClass::bindToJavascript(WebFrame* frame, const WebString& classname)
    344 {
    345     // BindToWindowObject will take its own reference to the NPObject, and clean
    346     // up after itself.  It will also (indirectly) register the object with V8,
    347     // so we must remember this so we can unregister it when we're destroyed.
    348     frame->bindToWindowObject(classname, NPVARIANT_TO_OBJECT(*getAsCppVariant()));
    349     m_boundToFrame = true;
    350 }
    351