// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This file contains definitions for CppBoundClass // Here's the control flow of a JS method getting forwarded to a class. // - Something calls our NPObject with a function like "Invoke". // - CppNPObject's static invoke() function forwards it to its attached // CppBoundClass's Invoke() method. // - CppBoundClass has then overridden Invoke() to look up the function // name in its internal map of methods, and then calls the appropriate // method. #include "base/compiler_specific.h" #include "base/logging.h" #include "base/string_util.h" #include "third_party/WebKit/WebKit/chromium/public/WebBindings.h" #include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/WebKit/chromium/public/WebString.h" #include "webkit/glue/cpp_bound_class.h" using WebKit::WebBindings; using WebKit::WebFrame; namespace { class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback { public: CppVariantPropertyCallback(CppVariant* value) : value_(value) { } virtual bool GetValue(CppVariant* value) { value->Set(*value_); return true; } virtual bool SetValue(const CppVariant& value) { value_->Set(value); return true; } private: CppVariant* value_; }; class GetterPropertyCallback : public CppBoundClass::PropertyCallback { public: GetterPropertyCallback(CppBoundClass::GetterCallback* callback) : callback_(callback) { } virtual bool GetValue(CppVariant* value) { callback_->Run(value); return true; } virtual bool SetValue(const CppVariant& value) { return false; } private: scoped_ptr callback_; }; } // Our special NPObject type. We extend an NPObject with a pointer to a // CppBoundClass, which is just a C++ interface that we forward all NPObject // callbacks to. struct CppNPObject { NPObject parent; // This must be the first field in the struct. CppBoundClass* bound_class; // // All following objects and functions are static, and just used to interface // with NPObject/NPClass. // // An NPClass associates static functions of CppNPObject with the // function pointers used by the JS runtime. static NPClass np_class_; // Allocate a new NPObject with the specified class. static NPObject* allocate(NPP npp, NPClass* aClass); // Free an object. static void deallocate(NPObject* obj); // Returns true if the C++ class associated with this NPObject exposes the // given property. Called by the JS runtime. static bool hasProperty(NPObject *obj, NPIdentifier ident); // Returns true if the C++ class associated with this NPObject exposes the // given method. Called by the JS runtime. static bool hasMethod(NPObject *obj, NPIdentifier ident); // If the given method is exposed by the C++ class associated with this // NPObject, invokes it with the given args and returns a result. Otherwise, // returns "undefined" (in the JavaScript sense). Called by the JS runtime. static bool invoke(NPObject *obj, NPIdentifier ident, const NPVariant *args, uint32_t arg_count, NPVariant *result); // If the given property is exposed by the C++ class associated with this // NPObject, returns its value. Otherwise, returns "undefined" (in the // JavaScript sense). Called by the JS runtime. static bool getProperty(NPObject *obj, NPIdentifier ident, NPVariant *result); // If the given property is exposed by the C++ class associated with this // NPObject, sets its value. Otherwise, does nothing. Called by the JS // runtime. static bool setProperty(NPObject *obj, NPIdentifier ident, const NPVariant *value); }; // Build CppNPObject's static function pointers into an NPClass, for use // in constructing NPObjects for the C++ classes. NPClass CppNPObject::np_class_ = { NP_CLASS_STRUCT_VERSION, CppNPObject::allocate, CppNPObject::deallocate, /* NPInvalidateFunctionPtr */ NULL, CppNPObject::hasMethod, CppNPObject::invoke, /* NPInvokeDefaultFunctionPtr */ NULL, CppNPObject::hasProperty, CppNPObject::getProperty, CppNPObject::setProperty, /* NPRemovePropertyFunctionPtr */ NULL }; /* static */ NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass) { CppNPObject* obj = new CppNPObject; // obj->parent will be initialized by the NPObject code calling this. obj->bound_class = NULL; return &obj->parent; } /* static */ void CppNPObject::deallocate(NPObject* np_obj) { CppNPObject* obj = reinterpret_cast(np_obj); delete obj; } /* static */ bool CppNPObject::hasMethod(NPObject* np_obj, NPIdentifier ident) { CppNPObject* obj = reinterpret_cast(np_obj); return obj->bound_class->HasMethod(ident); } /* static */ bool CppNPObject::hasProperty(NPObject* np_obj, NPIdentifier ident) { CppNPObject* obj = reinterpret_cast(np_obj); return obj->bound_class->HasProperty(ident); } /* static */ bool CppNPObject::invoke(NPObject* np_obj, NPIdentifier ident, const NPVariant* args, uint32_t arg_count, NPVariant* result) { CppNPObject* obj = reinterpret_cast(np_obj); return obj->bound_class->Invoke(ident, args, arg_count, result); } /* static */ bool CppNPObject::getProperty(NPObject* np_obj, NPIdentifier ident, NPVariant* result) { CppNPObject* obj = reinterpret_cast(np_obj); return obj->bound_class->GetProperty(ident, result); } /* static */ bool CppNPObject::setProperty(NPObject* np_obj, NPIdentifier ident, const NPVariant* value) { CppNPObject* obj = reinterpret_cast(np_obj); return obj->bound_class->SetProperty(ident, value); } CppBoundClass::~CppBoundClass() { for (MethodList::iterator i = methods_.begin(); i != methods_.end(); ++i) delete i->second; for (PropertyList::iterator i = properties_.begin(); i != properties_.end(); ++i) { delete i->second; } // Unregister ourselves if we were bound to a frame. if (bound_to_frame_) WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(self_variant_)); } bool CppBoundClass::HasMethod(NPIdentifier ident) const { return (methods_.find(ident) != methods_.end()); } bool CppBoundClass::HasProperty(NPIdentifier ident) const { return (properties_.find(ident) != properties_.end()); } bool CppBoundClass::Invoke(NPIdentifier ident, const NPVariant* args, size_t arg_count, NPVariant* result) { MethodList::const_iterator method = methods_.find(ident); Callback* callback; if (method == methods_.end()) { if (fallback_callback_.get()) { callback = fallback_callback_.get(); } else { VOID_TO_NPVARIANT(*result); return false; } } else { callback = (*method).second; } // Build a CppArgumentList argument vector from the NPVariants coming in. CppArgumentList cpp_args(arg_count); for (size_t i = 0; i < arg_count; i++) cpp_args[i].Set(args[i]); CppVariant cpp_result; callback->Run(cpp_args, &cpp_result); cpp_result.CopyToNPVariant(result); return true; } bool CppBoundClass::GetProperty(NPIdentifier ident, NPVariant* result) const { PropertyList::const_iterator callback = properties_.find(ident); if (callback == properties_.end()) { VOID_TO_NPVARIANT(*result); return false; } CppVariant cpp_value; if (!callback->second->GetValue(&cpp_value)) return false; cpp_value.CopyToNPVariant(result); return true; } bool CppBoundClass::SetProperty(NPIdentifier ident, const NPVariant* value) { PropertyList::iterator callback = properties_.find(ident); if (callback == properties_.end()) return false; CppVariant cpp_value; cpp_value.Set(*value); return (*callback).second->SetValue(cpp_value); } void CppBoundClass::BindCallback(const std::string& name, Callback* callback) { NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); MethodList::iterator old_callback = methods_.find(ident); if (old_callback != methods_.end()) { delete old_callback->second; if (callback == NULL) { methods_.erase(old_callback); return; } } methods_[ident] = callback; } void CppBoundClass::BindGetterCallback(const std::string& name, GetterCallback* callback) { PropertyCallback* property_callback = callback == NULL ? NULL : new GetterPropertyCallback(callback); BindProperty(name, property_callback); } void CppBoundClass::BindProperty(const std::string& name, CppVariant* prop) { PropertyCallback* property_callback = prop == NULL ? NULL : new CppVariantPropertyCallback(prop); BindProperty(name, property_callback); } void CppBoundClass::BindProperty(const std::string& name, PropertyCallback* callback) { NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); PropertyList::iterator old_callback = properties_.find(ident); if (old_callback != properties_.end()) { delete old_callback->second; if (callback == NULL) { properties_.erase(old_callback); return; } } properties_[ident] = callback; } bool CppBoundClass::IsMethodRegistered(const std::string& name) const { NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); MethodList::const_iterator callback = methods_.find(ident); return (callback != methods_.end()); } CppVariant* CppBoundClass::GetAsCppVariant() { if (!self_variant_.isObject()) { // Create an NPObject using our static NPClass. The first argument (a // plugin's instance handle) is passed through to the allocate function // directly, and we don't use it, so it's ok to be 0. NPObject* np_obj = WebBindings::createObject(0, &CppNPObject::np_class_); CppNPObject* obj = reinterpret_cast(np_obj); obj->bound_class = this; self_variant_.Set(np_obj); WebBindings::releaseObject(np_obj); // CppVariant takes the reference. } DCHECK(self_variant_.isObject()); return &self_variant_; } void CppBoundClass::BindToJavascript(WebFrame* frame, const std::wstring& classname) { #if WEBKIT_USING_JSC #error "This is not going to work anymore...but it's not clear what the solution is...or if it's still necessary." JSC::JSLock lock(false); #endif // BindToWindowObject will take its own reference to the NPObject, and clean // up after itself. It will also (indirectly) register the object with V8, // so we must remember this so we can unregister it when we're destroyed. frame->bindToWindowObject(WideToUTF16Hack(classname), NPVARIANT_TO_OBJECT(*GetAsCppVariant())); bound_to_frame_ = true; }