// 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/serialized_var.h" #include "base/logging.h" #include "ipc/ipc_message_utils.h" #include "ppapi/c/pp_instance.h" #include "ppapi/proxy/dispatcher.h" #include "ppapi/proxy/interface_proxy.h" #include "ppapi/proxy/ppapi_param_traits.h" #include "ppapi/proxy/ppb_buffer_proxy.h" #include "ppapi/shared_impl/ppapi_globals.h" #include "ppapi/shared_impl/var.h" #include "ppapi/thunk/enter.h" namespace ppapi { namespace proxy { namespace { void DefaultHandleWriter(IPC::Message* m, const SerializedHandle& handle) { IPC::ParamTraits::Write(m, handle); } } // namespace // SerializedVar::Inner -------------------------------------------------------- SerializedVar::Inner::Inner() : serialization_rules_(NULL), var_(PP_MakeUndefined()), instance_(0), cleanup_mode_(CLEANUP_NONE), is_valid_var_(true) { #ifndef NDEBUG has_been_serialized_ = false; has_been_deserialized_ = false; #endif } SerializedVar::Inner::Inner(VarSerializationRules* serialization_rules) : serialization_rules_(serialization_rules), var_(PP_MakeUndefined()), instance_(0), cleanup_mode_(CLEANUP_NONE) { #ifndef NDEBUG has_been_serialized_ = false; has_been_deserialized_ = false; #endif } SerializedVar::Inner::~Inner() { switch (cleanup_mode_) { case END_SEND_PASS_REF: serialization_rules_->EndSendPassRef(var_); break; case END_RECEIVE_CALLER_OWNED: serialization_rules_->EndReceiveCallerOwned(var_); break; default: break; } } PP_Var SerializedVar::Inner::GetVar() { DCHECK(serialization_rules_.get()); #if defined(NACL_WIN64) NOTREACHED(); return PP_MakeUndefined(); #else if (raw_var_data_.get()) { var_ = raw_var_data_->CreatePPVar(instance_); raw_var_data_.reset(NULL); } return var_; #endif } void SerializedVar::Inner::SetVar(PP_Var var) { // Sanity check, when updating the var we should have received a // serialization rules pointer already. DCHECK(serialization_rules_.get()); var_ = var; raw_var_data_.reset(NULL); } void SerializedVar::Inner::SetInstance(PP_Instance instance) { instance_ = instance; } void SerializedVar::Inner::ForceSetVarValueForTest(PP_Var value) { var_ = value; raw_var_data_.reset(NULL); } void SerializedVar::Inner::WriteToMessage(IPC::Message* m) const { // When writing to the IPC messages, a serialization rules handler should // always have been set. // // When sending a message, it should be difficult to trigger this if you're // using the SerializedVarSendInput class and giving a non-NULL dispatcher. // Make sure you're using the proper "Send" helper class. // // It should be more common to see this when handling an incoming message // that returns a var. This means the message handler didn't write to the // output parameter, or possibly you used the wrong helper class // (normally SerializedVarReturnValue). DCHECK(serialization_rules_.get()); #ifndef NDEBUG // We should only be serializing something once. DCHECK(!has_been_serialized_); has_been_serialized_ = true; #endif scoped_ptr data = RawVarDataGraph::Create(var_, instance_); if (data) { m->WriteBool(true); // Success. data->Write(m, base::Bind(&DefaultHandleWriter)); } else { m->WriteBool(false); // Failure. } } void SerializedVar::Inner::WriteDataToMessage( IPC::Message* m, const HandleWriter& handle_writer) const { if (raw_var_data_) { m->WriteBool(true); // Success. raw_var_data_->Write(m, handle_writer); } else { m->WriteBool(false); // Failure. } } bool SerializedVar::Inner::ReadFromMessage(const IPC::Message* m, PickleIterator* iter) { #ifndef NDEBUG // We should only deserialize something once or will end up with leaked // references. // // One place this has happened in the past is using // std::vector.resize(). If you're doing this manually instead // of using the helper classes for handling in/out vectors of vars, be // sure you use the same pattern as the SerializedVarVector classes. DCHECK(!has_been_deserialized_); has_been_deserialized_ = true; #endif // When reading, the dispatcher should be set when we get a Deserialize // call (which will supply a dispatcher). if (!m->ReadBool(iter, &is_valid_var_)) return false; if (is_valid_var_) { raw_var_data_ = RawVarDataGraph::Read(m, iter); if (!raw_var_data_) return false; } return true; } void SerializedVar::Inner::SetCleanupModeToEndSendPassRef() { cleanup_mode_ = END_SEND_PASS_REF; } void SerializedVar::Inner::SetCleanupModeToEndReceiveCallerOwned() { cleanup_mode_ = END_RECEIVE_CALLER_OWNED; } // SerializedVar --------------------------------------------------------------- SerializedVar::SerializedVar() : inner_(new Inner) { } SerializedVar::SerializedVar(VarSerializationRules* serialization_rules) : inner_(new Inner(serialization_rules)) { } SerializedVar::~SerializedVar() { } // SerializedVarSendInput ------------------------------------------------------ SerializedVarSendInput::SerializedVarSendInput(Dispatcher* dispatcher, const PP_Var& var) : SerializedVar(dispatcher->serialization_rules()) { inner_->SetVar(dispatcher->serialization_rules()->SendCallerOwned(var)); } // static void SerializedVarSendInput::ConvertVector(Dispatcher* dispatcher, const PP_Var* input, size_t input_count, std::vector* output) { output->reserve(input_count); for (size_t i = 0; i < input_count; i++) output->push_back(SerializedVarSendInput(dispatcher, input[i])); } // SerializedVarSendInputShmem ------------------------------------------------- SerializedVarSendInputShmem::SerializedVarSendInputShmem( Dispatcher* dispatcher, const PP_Var& var, const PP_Instance& instance) : SerializedVar(dispatcher->serialization_rules()) { inner_->SetVar(dispatcher->serialization_rules()->SendCallerOwned(var)); inner_->SetInstance(instance); } // ReceiveSerializedVarReturnValue --------------------------------------------- ReceiveSerializedVarReturnValue::ReceiveSerializedVarReturnValue() { } ReceiveSerializedVarReturnValue::ReceiveSerializedVarReturnValue( const SerializedVar& serialized) : SerializedVar(serialized) { } PP_Var ReceiveSerializedVarReturnValue::Return(Dispatcher* dispatcher) { inner_->set_serialization_rules(dispatcher->serialization_rules()); inner_->SetVar(inner_->serialization_rules()->ReceivePassRef( inner_->GetVar())); return inner_->GetVar(); } // ReceiveSerializedException -------------------------------------------------- ReceiveSerializedException::ReceiveSerializedException(Dispatcher* dispatcher, PP_Var* exception) : SerializedVar(dispatcher->serialization_rules()), exception_(exception) { } ReceiveSerializedException::~ReceiveSerializedException() { if (exception_) { // When an output exception is specified, it will take ownership of the // reference. inner_->SetVar( inner_->serialization_rules()->ReceivePassRef(inner_->GetVar())); *exception_ = inner_->GetVar(); } else { // When no output exception is specified, the browser thinks we have a ref // to an object that we don't want (this will happen only in the plugin // since the browser will always specify an out exception for the plugin to // write into). // // Strings don't need this handling since we can just avoid creating a // Var from the std::string in the first place. if (inner_->GetVar().type == PP_VARTYPE_OBJECT) inner_->serialization_rules()->ReleaseObjectRef(inner_->GetVar()); } } bool ReceiveSerializedException::IsThrown() const { return exception_ && exception_->type != PP_VARTYPE_UNDEFINED; } // ReceiveSerializedVarVectorOutParam ------------------------------------------ ReceiveSerializedVarVectorOutParam::ReceiveSerializedVarVectorOutParam( Dispatcher* dispatcher, uint32_t* output_count, PP_Var** output) : dispatcher_(dispatcher), output_count_(output_count), output_(output) { } ReceiveSerializedVarVectorOutParam::~ReceiveSerializedVarVectorOutParam() { *output_count_ = static_cast(vector_.size()); if (!vector_.size()) { *output_ = NULL; return; } *output_ = static_cast(malloc(vector_.size() * sizeof(PP_Var))); for (size_t i = 0; i < vector_.size(); i++) { // Here we just mimic what happens when returning a value. ReceiveSerializedVarReturnValue converted; SerializedVar* serialized = &converted; *serialized = vector_[i]; (*output_)[i] = converted.Return(dispatcher_); } } std::vector* ReceiveSerializedVarVectorOutParam::OutParam() { return &vector_; } // SerializedVarReceiveInput --------------------------------------------------- SerializedVarReceiveInput::SerializedVarReceiveInput( const SerializedVar& serialized) : serialized_(serialized) { } SerializedVarReceiveInput::~SerializedVarReceiveInput() { } PP_Var SerializedVarReceiveInput::Get(Dispatcher* dispatcher) { serialized_.inner_->set_serialization_rules( dispatcher->serialization_rules()); // Ensure that when the serialized var goes out of scope it cleans up the // stuff we're making in BeginReceiveCallerOwned. serialized_.inner_->SetCleanupModeToEndReceiveCallerOwned(); serialized_.inner_->SetVar( serialized_.inner_->serialization_rules()->BeginReceiveCallerOwned( serialized_.inner_->GetVar())); return serialized_.inner_->GetVar(); } PP_Var SerializedVarReceiveInput::GetForInstance(Dispatcher* dispatcher, PP_Instance instance) { serialized_.inner_->SetInstance(instance); return Get(dispatcher); } // SerializedVarVectorReceiveInput --------------------------------------------- SerializedVarVectorReceiveInput::SerializedVarVectorReceiveInput( const std::vector& serialized) : serialized_(serialized) { } SerializedVarVectorReceiveInput::~SerializedVarVectorReceiveInput() { for (size_t i = 0; i < deserialized_.size(); i++) { serialized_[i].inner_->serialization_rules()->EndReceiveCallerOwned( deserialized_[i]); } } PP_Var* SerializedVarVectorReceiveInput::Get(Dispatcher* dispatcher, uint32_t* array_size) { deserialized_.resize(serialized_.size()); for (size_t i = 0; i < serialized_.size(); i++) { // The vectors must be able to clean themselves up after this call is // torn down. serialized_[i].inner_->set_serialization_rules( dispatcher->serialization_rules()); serialized_[i].inner_->SetVar( serialized_[i].inner_->serialization_rules()->BeginReceiveCallerOwned( serialized_[i].inner_->GetVar())); deserialized_[i] = serialized_[i].inner_->GetVar(); } *array_size = static_cast(serialized_.size()); return deserialized_.empty() ? NULL : &deserialized_[0]; } // SerializedVarReturnValue ---------------------------------------------------- SerializedVarReturnValue::SerializedVarReturnValue(SerializedVar* serialized) : serialized_(serialized) { } void SerializedVarReturnValue::Return(Dispatcher* dispatcher, const PP_Var& var) { serialized_->inner_->set_serialization_rules( dispatcher->serialization_rules()); // Var must clean up after our BeginSendPassRef call. serialized_->inner_->SetCleanupModeToEndSendPassRef(); serialized_->inner_->SetVar( dispatcher->serialization_rules()->BeginSendPassRef(var)); } // static SerializedVar SerializedVarReturnValue::Convert(Dispatcher* dispatcher, const PP_Var& var) { // Mimic what happens in the normal case. SerializedVar result; SerializedVarReturnValue retvalue(&result); retvalue.Return(dispatcher, var); return result; } // SerializedVarOutParam ------------------------------------------------------- SerializedVarOutParam::SerializedVarOutParam(SerializedVar* serialized) : serialized_(serialized), writable_var_(PP_MakeUndefined()), dispatcher_(NULL) { } SerializedVarOutParam::~SerializedVarOutParam() { if (serialized_->inner_->serialization_rules()) { // When unset, OutParam wasn't called. We'll just leave the var untouched // in that case. serialized_->inner_->SetVar( serialized_->inner_->serialization_rules()->BeginSendPassRef( writable_var_)); // Normally the current object will be created on the stack to wrap a // SerializedVar and won't have a scope around the actual IPC send. So we // need to tell the SerializedVar to do the begin/end send pass ref calls. serialized_->inner_->SetCleanupModeToEndSendPassRef(); } } PP_Var* SerializedVarOutParam::OutParam(Dispatcher* dispatcher) { dispatcher_ = dispatcher; serialized_->inner_->set_serialization_rules( dispatcher->serialization_rules()); return &writable_var_; } // SerializedVarVectorOutParam ------------------------------------------------- SerializedVarVectorOutParam::SerializedVarVectorOutParam( std::vector* serialized) : dispatcher_(NULL), serialized_(serialized), count_(0), array_(NULL) { } SerializedVarVectorOutParam::~SerializedVarVectorOutParam() { DCHECK(dispatcher_); // Convert the array written by the pepper code to the serialized structure. // Note we can't use resize here, we have to allocate a new SerializedVar // for each serialized item. See ParamTraits>::Read. serialized_->reserve(count_); for (uint32_t i = 0; i < count_; i++) { // Just mimic what we do for regular OutParams. SerializedVar var; SerializedVarOutParam out(&var); *out.OutParam(dispatcher_) = array_[i]; serialized_->push_back(var); } // When returning arrays, the pepper code expects the caller to take // ownership of the array. free(array_); } PP_Var** SerializedVarVectorOutParam::ArrayOutParam(Dispatcher* dispatcher) { DCHECK(!dispatcher_); // Should only be called once. dispatcher_ = dispatcher; return &array_; } SerializedVarTestConstructor::SerializedVarTestConstructor( const PP_Var& pod_var) { DCHECK(pod_var.type != PP_VARTYPE_STRING); inner_->ForceSetVarValueForTest(pod_var); } SerializedVarTestConstructor::SerializedVarTestConstructor( const std::string& str) { inner_->ForceSetVarValueForTest(StringVar::StringToPPVar(str)); } SerializedVarTestReader::SerializedVarTestReader(const SerializedVar& var) : SerializedVar(var) { } } // namespace proxy } // namespace ppapi