blob: bafbada0ddd44d9ba23ee561e9bd9cb390bcee08 [file] [log] [blame]
// Copyright (c) 2012 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 "content/renderer/pepper/ppb_var_deprecated_impl.h"
#include <limits>
#include "content/renderer/pepper/host_globals.h"
#include "content/renderer/pepper/npapi_glue.h"
#include "content/renderer/pepper/npobject_var.h"
#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
#include "content/renderer/pepper/plugin_module.h"
#include "content/renderer/pepper/plugin_object.h"
#include "ppapi/c/dev/ppb_var_deprecated.h"
#include "ppapi/c/ppb_var.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/shared_impl/ppb_var_shared.h"
#include "third_party/WebKit/public/web/WebBindings.h"
#include "third_party/WebKit/public/web/WebScopedUserGesture.h"
using ppapi::NPObjectVar;
using ppapi::PpapiGlobals;
using ppapi::StringVar;
using ppapi::Var;
using blink::WebBindings;
namespace content {
namespace {
const char kInvalidObjectException[] = "Error: Invalid object";
const char kInvalidPropertyException[] = "Error: Invalid property";
const char kInvalidValueException[] = "Error: Invalid value";
const char kUnableToGetPropertyException[] = "Error: Unable to get property";
const char kUnableToSetPropertyException[] = "Error: Unable to set property";
const char kUnableToRemovePropertyException[] =
"Error: Unable to remove property";
const char kUnableToGetAllPropertiesException[] =
"Error: Unable to get all properties";
const char kUnableToCallMethodException[] = "Error: Unable to call method";
const char kUnableToConstructException[] = "Error: Unable to construct";
// ---------------------------------------------------------------------------
// Utilities
// Converts the given PP_Var to an NPVariant, returning true on success.
// False means that the given variant is invalid. In this case, the result
// NPVariant will be set to a void one.
//
// The contents of the PP_Var will NOT be copied, so you need to ensure that
// the PP_Var remains valid while the resultant NPVariant is in use.
bool PPVarToNPVariantNoCopy(PP_Var var, NPVariant* result) {
switch (var.type) {
case PP_VARTYPE_UNDEFINED:
VOID_TO_NPVARIANT(*result);
break;
case PP_VARTYPE_NULL:
NULL_TO_NPVARIANT(*result);
break;
case PP_VARTYPE_BOOL:
BOOLEAN_TO_NPVARIANT(var.value.as_bool, *result);
break;
case PP_VARTYPE_INT32:
INT32_TO_NPVARIANT(var.value.as_int, *result);
break;
case PP_VARTYPE_DOUBLE:
DOUBLE_TO_NPVARIANT(var.value.as_double, *result);
break;
case PP_VARTYPE_STRING: {
StringVar* string = StringVar::FromPPVar(var);
if (!string) {
VOID_TO_NPVARIANT(*result);
return false;
}
const std::string& value = string->value();
STRINGN_TO_NPVARIANT(value.c_str(), value.size(), *result);
break;
}
case PP_VARTYPE_OBJECT: {
scoped_refptr<NPObjectVar> object(NPObjectVar::FromPPVar(var));
if (!object.get()) {
VOID_TO_NPVARIANT(*result);
return false;
}
OBJECT_TO_NPVARIANT(object->np_object(), *result);
break;
}
default:
VOID_TO_NPVARIANT(*result);
return false;
}
return true;
}
// ObjectAccessorTryCatch ------------------------------------------------------
// Automatically sets up a TryCatch for accessing the object identified by the
// given PP_Var. The module from the object will be used for the exception
// strings generated by the TryCatch.
//
// This will automatically retrieve the ObjectVar from the object and throw
// an exception if it's invalid. At the end of construction, if there is no
// exception, you know that there is no previously set exception, that the
// object passed in is valid and ready to use (via the object() getter), and
// that the TryCatch's pp_module() getter is also set up properly and ready to
// use.
class ObjectAccessorTryCatch : public TryCatch {
public:
ObjectAccessorTryCatch(PP_Var object, PP_Var* exception)
: TryCatch(exception), object_(NPObjectVar::FromPPVar(object)) {
if (!object_.get()) {
SetException(kInvalidObjectException);
}
}
NPObjectVar* object() { return object_.get(); }
PepperPluginInstanceImpl* GetPluginInstance() {
return HostGlobals::Get()->GetInstance(object()->pp_instance());
}
protected:
scoped_refptr<NPObjectVar> object_;
DISALLOW_COPY_AND_ASSIGN(ObjectAccessorTryCatch);
};
// ObjectAccessiorWithIdentifierTryCatch ---------------------------------------
// Automatically sets up a TryCatch for accessing the identifier on the given
// object. This just extends ObjectAccessorTryCatch to additionally convert
// the given identifier to an NPIdentifier and validate it, throwing an
// exception if it's invalid.
//
// At the end of construction, if there is no exception, you know that there is
// no previously set exception, that the object passed in is valid and ready to
// use (via the object() getter), that the identifier is valid and ready to
// use (via the identifier() getter), and that the TryCatch's pp_module() getter
// is also set up properly and ready to use.
class ObjectAccessorWithIdentifierTryCatch : public ObjectAccessorTryCatch {
public:
ObjectAccessorWithIdentifierTryCatch(PP_Var object,
PP_Var identifier,
PP_Var* exception)
: ObjectAccessorTryCatch(object, exception), identifier_(0) {
if (!has_exception()) {
identifier_ = PPVarToNPIdentifier(identifier);
if (!identifier_)
SetException(kInvalidPropertyException);
}
}
NPIdentifier identifier() const { return identifier_; }
private:
NPIdentifier identifier_;
DISALLOW_COPY_AND_ASSIGN(ObjectAccessorWithIdentifierTryCatch);
};
PP_Bool HasProperty(PP_Var var, PP_Var name, PP_Var* exception) {
ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception);
if (accessor.has_exception())
return PP_FALSE;
return PP_FromBool(WebBindings::hasProperty(
NULL, accessor.object()->np_object(), accessor.identifier()));
}
bool HasPropertyDeprecated(PP_Var var, PP_Var name, PP_Var* exception) {
return PP_ToBool(HasProperty(var, name, exception));
}
bool HasMethodDeprecated(PP_Var var, PP_Var name, PP_Var* exception) {
ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception);
if (accessor.has_exception())
return false;
return WebBindings::hasMethod(
NULL, accessor.object()->np_object(), accessor.identifier());
}
PP_Var GetProperty(PP_Var var, PP_Var name, PP_Var* exception) {
ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception);
if (accessor.has_exception())
return PP_MakeUndefined();
NPVariant result;
if (!WebBindings::getProperty(NULL,
accessor.object()->np_object(),
accessor.identifier(),
&result)) {
// An exception may have been raised.
accessor.SetException(kUnableToGetPropertyException);
return PP_MakeUndefined();
}
PP_Var ret = NPVariantToPPVar(accessor.GetPluginInstance(), &result);
WebBindings::releaseVariantValue(&result);
return ret;
}
void EnumerateProperties(PP_Var var,
uint32_t* property_count,
PP_Var** properties,
PP_Var* exception) {
*properties = NULL;
*property_count = 0;
ObjectAccessorTryCatch accessor(var, exception);
if (accessor.has_exception())
return;
NPIdentifier* identifiers = NULL;
uint32_t count = 0;
if (!WebBindings::enumerate(
NULL, accessor.object()->np_object(), &identifiers, &count)) {
accessor.SetException(kUnableToGetAllPropertiesException);
return;
}
if (count == 0)
return;
*property_count = count;
*properties = static_cast<PP_Var*>(malloc(sizeof(PP_Var) * count));
for (uint32_t i = 0; i < count; ++i) {
(*properties)[i] = NPIdentifierToPPVar(identifiers[i]);
}
free(identifiers);
}
void SetPropertyDeprecated(PP_Var var,
PP_Var name,
PP_Var value,
PP_Var* exception) {
ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception);
if (accessor.has_exception())
return;
NPVariant variant;
if (!PPVarToNPVariantNoCopy(value, &variant)) {
accessor.SetException(kInvalidValueException);
return;
}
if (!WebBindings::setProperty(NULL,
accessor.object()->np_object(),
accessor.identifier(),
&variant))
accessor.SetException(kUnableToSetPropertyException);
}
void DeletePropertyDeprecated(PP_Var var, PP_Var name, PP_Var* exception) {
ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception);
if (accessor.has_exception())
return;
if (!WebBindings::removeProperty(
NULL, accessor.object()->np_object(), accessor.identifier()))
accessor.SetException(kUnableToRemovePropertyException);
}
PP_Var InternalCallDeprecated(ObjectAccessorTryCatch* accessor,
PP_Var method_name,
uint32_t argc,
PP_Var* argv,
PP_Var* exception) {
NPIdentifier identifier;
if (method_name.type == PP_VARTYPE_UNDEFINED) {
identifier = NULL;
} else if (method_name.type == PP_VARTYPE_STRING) {
// Specifically allow only string functions to be called.
identifier = PPVarToNPIdentifier(method_name);
if (!identifier) {
accessor->SetException(kInvalidPropertyException);
return PP_MakeUndefined();
}
} else {
accessor->SetException(kInvalidPropertyException);
return PP_MakeUndefined();
}
scoped_ptr<NPVariant[]> args;
if (argc) {
args.reset(new NPVariant[argc]);
for (uint32_t i = 0; i < argc; ++i) {
if (!PPVarToNPVariantNoCopy(argv[i], &args[i])) {
// This argument was invalid, throw an exception & give up.
accessor->SetException(kInvalidValueException);
return PP_MakeUndefined();
}
}
}
bool ok;
NPVariant result;
if (identifier) {
ok = WebBindings::invoke(NULL,
accessor->object()->np_object(),
identifier,
args.get(),
argc,
&result);
} else {
ok = WebBindings::invokeDefault(
NULL, accessor->object()->np_object(), args.get(), argc, &result);
}
if (!ok) {
// An exception may have been raised.
accessor->SetException(kUnableToCallMethodException);
return PP_MakeUndefined();
}
PP_Var ret = NPVariantToPPVar(accessor->GetPluginInstance(), &result);
WebBindings::releaseVariantValue(&result);
return ret;
}
PP_Var CallDeprecated(PP_Var var,
PP_Var method_name,
uint32_t argc,
PP_Var* argv,
PP_Var* exception) {
ObjectAccessorTryCatch accessor(var, exception);
if (accessor.has_exception())
return PP_MakeUndefined();
PepperPluginInstanceImpl* plugin = accessor.GetPluginInstance();
if (plugin && plugin->IsProcessingUserGesture()) {
blink::WebScopedUserGesture user_gesture(plugin->CurrentUserGestureToken());
return InternalCallDeprecated(
&accessor, method_name, argc, argv, exception);
}
return InternalCallDeprecated(&accessor, method_name, argc, argv, exception);
}
PP_Var Construct(PP_Var var, uint32_t argc, PP_Var* argv, PP_Var* exception) {
ObjectAccessorTryCatch accessor(var, exception);
if (accessor.has_exception())
return PP_MakeUndefined();
scoped_ptr<NPVariant[]> args;
if (argc) {
args.reset(new NPVariant[argc]);
for (uint32_t i = 0; i < argc; ++i) {
if (!PPVarToNPVariantNoCopy(argv[i], &args[i])) {
// This argument was invalid, throw an exception & give up.
accessor.SetException(kInvalidValueException);
return PP_MakeUndefined();
}
}
}
NPVariant result;
if (!WebBindings::construct(
NULL, accessor.object()->np_object(), args.get(), argc, &result)) {
// An exception may have been raised.
accessor.SetException(kUnableToConstructException);
return PP_MakeUndefined();
}
PP_Var ret = NPVariantToPPVar(accessor.GetPluginInstance(), &result);
WebBindings::releaseVariantValue(&result);
return ret;
}
bool IsInstanceOfDeprecated(PP_Var var,
const PPP_Class_Deprecated* ppp_class,
void** ppp_class_data) {
scoped_refptr<NPObjectVar> object(NPObjectVar::FromPPVar(var));
if (!object.get())
return false; // Not an object at all.
return PluginObject::IsInstanceOf(
object->np_object(), ppp_class, ppp_class_data);
}
PP_Var CreateObjectDeprecated(PP_Instance pp_instance,
const PPP_Class_Deprecated* ppp_class,
void* ppp_class_data) {
PepperPluginInstanceImpl* instance =
HostGlobals::Get()->GetInstance(pp_instance);
if (!instance) {
DLOG(ERROR) << "Create object passed an invalid instance.";
return PP_MakeNull();
}
return PluginObject::Create(instance, ppp_class, ppp_class_data);
}
PP_Var CreateObjectWithModuleDeprecated(PP_Module pp_module,
const PPP_Class_Deprecated* ppp_class,
void* ppp_class_data) {
PluginModule* module = HostGlobals::Get()->GetModule(pp_module);
if (!module)
return PP_MakeNull();
return PluginObject::Create(
module->GetSomeInstance(), ppp_class, ppp_class_data);
}
} // namespace
// static
const PPB_Var_Deprecated* PPB_Var_Deprecated_Impl::GetVarDeprecatedInterface() {
static const PPB_Var_Deprecated var_deprecated_interface = {
ppapi::PPB_Var_Shared::GetVarInterface1_0()->AddRef,
ppapi::PPB_Var_Shared::GetVarInterface1_0()->Release,
ppapi::PPB_Var_Shared::GetVarInterface1_0()->VarFromUtf8,
ppapi::PPB_Var_Shared::GetVarInterface1_0()->VarToUtf8,
&HasPropertyDeprecated,
&HasMethodDeprecated,
&GetProperty,
&EnumerateProperties,
&SetPropertyDeprecated,
&DeletePropertyDeprecated,
&CallDeprecated,
&Construct,
&IsInstanceOfDeprecated,
&CreateObjectDeprecated,
&CreateObjectWithModuleDeprecated, };
return &var_deprecated_interface;
}
} // namespace content