// 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 "base/memory/ref_counted.h" #include "ipc/ipc_message_utils.h" #include "ppapi/proxy/dispatcher.h" #include "ppapi/proxy/interface_proxy.h" #include "ppapi/proxy/ppapi_param_traits.h" #include "ppapi/proxy/var_serialization_rules.h" #include "ppapi/shared_impl/ppapi_globals.h" #include "ppapi/shared_impl/var.h" namespace ppapi { namespace proxy { // SerializedVar::Inner -------------------------------------------------------- SerializedVar::Inner::Inner() : serialization_rules_(NULL), var_(PP_MakeUndefined()), cleanup_mode_(CLEANUP_NONE), dispatcher_for_end_send_pass_ref_(NULL) { #ifndef NDEBUG has_been_serialized_ = false; has_been_deserialized_ = false; #endif } SerializedVar::Inner::Inner(VarSerializationRules* serialization_rules) : serialization_rules_(serialization_rules), var_(PP_MakeUndefined()), cleanup_mode_(CLEANUP_NONE), dispatcher_for_end_send_pass_ref_(NULL) { #ifndef NDEBUG has_been_serialized_ = false; has_been_deserialized_ = false; #endif } SerializedVar::Inner::~Inner() { switch (cleanup_mode_) { case END_SEND_PASS_REF: DCHECK(dispatcher_for_end_send_pass_ref_); serialization_rules_->EndSendPassRef(var_, dispatcher_for_end_send_pass_ref_); break; case END_RECEIVE_CALLER_OWNED: serialization_rules_->EndReceiveCallerOwned(var_); break; default: break; } } PP_Var SerializedVar::Inner::GetVar() const { DCHECK(serialization_rules_); return var_; } 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_); var_ = var; } void SerializedVar::Inner::ForceSetVarValueForTest(PP_Var value) { var_ = value; } void SerializedVar::Inner::WriteToMessage(IPC::Message* m) const { // When writing to the IPC messages, a serization 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_); #ifndef NDEBUG // We should only be serializing something once. DCHECK(!has_been_serialized_); has_been_serialized_ = true; #endif m->WriteInt(static_cast(var_.type)); switch (var_.type) { case PP_VARTYPE_UNDEFINED: case PP_VARTYPE_NULL: // These don't need any data associated with them other than the type we // just serialized. break; case PP_VARTYPE_BOOL: m->WriteBool(PP_ToBool(var_.value.as_bool)); break; case PP_VARTYPE_INT32: m->WriteInt(var_.value.as_int); break; case PP_VARTYPE_DOUBLE: IPC::ParamTraits::Write(m, var_.value.as_double); break; case PP_VARTYPE_STRING: { // TODO(brettw) in the case of an invalid string ID, it would be nice // to send something to the other side such that a 0 ID would be // generated there. Then the function implementing the interface can // handle the invalid string as if it was in process rather than seeing // what looks like a valid empty string. StringVar* string_var = StringVar::FromPPVar(var_); m->WriteString(string_var ? *string_var->ptr() : std::string()); break; } case PP_VARTYPE_ARRAY_BUFFER: { // TODO(dmichael) in the case of an invalid var ID, it would be nice // to send something to the other side such that a 0 ID would be // generated there. Then the function implementing the interface can // handle the invalid string as if it was in process rather than seeing // what looks like a valid empty ArraryBuffer. ArrayBufferVar* buffer_var = ArrayBufferVar::FromPPVar(var_); if (buffer_var) { // TODO(dmichael): If it wasn't already Mapped, Unmap it. (Though once // we use shared memory, this will probably be // completely different anyway). m->WriteData(static_cast(buffer_var->Map()), buffer_var->ByteLength()); } else { m->WriteData(NULL, 0); } break; } case PP_VARTYPE_OBJECT: m->WriteInt64(var_.value.as_id); break; case PP_VARTYPE_ARRAY: case PP_VARTYPE_DICTIONARY: // TODO(brettw) when these are supported, implement this. NOTIMPLEMENTED(); break; } } bool SerializedVar::Inner::ReadFromMessage(const IPC::Message* m, void** 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). int type; if (!m->ReadInt(iter, &type)) return false; bool success = false; switch (type) { case PP_VARTYPE_UNDEFINED: case PP_VARTYPE_NULL: // These don't have any data associated with them other than the type we // just serialized. success = true; break; case PP_VARTYPE_BOOL: { bool bool_value; success = m->ReadBool(iter, &bool_value); var_.value.as_bool = PP_FromBool(bool_value); break; } case PP_VARTYPE_INT32: success = m->ReadInt(iter, &var_.value.as_int); break; case PP_VARTYPE_DOUBLE: success = IPC::ParamTraits::Read(m, iter, &var_.value.as_double); break; case PP_VARTYPE_STRING: { std::string string_from_ipc; success = m->ReadString(iter, &string_from_ipc); var_ = StringVar::SwapValidatedUTF8StringIntoPPVar(&string_from_ipc); break; } case PP_VARTYPE_ARRAY_BUFFER: { int length = 0; const char* message_bytes = NULL; success = m->ReadData(iter, &message_bytes, &length); if (success) { var_ = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( length, message_bytes); } break; } case PP_VARTYPE_OBJECT: success = m->ReadInt64(iter, &var_.value.as_id); break; case PP_VARTYPE_ARRAY: case PP_VARTYPE_DICTIONARY: // TODO(brettw) when these types are supported, implement this. NOTIMPLEMENTED(); break; default: // Leave success as false. break; } // All success cases get here. We avoid writing the type above so that the // output param is untouched (defaults to VARTYPE_UNDEFINED) even in the // failure case. if (success) var_.type = static_cast(type); return success; } void SerializedVar::Inner::SetCleanupModeToEndSendPassRef( Dispatcher* dispatcher) { DCHECK(dispatcher); DCHECK(!dispatcher_for_end_send_pass_ref_); dispatcher_for_end_send_pass_ref_ = dispatcher; 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])); } // 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(), dispatcher)); return inner_->GetVar(); } // ReceiveSerializedException -------------------------------------------------- ReceiveSerializedException::ReceiveSerializedException(Dispatcher* dispatcher, PP_Var* exception) : SerializedVar(dispatcher->serialization_rules()), dispatcher_(dispatcher), 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(), dispatcher_)); *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), dispatcher_(NULL), var_(PP_MakeUndefined()) { } 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(), dispatcher)); return serialized_.inner_->GetVar(); } // 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(), dispatcher)); 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(dispatcher); 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(dispatcher_); } } 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