cef/libcef/jscontainer.cc

303 lines
10 KiB
C++

// Copyright (c) 2008 The Chromium Embedded Framework Authors.
// Portions 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.
#include "precompiled_libcef.h"
#include "jscontainer.h"
#include "variant_impl.h"
// Here's the control flow of a JS method getting forwarded to a class.
// - Something calls our NPObject with a function like "Invoke".
// - CefNPObject's static invoke() function forwards it to its attached
// CefJSContainer's Invoke() method.
// - CefJSContainer 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 "config.h"
// This is required for the KJS build due to an artifact of the
// npruntime_priv.h file from JavaScriptCore/bindings.
MSVC_PUSH_DISABLE_WARNING(4067)
#include "npruntime_priv.h"
MSVC_POP_WARNING()
#if USE(JSC)
MSVC_PUSH_WARNING_LEVEL(0)
#include <runtime/JSLock.h>
MSVC_POP_WARNING()
#endif
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "webkit/glue/webframe.h"
#include "webkit/glue/webview.h"
// Our special NPObject type. We extend an NPObject with a pointer to a
// CefJSContainer, which is just a C++ interface that we forward all NPObject
// callbacks to.
struct CefNPObject {
NPObject parent; // This must be the first field in the struct.
CefJSContainer* container;
WebFrame* webframe;
//
// All following objects and functions are static, and just used to interface
// with NPObject/NPClass.
//
// An NPClass associates static functions of CefNPObject 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 CefNPObject's static function pointers into an NPClass, for use
// in constructing NPObjects for the C++ classes.
NPClass CefNPObject::np_class_ = {
NP_CLASS_STRUCT_VERSION,
CefNPObject::allocate,
CefNPObject::deallocate,
/* NPInvalidateFunctionPtr */ NULL,
CefNPObject::hasMethod,
CefNPObject::invoke,
/* NPInvokeDefaultFunctionPtr */ NULL,
CefNPObject::hasProperty,
CefNPObject::getProperty,
CefNPObject::setProperty,
/* NPRemovePropertyFunctionPtr */ NULL
};
/* static */ NPObject* CefNPObject::allocate(NPP npp, NPClass* aClass) {
CefNPObject* obj = new CefNPObject;
// obj->parent will be initialized by the NPObject code calling this.
obj->container = NULL;
obj->webframe = NULL;
return &obj->parent;
}
/* static */ void CefNPObject::deallocate(NPObject* np_obj) {
CefNPObject* obj = reinterpret_cast<CefNPObject*>(np_obj);
delete obj;
}
/* static */ bool CefNPObject::hasMethod(NPObject* np_obj,
NPIdentifier ident) {
CefNPObject* obj = reinterpret_cast<CefNPObject*>(np_obj);
return obj->container->HasMethod(ident);
}
/* static */ bool CefNPObject::hasProperty(NPObject* np_obj,
NPIdentifier ident) {
CefNPObject* obj = reinterpret_cast<CefNPObject*>(np_obj);
return obj->container->HasProperty(ident);
}
/* static */ bool CefNPObject::invoke(NPObject* np_obj, NPIdentifier ident,
const NPVariant* args, uint32_t arg_count,
NPVariant* result) {
CefNPObject* obj = reinterpret_cast<CefNPObject*>(np_obj);
return obj->container->Invoke(ident, obj->webframe, args, arg_count, result);
}
/* static */ bool CefNPObject::getProperty(NPObject* np_obj,
NPIdentifier ident,
NPVariant* result) {
CefNPObject* obj = reinterpret_cast<CefNPObject*>(np_obj);
return obj->container->GetProperty(ident, obj->webframe, result);
}
/* static */ bool CefNPObject::setProperty(NPObject* np_obj,
NPIdentifier ident,
const NPVariant* value) {
CefNPObject* obj = reinterpret_cast<CefNPObject*>(np_obj);
return obj->container->SetProperty(ident, obj->webframe, value);
}
CefJSContainer::CefJSContainer(CefBrowser* browser,
CefRefPtr<CefJSHandler> handler)
: browser_(browser), handler_(handler)
{
DCHECK(browser_ != NULL);
DCHECK(handler_.get() != NULL);
}
CefJSContainer::~CefJSContainer()
{
// Unregister objects we created and bound to a frame.
for (BoundObjectList::iterator i = bound_objects_.begin();
i != bound_objects_.end(); ++i) {
#if USE(V8)
_NPN_UnregisterObject(*i);
#endif
NPN_ReleaseObject(*i);
}
}
bool CefJSContainer::HasMethod(NPIdentifier ident)
{
std::wstring name = UTF8ToWide(NPN_UTF8FromIdentifier(ident));
return handler_->HasMethod(browser_, name);
}
bool CefJSContainer::HasProperty(NPIdentifier ident)
{
std::wstring name = UTF8ToWide(NPN_UTF8FromIdentifier(ident));
return handler_->HasProperty(browser_, name);
}
bool CefJSContainer::Invoke(NPIdentifier ident,
WebFrame* frame,
const NPVariant* args,
size_t arg_count,
NPVariant* result)
{
std::wstring name = UTF8ToWide(NPN_UTF8FromIdentifier(ident));
// Build a VariantVector argument vector from the NPVariants coming in.
CefJSHandler::VariantVector cef_args(arg_count);
for (size_t i = 0; i < arg_count; i++) {
cef_args[i] = new CefVariantImpl(frame);
static_cast<CefVariantImpl*>(cef_args[i].get())->Set(args[i]);
}
CefRefPtr<CefVariant> cef_retval(new CefVariantImpl(frame));
// Execute the handler method
bool rv = handler_->ExecuteMethod(browser_, name, cef_args, cef_retval);
if(rv) {
// Assign the return value
static_cast<CefVariantImpl*>(cef_retval.get())->CopyToNPVariant(result);
}
return rv;
}
bool CefJSContainer::GetProperty(NPIdentifier ident,
WebFrame* frame,
NPVariant* result)
{
CefRefPtr<CefVariant> cef_result(new CefVariantImpl(frame));
std::wstring name = UTF8ToWide(NPN_UTF8FromIdentifier(ident));
// Execute the handler method
bool rv = handler_->GetProperty(browser_, name, cef_result);
if(rv) {
// Assign the return value
static_cast<CefVariantImpl*>(cef_result.get())->CopyToNPVariant(result);
}
return rv;
}
bool CefJSContainer::SetProperty(NPIdentifier ident,
WebFrame* frame,
const NPVariant* value)
{
std::wstring name = UTF8ToWide(NPN_UTF8FromIdentifier(ident));
// Assign the input value
CefRefPtr<CefVariant> cef_value(new CefVariantImpl(frame));
static_cast<CefVariantImpl*>(cef_value.get())->Set(*value);
// Execute the handler method
return handler_->SetProperty(browser_, name, cef_value);
}
// Check if the specified frame exists by comparing to all frames currently
// attached to the view.
static bool FrameExists(WebView* view, WebFrame* frame) {
WebFrame* main_frame = view->GetMainFrame();
WebFrame* it = main_frame;
do {
if (it == frame)
return true;
it = view->GetNextFrameAfter(it, true);
} while (it != main_frame);
return false;
}
void CefJSContainer::BindToJavascript(WebFrame* frame,
const std::wstring& classname) {
#if USE(JSC)
JSC::JSLock lock(false);
#endif
NPObject* np_obj = NULL;
CefNPObject* obj = NULL;
Lock();
WebView* view = frame->GetView();
BoundObjectList::iterator it = bound_objects_.begin();
for(; it != bound_objects_.end(); ) {
obj = reinterpret_cast<CefNPObject*>(*it);
if(obj->webframe == frame) {
// An NPObject is already bound to this particular frame.
np_obj = *it;
} else if(!FrameExists(view, obj->webframe)) {
// Remove bindings to non-existent frames.
#if USE(V8)
_NPN_UnregisterObject(*it);
#endif
NPN_ReleaseObject(*it);
it = bound_objects_.erase(it);
continue;
}
++it;
}
Unlock();
if(!np_obj) {
// 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.
np_obj = NPN_CreateObject(0, &CefNPObject::np_class_);
obj = reinterpret_cast<CefNPObject*>(np_obj);
obj->container = this;
obj->webframe = frame;
Lock();
bound_objects_.push_back(np_obj);
Unlock();
}
// BindToWindowObject will (indirectly) retain the np_object. We save it
// so we can release it when we're destroyed.
frame->BindToWindowObject(classname, np_obj);
}