| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ppapi/proxy/serialized_var.h" | 5 #include "ppapi/proxy/serialized_var.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "ipc/ipc_message_utils.h" | 8 #include "ipc/ipc_message_utils.h" |
| 9 #include "ppapi/c/pp_instance.h" | 9 #include "ppapi/c/pp_instance.h" |
| 10 #include "ppapi/proxy/dispatcher.h" | 10 #include "ppapi/proxy/dispatcher.h" |
| 11 #include "ppapi/proxy/interface_proxy.h" | 11 #include "ppapi/proxy/interface_proxy.h" |
| 12 #include "ppapi/proxy/ppapi_param_traits.h" | 12 #include "ppapi/proxy/ppapi_param_traits.h" |
| 13 #include "ppapi/proxy/ppb_buffer_proxy.h" | 13 #include "ppapi/proxy/ppb_buffer_proxy.h" |
| 14 #include "ppapi/shared_impl/ppapi_globals.h" | 14 #include "ppapi/shared_impl/ppapi_globals.h" |
| 15 #include "ppapi/shared_impl/var.h" | 15 #include "ppapi/shared_impl/var.h" |
| 16 #include "ppapi/thunk/enter.h" | 16 #include "ppapi/thunk/enter.h" |
| 17 | 17 |
| 18 namespace ppapi { | 18 namespace ppapi { |
| 19 namespace proxy { | 19 namespace proxy { |
| 20 | 20 |
| 21 // When sending array buffers, if the size is over 256K, we use shared | |
| 22 // memory instead of sending the data over IPC. Light testing suggests | |
| 23 // shared memory is much faster for 256K and larger messages. | |
| 24 static const uint32 kMinimumArrayBufferSizeForShmem = 256 * 1024; | |
| 25 | |
| 26 // SerializedVar::Inner -------------------------------------------------------- | 21 // SerializedVar::Inner -------------------------------------------------------- |
| 27 | 22 |
| 28 SerializedVar::Inner::Inner() | 23 SerializedVar::Inner::Inner() |
| 29 : serialization_rules_(NULL), | 24 : serialization_rules_(NULL), |
| 30 var_(PP_MakeUndefined()), | 25 var_(PP_MakeUndefined()), |
| 31 instance_(0), | 26 instance_(0), |
| 32 cleanup_mode_(CLEANUP_NONE) { | 27 cleanup_mode_(CLEANUP_NONE) { |
| 33 #ifndef NDEBUG | 28 #ifndef NDEBUG |
| 34 has_been_serialized_ = false; | 29 has_been_serialized_ = false; |
| 35 has_been_deserialized_ = false; | 30 has_been_deserialized_ = false; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 56 serialization_rules_->EndReceiveCallerOwned(var_); | 51 serialization_rules_->EndReceiveCallerOwned(var_); |
| 57 break; | 52 break; |
| 58 default: | 53 default: |
| 59 break; | 54 break; |
| 60 } | 55 } |
| 61 } | 56 } |
| 62 | 57 |
| 63 PP_Var SerializedVar::Inner::GetVar() { | 58 PP_Var SerializedVar::Inner::GetVar() { |
| 64 DCHECK(serialization_rules_); | 59 DCHECK(serialization_rules_); |
| 65 | 60 |
| 66 ConvertRawVarData(); | 61 #if defined(NACL_WIN64) |
| 62 NOTREACHED(); |
| 63 return PP_MakeUndefined(); |
| 64 #endif |
| 65 |
| 66 if (raw_var_data_.get()) { |
| 67 var_ = raw_var_data_->CreatePPVar(instance_); |
| 68 raw_var_data_.reset(NULL); |
| 69 } |
| 70 |
| 67 return var_; | 71 return var_; |
| 68 } | 72 } |
| 69 | 73 |
| 70 void SerializedVar::Inner::SetVar(PP_Var var) { | 74 void SerializedVar::Inner::SetVar(PP_Var var) { |
| 71 // Sanity check, when updating the var we should have received a | 75 // Sanity check, when updating the var we should have received a |
| 72 // serialization rules pointer already. | 76 // serialization rules pointer already. |
| 73 DCHECK(serialization_rules_); | 77 DCHECK(serialization_rules_); |
| 74 var_ = var; | 78 var_ = var; |
| 75 raw_var_data_.reset(NULL); | 79 raw_var_data_.reset(NULL); |
| 76 } | 80 } |
| 77 | 81 |
| 78 void SerializedVar::Inner::SetInstance(PP_Instance instance) { | 82 void SerializedVar::Inner::SetInstance(PP_Instance instance) { |
| 79 instance_ = instance; | 83 instance_ = instance; |
| 80 } | 84 } |
| 81 | 85 |
| 82 void SerializedVar::Inner::ForceSetVarValueForTest(PP_Var value) { | 86 void SerializedVar::Inner::ForceSetVarValueForTest(PP_Var value) { |
| 83 var_ = value; | 87 var_ = value; |
| 84 raw_var_data_.reset(NULL); | 88 raw_var_data_.reset(NULL); |
| 85 } | 89 } |
| 86 | 90 |
| 87 void SerializedVar::Inner::WriteRawVarHeader(IPC::Message* m) const { | |
| 88 // Write raw_var_data_ when we're called from | |
| 89 // chrome/nacl/nacl_ipc_adapter.cc. | |
| 90 DCHECK(raw_var_data_.get()); | |
| 91 DCHECK_EQ(PP_VARTYPE_ARRAY_BUFFER, raw_var_data_->type); | |
| 92 DCHECK(raw_var_data_->shmem_size != 0); | |
| 93 | |
| 94 // The serialization for this message MUST MATCH the implementation at | |
| 95 // SerializedVar::Inner::WriteToMessage for ARRAY_BUFFER_SHMEM_PLUGIN. | |
| 96 m->WriteInt(static_cast<int>(raw_var_data_->type)); | |
| 97 m->WriteInt(ARRAY_BUFFER_SHMEM_PLUGIN); | |
| 98 m->WriteInt(raw_var_data_->shmem_size); | |
| 99 // NaClIPCAdapter will write the handles for us. | |
| 100 } | |
| 101 | |
| 102 void SerializedVar::Inner::WriteToMessage(IPC::Message* m) const { | 91 void SerializedVar::Inner::WriteToMessage(IPC::Message* m) const { |
| 103 // When writing to the IPC messages, a serialization rules handler should | 92 // When writing to the IPC messages, a serialization rules handler should |
| 104 // always have been set. | 93 // always have been set. |
| 105 // | 94 // |
| 106 // When sending a message, it should be difficult to trigger this if you're | 95 // When sending a message, it should be difficult to trigger this if you're |
| 107 // using the SerializedVarSendInput class and giving a non-NULL dispatcher. | 96 // using the SerializedVarSendInput class and giving a non-NULL dispatcher. |
| 108 // Make sure you're using the proper "Send" helper class. | 97 // Make sure you're using the proper "Send" helper class. |
| 109 // | 98 // |
| 110 // It should be more common to see this when handling an incoming message | 99 // It should be more common to see this when handling an incoming message |
| 111 // that returns a var. This means the message handler didn't write to the | 100 // that returns a var. This means the message handler didn't write to the |
| 112 // output parameter, or possibly you used the wrong helper class | 101 // output parameter, or possibly you used the wrong helper class |
| 113 // (normally SerializedVarReturnValue). | 102 // (normally SerializedVarReturnValue). |
| 114 DCHECK(serialization_rules_); | 103 DCHECK(serialization_rules_); |
| 115 | 104 |
| 116 #ifndef NDEBUG | 105 #ifndef NDEBUG |
| 117 // We should only be serializing something once. | 106 // We should only be serializing something once. |
| 118 DCHECK(!has_been_serialized_); | 107 DCHECK(!has_been_serialized_); |
| 119 has_been_serialized_ = true; | 108 has_been_serialized_ = true; |
| 120 #endif | 109 #endif |
| 121 | 110 RawVarDataGraph::Create(var_, instance_)->Write(m); |
| 122 DCHECK(!raw_var_data_.get()); | |
| 123 m->WriteInt(static_cast<int>(var_.type)); | |
| 124 switch (var_.type) { | |
| 125 case PP_VARTYPE_UNDEFINED: | |
| 126 case PP_VARTYPE_NULL: | |
| 127 // These don't need any data associated with them other than the type we | |
| 128 // just serialized. | |
| 129 break; | |
| 130 case PP_VARTYPE_BOOL: | |
| 131 m->WriteBool(PP_ToBool(var_.value.as_bool)); | |
| 132 break; | |
| 133 case PP_VARTYPE_INT32: | |
| 134 m->WriteInt(var_.value.as_int); | |
| 135 break; | |
| 136 case PP_VARTYPE_DOUBLE: | |
| 137 IPC::ParamTraits<double>::Write(m, var_.value.as_double); | |
| 138 break; | |
| 139 case PP_VARTYPE_STRING: { | |
| 140 // TODO(brettw) in the case of an invalid string ID, it would be nice | |
| 141 // to send something to the other side such that a 0 ID would be | |
| 142 // generated there. Then the function implementing the interface can | |
| 143 // handle the invalid string as if it was in process rather than seeing | |
| 144 // what looks like a valid empty string. | |
| 145 StringVar* string_var = StringVar::FromPPVar(var_); | |
| 146 m->WriteString(string_var ? *string_var->ptr() : std::string()); | |
| 147 break; | |
| 148 } | |
| 149 case PP_VARTYPE_ARRAY_BUFFER: { | |
| 150 // TODO(dmichael) in the case of an invalid var ID, it would be nice | |
| 151 // to send something to the other side such that a 0 ID would be | |
| 152 // generated there. Then the function implementing the interface can | |
| 153 // handle the invalid string as if it was in process rather than seeing | |
| 154 // what looks like a valid empty ArraryBuffer. | |
| 155 ArrayBufferVar* buffer_var = ArrayBufferVar::FromPPVar(var_); | |
| 156 bool using_shmem = false; | |
| 157 if (buffer_var && | |
| 158 buffer_var->ByteLength() >= kMinimumArrayBufferSizeForShmem && | |
| 159 instance_ != 0) { | |
| 160 int host_shm_handle_id; | |
| 161 base::SharedMemoryHandle plugin_shm_handle; | |
| 162 using_shmem = buffer_var->CopyToNewShmem(instance_, | |
| 163 &host_shm_handle_id, | |
| 164 &plugin_shm_handle); | |
| 165 if (using_shmem) { | |
| 166 // The serialization for this message MUST MATCH the implementation | |
| 167 // at SerializedVar::Inner::WriteRawVarHeader for | |
| 168 // ARRAY_BUFFER_SHMEM_PLUGIN. | |
| 169 if (host_shm_handle_id != -1) { | |
| 170 DCHECK(!base::SharedMemory::IsHandleValid(plugin_shm_handle)); | |
| 171 DCHECK(PpapiGlobals::Get()->IsPluginGlobals()); | |
| 172 m->WriteInt(ARRAY_BUFFER_SHMEM_HOST); | |
| 173 m->WriteInt(host_shm_handle_id); | |
| 174 } else { | |
| 175 DCHECK(base::SharedMemory::IsHandleValid(plugin_shm_handle)); | |
| 176 DCHECK(PpapiGlobals::Get()->IsHostGlobals()); | |
| 177 m->WriteInt(ARRAY_BUFFER_SHMEM_PLUGIN); | |
| 178 m->WriteInt(buffer_var->ByteLength()); | |
| 179 SerializedHandle handle(plugin_shm_handle, | |
| 180 buffer_var->ByteLength()); | |
| 181 IPC::ParamTraits<SerializedHandle>::Write(m, handle); | |
| 182 } | |
| 183 } | |
| 184 } | |
| 185 if (!using_shmem) { | |
| 186 if (buffer_var) { | |
| 187 m->WriteInt(ARRAY_BUFFER_NO_SHMEM); | |
| 188 m->WriteData(static_cast<const char*>(buffer_var->Map()), | |
| 189 buffer_var->ByteLength()); | |
| 190 } else { | |
| 191 // TODO(teravest): Introduce an ARRAY_BUFFER_EMPTY message type. | |
| 192 m->WriteBool(ARRAY_BUFFER_NO_SHMEM); | |
| 193 m->WriteData(NULL, 0); | |
| 194 } | |
| 195 } | |
| 196 break; | |
| 197 } | |
| 198 case PP_VARTYPE_OBJECT: | |
| 199 m->WriteInt64(var_.value.as_id); | |
| 200 break; | |
| 201 case PP_VARTYPE_ARRAY: | |
| 202 case PP_VARTYPE_DICTIONARY: | |
| 203 // TODO(yzshen) when these are supported, implement this. | |
| 204 NOTIMPLEMENTED(); | |
| 205 break; | |
| 206 } | |
| 207 } | 111 } |
| 208 | 112 |
| 209 bool SerializedVar::Inner::ReadFromMessage(const IPC::Message* m, | 113 bool SerializedVar::Inner::ReadFromMessage(const IPC::Message* m, |
| 210 PickleIterator* iter) { | 114 PickleIterator* iter) { |
| 211 #ifndef NDEBUG | 115 #ifndef NDEBUG |
| 212 // We should only deserialize something once or will end up with leaked | 116 // We should only deserialize something once or will end up with leaked |
| 213 // references. | 117 // references. |
| 214 // | 118 // |
| 215 // One place this has happened in the past is using | 119 // One place this has happened in the past is using |
| 216 // std::vector<SerializedVar>.resize(). If you're doing this manually instead | 120 // std::vector<SerializedVar>.resize(). If you're doing this manually instead |
| 217 // of using the helper classes for handling in/out vectors of vars, be | 121 // of using the helper classes for handling in/out vectors of vars, be |
| 218 // sure you use the same pattern as the SerializedVarVector classes. | 122 // sure you use the same pattern as the SerializedVarVector classes. |
| 219 DCHECK(!has_been_deserialized_); | 123 DCHECK(!has_been_deserialized_); |
| 220 has_been_deserialized_ = true; | 124 has_been_deserialized_ = true; |
| 221 #endif | 125 #endif |
| 222 // When reading, the dispatcher should be set when we get a Deserialize | 126 // When reading, the dispatcher should be set when we get a Deserialize |
| 223 // call (which will supply a dispatcher). | 127 // call (which will supply a dispatcher). |
| 224 int type; | 128 raw_var_data_ = RawVarDataGraph::Read(m, iter); |
| 225 if (!m->ReadInt(iter, &type)) | 129 return raw_var_data_.get() != NULL; |
| 226 return false; | |
| 227 | |
| 228 bool success = false; | |
| 229 switch (type) { | |
| 230 case PP_VARTYPE_UNDEFINED: | |
| 231 case PP_VARTYPE_NULL: | |
| 232 // These don't have any data associated with them other than the type we | |
| 233 // just serialized. | |
| 234 success = true; | |
| 235 break; | |
| 236 case PP_VARTYPE_BOOL: { | |
| 237 bool bool_value; | |
| 238 success = m->ReadBool(iter, &bool_value); | |
| 239 var_.value.as_bool = PP_FromBool(bool_value); | |
| 240 break; | |
| 241 } | |
| 242 case PP_VARTYPE_INT32: | |
| 243 success = m->ReadInt(iter, &var_.value.as_int); | |
| 244 break; | |
| 245 case PP_VARTYPE_DOUBLE: | |
| 246 success = IPC::ParamTraits<double>::Read(m, iter, &var_.value.as_double); | |
| 247 break; | |
| 248 case PP_VARTYPE_STRING: { | |
| 249 raw_var_data_.reset(new RawVarData); | |
| 250 raw_var_data_->type = PP_VARTYPE_STRING; | |
| 251 success = m->ReadString(iter, &raw_var_data_->data); | |
| 252 if (!success) | |
| 253 raw_var_data_.reset(NULL); | |
| 254 break; | |
| 255 } | |
| 256 case PP_VARTYPE_ARRAY_BUFFER: { | |
| 257 int length = 0; | |
| 258 const char* message_bytes = NULL; | |
| 259 int shmem_type; | |
| 260 success = m->ReadInt(iter, &shmem_type); | |
| 261 if (success) { | |
| 262 if (shmem_type == ARRAY_BUFFER_NO_SHMEM) { | |
| 263 success = m->ReadData(iter, &message_bytes, &length); | |
| 264 if (success) { | |
| 265 raw_var_data_.reset(new RawVarData); | |
| 266 raw_var_data_->type = PP_VARTYPE_ARRAY_BUFFER; | |
| 267 raw_var_data_->shmem_type = static_cast<ShmemType>(shmem_type); | |
| 268 raw_var_data_->shmem_size = 0; | |
| 269 raw_var_data_->data.assign(message_bytes, length); | |
| 270 } | |
| 271 } else if (shmem_type == ARRAY_BUFFER_SHMEM_HOST) { | |
| 272 int host_handle_id; | |
| 273 success = m->ReadInt(iter, &host_handle_id); | |
| 274 if (success) { | |
| 275 raw_var_data_.reset(new RawVarData); | |
| 276 raw_var_data_->type = PP_VARTYPE_ARRAY_BUFFER; | |
| 277 raw_var_data_->shmem_type = static_cast<ShmemType>(shmem_type); | |
| 278 raw_var_data_->host_handle_id = host_handle_id; | |
| 279 } | |
| 280 } else if (shmem_type == ARRAY_BUFFER_SHMEM_PLUGIN) { | |
| 281 SerializedHandle plugin_handle; | |
| 282 success = m->ReadInt(iter, &length); | |
| 283 success &= IPC::ParamTraits<SerializedHandle>::Read( | |
| 284 m, iter, &plugin_handle); | |
| 285 if (success) { | |
| 286 raw_var_data_.reset(new RawVarData); | |
| 287 raw_var_data_->type = PP_VARTYPE_ARRAY_BUFFER; | |
| 288 raw_var_data_->shmem_type = static_cast<ShmemType>(shmem_type); | |
| 289 raw_var_data_->shmem_size = length; | |
| 290 raw_var_data_->plugin_handle = plugin_handle; | |
| 291 } | |
| 292 } | |
| 293 } | |
| 294 break; | |
| 295 } | |
| 296 case PP_VARTYPE_OBJECT: | |
| 297 success = m->ReadInt64(iter, &var_.value.as_id); | |
| 298 break; | |
| 299 case PP_VARTYPE_ARRAY: | |
| 300 case PP_VARTYPE_DICTIONARY: | |
| 301 // TODO(yzshen) when these types are supported, implement this. | |
| 302 NOTIMPLEMENTED(); | |
| 303 break; | |
| 304 default: | |
| 305 // Leave success as false. | |
| 306 break; | |
| 307 } | |
| 308 | |
| 309 // All success cases get here. We avoid writing the type above so that the | |
| 310 // output param is untouched (defaults to VARTYPE_UNDEFINED) even in the | |
| 311 // failure case. | |
| 312 // We also don't write the type if |raw_var_data_| is set. |var_| will be | |
| 313 // updated lazily when GetVar() is called. | |
| 314 if (success && !raw_var_data_.get()) | |
| 315 var_.type = static_cast<PP_VarType>(type); | |
| 316 return success; | |
| 317 } | 130 } |
| 318 | 131 |
| 319 void SerializedVar::Inner::SetCleanupModeToEndSendPassRef() { | 132 void SerializedVar::Inner::SetCleanupModeToEndSendPassRef() { |
| 320 cleanup_mode_ = END_SEND_PASS_REF; | 133 cleanup_mode_ = END_SEND_PASS_REF; |
| 321 } | 134 } |
| 322 | 135 |
| 323 void SerializedVar::Inner::SetCleanupModeToEndReceiveCallerOwned() { | 136 void SerializedVar::Inner::SetCleanupModeToEndReceiveCallerOwned() { |
| 324 cleanup_mode_ = END_RECEIVE_CALLER_OWNED; | 137 cleanup_mode_ = END_RECEIVE_CALLER_OWNED; |
| 325 } | 138 } |
| 326 | 139 |
| 327 void SerializedVar::Inner::ConvertRawVarData() { | |
| 328 #if defined(NACL_WIN64) | |
| 329 NOTREACHED(); | |
| 330 #else | |
| 331 if (!raw_var_data_.get()) | |
| 332 return; | |
| 333 | |
| 334 DCHECK_EQ(PP_VARTYPE_UNDEFINED, var_.type); | |
| 335 switch (raw_var_data_->type) { | |
| 336 case PP_VARTYPE_STRING: { | |
| 337 var_ = StringVar::SwapValidatedUTF8StringIntoPPVar( | |
| 338 &raw_var_data_->data); | |
| 339 break; | |
| 340 } | |
| 341 case PP_VARTYPE_ARRAY_BUFFER: { | |
| 342 if (raw_var_data_->shmem_type == ARRAY_BUFFER_SHMEM_HOST) { | |
| 343 base::SharedMemoryHandle host_handle; | |
| 344 uint32 size_in_bytes; | |
| 345 bool ok = | |
| 346 PpapiGlobals::Get()->GetVarTracker()-> | |
| 347 StopTrackingSharedMemoryHandle(raw_var_data_->host_handle_id, | |
| 348 instance_, | |
| 349 &host_handle, | |
| 350 &size_in_bytes); | |
| 351 if (ok) { | |
| 352 var_ = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( | |
| 353 size_in_bytes, host_handle); | |
| 354 } else { | |
| 355 LOG(ERROR) << "Couldn't find array buffer id: " | |
| 356 << raw_var_data_->host_handle_id; | |
| 357 var_ = PP_MakeUndefined(); | |
| 358 } | |
| 359 } else if (raw_var_data_->shmem_type == ARRAY_BUFFER_SHMEM_PLUGIN) { | |
| 360 var_ = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( | |
| 361 raw_var_data_->shmem_size, | |
| 362 raw_var_data_->plugin_handle.shmem()); | |
| 363 } else { | |
| 364 var_ = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( | |
| 365 static_cast<uint32>(raw_var_data_->data.size()), | |
| 366 raw_var_data_->data.data()); | |
| 367 } | |
| 368 break; | |
| 369 } | |
| 370 default: | |
| 371 NOTREACHED(); | |
| 372 } | |
| 373 raw_var_data_.reset(NULL); | |
| 374 #endif | |
| 375 } | |
| 376 | |
| 377 SerializedHandle* SerializedVar::Inner::GetPluginShmemHandle() const { | |
| 378 if (raw_var_data_.get()) { | |
| 379 if (raw_var_data_->type == PP_VARTYPE_ARRAY_BUFFER) { | |
| 380 if (raw_var_data_->shmem_size != 0) | |
| 381 return &raw_var_data_->plugin_handle; | |
| 382 } | |
| 383 } | |
| 384 return NULL; | |
| 385 } | |
| 386 | |
| 387 // SerializedVar --------------------------------------------------------------- | 140 // SerializedVar --------------------------------------------------------------- |
| 388 | 141 |
| 389 SerializedVar::SerializedVar() : inner_(new Inner) { | 142 SerializedVar::SerializedVar() : inner_(new Inner) { |
| 390 } | 143 } |
| 391 | 144 |
| 392 SerializedVar::SerializedVar(VarSerializationRules* serialization_rules) | 145 SerializedVar::SerializedVar(VarSerializationRules* serialization_rules) |
| 393 : inner_(new Inner(serialization_rules)) { | 146 : inner_(new Inner(serialization_rules)) { |
| 394 } | 147 } |
| 395 | 148 |
| 396 SerializedVar::~SerializedVar() { | 149 SerializedVar::~SerializedVar() { |
| (...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 674 const std::string& str) { | 427 const std::string& str) { |
| 675 inner_->ForceSetVarValueForTest(StringVar::StringToPPVar(str)); | 428 inner_->ForceSetVarValueForTest(StringVar::StringToPPVar(str)); |
| 676 } | 429 } |
| 677 | 430 |
| 678 SerializedVarTestReader::SerializedVarTestReader(const SerializedVar& var) | 431 SerializedVarTestReader::SerializedVarTestReader(const SerializedVar& var) |
| 679 : SerializedVar(var) { | 432 : SerializedVar(var) { |
| 680 } | 433 } |
| 681 | 434 |
| 682 } // namespace proxy | 435 } // namespace proxy |
| 683 } // namespace ppapi | 436 } // namespace ppapi |
| OLD | NEW |