text: // Copyright (c) 2013 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/raw_var_data.h"
  
  #include <stack>
  
  #include "base/hash_tables.h"
  #include "base/stl_util.h"
  #include "ipc/ipc_message.h"
  #include "ppapi/proxy/ppapi_param_traits.h"
  #include "ppapi/shared_impl/array_var.h"
  #include "ppapi/shared_impl/dictionary_var.h"
  #include "ppapi/shared_impl/ppapi_globals.h"
  #include "ppapi/shared_impl/scoped_pp_var.h"
  #include "ppapi/shared_impl/var.h"
  #include "ppapi/shared_impl/var_tracker.h"
  
  using std::make_pair;
  
  namespace ppapi {
  namespace proxy {
  
  namespace {
  
  // When sending array buffers, if the size is over 256K, we use shared
  // memory instead of sending the data over IPC. Light testing suggests
  // shared memory is much faster for 256K and larger messages.
  static const uint32 kMinimumArrayBufferSizeForShmem = 256 * 1024;
  static uint32 g_minimum_array_buffer_size_for_shmem =
      kMinimumArrayBufferSizeForShmem;
  
  void DefaultHandleWriter(IPC::Message* m, const SerializedHandle& handle) {
    IPC::ParamTraits<SerializedHandle>::Write(m, handle);
  }
  
  // For a given PP_Var, returns the RawVarData associated with it, or creates a
  // new one if there is no existing one. The data is appended to |data| if it
  // is newly created. The index into |data| pointing to the result is returned.
  // |id_map| keeps track of RawVarDatas that have already been created.
  size_t GetOrCreateRawVarData(const PP_Var& var,
                               base::hash_map<int64_t, size_t>* id_map,
                               ScopedVector<RawVarData>* data) {
    if (VarTracker::IsVarTypeRefcounted(var.type)) {
      base::hash_map<int64_t, size_t>::iterator it = id_map->find(
          var.value.as_id);
      if (it != id_map->end()) {
        return it->second;
      } else {
        data->push_back(RawVarData::Create(var.type));
        (*id_map)[var.value.as_id] = data->size() - 1;
      }
    } else {
      data->push_back(RawVarData::Create(var.type));
    }
    return data->size() - 1;
  }
  
  }  // namespace
  
  // RawVarDataGraph ------------------------------------------------------------
  RawVarDataGraph::RawVarDataGraph() {
  }
  
  RawVarDataGraph::~RawVarDataGraph() {
  }
  
  // static
  scoped_ptr<RawVarDataGraph> RawVarDataGraph::Create(const PP_Var& var,
                                                      PP_Instance instance) {
    scoped_ptr<RawVarDataGraph> graph(new RawVarDataGraph);
    // Map of |var.value.as_id| to a RawVarData index in RawVarDataGraph.
    base::hash_map<int64_t, size_t> id_map;
  
    std::stack<std::pair<PP_Var, size_t> > stack;
    stack.push(make_pair(var, GetOrCreateRawVarData(var, &id_map,
                                                    &graph->data_)));
  
    // Traverse the PP_Var graph with DFS.
    while (!stack.empty()) {
      PP_Var current_var = stack.top().first;
      RawVarData* current_var_data = graph->data_[stack.top().second];
      stack.pop();
  
      // If the node is initialized, it means we have visited it.
      if (current_var_data->initialized())
        continue;
  
      if (!current_var_data->Init(current_var, instance)) {
        NOTREACHED();
        return scoped_ptr<RawVarDataGraph>();
      }
  
      // Add child nodes to the stack.
      if (current_var.type == PP_VARTYPE_ARRAY) {
        ArrayVar* array_var = ArrayVar::FromPPVar(current_var);
        if (!array_var) {
          NOTREACHED();
          return scoped_ptr<RawVarDataGraph>();
        }
        for (ArrayVar::ElementVector::const_iterator iter =
                 array_var->elements().begin();
             iter != array_var->elements().end();
             ++iter) {
          size_t child_id = GetOrCreateRawVarData(iter->get(), &id_map,
                                                  &graph->data_);
          static_cast<ArrayRawVarData*>(current_var_data)->AddChild(child_id);
          stack.push(make_pair(iter->get(), child_id));
        }
      } else if (current_var.type == PP_VARTYPE_DICTIONARY) {
        DictionaryVar* dict_var = DictionaryVar::FromPPVar(current_var);
        if (!dict_var) {
          NOTREACHED();
          return scoped_ptr<RawVarDataGraph>();
        }
        for (DictionaryVar::KeyValueMap::const_iterator iter =
                 dict_var->key_value_map().begin();
             iter != dict_var->key_value_map().end();
             ++iter) {
          size_t child_id = GetOrCreateRawVarData(iter->second.get(), &id_map,
                                                  &graph->data_);
          static_cast<DictionaryRawVarData*>(
              current_var_data)->AddChild(iter->first, child_id);
          stack.push(make_pair(iter->second.get(), child_id));
        }
      }
    }
    return graph.Pass();
  }
  
  PP_Var RawVarDataGraph::CreatePPVar(PP_Instance instance) {
    // Create and initialize each node in the graph.
    std::vector<PP_Var> graph;
    for (size_t i = 0; i < data_.size(); ++i)
      graph.push_back(data_[i]->CreatePPVar(instance));
    for (size_t i = 0; i < data_.size(); ++i)
      data_[i]->PopulatePPVar(graph[i], graph);
    // Everything except the root will have one extra ref. Remove that ref.
    for (size_t i = 1; i < data_.size(); ++i)
      ScopedPPVar(ScopedPPVar::PassRef(), graph[i]);
    // The first element is the root.
    return graph[0];
  }
  
  void RawVarDataGraph::Write(IPC::Message* m,
                              const HandleWriter& handle_writer) {
    // Write the size, followed by each node in the graph.
    m->WriteUInt32(static_cast<uint32_t>(data_.size()));
    for (size_t i = 0; i < data_.size(); ++i) {
      m->WriteInt(data_[i]->Type());
      data_[i]->Write(m, handle_writer);
    }
  }
  
  void RawVarDataGraph::Write(IPC::Message* m) {
    Write(m, base::Bind(&DefaultHandleWriter));
  }
  
  // static
  scoped_ptr<RawVarDataGraph> RawVarDataGraph::Read(const IPC::Message* m,
                                                    PickleIterator* iter) {
    scoped_ptr<RawVarDataGraph> result(new RawVarDataGraph);
    uint32_t size = 0;
    if (!m->ReadUInt32(iter, &size))
      return scoped_ptr<RawVarDataGraph>();
    for (uint32_t i = 0; i < size; ++i) {
      int32_t type;
      if (!m->ReadInt(iter, &type))
        return scoped_ptr<RawVarDataGraph>();
      PP_VarType var_type = static_cast<PP_VarType>(type);
      result->data_.push_back(RawVarData::Create(var_type));
      if (!result->data_.back()->Read(var_type, m, iter))
        return scoped_ptr<RawVarDataGraph>();
    }
    return result.Pass();
  }
  
  std::vector<SerializedHandle*> RawVarDataGraph::GetHandles() {
    std::vector<SerializedHandle*> result;
    for (size_t i = 0; i < data_.size(); ++i) {
      SerializedHandle* handle = data_[i]->GetHandle();
      if (handle)
        result.push_back(handle);
    }
    return result;
  }
  
  // static
  void RawVarDataGraph::SetMinimumArrayBufferSizeForShmemForTest(
      uint32 threshold) {
    if (threshold == 0)
      g_minimum_array_buffer_size_for_shmem = kMinimumArrayBufferSizeForShmem;
    else
      g_minimum_array_buffer_size_for_shmem = threshold;
  }
  
  // RawVarData ------------------------------------------------------------------
  
  // static
  RawVarData* RawVarData::Create(PP_VarType type) {
    switch (type) {
      case PP_VARTYPE_UNDEFINED:
      case PP_VARTYPE_NULL:
      case PP_VARTYPE_BOOL:
      case PP_VARTYPE_INT32:
      case PP_VARTYPE_DOUBLE:
      case PP_VARTYPE_OBJECT:
        return new BasicRawVarData();
      case PP_VARTYPE_STRING:
        return new StringRawVarData();
      case PP_VARTYPE_ARRAY_BUFFER:
        return new ArrayBufferRawVarData();
      case PP_VARTYPE_ARRAY:
        return new ArrayRawVarData();
      case PP_VARTYPE_DICTIONARY:
        return new DictionaryRawVarData();
    }
    NOTREACHED();
    return NULL;
  }
  
  RawVarData::RawVarData() : initialized_(false) {
  }
  
  RawVarData::~RawVarData() {
  }
  
  SerializedHandle* RawVarData::GetHandle() {
    return NULL;
  }
  
  // BasicRawVarData -------------------------------------------------------------
  BasicRawVarData::BasicRawVarData() {
  }
  
  BasicRawVarData::~BasicRawVarData() {
  }
  
  PP_VarType BasicRawVarData::Type() {
    return var_.type;
  }
  
  bool BasicRawVarData::Init(const PP_Var& var, PP_Instance /*instance*/) {
    var_ = var;
    initialized_ = true;
    return true;
  }
  
  PP_Var BasicRawVarData::CreatePPVar(PP_Instance instance) {
    return var_;
  }
  
  void BasicRawVarData::PopulatePPVar(const PP_Var& var,
                                      const std::vector<PP_Var>& graph) {
  }
  
  void BasicRawVarData::Write(
      IPC::Message* m,
      const HandleWriter& handle_writer) {
    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_OBJECT:
        m->WriteInt64(var_.value.as_id);
        break;
      default:
        NOTREACHED();
        break;
    }
  }
  
  bool BasicRawVarData::Read(PP_VarType type,
                             const IPC::Message* m,
                             PickleIterator* iter) {
    PP_Var result;
    result.type = type;
    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.
        break;
      case PP_VARTYPE_BOOL: {
        bool bool_value;
        if (!m->ReadBool(iter, &bool_value))
          return false;
        result.value.as_bool = PP_FromBool(bool_value);
        break;
      }
      case PP_VARTYPE_INT32:
        if (!m->ReadInt(iter, &result.value.as_int))
          return false;
        break;
      case PP_VARTYPE_DOUBLE:
        if (!IPC::ParamTraits<double>::Read(m, iter, &result.value.as_double))
          return false;
        break;
      case PP_VARTYPE_OBJECT:
        if (!m->ReadInt64(iter, &result.value.as_id))
          return false;
        break;
      default:
        NOTREACHED();
        return false;
    }
    var_ = result;
    return true;
  }
  
  // StringRawVarData ------------------------------------------------------------
  StringRawVarData::StringRawVarData() {
  }
  
  StringRawVarData::~StringRawVarData() {
  }
  
  PP_VarType StringRawVarData::Type() {
    return PP_VARTYPE_STRING;
  }
  
  bool StringRawVarData::Init(const PP_Var& var, PP_Instance /*instance*/) {
    DCHECK(var.type == PP_VARTYPE_STRING);
    StringVar* string_var = StringVar::FromPPVar(var);
    if (!string_var)
      return false;
    data_ = string_var->value();
    initialized_ = true;
    return true;
  }
  
  PP_Var StringRawVarData::CreatePPVar(PP_Instance instance) {
    return StringVar::SwapValidatedUTF8StringIntoPPVar(&data_);
  }
  
  void StringRawVarData::PopulatePPVar(const PP_Var& var,
                                       const std::vector<PP_Var>& graph) {
  }
  
  void StringRawVarData::Write(IPC::Message* m,
                               const HandleWriter& handle_writer) {
    m->WriteString(data_);
  }
  
  bool StringRawVarData::Read(PP_VarType type,
                              const IPC::Message* m,
                              PickleIterator* iter) {
    if (!m->ReadString(iter, &data_))
      return false;
    return true;
  }
  
  // ArrayBufferRawVarData -------------------------------------------------------
  ArrayBufferRawVarData::ArrayBufferRawVarData() {
  }
  
  ArrayBufferRawVarData::~ArrayBufferRawVarData() {
  }
  
  PP_VarType ArrayBufferRawVarData::Type() {
    return PP_VARTYPE_ARRAY_BUFFER;
  }
  
  bool ArrayBufferRawVarData::Init(const PP_Var& var,
                                   PP_Instance instance) {
    DCHECK(var.type == PP_VARTYPE_ARRAY_BUFFER);
    ArrayBufferVar* buffer_var = ArrayBufferVar::FromPPVar(var);
    if (!buffer_var)
      return false;
    bool using_shmem = false;
    if (buffer_var->ByteLength() >= g_minimum_array_buffer_size_for_shmem &&
        instance != 0) {
      int host_handle_id;
      base::SharedMemoryHandle plugin_handle;
      using_shmem = buffer_var->CopyToNewShmem(instance,
                                               &host_handle_id,
                                               &plugin_handle);
      if (using_shmem) {
        if (host_handle_id != -1) {
          DCHECK(!base::SharedMemory::IsHandleValid(plugin_handle));
          DCHECK(PpapiGlobals::Get()->IsPluginGlobals());
          type_ = ARRAY_BUFFER_SHMEM_HOST;
          host_shm_handle_id_ = host_handle_id;
        } else {
          DCHECK(base::SharedMemory::IsHandleValid(plugin_handle));
          DCHECK(PpapiGlobals::Get()->IsHostGlobals());
          type_ = ARRAY_BUFFER_SHMEM_PLUGIN;
          plugin_shm_handle_ = SerializedHandle(plugin_handle,
                                                buffer_var->ByteLength());
        }
      }
    }
    if (!using_shmem) {
      type_ = ARRAY_BUFFER_NO_SHMEM;
      data_ = std::string(static_cast<const char*>(buffer_var->Map()),
                          buffer_var->ByteLength());
    }
    initialized_ = true;
    return true;
  }
  
  PP_Var ArrayBufferRawVarData::CreatePPVar(PP_Instance instance) {
    PP_Var result = PP_MakeUndefined();
    switch (type_) {
      case ARRAY_BUFFER_SHMEM_HOST: {
        base::SharedMemoryHandle host_handle;
        uint32 size_in_bytes;
        bool ok = PpapiGlobals::Get()->GetVarTracker()->
            StopTrackingSharedMemoryHandle(host_shm_handle_id_,
                                           instance,
                                           &host_handle,
                                           &size_in_bytes);
        if (ok) {
          result = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
              size_in_bytes, host_handle);
        } else {
          LOG(ERROR) << "Couldn't find array buffer id: " << host_shm_handle_id_;
          return PP_MakeUndefined();
        }
        break;
      }
      case ARRAY_BUFFER_SHMEM_PLUGIN: {
        result = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
            plugin_shm_handle_.size(),
            plugin_shm_handle_.shmem());
        break;
      }
      case ARRAY_BUFFER_NO_SHMEM: {
        result = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
            static_cast<uint32>(data_.size()), data_.data());
        break;
      }
      default:
        NOTREACHED();
        return PP_MakeUndefined();
    }
    DCHECK(result.type == PP_VARTYPE_ARRAY_BUFFER);
    return result;
  }
  
  void ArrayBufferRawVarData::PopulatePPVar(const PP_Var& var,
                                            const std::vector<PP_Var>& graph) {
  }
  
  void ArrayBufferRawVarData::Write(
      IPC::Message* m,
      const HandleWriter& handle_writer) {
    m->WriteInt(type_);
    switch (type_) {
      case ARRAY_BUFFER_SHMEM_HOST:
        m->WriteInt(host_shm_handle_id_);
        break;
      case ARRAY_BUFFER_SHMEM_PLUGIN:
        handle_writer.Run(m, plugin_shm_handle_);
        break;
      case ARRAY_BUFFER_NO_SHMEM:
        m->WriteString(data_);
        break;
    }
  }
  
  bool ArrayBufferRawVarData::Read(PP_VarType type,
                                   const IPC::Message* m,
                                   PickleIterator* iter) {
    int shmem_type;
    if (!m->ReadInt(iter, &shmem_type))
      return false;
    type_ = static_cast<ShmemType>(shmem_type);
    switch (type_) {
      case ARRAY_BUFFER_SHMEM_HOST:
        if (!m->ReadInt(iter, &host_shm_handle_id_))
          return false;
        break;
      case ARRAY_BUFFER_SHMEM_PLUGIN:
        if (!IPC::ParamTraits<SerializedHandle>::Read(
                m, iter, &plugin_shm_handle_)) {
          return false;
        }
        break;
      case ARRAY_BUFFER_NO_SHMEM:
        if (!m->ReadString(iter, &data_))
          return false;
        break;
      default:
        // We read an invalid ID.
        NOTREACHED();
        return false;
    }
    return true;
  }
  
  SerializedHandle* ArrayBufferRawVarData::GetHandle() {
    if (type_ == ARRAY_BUFFER_SHMEM_PLUGIN && plugin_shm_handle_.size() != 0)
      return &plugin_shm_handle_;
    return NULL;
  }
  
  // ArrayRawVarData -------------------------------------------------------------
  ArrayRawVarData::ArrayRawVarData() {
  }
  
  ArrayRawVarData::~ArrayRawVarData() {
  }
  
  void ArrayRawVarData::AddChild(size_t element) {
    children_.push_back(element);
  }
  
  PP_VarType ArrayRawVarData::Type() {
    return PP_VARTYPE_ARRAY;
  }
  
  bool ArrayRawVarData::Init(const PP_Var& var, PP_Instance /*instance*/) {
    initialized_ = true;
    DCHECK(var.type == PP_VARTYPE_ARRAY);
    initialized_ = true;
    return true;
  }
  
  PP_Var ArrayRawVarData::CreatePPVar(PP_Instance instance) {
    return (new ArrayVar())->GetPPVar();
  }
  
  void ArrayRawVarData::PopulatePPVar(const PP_Var& var,
                                      const std::vector<PP_Var>& graph) {
    if (var.type != PP_VARTYPE_ARRAY) {
      NOTREACHED();
      return;
    }
    ArrayVar* array_var = ArrayVar::FromPPVar(var);
    DCHECK(array_var->elements().empty());
    for (size_t i = 0; i < children_.size(); ++i)
      array_var->elements().push_back(ScopedPPVar(graph[children_[i]]));
  }
  
  void ArrayRawVarData::Write(IPC::Message* m,
                              const HandleWriter& handle_writer) {
    m->WriteUInt32(static_cast<uint32_t>(children_.size()));
    for (size_t i = 0; i < children_.size(); ++i)
      m->WriteUInt32(static_cast<uint32_t>(children_[i]));
  }
  
  bool ArrayRawVarData::Read(PP_VarType type,
                             const IPC::Message* m,
                             PickleIterator* iter) {
    uint32_t size;
    if (!m->ReadUInt32(iter, &size))
      return false;
    for (uint32_t i = 0; i < size; ++i) {
      uint32_t index;
      if (!m->ReadUInt32(iter, &index))
        return false;
      children_.push_back(index);
    }
    return true;
  }
  
  // DictionaryRawVarData --------------------------------------------------------
  DictionaryRawVarData::DictionaryRawVarData() {
  }
  
  DictionaryRawVarData::~DictionaryRawVarData() {
  }
  
  void DictionaryRawVarData::AddChild(const std::string& key,
                                      size_t value) {
    children_.push_back(make_pair(key, value));
  }
  
  PP_VarType DictionaryRawVarData::Type() {
    return PP_VARTYPE_DICTIONARY;
  }
  
  bool DictionaryRawVarData::Init(const PP_Var& var, PP_Instance /*instance*/) {
    DCHECK(var.type == PP_VARTYPE_DICTIONARY);
    initialized_ = true;
    return true;
  }
  
  PP_Var DictionaryRawVarData::CreatePPVar(PP_Instance instance) {
    return (new DictionaryVar())->GetPPVar();
  }
  
  void DictionaryRawVarData::PopulatePPVar(const PP_Var& var,
                                           const std::vector<PP_Var>& graph) {
    if (var.type != PP_VARTYPE_DICTIONARY) {
      NOTREACHED();
      return;
    }
    DictionaryVar* dictionary_var = DictionaryVar::FromPPVar(var);
    DCHECK(dictionary_var->key_value_map().empty());
    for (size_t i = 0; i < children_.size(); ++i) {
      bool success = dictionary_var->SetWithStringKey(children_[i].first,
                                                      graph[children_[i].second]);
      DCHECK(success);
    }
  }
  
  void DictionaryRawVarData::Write(
      IPC::Message* m,
      const HandleWriter& handle_writer) {
    m->WriteUInt32(static_cast<uint32_t>(children_.size()));
    for (size_t i = 0; i < children_.size(); ++i) {
      m->WriteString(children_[i].first);
      m->WriteUInt32(static_cast<uint32_t>(children_[i].second));
    }
  }
  
  bool DictionaryRawVarData::Read(PP_VarType type,
                                  const IPC::Message* m,
                                  PickleIterator* iter) {
    uint32_t size;
    if (!m->ReadUInt32(iter, &size))
      return false;
    for (uint32_t i = 0; i < size; ++i) {
      std::string key;
      uint32_t value;
      if (!m->ReadString(iter, &key))
        return false;
      if (!m->ReadUInt32(iter, &value))
        return false;
      children_.push_back(make_pair(key, value));
    }
    return true;
  }
  
  }  // namespace proxy
  }  // namespace ppapi
  