1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ppapi/proxy/raw_var_data.h"
9 #include "base/containers/hash_tables.h"
10 #include "base/stl_util.h"
11 #include "ipc/ipc_message.h"
12 #include "ppapi/proxy/ppapi_param_traits.h"
13 #include "ppapi/shared_impl/array_var.h"
14 #include "ppapi/shared_impl/dictionary_var.h"
15 #include "ppapi/shared_impl/ppapi_globals.h"
16 #include "ppapi/shared_impl/scoped_pp_var.h"
17 #include "ppapi/shared_impl/var.h"
18 #include "ppapi/shared_impl/var_tracker.h"
27 // When sending array buffers, if the size is over 256K, we use shared
28 // memory instead of sending the data over IPC. Light testing suggests
29 // shared memory is much faster for 256K and larger messages.
30 static const uint32 kMinimumArrayBufferSizeForShmem
= 256 * 1024;
31 static uint32 g_minimum_array_buffer_size_for_shmem
=
32 kMinimumArrayBufferSizeForShmem
;
35 StackEntry(PP_Var v
, size_t i
) : var(v
), data_index(i
) {}
40 // For a given PP_Var, returns the RawVarData associated with it, or creates a
41 // new one if there is no existing one. The data is appended to |data| if it
42 // is newly created. The index into |data| pointing to the result is returned.
43 // |visited_map| keeps track of RawVarDatas that have already been created.
44 size_t GetOrCreateRawVarData(const PP_Var
& var
,
45 base::hash_map
<int64_t, size_t>* visited_map
,
46 ScopedVector
<RawVarData
>* data
) {
47 if (VarTracker::IsVarTypeRefcounted(var
.type
)) {
48 base::hash_map
<int64_t, size_t>::iterator it
= visited_map
->find(
50 if (it
!= visited_map
->end()) {
53 data
->push_back(RawVarData::Create(var
.type
));
54 (*visited_map
)[var
.value
.as_id
] = data
->size() - 1;
57 data
->push_back(RawVarData::Create(var
.type
));
59 return data
->size() - 1;
62 bool CanHaveChildren(PP_Var var
) {
63 return var
.type
== PP_VARTYPE_ARRAY
|| var
.type
== PP_VARTYPE_DICTIONARY
;
68 // RawVarDataGraph ------------------------------------------------------------
69 RawVarDataGraph::RawVarDataGraph() {
72 RawVarDataGraph::~RawVarDataGraph() {
75 // This function uses a stack-based DFS search to traverse the var graph. Each
76 // iteration, the top node on the stack examined. If the node has not been
77 // visited yet (i.e. !initialized()) then it is added to the list of
78 // |parent_ids| which contains all of the nodes on the path from the start node
79 // to the current node. Each of that nodes children are examined. If they appear
80 // in the list of |parent_ids| it means we have a cycle and we return NULL.
81 // Otherwise, if they haven't been visited yet we add them to the stack, If the
82 // node at the top of the stack has already been visited, then we pop it off the
83 // stack and erase it from |parent_ids|.
85 scoped_ptr
<RawVarDataGraph
> RawVarDataGraph::Create(const PP_Var
& var
,
86 PP_Instance instance
) {
87 scoped_ptr
<RawVarDataGraph
> graph(new RawVarDataGraph
);
88 // Map of |var.value.as_id| to a RawVarData index in RawVarDataGraph.
89 base::hash_map
<int64_t, size_t> visited_map
;
90 base::hash_set
<int64_t> parent_ids
;
92 std::stack
<StackEntry
> stack
;
93 stack
.push(StackEntry(var
, GetOrCreateRawVarData(var
, &visited_map
,
96 while (!stack
.empty()) {
97 PP_Var current_var
= stack
.top().var
;
98 RawVarData
* current_var_data
= graph
->data_
[stack
.top().data_index
];
100 if (current_var_data
->initialized()) {
102 if (CanHaveChildren(current_var
))
103 parent_ids
.erase(current_var
.value
.as_id
);
107 if (CanHaveChildren(current_var
))
108 parent_ids
.insert(current_var
.value
.as_id
);
109 if (!current_var_data
->Init(current_var
, instance
)) {
111 return scoped_ptr
<RawVarDataGraph
>();
114 // Add child nodes to the stack.
115 if (current_var
.type
== PP_VARTYPE_ARRAY
) {
116 ArrayVar
* array_var
= ArrayVar::FromPPVar(current_var
);
119 return scoped_ptr
<RawVarDataGraph
>();
121 for (ArrayVar::ElementVector::const_iterator iter
=
122 array_var
->elements().begin();
123 iter
!= array_var
->elements().end();
125 const PP_Var
& child
= iter
->get();
126 // If a child of this node is already in parent_ids, we have a cycle so
127 // we just return null.
128 if (CanHaveChildren(child
) && parent_ids
.count(child
.value
.as_id
) != 0)
129 return scoped_ptr
<RawVarDataGraph
>();
130 size_t child_id
= GetOrCreateRawVarData(child
, &visited_map
,
132 static_cast<ArrayRawVarData
*>(current_var_data
)->AddChild(child_id
);
133 if (!graph
->data_
[child_id
]->initialized())
134 stack
.push(StackEntry(child
, child_id
));
136 } else if (current_var
.type
== PP_VARTYPE_DICTIONARY
) {
137 DictionaryVar
* dict_var
= DictionaryVar::FromPPVar(current_var
);
140 return scoped_ptr
<RawVarDataGraph
>();
142 for (DictionaryVar::KeyValueMap::const_iterator iter
=
143 dict_var
->key_value_map().begin();
144 iter
!= dict_var
->key_value_map().end();
146 const PP_Var
& child
= iter
->second
.get();
147 if (CanHaveChildren(child
) && parent_ids
.count(child
.value
.as_id
) != 0)
148 return scoped_ptr
<RawVarDataGraph
>();
149 size_t child_id
= GetOrCreateRawVarData(child
, &visited_map
,
151 static_cast<DictionaryRawVarData
*>(
152 current_var_data
)->AddChild(iter
->first
, child_id
);
153 if (!graph
->data_
[child_id
]->initialized())
154 stack
.push(StackEntry(child
, child_id
));
161 PP_Var
RawVarDataGraph::CreatePPVar(PP_Instance instance
) {
162 // Create and initialize each node in the graph.
163 std::vector
<PP_Var
> graph
;
164 for (size_t i
= 0; i
< data_
.size(); ++i
)
165 graph
.push_back(data_
[i
]->CreatePPVar(instance
));
166 for (size_t i
= 0; i
< data_
.size(); ++i
)
167 data_
[i
]->PopulatePPVar(graph
[i
], graph
);
168 // Everything except the root will have one extra ref. Remove that ref.
169 for (size_t i
= 1; i
< data_
.size(); ++i
)
170 ScopedPPVar(ScopedPPVar::PassRef(), graph
[i
]);
171 // The first element is the root.
175 void RawVarDataGraph::Write(IPC::Message
* m
,
176 const HandleWriter
& handle_writer
) {
177 // Write the size, followed by each node in the graph.
178 m
->WriteUInt32(static_cast<uint32_t>(data_
.size()));
179 for (size_t i
= 0; i
< data_
.size(); ++i
) {
180 m
->WriteInt(data_
[i
]->Type());
181 data_
[i
]->Write(m
, handle_writer
);
186 scoped_ptr
<RawVarDataGraph
> RawVarDataGraph::Read(const IPC::Message
* m
,
187 PickleIterator
* iter
) {
188 scoped_ptr
<RawVarDataGraph
> result(new RawVarDataGraph
);
190 if (!m
->ReadUInt32(iter
, &size
))
191 return scoped_ptr
<RawVarDataGraph
>();
192 for (uint32_t i
= 0; i
< size
; ++i
) {
194 if (!m
->ReadInt(iter
, &type
))
195 return scoped_ptr
<RawVarDataGraph
>();
196 PP_VarType var_type
= static_cast<PP_VarType
>(type
);
197 result
->data_
.push_back(RawVarData::Create(var_type
));
198 if (!result
->data_
.back()->Read(var_type
, m
, iter
))
199 return scoped_ptr
<RawVarDataGraph
>();
201 return result
.Pass();
204 std::vector
<SerializedHandle
*> RawVarDataGraph::GetHandles() {
205 std::vector
<SerializedHandle
*> result
;
206 for (size_t i
= 0; i
< data_
.size(); ++i
) {
207 SerializedHandle
* handle
= data_
[i
]->GetHandle();
209 result
.push_back(handle
);
215 void RawVarDataGraph::SetMinimumArrayBufferSizeForShmemForTest(
218 g_minimum_array_buffer_size_for_shmem
= kMinimumArrayBufferSizeForShmem
;
220 g_minimum_array_buffer_size_for_shmem
= threshold
;
223 // RawVarData ------------------------------------------------------------------
226 RawVarData
* RawVarData::Create(PP_VarType type
) {
228 case PP_VARTYPE_UNDEFINED
:
229 case PP_VARTYPE_NULL
:
230 case PP_VARTYPE_BOOL
:
231 case PP_VARTYPE_INT32
:
232 case PP_VARTYPE_DOUBLE
:
233 case PP_VARTYPE_OBJECT
:
234 return new BasicRawVarData();
235 case PP_VARTYPE_STRING
:
236 return new StringRawVarData();
237 case PP_VARTYPE_ARRAY_BUFFER
:
238 return new ArrayBufferRawVarData();
239 case PP_VARTYPE_ARRAY
:
240 return new ArrayRawVarData();
241 case PP_VARTYPE_DICTIONARY
:
242 return new DictionaryRawVarData();
248 RawVarData::RawVarData() : initialized_(false) {
251 RawVarData::~RawVarData() {
254 SerializedHandle
* RawVarData::GetHandle() {
258 // BasicRawVarData -------------------------------------------------------------
259 BasicRawVarData::BasicRawVarData() {
262 BasicRawVarData::~BasicRawVarData() {
265 PP_VarType
BasicRawVarData::Type() {
269 bool BasicRawVarData::Init(const PP_Var
& var
, PP_Instance
/*instance*/) {
275 PP_Var
BasicRawVarData::CreatePPVar(PP_Instance instance
) {
279 void BasicRawVarData::PopulatePPVar(const PP_Var
& var
,
280 const std::vector
<PP_Var
>& graph
) {
283 void BasicRawVarData::Write(
285 const HandleWriter
& handle_writer
) {
287 case PP_VARTYPE_UNDEFINED
:
288 case PP_VARTYPE_NULL
:
289 // These don't need any data associated with them other than the type we
292 case PP_VARTYPE_BOOL
:
293 m
->WriteBool(PP_ToBool(var_
.value
.as_bool
));
295 case PP_VARTYPE_INT32
:
296 m
->WriteInt(var_
.value
.as_int
);
298 case PP_VARTYPE_DOUBLE
:
299 IPC::ParamTraits
<double>::Write(m
, var_
.value
.as_double
);
301 case PP_VARTYPE_OBJECT
:
302 m
->WriteInt64(var_
.value
.as_id
);
310 bool BasicRawVarData::Read(PP_VarType type
,
311 const IPC::Message
* m
,
312 PickleIterator
* iter
) {
316 case PP_VARTYPE_UNDEFINED
:
317 case PP_VARTYPE_NULL
:
318 // These don't have any data associated with them other than the type we
321 case PP_VARTYPE_BOOL
: {
323 if (!m
->ReadBool(iter
, &bool_value
))
325 result
.value
.as_bool
= PP_FromBool(bool_value
);
328 case PP_VARTYPE_INT32
:
329 if (!m
->ReadInt(iter
, &result
.value
.as_int
))
332 case PP_VARTYPE_DOUBLE
:
333 if (!IPC::ParamTraits
<double>::Read(m
, iter
, &result
.value
.as_double
))
336 case PP_VARTYPE_OBJECT
:
337 if (!m
->ReadInt64(iter
, &result
.value
.as_id
))
348 // StringRawVarData ------------------------------------------------------------
349 StringRawVarData::StringRawVarData() {
352 StringRawVarData::~StringRawVarData() {
355 PP_VarType
StringRawVarData::Type() {
356 return PP_VARTYPE_STRING
;
359 bool StringRawVarData::Init(const PP_Var
& var
, PP_Instance
/*instance*/) {
360 DCHECK(var
.type
== PP_VARTYPE_STRING
);
361 StringVar
* string_var
= StringVar::FromPPVar(var
);
364 data_
= string_var
->value();
369 PP_Var
StringRawVarData::CreatePPVar(PP_Instance instance
) {
370 return StringVar::SwapValidatedUTF8StringIntoPPVar(&data_
);
373 void StringRawVarData::PopulatePPVar(const PP_Var
& var
,
374 const std::vector
<PP_Var
>& graph
) {
377 void StringRawVarData::Write(IPC::Message
* m
,
378 const HandleWriter
& handle_writer
) {
379 m
->WriteString(data_
);
382 bool StringRawVarData::Read(PP_VarType type
,
383 const IPC::Message
* m
,
384 PickleIterator
* iter
) {
385 if (!m
->ReadString(iter
, &data_
))
390 // ArrayBufferRawVarData -------------------------------------------------------
391 ArrayBufferRawVarData::ArrayBufferRawVarData() {
394 ArrayBufferRawVarData::~ArrayBufferRawVarData() {
397 PP_VarType
ArrayBufferRawVarData::Type() {
398 return PP_VARTYPE_ARRAY_BUFFER
;
401 bool ArrayBufferRawVarData::Init(const PP_Var
& var
,
402 PP_Instance instance
) {
403 DCHECK(var
.type
== PP_VARTYPE_ARRAY_BUFFER
);
404 ArrayBufferVar
* buffer_var
= ArrayBufferVar::FromPPVar(var
);
407 bool using_shmem
= false;
408 if (buffer_var
->ByteLength() >= g_minimum_array_buffer_size_for_shmem
&&
411 base::SharedMemoryHandle plugin_handle
;
412 using_shmem
= buffer_var
->CopyToNewShmem(instance
,
416 if (host_handle_id
!= -1) {
417 DCHECK(!base::SharedMemory::IsHandleValid(plugin_handle
));
418 DCHECK(PpapiGlobals::Get()->IsPluginGlobals());
419 type_
= ARRAY_BUFFER_SHMEM_HOST
;
420 host_shm_handle_id_
= host_handle_id
;
422 DCHECK(base::SharedMemory::IsHandleValid(plugin_handle
));
423 DCHECK(PpapiGlobals::Get()->IsHostGlobals());
424 type_
= ARRAY_BUFFER_SHMEM_PLUGIN
;
425 plugin_shm_handle_
= SerializedHandle(plugin_handle
,
426 buffer_var
->ByteLength());
431 type_
= ARRAY_BUFFER_NO_SHMEM
;
432 data_
= std::string(static_cast<const char*>(buffer_var
->Map()),
433 buffer_var
->ByteLength());
439 PP_Var
ArrayBufferRawVarData::CreatePPVar(PP_Instance instance
) {
440 PP_Var result
= PP_MakeUndefined();
442 case ARRAY_BUFFER_SHMEM_HOST
: {
443 base::SharedMemoryHandle host_handle
;
444 uint32 size_in_bytes
;
445 bool ok
= PpapiGlobals::Get()->GetVarTracker()->
446 StopTrackingSharedMemoryHandle(host_shm_handle_id_
,
451 result
= PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
452 size_in_bytes
, host_handle
);
454 LOG(ERROR
) << "Couldn't find array buffer id: " << host_shm_handle_id_
;
455 return PP_MakeUndefined();
459 case ARRAY_BUFFER_SHMEM_PLUGIN
: {
460 result
= PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
461 plugin_shm_handle_
.size(),
462 plugin_shm_handle_
.shmem());
465 case ARRAY_BUFFER_NO_SHMEM
: {
466 result
= PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
467 static_cast<uint32
>(data_
.size()), data_
.data());
472 return PP_MakeUndefined();
474 DCHECK(result
.type
== PP_VARTYPE_ARRAY_BUFFER
);
478 void ArrayBufferRawVarData::PopulatePPVar(const PP_Var
& var
,
479 const std::vector
<PP_Var
>& graph
) {
482 void ArrayBufferRawVarData::Write(
484 const HandleWriter
& handle_writer
) {
487 case ARRAY_BUFFER_SHMEM_HOST
:
488 m
->WriteInt(host_shm_handle_id_
);
490 case ARRAY_BUFFER_SHMEM_PLUGIN
:
491 handle_writer
.Run(m
, plugin_shm_handle_
);
493 case ARRAY_BUFFER_NO_SHMEM
:
494 m
->WriteString(data_
);
499 bool ArrayBufferRawVarData::Read(PP_VarType type
,
500 const IPC::Message
* m
,
501 PickleIterator
* iter
) {
503 if (!m
->ReadInt(iter
, &shmem_type
))
505 type_
= static_cast<ShmemType
>(shmem_type
);
507 case ARRAY_BUFFER_SHMEM_HOST
:
508 if (!m
->ReadInt(iter
, &host_shm_handle_id_
))
511 case ARRAY_BUFFER_SHMEM_PLUGIN
:
512 if (!IPC::ParamTraits
<SerializedHandle
>::Read(
513 m
, iter
, &plugin_shm_handle_
)) {
517 case ARRAY_BUFFER_NO_SHMEM
:
518 if (!m
->ReadString(iter
, &data_
))
522 // We read an invalid ID.
529 SerializedHandle
* ArrayBufferRawVarData::GetHandle() {
530 if (type_
== ARRAY_BUFFER_SHMEM_PLUGIN
&& plugin_shm_handle_
.size() != 0)
531 return &plugin_shm_handle_
;
535 // ArrayRawVarData -------------------------------------------------------------
536 ArrayRawVarData::ArrayRawVarData() {
539 ArrayRawVarData::~ArrayRawVarData() {
542 void ArrayRawVarData::AddChild(size_t element
) {
543 children_
.push_back(element
);
546 PP_VarType
ArrayRawVarData::Type() {
547 return PP_VARTYPE_ARRAY
;
550 bool ArrayRawVarData::Init(const PP_Var
& var
, PP_Instance
/*instance*/) {
552 DCHECK(var
.type
== PP_VARTYPE_ARRAY
);
557 PP_Var
ArrayRawVarData::CreatePPVar(PP_Instance instance
) {
558 return (new ArrayVar())->GetPPVar();
561 void ArrayRawVarData::PopulatePPVar(const PP_Var
& var
,
562 const std::vector
<PP_Var
>& graph
) {
563 if (var
.type
!= PP_VARTYPE_ARRAY
) {
567 ArrayVar
* array_var
= ArrayVar::FromPPVar(var
);
568 DCHECK(array_var
->elements().empty());
569 for (size_t i
= 0; i
< children_
.size(); ++i
)
570 array_var
->elements().push_back(ScopedPPVar(graph
[children_
[i
]]));
573 void ArrayRawVarData::Write(IPC::Message
* m
,
574 const HandleWriter
& handle_writer
) {
575 m
->WriteUInt32(static_cast<uint32_t>(children_
.size()));
576 for (size_t i
= 0; i
< children_
.size(); ++i
)
577 m
->WriteUInt32(static_cast<uint32_t>(children_
[i
]));
580 bool ArrayRawVarData::Read(PP_VarType type
,
581 const IPC::Message
* m
,
582 PickleIterator
* iter
) {
584 if (!m
->ReadUInt32(iter
, &size
))
586 for (uint32_t i
= 0; i
< size
; ++i
) {
588 if (!m
->ReadUInt32(iter
, &index
))
590 children_
.push_back(index
);
595 // DictionaryRawVarData --------------------------------------------------------
596 DictionaryRawVarData::DictionaryRawVarData() {
599 DictionaryRawVarData::~DictionaryRawVarData() {
602 void DictionaryRawVarData::AddChild(const std::string
& key
,
604 children_
.push_back(make_pair(key
, value
));
607 PP_VarType
DictionaryRawVarData::Type() {
608 return PP_VARTYPE_DICTIONARY
;
611 bool DictionaryRawVarData::Init(const PP_Var
& var
, PP_Instance
/*instance*/) {
612 DCHECK(var
.type
== PP_VARTYPE_DICTIONARY
);
617 PP_Var
DictionaryRawVarData::CreatePPVar(PP_Instance instance
) {
618 return (new DictionaryVar())->GetPPVar();
621 void DictionaryRawVarData::PopulatePPVar(const PP_Var
& var
,
622 const std::vector
<PP_Var
>& graph
) {
623 if (var
.type
!= PP_VARTYPE_DICTIONARY
) {
627 DictionaryVar
* dictionary_var
= DictionaryVar::FromPPVar(var
);
628 DCHECK(dictionary_var
->key_value_map().empty());
629 for (size_t i
= 0; i
< children_
.size(); ++i
) {
630 bool success
= dictionary_var
->SetWithStringKey(children_
[i
].first
,
631 graph
[children_
[i
].second
]);
636 void DictionaryRawVarData::Write(
638 const HandleWriter
& handle_writer
) {
639 m
->WriteUInt32(static_cast<uint32_t>(children_
.size()));
640 for (size_t i
= 0; i
< children_
.size(); ++i
) {
641 m
->WriteString(children_
[i
].first
);
642 m
->WriteUInt32(static_cast<uint32_t>(children_
[i
].second
));
646 bool DictionaryRawVarData::Read(PP_VarType type
,
647 const IPC::Message
* m
,
648 PickleIterator
* iter
) {
650 if (!m
->ReadUInt32(iter
, &size
))
652 for (uint32_t i
= 0; i
< size
; ++i
) {
655 if (!m
->ReadString(iter
, &key
))
657 if (!m
->ReadUInt32(iter
, &value
))
659 children_
.push_back(make_pair(key
, value
));