// 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 #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::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* id_map, ScopedVector* data) { if (VarTracker::IsVarTypeRefcounted(var.type)) { base::hash_map::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::Create(const PP_Var& var, PP_Instance instance) { scoped_ptr graph(new RawVarDataGraph); // Map of |var.value.as_id| to a RawVarData index in RawVarDataGraph. base::hash_map id_map; std::stack > 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(); } // 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(); } 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(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(); } 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( 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 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(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::Read(const IPC::Message* m, PickleIterator* iter) { scoped_ptr result(new RawVarDataGraph); uint32_t size = 0; if (!m->ReadUInt32(iter, &size)) return scoped_ptr(); for (uint32_t i = 0; i < size; ++i) { int32_t type; if (!m->ReadInt(iter, &type)) return scoped_ptr(); PP_VarType var_type = static_cast(type); result->data_.push_back(RawVarData::Create(var_type)); if (!result->data_.back()->Read(var_type, m, iter)) return scoped_ptr(); } return result.Pass(); } std::vector RawVarDataGraph::GetHandles() { std::vector 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& 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::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::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& 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(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(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& 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(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::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& 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(children_.size())); for (size_t i = 0; i < children_.size(); ++i) m->WriteUInt32(static_cast(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& 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(children_.size())); for (size_t i = 0; i < children_.size(); ++i) { m->WriteString(children_[i].first); m->WriteUInt32(static_cast(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