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_stub.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 "content/public/common/content_client.h" 11 #include "content/public/common/content_switches.h" 12 #include "third_party/WebKit/public/web/WebBindings.h" 13 #include "third_party/npapi/bindings/npapi.h" 14 #include "third_party/npapi/bindings/npruntime.h" 15 16 #if defined(OS_WIN) 17 #include "base/command_line.h" 18 #include "content/common/plugin_constants_win.h" 19 #endif 20 21 using blink::WebBindings; 22 23 namespace content { 24 25 NPObjectStub::NPObjectStub( 26 NPObject* npobject, 27 NPChannelBase* channel, 28 int route_id, 29 int render_view_id, 30 const GURL& page_url) 31 : npobject_(npobject), 32 channel_(channel), 33 route_id_(route_id), 34 render_view_id_(render_view_id), 35 page_url_(page_url) { 36 channel_->AddMappingForNPObjectStub(route_id, npobject); 37 channel_->AddRoute(route_id, this, this); 38 39 // We retain the object just as PluginHost does if everything was in-process. 40 WebBindings::retainObject(npobject_); 41 } 42 43 NPObjectStub::~NPObjectStub() { 44 channel_->RemoveRoute(route_id_); 45 DCHECK(!npobject_); 46 } 47 48 void NPObjectStub::DeleteSoon() { 49 if (npobject_) { 50 channel_->RemoveMappingForNPObjectStub(route_id_, npobject_); 51 52 // We need to NULL npobject_ prior to calling releaseObject() to avoid 53 // problems with re-entrancy. See http://crbug.com/94179#c17 for more 54 // details on how this can happen. 55 NPObject* npobject = npobject_; 56 npobject_ = NULL; 57 58 WebBindings::releaseObject(npobject); 59 60 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 61 } 62 } 63 64 bool NPObjectStub::Send(IPC::Message* msg) { 65 return channel_->Send(msg); 66 } 67 68 NPObject* NPObjectStub::GetUnderlyingNPObject() { 69 return npobject_; 70 } 71 72 IPC::Listener* NPObjectStub::GetChannelListener() { 73 return static_cast<IPC::Listener*>(this); 74 } 75 76 bool NPObjectStub::OnMessageReceived(const IPC::Message& msg) { 77 GetContentClient()->SetActiveURL(page_url_); 78 if (!npobject_) { 79 if (msg.is_sync()) { 80 // The object could be garbage because the frame has gone away, so 81 // just send an error reply to the caller. 82 IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg); 83 reply->set_reply_error(); 84 Send(reply); 85 } 86 87 return true; 88 } 89 90 bool handled = true; 91 IPC_BEGIN_MESSAGE_MAP(NPObjectStub, msg) 92 IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Release, OnRelease); 93 IPC_MESSAGE_HANDLER(NPObjectMsg_HasMethod, OnHasMethod); 94 IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Invoke, OnInvoke); 95 IPC_MESSAGE_HANDLER(NPObjectMsg_HasProperty, OnHasProperty); 96 IPC_MESSAGE_HANDLER(NPObjectMsg_GetProperty, OnGetProperty); 97 IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_SetProperty, OnSetProperty); 98 IPC_MESSAGE_HANDLER(NPObjectMsg_RemoveProperty, OnRemoveProperty); 99 IPC_MESSAGE_HANDLER(NPObjectMsg_Invalidate, OnInvalidate); 100 IPC_MESSAGE_HANDLER(NPObjectMsg_Enumeration, OnEnumeration); 101 IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Construct, OnConstruct); 102 IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Evaluate, OnEvaluate); 103 IPC_MESSAGE_UNHANDLED(handled = false) 104 IPC_END_MESSAGE_MAP() 105 DCHECK(handled); 106 return handled; 107 } 108 109 void NPObjectStub::OnChannelError() { 110 DeleteSoon(); 111 } 112 113 void NPObjectStub::OnRelease(IPC::Message* reply_msg) { 114 Send(reply_msg); 115 DeleteSoon(); 116 } 117 118 void NPObjectStub::OnHasMethod(const NPIdentifier_Param& name, 119 bool* result) { 120 NPIdentifier id = CreateNPIdentifier(name); 121 // If we're in the plugin process, then the stub is holding onto an NPObject 122 // from the plugin, so all function calls on it need to go through the 123 // functions in NPClass. If we're in the renderer process, then we just call 124 // the NPN_ functions. 125 if (IsPluginProcess()) { 126 if (npobject_->_class->hasMethod) { 127 *result = npobject_->_class->hasMethod(npobject_, id); 128 } else { 129 *result = false; 130 } 131 } else { 132 *result = WebBindings::hasMethod(0, npobject_, id); 133 } 134 } 135 136 void NPObjectStub::OnInvoke(bool is_default, 137 const NPIdentifier_Param& method, 138 const std::vector<NPVariant_Param>& args, 139 IPC::Message* reply_msg) { 140 bool return_value = false; 141 NPVariant_Param result_param; 142 NPVariant result_var; 143 144 VOID_TO_NPVARIANT(result_var); 145 result_param.type = NPVARIANT_PARAM_VOID; 146 147 int arg_count = static_cast<int>(args.size()); 148 NPVariant* args_var = new NPVariant[arg_count]; 149 for (int i = 0; i < arg_count; ++i) { 150 if (!CreateNPVariant(args[i], 151 channel_.get(), 152 &(args_var[i]), 153 render_view_id_, 154 page_url_)) { 155 NPObjectMsg_Invoke::WriteReplyParams( 156 reply_msg, result_param, return_value); 157 channel_->Send(reply_msg); 158 delete[] args_var; 159 return; 160 } 161 } 162 163 if (is_default) { 164 if (IsPluginProcess()) { 165 if (npobject_->_class->invokeDefault) { 166 return_value = npobject_->_class->invokeDefault( 167 npobject_, args_var, arg_count, &result_var); 168 } else { 169 return_value = false; 170 } 171 } else { 172 return_value = WebBindings::invokeDefault( 173 0, npobject_, args_var, arg_count, &result_var); 174 } 175 } else { 176 NPIdentifier id = CreateNPIdentifier(method); 177 if (IsPluginProcess()) { 178 if (npobject_->_class->invoke) { 179 return_value = npobject_->_class->invoke( 180 npobject_, id, args_var, arg_count, &result_var); 181 } else { 182 return_value = false; 183 } 184 } else { 185 return_value = WebBindings::invoke( 186 0, npobject_, id, args_var, arg_count, &result_var); 187 } 188 } 189 190 for (int i = 0; i < arg_count; ++i) 191 WebBindings::releaseVariantValue(&(args_var[i])); 192 193 delete[] args_var; 194 195 CreateNPVariantParam(result_var, 196 channel_.get(), 197 &result_param, 198 true, 199 render_view_id_, 200 page_url_); 201 NPObjectMsg_Invoke::WriteReplyParams(reply_msg, result_param, return_value); 202 channel_->Send(reply_msg); 203 } 204 205 void NPObjectStub::OnHasProperty(const NPIdentifier_Param& name, 206 bool* result) { 207 NPIdentifier id = CreateNPIdentifier(name); 208 if (IsPluginProcess()) { 209 if (npobject_->_class->hasProperty) { 210 *result = npobject_->_class->hasProperty(npobject_, id); 211 } else { 212 *result = false; 213 } 214 } else { 215 *result = WebBindings::hasProperty(0, npobject_, id); 216 } 217 } 218 219 void NPObjectStub::OnGetProperty(const NPIdentifier_Param& name, 220 NPVariant_Param* property, 221 bool* result) { 222 NPVariant result_var; 223 VOID_TO_NPVARIANT(result_var); 224 NPIdentifier id = CreateNPIdentifier(name); 225 226 if (IsPluginProcess()) { 227 if (npobject_->_class->getProperty) { 228 *result = npobject_->_class->getProperty(npobject_, id, &result_var); 229 } else { 230 *result = false; 231 } 232 } else { 233 *result = WebBindings::getProperty(0, npobject_, id, &result_var); 234 } 235 236 CreateNPVariantParam( 237 result_var, channel_.get(), property, true, render_view_id_, page_url_); 238 } 239 240 void NPObjectStub::OnSetProperty(const NPIdentifier_Param& name, 241 const NPVariant_Param& property, 242 IPC::Message* reply_msg) { 243 bool result = false; 244 NPIdentifier id = CreateNPIdentifier(name); 245 NPVariant property_var; 246 if (!CreateNPVariant(property, 247 channel_.get(), 248 &property_var, 249 render_view_id_, 250 page_url_)) { 251 NPObjectMsg_SetProperty::WriteReplyParams(reply_msg, result); 252 channel_->Send(reply_msg); 253 return; 254 } 255 256 if (IsPluginProcess()) { 257 if (npobject_->_class->setProperty) { 258 #if defined(OS_WIN) 259 static base::FilePath plugin_path = 260 CommandLine::ForCurrentProcess()->GetSwitchValuePath( 261 switches::kPluginPath); 262 static std::wstring filename = StringToLowerASCII( 263 plugin_path.BaseName().value()); 264 static NPIdentifier fullscreen = 265 WebBindings::getStringIdentifier("fullScreen"); 266 if (filename == kNewWMPPlugin && id == fullscreen) { 267 // Workaround for bug 15985, which is if Flash causes WMP to go 268 // full screen a deadlock can occur when WMP calls SetFocus. 269 NPObjectMsg_SetProperty::WriteReplyParams(reply_msg, true); 270 Send(reply_msg); 271 reply_msg = NULL; 272 } 273 #endif 274 result = npobject_->_class->setProperty(npobject_, id, &property_var); 275 } else { 276 result = false; 277 } 278 } else { 279 result = WebBindings::setProperty(0, npobject_, id, &property_var); 280 } 281 282 WebBindings::releaseVariantValue(&property_var); 283 284 if (reply_msg) { 285 NPObjectMsg_SetProperty::WriteReplyParams(reply_msg, result); 286 Send(reply_msg); 287 } 288 } 289 290 void NPObjectStub::OnRemoveProperty(const NPIdentifier_Param& name, 291 bool* result) { 292 NPIdentifier id = CreateNPIdentifier(name); 293 if (IsPluginProcess()) { 294 if (npobject_->_class->removeProperty) { 295 *result = npobject_->_class->removeProperty(npobject_, id); 296 } else { 297 *result = false; 298 } 299 } else { 300 *result = WebBindings::removeProperty(0, npobject_, id); 301 } 302 } 303 304 void NPObjectStub::OnInvalidate() { 305 if (!IsPluginProcess()) { 306 NOTREACHED() << "Should only be called on NPObjects in the plugin"; 307 return; 308 } 309 310 if (!npobject_->_class->invalidate) 311 return; 312 313 npobject_->_class->invalidate(npobject_); 314 } 315 316 void NPObjectStub::OnEnumeration(std::vector<NPIdentifier_Param>* value, 317 bool* result) { 318 NPIdentifier* value_np = NULL; 319 unsigned int count = 0; 320 if (!IsPluginProcess()) { 321 *result = WebBindings::enumerate(0, npobject_, &value_np, &count); 322 } else { 323 if (npobject_->_class->structVersion < NP_CLASS_STRUCT_VERSION_ENUM || 324 !npobject_->_class->enumerate) { 325 *result = false; 326 return; 327 } 328 329 *result = npobject_->_class->enumerate(npobject_, &value_np, &count); 330 } 331 332 if (!*result) 333 return; 334 335 for (unsigned int i = 0; i < count; ++i) { 336 NPIdentifier_Param param; 337 CreateNPIdentifierParam(value_np[i], ¶m); 338 value->push_back(param); 339 } 340 341 free(value_np); 342 } 343 344 void NPObjectStub::OnConstruct(const std::vector<NPVariant_Param>& args, 345 IPC::Message* reply_msg) { 346 bool return_value = false; 347 NPVariant_Param result_param; 348 NPVariant result_var; 349 350 VOID_TO_NPVARIANT(result_var); 351 352 int arg_count = static_cast<int>(args.size()); 353 NPVariant* args_var = new NPVariant[arg_count]; 354 for (int i = 0; i < arg_count; ++i) { 355 if (!CreateNPVariant(args[i], 356 channel_.get(), 357 &(args_var[i]), 358 render_view_id_, 359 page_url_)) { 360 NPObjectMsg_Invoke::WriteReplyParams( 361 reply_msg, result_param, return_value); 362 channel_->Send(reply_msg); 363 delete[] args_var; 364 return; 365 } 366 } 367 368 if (IsPluginProcess()) { 369 if (npobject_->_class->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR && 370 npobject_->_class->construct) { 371 return_value = npobject_->_class->construct( 372 npobject_, args_var, arg_count, &result_var); 373 } else { 374 return_value = false; 375 } 376 } else { 377 return_value = WebBindings::construct( 378 0, npobject_, args_var, arg_count, &result_var); 379 } 380 381 for (int i = 0; i < arg_count; ++i) 382 WebBindings::releaseVariantValue(&(args_var[i])); 383 384 delete[] args_var; 385 386 CreateNPVariantParam(result_var, 387 channel_.get(), 388 &result_param, 389 true, 390 render_view_id_, 391 page_url_); 392 NPObjectMsg_Invoke::WriteReplyParams(reply_msg, result_param, return_value); 393 channel_->Send(reply_msg); 394 } 395 396 void NPObjectStub::OnEvaluate(const std::string& script, 397 bool popups_allowed, 398 IPC::Message* reply_msg) { 399 if (IsPluginProcess()) { 400 NOTREACHED() << "Should only be called on NPObjects in the renderer"; 401 return; 402 } 403 404 NPVariant result_var; 405 NPString script_string; 406 script_string.UTF8Characters = script.c_str(); 407 script_string.UTF8Length = static_cast<unsigned int>(script.length()); 408 409 bool return_value = WebBindings::evaluateHelper(0, popups_allowed, npobject_, 410 &script_string, &result_var); 411 412 NPVariant_Param result_param; 413 CreateNPVariantParam(result_var, 414 channel_.get(), 415 &result_param, 416 true, 417 render_view_id_, 418 page_url_); 419 NPObjectMsg_Evaluate::WriteReplyParams(reply_msg, result_param, return_value); 420 channel_->Send(reply_msg); 421 } 422 423 } // namespace content 424