// Copyright (c) 2011 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 "ppapi/proxy/ppb_var_deprecated_proxy.h" #include // For malloc #include "base/logging.h" #include "base/message_loop.h" #include "base/task.h" #include "ppapi/c/dev/ppb_var_deprecated.h" #include "ppapi/c/pp_var.h" #include "ppapi/c/ppb_core.h" #include "ppapi/proxy/host_dispatcher.h" #include "ppapi/proxy/plugin_dispatcher.h" #include "ppapi/proxy/plugin_resource_tracker.h" #include "ppapi/proxy/plugin_var_tracker.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/ppp_class_proxy.h" #include "ppapi/proxy/serialized_var.h" #include "ppapi/shared_impl/var.h" namespace ppapi { namespace proxy { namespace { // Used to do get the set-up information for calling a var object. If the // exception is set, returns NULL. Otherwise, computes the dispatcher for the // given var object. If the var is not a valid object, returns NULL and sets // the exception. PluginDispatcher* CheckExceptionAndGetDispatcher(const PP_Var& object, PP_Var* exception) { // If an exception is already set, we don't need to do anything, just return // an error to the caller. if (exception && exception->type != PP_VARTYPE_UNDEFINED) return NULL; if (object.type == PP_VARTYPE_OBJECT) { // Get the dispatcher for the object. PluginDispatcher* dispatcher = PluginResourceTracker::GetInstance()-> var_tracker().DispatcherForPluginObject(object); if (dispatcher) return dispatcher; } // The object is invalid. This means we can't figure out which dispatcher // to use, which is OK because the call will fail anyway. Set the exception. if (exception) { *exception = StringVar::StringToPPVar(0, std::string("Attempting to use an invalid object")); } return NULL; } // PPP_Var_Deprecated plugin --------------------------------------------------- void AddRefVar(PP_Var var) { PluginResourceTracker::GetInstance()->var_tracker().AddRefVar(var); } void ReleaseVar(PP_Var var) { PluginResourceTracker::GetInstance()->var_tracker().ReleaseVar(var); } PP_Var VarFromUtf8(PP_Module module, const char* data, uint32_t len) { return StringVar::StringToPPVar(module, data, len); } const char* VarToUtf8(PP_Var var, uint32_t* len) { StringVar* str = StringVar::FromPPVar(var); if (str) { *len = static_cast(str->value().size()); return str->value().c_str(); } *len = 0; return NULL; } bool HasProperty(PP_Var var, PP_Var name, PP_Var* exception) { Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, exception); if (!dispatcher) return false; ReceiveSerializedException se(dispatcher, exception); PP_Bool result = PP_FALSE; if (!se.IsThrown()) { dispatcher->Send(new PpapiHostMsg_PPBVar_HasProperty( INTERFACE_ID_PPB_VAR_DEPRECATED, SerializedVarSendInput(dispatcher, var), SerializedVarSendInput(dispatcher, name), &se, &result)); } return PP_ToBool(result); } bool HasMethod(PP_Var var, PP_Var name, PP_Var* exception) { Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, exception); if (!dispatcher) return false; ReceiveSerializedException se(dispatcher, exception); PP_Bool result = PP_FALSE; if (!se.IsThrown()) { dispatcher->Send(new PpapiHostMsg_PPBVar_HasMethodDeprecated( INTERFACE_ID_PPB_VAR_DEPRECATED, SerializedVarSendInput(dispatcher, var), SerializedVarSendInput(dispatcher, name), &se, &result)); } return PP_ToBool(result); } PP_Var GetProperty(PP_Var var, PP_Var name, PP_Var* exception) { Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, exception); if (!dispatcher) return PP_MakeUndefined(); ReceiveSerializedException se(dispatcher, exception); ReceiveSerializedVarReturnValue result; if (!se.IsThrown()) { dispatcher->Send(new PpapiHostMsg_PPBVar_GetProperty( INTERFACE_ID_PPB_VAR_DEPRECATED, SerializedVarSendInput(dispatcher, var), SerializedVarSendInput(dispatcher, name), &se, &result)); } return result.Return(dispatcher); } void EnumerateProperties(PP_Var var, uint32_t* property_count, PP_Var** properties, PP_Var* exception) { Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, exception); if (!dispatcher) { *property_count = 0; *properties = NULL; return; } ReceiveSerializedVarVectorOutParam out_vector(dispatcher, property_count, properties); ReceiveSerializedException se(dispatcher, exception); if (!se.IsThrown()) { dispatcher->Send(new PpapiHostMsg_PPBVar_EnumerateProperties( INTERFACE_ID_PPB_VAR_DEPRECATED, SerializedVarSendInput(dispatcher, var), out_vector.OutParam(), &se)); } } void SetProperty(PP_Var var, PP_Var name, PP_Var value, PP_Var* exception) { Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, exception); if (!dispatcher) return; ReceiveSerializedException se(dispatcher, exception); if (!se.IsThrown()) { dispatcher->Send(new PpapiHostMsg_PPBVar_SetPropertyDeprecated( INTERFACE_ID_PPB_VAR_DEPRECATED, SerializedVarSendInput(dispatcher, var), SerializedVarSendInput(dispatcher, name), SerializedVarSendInput(dispatcher, value), &se)); } } void RemoveProperty(PP_Var var, PP_Var name, PP_Var* exception) { Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, exception); if (!dispatcher) return; ReceiveSerializedException se(dispatcher, exception); PP_Bool result = PP_FALSE; if (!se.IsThrown()) { dispatcher->Send(new PpapiHostMsg_PPBVar_DeleteProperty( INTERFACE_ID_PPB_VAR_DEPRECATED, SerializedVarSendInput(dispatcher, var), SerializedVarSendInput(dispatcher, name), &se, &result)); } } PP_Var Call(PP_Var object, PP_Var method_name, uint32_t argc, PP_Var* argv, PP_Var* exception) { Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(object, exception); if (!dispatcher) return PP_MakeUndefined(); ReceiveSerializedVarReturnValue result; ReceiveSerializedException se(dispatcher, exception); if (!se.IsThrown()) { std::vector argv_vect; SerializedVarSendInput::ConvertVector(dispatcher, argv, argc, &argv_vect); dispatcher->Send(new PpapiHostMsg_PPBVar_CallDeprecated( INTERFACE_ID_PPB_VAR_DEPRECATED, SerializedVarSendInput(dispatcher, object), SerializedVarSendInput(dispatcher, method_name), argv_vect, &se, &result)); } return result.Return(dispatcher); } PP_Var Construct(PP_Var object, uint32_t argc, PP_Var* argv, PP_Var* exception) { Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(object, exception); if (!dispatcher) return PP_MakeUndefined(); ReceiveSerializedVarReturnValue result; ReceiveSerializedException se(dispatcher, exception); if (!se.IsThrown()) { std::vector argv_vect; SerializedVarSendInput::ConvertVector(dispatcher, argv, argc, &argv_vect); dispatcher->Send(new PpapiHostMsg_PPBVar_Construct( INTERFACE_ID_PPB_VAR_DEPRECATED, SerializedVarSendInput(dispatcher, object), argv_vect, &se, &result)); } return result.Return(dispatcher); } bool IsInstanceOf(PP_Var var, const PPP_Class_Deprecated* ppp_class, void** ppp_class_data) { Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, NULL); if (!dispatcher) return false; PP_Bool result = PP_FALSE; int64 class_int = static_cast(reinterpret_cast(ppp_class)); int64 class_data_int = 0; dispatcher->Send(new PpapiHostMsg_PPBVar_IsInstanceOfDeprecated( INTERFACE_ID_PPB_VAR_DEPRECATED, SerializedVarSendInput(dispatcher, var), class_int, &class_data_int, &result)); *ppp_class_data = reinterpret_cast(static_cast(class_data_int)); return PP_ToBool(result); } PP_Var CreateObject(PP_Instance instance, const PPP_Class_Deprecated* ppp_class, void* ppp_class_data) { Dispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); if (!dispatcher) return PP_MakeUndefined(); ReceiveSerializedVarReturnValue result; int64 class_int = static_cast(reinterpret_cast(ppp_class)); int64 data_int = static_cast(reinterpret_cast(ppp_class_data)); dispatcher->Send(new PpapiHostMsg_PPBVar_CreateObjectDeprecated( INTERFACE_ID_PPB_VAR_DEPRECATED, instance, class_int, data_int, &result)); return result.Return(dispatcher); } const PPB_Var_Deprecated var_deprecated_interface = { &AddRefVar, &ReleaseVar, &VarFromUtf8, &VarToUtf8, &HasProperty, &HasMethod, &GetProperty, &EnumerateProperties, &SetProperty, &RemoveProperty, &Call, &Construct, &IsInstanceOf, &CreateObject }; InterfaceProxy* CreateVarDeprecatedProxy(Dispatcher* dispatcher) { return new PPB_Var_Deprecated_Proxy(dispatcher ); } } // namespace PPB_Var_Deprecated_Proxy::PPB_Var_Deprecated_Proxy( Dispatcher* dispatcher) : InterfaceProxy(dispatcher), task_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), ppb_var_impl_(NULL) { if (!dispatcher->IsPlugin()) { ppb_var_impl_ = static_cast( dispatcher->local_get_interface()(PPB_VAR_DEPRECATED_INTERFACE)); } } PPB_Var_Deprecated_Proxy::~PPB_Var_Deprecated_Proxy() { } // static const InterfaceProxy::Info* PPB_Var_Deprecated_Proxy::GetInfo() { static const Info info = { &var_deprecated_interface, PPB_VAR_DEPRECATED_INTERFACE, INTERFACE_ID_PPB_VAR_DEPRECATED, false, &CreateVarDeprecatedProxy, }; return &info; } bool PPB_Var_Deprecated_Proxy::OnMessageReceived(const IPC::Message& msg) { // Prevent the dispatcher from going away during a call to Call or other // function that could mutate the DOM. This must happen OUTSIDE of // the message handlers since the SerializedVars use the dispatcher upon // return of the function (converting the SerializedVarReturnValue/OutParam // to a SerializedVar in the destructor). ScopedModuleReference death_grip(dispatcher()); bool handled = true; IPC_BEGIN_MESSAGE_MAP(PPB_Var_Deprecated_Proxy, msg) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_AddRefObject, OnMsgAddRefObject) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_ReleaseObject, OnMsgReleaseObject) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_HasProperty, OnMsgHasProperty) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_HasMethodDeprecated, OnMsgHasMethodDeprecated) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_GetProperty, OnMsgGetProperty) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_DeleteProperty, OnMsgDeleteProperty) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_EnumerateProperties, OnMsgEnumerateProperties) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_SetPropertyDeprecated, OnMsgSetPropertyDeprecated) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_CallDeprecated, OnMsgCallDeprecated) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_Construct, OnMsgConstruct) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_IsInstanceOfDeprecated, OnMsgIsInstanceOfDeprecated) IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_CreateObjectDeprecated, OnMsgCreateObjectDeprecated) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() // TODO(brettw) handle bad messages! return handled; } void PPB_Var_Deprecated_Proxy::OnMsgAddRefObject(int64 object_id, int* /* unused */) { PP_Var var; var.type = PP_VARTYPE_OBJECT; var.value.as_id = object_id; ppb_var_impl_->AddRef(var); } void PPB_Var_Deprecated_Proxy::OnMsgReleaseObject(int64 object_id) { // Ok, so this is super subtle. // When the browser side sends a sync IPC message that returns a var, and the // plugin wants to give ownership of that var to the browser, dropping all // references, it may call ReleaseObject right after returning the result. // However, the IPC system doesn't enforce strict ordering of messages in that // case, where a message that is set to unblock (e.g. a sync message, or in // our case all messages coming from the plugin) that is sent *after* the // result may be dispatched on the browser side *before* the sync send // returned (see ipc_sync_channel.cc). In this case, that means it could // release the object before it is AddRef'ed on the browser side. // To work around this, we post a task here, that will not execute before // control goes back to the main message loop, that will ensure the sync send // has returned and the browser side can take its reference before we Release. // Note: if the instance is gone by the time the task is executed, then it // will Release the objects itself and this Release will be a NOOP (aside of a // spurious warning). // TODO(piman): See if we can fix the IPC code to enforce strict ordering, and // then remove this. MessageLoop::current()->PostNonNestableTask(FROM_HERE, task_factory_.NewRunnableMethod( &PPB_Var_Deprecated_Proxy::DoReleaseObject, object_id)); } void PPB_Var_Deprecated_Proxy::OnMsgHasProperty( SerializedVarReceiveInput var, SerializedVarReceiveInput name, SerializedVarOutParam exception, PP_Bool* result) { SetAllowPluginReentrancy(); *result = PP_FromBool(ppb_var_impl_->HasProperty( var.Get(dispatcher()), name.Get(dispatcher()), exception.OutParam(dispatcher()))); } void PPB_Var_Deprecated_Proxy::OnMsgHasMethodDeprecated( SerializedVarReceiveInput var, SerializedVarReceiveInput name, SerializedVarOutParam exception, PP_Bool* result) { SetAllowPluginReentrancy(); *result = PP_FromBool(ppb_var_impl_->HasMethod( var.Get(dispatcher()), name.Get(dispatcher()), exception.OutParam(dispatcher()))); } void PPB_Var_Deprecated_Proxy::OnMsgGetProperty( SerializedVarReceiveInput var, SerializedVarReceiveInput name, SerializedVarOutParam exception, SerializedVarReturnValue result) { SetAllowPluginReentrancy(); result.Return(dispatcher(), ppb_var_impl_->GetProperty( var.Get(dispatcher()), name.Get(dispatcher()), exception.OutParam(dispatcher()))); } void PPB_Var_Deprecated_Proxy::OnMsgEnumerateProperties( SerializedVarReceiveInput var, SerializedVarVectorOutParam props, SerializedVarOutParam exception) { SetAllowPluginReentrancy(); ppb_var_impl_->GetAllPropertyNames(var.Get(dispatcher()), props.CountOutParam(), props.ArrayOutParam(dispatcher()), exception.OutParam(dispatcher())); } void PPB_Var_Deprecated_Proxy::OnMsgSetPropertyDeprecated( SerializedVarReceiveInput var, SerializedVarReceiveInput name, SerializedVarReceiveInput value, SerializedVarOutParam exception) { SetAllowPluginReentrancy(); ppb_var_impl_->SetProperty(var.Get(dispatcher()), name.Get(dispatcher()), value.Get(dispatcher()), exception.OutParam(dispatcher())); } void PPB_Var_Deprecated_Proxy::OnMsgDeleteProperty( SerializedVarReceiveInput var, SerializedVarReceiveInput name, SerializedVarOutParam exception, PP_Bool* result) { SetAllowPluginReentrancy(); ppb_var_impl_->RemoveProperty(var.Get(dispatcher()), name.Get(dispatcher()), exception.OutParam(dispatcher())); // This deprecated function doesn't actually return a value, but we re-use // the message from the non-deprecated interface with the return value. *result = PP_TRUE; } void PPB_Var_Deprecated_Proxy::OnMsgCallDeprecated( SerializedVarReceiveInput object, SerializedVarReceiveInput method_name, SerializedVarVectorReceiveInput arg_vector, SerializedVarOutParam exception, SerializedVarReturnValue result) { SetAllowPluginReentrancy(); uint32_t arg_count = 0; PP_Var* args = arg_vector.Get(dispatcher(), &arg_count); result.Return(dispatcher(), ppb_var_impl_->Call( object.Get(dispatcher()), method_name.Get(dispatcher()), arg_count, args, exception.OutParam(dispatcher()))); } void PPB_Var_Deprecated_Proxy::OnMsgConstruct( SerializedVarReceiveInput var, SerializedVarVectorReceiveInput arg_vector, SerializedVarOutParam exception, SerializedVarReturnValue result) { SetAllowPluginReentrancy(); uint32_t arg_count = 0; PP_Var* args = arg_vector.Get(dispatcher(), &arg_count); result.Return(dispatcher(), ppb_var_impl_->Construct( var.Get(dispatcher()), arg_count, args, exception.OutParam(dispatcher()))); } void PPB_Var_Deprecated_Proxy::OnMsgIsInstanceOfDeprecated( SerializedVarReceiveInput var, int64 ppp_class, int64* ppp_class_data, PP_Bool* result) { // TODO(brettw) write this. } void PPB_Var_Deprecated_Proxy::OnMsgCreateObjectDeprecated( PP_Instance instance, int64 ppp_class, int64 class_data, SerializedVarReturnValue result) { SetAllowPluginReentrancy(); result.Return(dispatcher(), PPP_Class_Proxy::CreateProxiedObject( ppb_var_impl_, dispatcher(), instance, ppp_class, class_data)); } void PPB_Var_Deprecated_Proxy::SetAllowPluginReentrancy() { if (dispatcher()->IsPlugin()) NOTREACHED(); else static_cast(dispatcher())->set_allow_plugin_reentrancy(); } void PPB_Var_Deprecated_Proxy::DoReleaseObject(int64 object_id) { PP_Var var; var.type = PP_VARTYPE_OBJECT; var.value.as_id = object_id; ppb_var_impl_->Release(var); } } // namespace proxy } // namespace ppapi