Home | History | Annotate | Download | only in runner
      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 "CppBoundClass.h"
     43 
     44 #include "TestCommon.h"
     45 #include "public/platform/WebString.h"
     46 #include "public/web/WebBindings.h"
     47 #include "public/web/WebFrame.h"
     48 #include <memory>
     49 
     50 using namespace WebKit;
     51 using namespace std;
     52 
     53 namespace WebTestRunner {
     54 
     55 namespace {
     56 
     57 class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback {
     58 public:
     59     CppVariantPropertyCallback(CppVariant* value) : m_value(value) { }
     60 
     61     virtual bool getValue(CppVariant* value)
     62     {
     63         value->set(*m_value);
     64         return true;
     65     }
     66 
     67     virtual bool setValue(const CppVariant& value)
     68     {
     69         m_value->set(value);
     70         return true;
     71     }
     72 
     73 private:
     74     CppVariant* m_value;
     75 };
     76 
     77 class GetterPropertyCallback : public CppBoundClass::PropertyCallback {
     78 public:
     79     GetterPropertyCallback(auto_ptr<CppBoundClass::GetterCallback> callback)
     80         : m_callback(callback)
     81     {
     82     }
     83 
     84     virtual bool getValue(CppVariant* value)
     85     {
     86         m_callback->run(value);
     87         return true;
     88     }
     89 
     90     virtual bool setValue(const CppVariant& value) { return false; }
     91 
     92 private:
     93     auto_ptr<CppBoundClass::GetterCallback> m_callback;
     94 };
     95 
     96 }
     97 
     98 // Our special NPObject type. We extend an NPObject with a pointer to a
     99 // CppBoundClass, which is just a C++ interface that we forward all NPObject
    100 // callbacks to.
    101 struct CppNPObject {
    102     NPObject parent; // This must be the first field in the struct.
    103     CppBoundClass* boundClass;
    104 
    105     //
    106     // All following objects and functions are static, and just used to interface
    107     // with NPObject/NPClass.
    108     //
    109 
    110     // An NPClass associates static functions of CppNPObject with the
    111     // function pointers used by the JS runtime.
    112     static NPClass npClass;
    113 
    114     // Allocate a new NPObject with the specified class.
    115     static NPObject* allocate(NPP, NPClass*);
    116 
    117     // Free an object.
    118     static void deallocate(NPObject*);
    119 
    120     // Returns true if the C++ class associated with this NPObject exposes the
    121     // given property. Called by the JS runtime.
    122     static bool hasProperty(NPObject*, NPIdentifier);
    123 
    124     // Returns true if the C++ class associated with this NPObject exposes the
    125     // given method. Called by the JS runtime.
    126     static bool hasMethod(NPObject*, NPIdentifier);
    127 
    128     // If the given method is exposed by the C++ class associated with this
    129     // NPObject, invokes it with the given arguments and returns a result. Otherwise,
    130     // returns "undefined" (in the JavaScript sense). Called by the JS runtime.
    131     static bool invoke(NPObject*, NPIdentifier,
    132                        const NPVariant* arguments, uint32_t argumentCount,
    133                        NPVariant* result);
    134 
    135     // If the given property is exposed by the C++ class associated with this
    136     // NPObject, returns its value. Otherwise, returns "undefined" (in the
    137     // JavaScript sense). Called by the JS runtime.
    138     static bool getProperty(NPObject*, NPIdentifier, NPVariant* result);
    139 
    140     // If the given property is exposed by the C++ class associated with this
    141     // NPObject, sets its value. Otherwise, does nothing. Called by the JS
    142     // runtime.
    143     static bool setProperty(NPObject*, NPIdentifier, const NPVariant* value);
    144 };
    145 
    146 // Build CppNPObject's static function pointers into an NPClass, for use
    147 // in constructing NPObjects for the C++ classes.
    148 NPClass CppNPObject::npClass = {
    149     NP_CLASS_STRUCT_VERSION,
    150     CppNPObject::allocate,
    151     CppNPObject::deallocate,
    152     /* NPInvalidateFunctionPtr */ 0,
    153     CppNPObject::hasMethod,
    154     CppNPObject::invoke,
    155     /* NPInvokeDefaultFunctionPtr */ 0,
    156     CppNPObject::hasProperty,
    157     CppNPObject::getProperty,
    158     CppNPObject::setProperty,
    159     /* NPRemovePropertyFunctionPtr */ 0
    160 };
    161 
    162 NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass)
    163 {
    164     CppNPObject* obj = new CppNPObject;
    165     // obj->parent will be initialized by the NPObject code calling this.
    166     obj->boundClass = 0;
    167     return &obj->parent;
    168 }
    169 
    170 void CppNPObject::deallocate(NPObject* npObj)
    171 {
    172     CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    173     delete obj;
    174 }
    175 
    176 bool CppNPObject::hasMethod(NPObject* npObj, NPIdentifier ident)
    177 {
    178     CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    179     return obj->boundClass->hasMethod(ident);
    180 }
    181 
    182 bool CppNPObject::hasProperty(NPObject* npObj, NPIdentifier ident)
    183 {
    184     CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    185     return obj->boundClass->hasProperty(ident);
    186 }
    187 
    188 bool CppNPObject::invoke(NPObject* npObj, NPIdentifier ident,
    189                          const NPVariant* arguments, uint32_t argumentCount,
    190                          NPVariant* result)
    191 {
    192     CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    193     return obj->boundClass->invoke(ident, arguments, argumentCount, result);
    194 }
    195 
    196 bool CppNPObject::getProperty(NPObject* npObj, NPIdentifier ident, NPVariant* result)
    197 {
    198     CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    199     return obj->boundClass->getProperty(ident, result);
    200 }
    201 
    202 bool CppNPObject::setProperty(NPObject* npObj, NPIdentifier ident, const NPVariant* value)
    203 {
    204     CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    205     return obj->boundClass->setProperty(ident, value);
    206 }
    207 
    208 CppBoundClass::~CppBoundClass()
    209 {
    210     for (MethodList::iterator i = m_methods.begin(); i != m_methods.end(); ++i)
    211         delete i->second;
    212 
    213     for (PropertyList::iterator i = m_properties.begin(); i != m_properties.end(); ++i)
    214         delete i->second;
    215 
    216     // Unregister ourselves if we were bound to a frame.
    217     if (m_boundToFrame)
    218         WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(m_selfVariant));
    219 }
    220 
    221 bool CppBoundClass::hasMethod(NPIdentifier ident) const
    222 {
    223     return m_methods.find(ident) != m_methods.end();
    224 }
    225 
    226 bool CppBoundClass::hasProperty(NPIdentifier ident) const
    227 {
    228     return m_properties.find(ident) != m_properties.end();
    229 }
    230 
    231 bool CppBoundClass::invoke(NPIdentifier ident,
    232                            const NPVariant* arguments,
    233                            size_t argumentCount,
    234                            NPVariant* result) {
    235     MethodList::const_iterator end = m_methods.end();
    236     MethodList::const_iterator method = m_methods.find(ident);
    237     Callback* callback;
    238     if (method == end) {
    239         if (!m_fallbackCallback.get()) {
    240             VOID_TO_NPVARIANT(*result);
    241             return false;
    242         }
    243         callback = m_fallbackCallback.get();
    244     } else
    245         callback = (*method).second;
    246 
    247     // Build a CppArgumentList argument vector from the NPVariants coming in.
    248     CppArgumentList cppArguments(argumentCount);
    249     for (size_t i = 0; i < argumentCount; i++)
    250         cppArguments[i].set(arguments[i]);
    251 
    252     CppVariant cppResult;
    253     callback->run(cppArguments, &cppResult);
    254 
    255     cppResult.copyToNPVariant(result);
    256     return true;
    257 }
    258 
    259 bool CppBoundClass::getProperty(NPIdentifier ident, NPVariant* result) const
    260 {
    261     PropertyList::const_iterator callback = m_properties.find(ident);
    262     if (callback == m_properties.end()) {
    263         VOID_TO_NPVARIANT(*result);
    264         return false;
    265     }
    266 
    267     CppVariant cppValue;
    268     if (!callback->second->getValue(&cppValue))
    269         return false;
    270     cppValue.copyToNPVariant(result);
    271     return true;
    272 }
    273 
    274 bool CppBoundClass::setProperty(NPIdentifier ident, const NPVariant* value)
    275 {
    276     PropertyList::iterator callback = m_properties.find(ident);
    277     if (callback == m_properties.end())
    278         return false;
    279 
    280     CppVariant cppValue;
    281     cppValue.set(*value);
    282     return (*callback).second->setValue(cppValue);
    283 }
    284 
    285 void CppBoundClass::bindCallback(const string& name, Callback* callback)
    286 {
    287     NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
    288     MethodList::iterator oldCallback = m_methods.find(ident);
    289     if (oldCallback != m_methods.end()) {
    290         delete oldCallback->second;
    291         if (!callback) {
    292             m_methods.erase(oldCallback);
    293             return;
    294         }
    295     }
    296 
    297     m_methods[ident] = callback;
    298 }
    299 
    300 void CppBoundClass::bindGetterCallback(const string& name, auto_ptr<GetterCallback> callback)
    301 {
    302     PropertyCallback* propertyCallback = callback.get() ? new GetterPropertyCallback(callback) : 0;
    303     bindProperty(name, propertyCallback);
    304 }
    305 
    306 void CppBoundClass::bindProperty(const string& name, CppVariant* prop)
    307 {
    308     PropertyCallback* propertyCallback = prop ? new CppVariantPropertyCallback(prop) : 0;
    309     bindProperty(name, propertyCallback);
    310 }
    311 
    312 void CppBoundClass::bindProperty(const string& name, PropertyCallback* callback)
    313 {
    314     NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
    315     PropertyList::iterator oldCallback = m_properties.find(ident);
    316     if (oldCallback != m_properties.end()) {
    317         delete oldCallback->second;
    318         if (!callback) {
    319             m_properties.erase(oldCallback);
    320             return;
    321         }
    322     }
    323 
    324     m_properties[ident] = callback;
    325 }
    326 
    327 bool CppBoundClass::isMethodRegistered(const string& name) const
    328 {
    329     NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
    330     MethodList::const_iterator callback = m_methods.find(ident);
    331     return callback != m_methods.end();
    332 }
    333 
    334 CppVariant* CppBoundClass::getAsCppVariant()
    335 {
    336     if (!m_selfVariant.isObject()) {
    337         // Create an NPObject using our static NPClass. The first argument (a
    338         // plugin's instance handle) is passed through to the allocate function
    339         // directly, and we don't use it, so it's ok to be 0.
    340         NPObject* npObj = WebBindings::createObject(0, &CppNPObject::npClass);
    341         CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj);
    342         obj->boundClass = this;
    343         m_selfVariant.set(npObj);
    344         WebBindings::releaseObject(npObj); // CppVariant takes the reference.
    345     }
    346     WEBKIT_ASSERT(m_selfVariant.isObject());
    347     return &m_selfVariant;
    348 }
    349 
    350 void CppBoundClass::bindToJavascript(WebFrame* frame, const WebString& classname)
    351 {
    352     // BindToWindowObject will take its own reference to the NPObject, and clean
    353     // up after itself. It will also (indirectly) register the object with V8,
    354     // so we must remember this so we can unregister it when we're destroyed.
    355     frame->bindToWindowObject(classname, NPVARIANT_TO_OBJECT(*getAsCppVariant()), 0);
    356     m_boundToFrame = true;
    357 }
    358 
    359 }
    360