| // 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 <stdlib.h> // For malloc | |
| #include "base/bind.h" | |
| #include "base/logging.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<SerializedVar> 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<SerializedVar> 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<int64_t>(reinterpret_cast<intptr_t>(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<void*>(static_cast<intptr_t>(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<int64_t>(reinterpret_cast<intptr_t>(ppp_class)); | |
| int64_t data_int = | |
| static_cast<int64_t>(reinterpret_cast<intptr_t>(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<const PPB_Var_Deprecated*>( | |
| 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_FLASH)) | |
| 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<HostDispatcher*>(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 |