blob: afd493fc6604cff1fa7a6125c1d2a0231ae00167 [file] [log] [blame]
// 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/serialized_var.h"
#include "base/logging.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"
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(VarSerializationRules* serialization_rules,
const PP_Var& var)
: serialization_rules_(serialization_rules),
var_(var),
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_);
// If we're a string var, we should have already converted the string value
// to a var ID.
DCHECK(var_.type != PP_VARTYPE_STRING || var_.value.as_id != 0);
return var_;
}
PP_Var SerializedVar::Inner::GetIncompleteVar() 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;
}
const std::string& SerializedVar::Inner::GetString() const {
DCHECK(serialization_rules_);
return string_value_;
}
std::string* SerializedVar::Inner::GetStringPtr() {
DCHECK(serialization_rules_);
return &string_value_;
}
void SerializedVar::Inner::ForceSetVarValueForTest(PP_Var value) {
var_ = value;
}
void SerializedVar::Inner::ForceSetStringValueForTest(const std::string& str) {
string_value_ = str;
}
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
// If the var is not a string type, we should not have ended up with any
// string data.
DCHECK(var_.type == PP_VARTYPE_STRING || string_value_.empty());
m->WriteInt(static_cast<int>(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<double>::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.
m->WriteString(string_value_);
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<SerializedVar>.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<double>::Read(m, iter, &var_.value.as_double);
break;
case PP_VARTYPE_STRING:
success = m->ReadString(iter, &string_value_);
var_.value.as_id = 0;
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<PP_VarType>(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(VarSerializationRules* serialization_rules,
const PP_Var& var)
: inner_(new Inner(serialization_rules, var)) {
}
SerializedVar::~SerializedVar() {
}
// SerializedVarSendInput ------------------------------------------------------
SerializedVarSendInput::SerializedVarSendInput(Dispatcher* dispatcher,
const PP_Var& var)
: SerializedVar(dispatcher->serialization_rules()) {
inner_->SetVar(dispatcher->serialization_rules()->SendCallerOwned(
var, inner_->GetStringPtr()));
}
// static
void SerializedVarSendInput::ConvertVector(Dispatcher* dispatcher,
const PP_Var* input,
size_t input_count,
std::vector<SerializedVar>* output) {
output->resize(input_count);
for (size_t i = 0; i < input_count; i++) {
SerializedVar& cur = (*output)[i];
cur = SerializedVar(dispatcher->serialization_rules());
cur.inner_->SetVar(dispatcher->serialization_rules()->SendCallerOwned(
input[i], cur.inner_->GetStringPtr()));
}
}
// 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_->GetIncompleteVar(), inner_->GetString(), 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_->GetIncompleteVar(), inner_->GetString(), 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<uint32_t>(vector_.size());
if (!vector_.size()) {
*output_ = NULL;
return;
}
*output_ = static_cast<PP_Var*>(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<SerializedVar>* 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_->GetIncompleteVar(),
serialized_.inner_->GetStringPtr(),
dispatcher));
return serialized_.inner_->GetVar();
}
// SerializedVarVectorReceiveInput ---------------------------------------------
SerializedVarVectorReceiveInput::SerializedVarVectorReceiveInput(
const std::vector<SerializedVar>& 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 vector 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_->GetIncompleteVar(),
serialized_[i].inner_->GetStringPtr(),
dispatcher));
deserialized_[i] = serialized_[i].inner_->GetVar();
}
*array_size = static_cast<uint32_t>(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,
serialized_->inner_->GetStringPtr()));
}
// 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_, serialized_->inner_->GetStringPtr()));
// 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<SerializedVar>* 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<vector<SerializedVar>>::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) {
PP_Var string_var = {};
string_var.type = PP_VARTYPE_STRING;
string_var.value.as_id = 0;
inner_->ForceSetVarValueForTest(string_var);
inner_->ForceSetStringValueForTest(str);
}
SerializedVarTestReader::SerializedVarTestReader(const SerializedVar& var)
: SerializedVar(var) {
}
} // namespace proxy
} // namespace ppapi