// 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(base::Pickle* m, const SerializedHandle& handle) {
  IPC::ParamTraits<SerializedHandle>::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(base::Pickle* 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
  std::unique_ptr<RawVarDataGraph> 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(
    base::Pickle* 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 base::Pickle* m,
                                           base::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<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).
  if (!iter->ReadBool(&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<SerializedVar>* 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<uint32_t>(vector_.size());
  if (vector_.empty()) {
    *output_ = nullptr;
    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) {
}

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<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 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<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();

  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<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) {
  inner_->ForceSetVarValueForTest(StringVar::StringToPPVar(str));
}

SerializedVarTestReader::SerializedVarTestReader(const SerializedVar& var)
    : SerializedVar(var) {
}

}  // namespace proxy
}  // namespace ppapi
