// 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 "ppapi/proxy/ppb_var_deprecated_proxy.h" #include // For malloc #include "base/bind.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "ppapi/c/dev/ppb_var_deprecated.h" #include "ppapi/c/pp_var.h" #include "ppapi/c/ppb_core.h" #include "ppapi/c/ppb_var.h" #include "ppapi/proxy/host_dispatcher.h" #include "ppapi/proxy/plugin_dispatcher.h" #include "ppapi/proxy/plugin_globals.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/proxy_object_var.h" #include "ppapi/proxy/serialized_var.h" #include "ppapi/shared_impl/ppapi_globals.h" #include "ppapi/shared_impl/ppb_var_shared.h" #include "ppapi/shared_impl/proxy_lock.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 = PluginGlobals::Get()->plugin_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( std::string("Attempting to use an invalid object")); } return NULL; } // PPB_Var_Deprecated plugin --------------------------------------------------- bool HasProperty(PP_Var var, PP_Var name, PP_Var* exception) { ProxyAutoLock lock; 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( API_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) { ProxyAutoLock lock; 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( API_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) { ProxyAutoLock lock; 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( API_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) { ProxyAutoLock lock; 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( API_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) { ProxyAutoLock lock; Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, exception); if (!dispatcher) return; ReceiveSerializedException se(dispatcher, exception); if (!se.IsThrown()) { dispatcher->Send(new PpapiHostMsg_PPBVar_SetPropertyDeprecated( API_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) { ProxyAutoLock lock; 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( API_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) { ProxyAutoLock lock; 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( API_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) { ProxyAutoLock lock; 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( API_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) { ProxyAutoLock lock; Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, NULL); if (!dispatcher) return false; PP_Bool result = PP_FALSE; int64_t class_int = static_cast(reinterpret_cast(ppp_class)); int64_t class_data_int = 0; dispatcher->Send(new PpapiHostMsg_PPBVar_IsInstanceOfDeprecated( API_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) { ProxyAutoLock lock; Dispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); if (!dispatcher) return PP_MakeUndefined(); PluginVarTracker* tracker = PluginGlobals::Get()->plugin_var_tracker(); if (tracker->IsPluginImplementedObjectAlive(ppp_class_data)) return PP_MakeUndefined(); // Object already exists with this user data. ReceiveSerializedVarReturnValue result; int64_t class_int = static_cast(reinterpret_cast(ppp_class)); int64_t data_int = static_cast(reinterpret_cast(ppp_class_data)); dispatcher->Send(new PpapiHostMsg_PPBVar_CreateObjectDeprecated( API_ID_PPB_VAR_DEPRECATED, instance, class_int, data_int, &result)); PP_Var ret_var = result.Return(dispatcher); // Register this object as being implemented by the plugin. if (ret_var.type == PP_VARTYPE_OBJECT) { tracker->PluginImplementedObjectCreated(instance, ret_var, ppp_class, ppp_class_data); } return ret_var; } } // namespace PPB_Var_Deprecated_Proxy::PPB_Var_Deprecated_Proxy( Dispatcher* dispatcher) : InterfaceProxy(dispatcher), ppb_var_impl_(NULL), task_factory_(this) { 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 PPB_Var_Deprecated* PPB_Var_Deprecated_Proxy::GetProxyInterface() { 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, &HasProperty, &HasMethod, &GetProperty, &EnumerateProperties, &SetProperty, &RemoveProperty, &Call, &Construct, &IsInstanceOf, &CreateObject }; return &var_deprecated_interface; } bool PPB_Var_Deprecated_Proxy::OnMessageReceived(const IPC::Message& msg) { if (!dispatcher()->permissions().HasPermission(PERMISSION_DEV)) return false; // 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_t object_id) { PP_Var var = { PP_VARTYPE_OBJECT }; var.value.as_id = object_id; ppb_var_impl_->AddRef(var); } void PPB_Var_Deprecated_Proxy::OnMsgReleaseObject(int64_t 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. PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostNonNestableTask( FROM_HERE, RunWhileLocked(base::Bind(&PPB_Var_Deprecated_Proxy::DoReleaseObject, task_factory_.GetWeakPtr(), 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_t ppp_class, int64_t* ppp_class_data, PP_Bool* result) { SetAllowPluginReentrancy(); *result = PPP_Class_Proxy::IsInstanceOf(ppb_var_impl_, var.Get(dispatcher()), ppp_class, ppp_class_data); } void PPB_Var_Deprecated_Proxy::OnMsgCreateObjectDeprecated( PP_Instance instance, int64_t ppp_class, int64_t 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_t object_id) { PP_Var var = { PP_VARTYPE_OBJECT }; var.value.as_id = object_id; ppb_var_impl_->Release(var); } } // namespace proxy } // namespace ppapi