1 // Copyright 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. 4 5 #include "content/child/npapi/npobject_proxy.h" 6 7 #include "content/child/npapi/np_channel_base.h" 8 #include "content/child/npapi/npobject_util.h" 9 #include "content/child/plugin_messages.h" 10 #include "third_party/WebKit/public/web/WebBindings.h" 11 12 #if defined(ENABLE_PLUGINS) 13 #include "content/child/npapi/plugin_instance.h" 14 #endif 15 16 using blink::WebBindings; 17 18 namespace content { 19 20 struct NPObjectWrapper { 21 NPObject object; 22 NPObjectProxy* proxy; 23 }; 24 25 NPClass NPObjectProxy::npclass_proxy_ = { 26 NP_CLASS_STRUCT_VERSION, 27 NPObjectProxy::NPAllocate, 28 NPObjectProxy::NPDeallocate, 29 NPObjectProxy::NPPInvalidate, 30 NPObjectProxy::NPHasMethod, 31 NPObjectProxy::NPInvoke, 32 NPObjectProxy::NPInvokeDefault, 33 NPObjectProxy::NPHasProperty, 34 NPObjectProxy::NPGetProperty, 35 NPObjectProxy::NPSetProperty, 36 NPObjectProxy::NPRemoveProperty, 37 NPObjectProxy::NPNEnumerate, 38 NPObjectProxy::NPNConstruct 39 }; 40 41 NPObjectProxy* NPObjectProxy::GetProxy(NPObject* object) { 42 NPObjectProxy* proxy = NULL; 43 44 // Wrapper exists only for NPObjects that we had created. 45 if (&npclass_proxy_ == object->_class) { 46 NPObjectWrapper* wrapper = reinterpret_cast<NPObjectWrapper*>(object); 47 proxy = wrapper->proxy; 48 } 49 50 return proxy; 51 } 52 53 NPObject* NPObjectProxy::GetUnderlyingNPObject() { 54 return NULL; 55 } 56 57 IPC::Listener* NPObjectProxy::GetChannelListener() { 58 return static_cast<IPC::Listener*>(this); 59 } 60 61 NPObjectProxy::NPObjectProxy( 62 NPChannelBase* channel, 63 int route_id, 64 int render_view_id, 65 const GURL& page_url) 66 : channel_(channel), 67 route_id_(route_id), 68 render_view_id_(render_view_id), 69 page_url_(page_url) { 70 channel_->AddRoute(route_id, this, this); 71 } 72 73 NPObjectProxy::~NPObjectProxy() { 74 if (channel_.get()) { 75 // This NPObjectProxy instance is now invalid and should not be reused for 76 // requests initiated by plugins. We may receive requests for the 77 // same NPObject in the context of the outgoing NPObjectMsg_Release call. 78 // We should be creating new NPObjectProxy instances to wrap these 79 // NPObjects. 80 channel_->RemoveMappingForNPObjectProxy(route_id_); 81 channel_->RemoveRoute(route_id_); 82 Send(new NPObjectMsg_Release(route_id_)); 83 } 84 } 85 86 NPObject* NPObjectProxy::Create(NPChannelBase* channel, 87 int route_id, 88 int render_view_id, 89 const GURL& page_url, 90 NPP owner) { 91 NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>( 92 WebBindings::createObject(owner, &npclass_proxy_)); 93 obj->proxy = new NPObjectProxy(channel, route_id, render_view_id, page_url); 94 channel->AddMappingForNPObjectProxy(route_id, &obj->object); 95 return reinterpret_cast<NPObject*>(obj); 96 } 97 98 bool NPObjectProxy::Send(IPC::Message* msg) { 99 if (channel_.get()) 100 return channel_->Send(msg); 101 102 delete msg; 103 return false; 104 } 105 106 NPObject* NPObjectProxy::NPAllocate(NPP, NPClass*) { 107 return reinterpret_cast<NPObject*>(new NPObjectWrapper); 108 } 109 110 void NPObjectProxy::NPDeallocate(NPObject* npObj) { 111 NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>(npObj); 112 delete obj->proxy; 113 delete obj; 114 } 115 116 bool NPObjectProxy::OnMessageReceived(const IPC::Message& msg) { 117 NOTREACHED(); 118 return false; 119 } 120 121 void NPObjectProxy::OnChannelError() { 122 // Release our ref count of the plugin channel object, as it addrefs the 123 // process. 124 channel_ = NULL; 125 } 126 127 bool NPObjectProxy::NPHasMethod(NPObject *obj, 128 NPIdentifier name) { 129 if (obj == NULL) 130 return false; 131 132 bool result = false; 133 NPObjectProxy* proxy = GetProxy(obj); 134 135 if (!proxy) { 136 return obj->_class->hasMethod(obj, name); 137 } 138 139 NPIdentifier_Param name_param; 140 CreateNPIdentifierParam(name, &name_param); 141 142 proxy->Send(new NPObjectMsg_HasMethod(proxy->route_id(), name_param, 143 &result)); 144 return result; 145 } 146 147 bool NPObjectProxy::NPInvoke(NPObject *obj, 148 NPIdentifier name, 149 const NPVariant *args, 150 uint32_t arg_count, 151 NPVariant *result) { 152 return NPInvokePrivate(0, obj, false, name, args, arg_count, result); 153 } 154 155 bool NPObjectProxy::NPInvokeDefault(NPObject *npobj, 156 const NPVariant *args, 157 uint32_t arg_count, 158 NPVariant *result) { 159 return NPInvokePrivate(0, npobj, true, 0, args, arg_count, result); 160 } 161 162 bool NPObjectProxy::NPInvokePrivate(NPP npp, 163 NPObject *obj, 164 bool is_default, 165 NPIdentifier name, 166 const NPVariant *args, 167 uint32_t arg_count, 168 NPVariant *np_result) { 169 if (obj == NULL) 170 return false; 171 172 NPObjectProxy* proxy = GetProxy(obj); 173 if (!proxy) { 174 if (is_default) { 175 return obj->_class->invokeDefault(obj, args, arg_count, np_result); 176 } else { 177 return obj->_class->invoke(obj, name, args, arg_count, np_result); 178 } 179 } 180 181 bool result = false; 182 int render_view_id = proxy->render_view_id_; 183 NPIdentifier_Param name_param; 184 if (is_default) { 185 // The data won't actually get used, but set it so we don't send random 186 // data. 187 name_param.identifier = NULL; 188 } else { 189 CreateNPIdentifierParam(name, &name_param); 190 } 191 192 // Note: This instance can get destroyed in the context of 193 // Send so addref the channel in this scope. 194 scoped_refptr<NPChannelBase> channel_copy = proxy->channel_; 195 std::vector<NPVariant_Param> args_param; 196 for (unsigned int i = 0; i < arg_count; ++i) { 197 NPVariant_Param param; 198 CreateNPVariantParam(args[i], 199 channel_copy.get(), 200 ¶m, 201 false, 202 render_view_id, 203 proxy->page_url_); 204 args_param.push_back(param); 205 } 206 207 NPVariant_Param param_result; 208 NPObjectMsg_Invoke* msg = new NPObjectMsg_Invoke( 209 proxy->route_id_, is_default, name_param, args_param, ¶m_result, 210 &result); 211 212 // If we're in the plugin process and this invoke leads to a dialog box, the 213 // plugin will hang the window hierarchy unless we pump the window message 214 // queue while waiting for a reply. We need to do this to simulate what 215 // happens when everything runs in-process (while calling MessageBox window 216 // messages are pumped). 217 if (IsPluginProcess() && proxy->channel()) { 218 msg->set_pump_messages_event( 219 proxy->channel()->GetModalDialogEvent(render_view_id)); 220 } 221 222 GURL page_url = proxy->page_url_; 223 proxy->Send(msg); 224 225 // Send may delete proxy. 226 proxy = NULL; 227 228 if (!result) 229 return false; 230 231 CreateNPVariant( 232 param_result, channel_copy.get(), np_result, render_view_id, page_url); 233 return true; 234 } 235 236 bool NPObjectProxy::NPHasProperty(NPObject *obj, 237 NPIdentifier name) { 238 if (obj == NULL) 239 return false; 240 241 bool result = false; 242 NPObjectProxy* proxy = GetProxy(obj); 243 if (!proxy) { 244 return obj->_class->hasProperty(obj, name); 245 } 246 247 NPIdentifier_Param name_param; 248 CreateNPIdentifierParam(name, &name_param); 249 250 NPVariant_Param param; 251 proxy->Send(new NPObjectMsg_HasProperty( 252 proxy->route_id(), name_param, &result)); 253 254 // Send may delete proxy. 255 proxy = NULL; 256 257 return result; 258 } 259 260 bool NPObjectProxy::NPGetProperty(NPObject *obj, 261 NPIdentifier name, 262 NPVariant *np_result) { 263 // Please refer to http://code.google.com/p/chromium/issues/detail?id=2556, 264 // which was a crash in the XStandard plugin during plugin shutdown. The 265 // crash occured because the plugin requests the plugin script object, 266 // which fails. The plugin does not check the result of the operation and 267 // invokes NPN_GetProperty on a NULL object which lead to the crash. If 268 // we observe similar crashes in other methods in the future, these null 269 // checks may have to be replicated in the other methods in this class. 270 if (obj == NULL) 271 return false; 272 273 NPObjectProxy* proxy = GetProxy(obj); 274 if (!proxy) { 275 return obj->_class->getProperty(obj, name, np_result); 276 } 277 278 bool result = false; 279 int render_view_id = proxy->render_view_id_; 280 NPIdentifier_Param name_param; 281 CreateNPIdentifierParam(name, &name_param); 282 283 NPVariant_Param param; 284 scoped_refptr<NPChannelBase> channel(proxy->channel_); 285 286 GURL page_url = proxy->page_url_; 287 proxy->Send(new NPObjectMsg_GetProperty( 288 proxy->route_id(), name_param, ¶m, &result)); 289 // Send may delete proxy. 290 proxy = NULL; 291 if (!result) 292 return false; 293 294 CreateNPVariant( 295 param, channel.get(), np_result, render_view_id, page_url); 296 297 return true; 298 } 299 300 bool NPObjectProxy::NPSetProperty(NPObject *obj, 301 NPIdentifier name, 302 const NPVariant *value) { 303 if (obj == NULL) 304 return false; 305 306 NPObjectProxy* proxy = GetProxy(obj); 307 if (!proxy) { 308 return obj->_class->setProperty(obj, name, value); 309 } 310 311 bool result = false; 312 int render_view_id = proxy->render_view_id_; 313 NPIdentifier_Param name_param; 314 CreateNPIdentifierParam(name, &name_param); 315 316 NPVariant_Param value_param; 317 CreateNPVariantParam( 318 *value, proxy->channel(), &value_param, false, render_view_id, 319 proxy->page_url_); 320 321 proxy->Send(new NPObjectMsg_SetProperty( 322 proxy->route_id(), name_param, value_param, &result)); 323 // Send may delete proxy. 324 proxy = NULL; 325 326 return result; 327 } 328 329 bool NPObjectProxy::NPRemoveProperty(NPObject *obj, 330 NPIdentifier name) { 331 if (obj == NULL) 332 return false; 333 334 bool result = false; 335 NPObjectProxy* proxy = GetProxy(obj); 336 if (!proxy) { 337 return obj->_class->removeProperty(obj, name); 338 } 339 340 NPIdentifier_Param name_param; 341 CreateNPIdentifierParam(name, &name_param); 342 343 NPVariant_Param param; 344 proxy->Send(new NPObjectMsg_RemoveProperty( 345 proxy->route_id(), name_param, &result)); 346 // Send may delete proxy. 347 proxy = NULL; 348 349 return result; 350 } 351 352 void NPObjectProxy::NPPInvalidate(NPObject *obj) { 353 if (obj == NULL) 354 return; 355 356 NPObjectProxy* proxy = GetProxy(obj); 357 if (!proxy) { 358 obj->_class->invalidate(obj); 359 return; 360 } 361 362 proxy->Send(new NPObjectMsg_Invalidate(proxy->route_id())); 363 // Send may delete proxy. 364 proxy = NULL; 365 } 366 367 bool NPObjectProxy::NPNEnumerate(NPObject *obj, 368 NPIdentifier **value, 369 uint32_t *count) { 370 if (obj == NULL) 371 return false; 372 373 bool result = false; 374 NPObjectProxy* proxy = GetProxy(obj); 375 if (!proxy) { 376 if (obj->_class->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM) { 377 return obj->_class->enumerate(obj, value, count); 378 } else { 379 return false; 380 } 381 } 382 383 std::vector<NPIdentifier_Param> value_param; 384 proxy->Send(new NPObjectMsg_Enumeration( 385 proxy->route_id(), &value_param, &result)); 386 // Send may delete proxy. 387 proxy = NULL; 388 389 if (!result) 390 return false; 391 392 *count = static_cast<unsigned int>(value_param.size()); 393 *value = static_cast<NPIdentifier *>(malloc(sizeof(NPIdentifier) * *count)); 394 for (unsigned int i = 0; i < *count; ++i) 395 (*value)[i] = CreateNPIdentifier(value_param[i]); 396 397 return true; 398 } 399 400 bool NPObjectProxy::NPNConstruct(NPObject *obj, 401 const NPVariant *args, 402 uint32_t arg_count, 403 NPVariant *np_result) { 404 if (obj == NULL) 405 return false; 406 407 NPObjectProxy* proxy = GetProxy(obj); 408 if (!proxy) { 409 if (obj->_class->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR) { 410 return obj->_class->construct(obj, args, arg_count, np_result); 411 } else { 412 return false; 413 } 414 } 415 416 bool result = false; 417 int render_view_id = proxy->render_view_id_; 418 419 // Note: This instance can get destroyed in the context of 420 // Send so addref the channel in this scope. 421 scoped_refptr<NPChannelBase> channel_copy = proxy->channel_; 422 std::vector<NPVariant_Param> args_param; 423 for (unsigned int i = 0; i < arg_count; ++i) { 424 NPVariant_Param param; 425 CreateNPVariantParam(args[i], 426 channel_copy.get(), 427 ¶m, 428 false, 429 render_view_id, 430 proxy->page_url_); 431 args_param.push_back(param); 432 } 433 434 NPVariant_Param param_result; 435 NPObjectMsg_Construct* msg = new NPObjectMsg_Construct( 436 proxy->route_id_, args_param, ¶m_result, &result); 437 438 // See comment in NPObjectProxy::NPInvokePrivate. 439 if (IsPluginProcess() && proxy->channel()) { 440 msg->set_pump_messages_event( 441 proxy->channel()->GetModalDialogEvent(proxy->render_view_id_)); 442 } 443 444 GURL page_url = proxy->page_url_; 445 proxy->Send(msg); 446 447 // Send may delete proxy. 448 proxy = NULL; 449 450 if (!result) 451 return false; 452 453 CreateNPVariant( 454 param_result, channel_copy.get(), np_result, render_view_id, page_url); 455 return true; 456 } 457 458 bool NPObjectProxy::NPNEvaluate(NPP npp, 459 NPObject *obj, 460 NPString *script, 461 NPVariant *result_var) { 462 NPObjectProxy* proxy = GetProxy(obj); 463 if (!proxy) { 464 return false; 465 } 466 467 bool result = false; 468 int render_view_id = proxy->render_view_id_; 469 bool popups_allowed = false; 470 471 #if defined(ENABLE_PLUGINS) 472 if (npp) { 473 PluginInstance* plugin_instance = 474 reinterpret_cast<PluginInstance*>(npp->ndata); 475 if (plugin_instance) 476 popups_allowed = plugin_instance->popups_allowed(); 477 } 478 #endif 479 480 NPVariant_Param result_param; 481 std::string script_str = std::string( 482 script->UTF8Characters, script->UTF8Length); 483 484 NPObjectMsg_Evaluate* msg = new NPObjectMsg_Evaluate(proxy->route_id(), 485 script_str, 486 popups_allowed, 487 &result_param, 488 &result); 489 490 // See comment in NPObjectProxy::NPInvokePrivate. 491 if (IsPluginProcess() && proxy->channel()) { 492 msg->set_pump_messages_event( 493 proxy->channel()->GetModalDialogEvent(render_view_id)); 494 } 495 scoped_refptr<NPChannelBase> channel(proxy->channel_); 496 497 GURL page_url = proxy->page_url_; 498 proxy->Send(msg); 499 // Send may delete proxy. 500 proxy = NULL; 501 if (!result) 502 return false; 503 504 CreateNPVariant( 505 result_param, channel.get(), result_var, render_view_id, page_url); 506 return true; 507 } 508 509 } // namespace content 510