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 3 // found in the LICENSE file. 4 5 #include "content/renderer/browser_plugin/browser_plugin_bindings.h" 6 7 #include <cstdlib> 8 #include <string> 9 10 #include "base/bind.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/strings/string16.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_split.h" 15 #include "base/strings/utf_string_conversions.h" 16 #include "content/common/browser_plugin/browser_plugin_constants.h" 17 #include "content/public/renderer/v8_value_converter.h" 18 #include "content/renderer/browser_plugin/browser_plugin.h" 19 #include "third_party/WebKit/public/platform/WebString.h" 20 #include "third_party/WebKit/public/web/WebBindings.h" 21 #include "third_party/WebKit/public/web/WebDOMMessageEvent.h" 22 #include "third_party/WebKit/public/web/WebDocument.h" 23 #include "third_party/WebKit/public/web/WebElement.h" 24 #include "third_party/WebKit/public/web/WebFrame.h" 25 #include "third_party/WebKit/public/web/WebNode.h" 26 #include "third_party/WebKit/public/web/WebPluginContainer.h" 27 #include "third_party/WebKit/public/web/WebView.h" 28 #include "third_party/npapi/bindings/npapi.h" 29 #include "v8/include/v8.h" 30 31 using blink::WebBindings; 32 using blink::WebElement; 33 using blink::WebDOMEvent; 34 using blink::WebDOMMessageEvent; 35 using blink::WebPluginContainer; 36 using blink::WebString; 37 38 namespace content { 39 40 namespace { 41 42 BrowserPluginBindings* GetBindings(NPObject* object) { 43 return static_cast<BrowserPluginBindings::BrowserPluginNPObject*>(object)-> 44 message_channel.get(); 45 } 46 47 std::string StringFromNPVariant(const NPVariant& variant) { 48 if (!NPVARIANT_IS_STRING(variant)) 49 return std::string(); 50 const NPString& np_string = NPVARIANT_TO_STRING(variant); 51 return std::string(np_string.UTF8Characters, np_string.UTF8Length); 52 } 53 54 // Depending on where the attribute comes from it could be a string, int32, 55 // or a double. Javascript tends to produce an int32 or a string, but setting 56 // the value from the developer tools console may also produce a double. 57 int IntFromNPVariant(const NPVariant& variant) { 58 int value = 0; 59 switch (variant.type) { 60 case NPVariantType_Double: 61 value = NPVARIANT_TO_DOUBLE(variant); 62 break; 63 case NPVariantType_Int32: 64 value = NPVARIANT_TO_INT32(variant); 65 break; 66 case NPVariantType_String: 67 base::StringToInt(StringFromNPVariant(variant), &value); 68 break; 69 default: 70 break; 71 } 72 return value; 73 } 74 75 //------------------------------------------------------------------------------ 76 // Implementations of NPClass functions. These are here to: 77 // - Implement src attribute. 78 //------------------------------------------------------------------------------ 79 NPObject* BrowserPluginBindingsAllocate(NPP npp, NPClass* the_class) { 80 return new BrowserPluginBindings::BrowserPluginNPObject; 81 } 82 83 void BrowserPluginBindingsDeallocate(NPObject* object) { 84 BrowserPluginBindings::BrowserPluginNPObject* instance = 85 static_cast<BrowserPluginBindings::BrowserPluginNPObject*>(object); 86 delete instance; 87 } 88 89 bool BrowserPluginBindingsHasMethod(NPObject* np_obj, NPIdentifier name) { 90 if (!np_obj) 91 return false; 92 93 BrowserPluginBindings* bindings = GetBindings(np_obj); 94 if (!bindings) 95 return false; 96 97 return bindings->HasMethod(name); 98 } 99 100 bool BrowserPluginBindingsInvoke(NPObject* np_obj, NPIdentifier name, 101 const NPVariant* args, uint32 arg_count, 102 NPVariant* result) { 103 if (!np_obj) 104 return false; 105 106 BrowserPluginBindings* bindings = GetBindings(np_obj); 107 if (!bindings) 108 return false; 109 110 return bindings->InvokeMethod(name, args, arg_count, result); 111 } 112 113 bool BrowserPluginBindingsInvokeDefault(NPObject* np_obj, 114 const NPVariant* args, 115 uint32 arg_count, 116 NPVariant* result) { 117 NOTIMPLEMENTED(); 118 return false; 119 } 120 121 bool BrowserPluginBindingsHasProperty(NPObject* np_obj, NPIdentifier name) { 122 if (!np_obj) 123 return false; 124 125 BrowserPluginBindings* bindings = GetBindings(np_obj); 126 if (!bindings) 127 return false; 128 129 return bindings->HasProperty(name); 130 } 131 132 bool BrowserPluginBindingsGetProperty(NPObject* np_obj, NPIdentifier name, 133 NPVariant* result) { 134 if (!np_obj) 135 return false; 136 137 if (!result) 138 return false; 139 140 // All attributes from here on rely on the bindings, so retrieve it once and 141 // return on failure. 142 BrowserPluginBindings* bindings = GetBindings(np_obj); 143 if (!bindings) 144 return false; 145 146 return bindings->GetProperty(name, result); 147 } 148 149 bool BrowserPluginBindingsSetProperty(NPObject* np_obj, NPIdentifier name, 150 const NPVariant* variant) { 151 if (!np_obj) 152 return false; 153 if (!variant) 154 return false; 155 156 // All attributes from here on rely on the bindings, so retrieve it once and 157 // return on failure. 158 BrowserPluginBindings* bindings = GetBindings(np_obj); 159 if (!bindings) 160 return false; 161 162 if (variant->type == NPVariantType_Null) 163 return bindings->RemoveProperty(np_obj, name); 164 165 return bindings->SetProperty(np_obj, name, variant); 166 } 167 168 bool BrowserPluginBindingsEnumerate(NPObject *np_obj, NPIdentifier **value, 169 uint32_t *count) { 170 NOTIMPLEMENTED(); 171 return true; 172 } 173 174 NPClass browser_plugin_message_class = { 175 NP_CLASS_STRUCT_VERSION, 176 &BrowserPluginBindingsAllocate, 177 &BrowserPluginBindingsDeallocate, 178 NULL, 179 &BrowserPluginBindingsHasMethod, 180 &BrowserPluginBindingsInvoke, 181 &BrowserPluginBindingsInvokeDefault, 182 &BrowserPluginBindingsHasProperty, 183 &BrowserPluginBindingsGetProperty, 184 &BrowserPluginBindingsSetProperty, 185 NULL, 186 &BrowserPluginBindingsEnumerate, 187 }; 188 189 } // namespace 190 191 // BrowserPluginMethodBinding -------------------------------------------------- 192 193 class BrowserPluginMethodBinding { 194 public: 195 BrowserPluginMethodBinding(const char name[], uint32 arg_count) 196 : name_(name), 197 arg_count_(arg_count) { 198 } 199 200 virtual ~BrowserPluginMethodBinding() {} 201 202 bool MatchesName(NPIdentifier name) const { 203 return WebBindings::getStringIdentifier(name_.c_str()) == name; 204 } 205 206 uint32 arg_count() const { return arg_count_; } 207 208 virtual bool Invoke(BrowserPluginBindings* bindings, 209 const NPVariant* args, 210 NPVariant* result) = 0; 211 212 private: 213 std::string name_; 214 uint32 arg_count_; 215 216 DISALLOW_COPY_AND_ASSIGN(BrowserPluginMethodBinding); 217 }; 218 219 class BrowserPluginBindingAttach: public BrowserPluginMethodBinding { 220 public: 221 BrowserPluginBindingAttach() 222 : BrowserPluginMethodBinding(browser_plugin::kMethodInternalAttach, 2) {} 223 224 virtual bool Invoke(BrowserPluginBindings* bindings, 225 const NPVariant* args, 226 NPVariant* result) OVERRIDE { 227 bool attached = InvokeHelper(bindings, args); 228 BOOLEAN_TO_NPVARIANT(attached, *result); 229 return true; 230 } 231 232 private: 233 bool InvokeHelper(BrowserPluginBindings* bindings, const NPVariant* args) { 234 if (!bindings->instance()->render_view()) 235 return false; 236 237 int instance_id = IntFromNPVariant(args[0]); 238 if (!instance_id) 239 return false; 240 241 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); 242 v8::Handle<v8::Value> obj(blink::WebBindings::toV8Value(&args[1])); 243 scoped_ptr<base::Value> value( 244 converter->FromV8Value(obj, bindings->instance()->render_view()-> 245 GetWebView()->mainFrame()->mainWorldScriptContext())); 246 if (!value) 247 return false; 248 249 if (!value->IsType(base::Value::TYPE_DICTIONARY)) 250 return false; 251 252 scoped_ptr<base::DictionaryValue> extra_params( 253 static_cast<base::DictionaryValue*>(value.release())); 254 bindings->instance()->Attach(instance_id, extra_params.Pass()); 255 return true; 256 } 257 DISALLOW_COPY_AND_ASSIGN(BrowserPluginBindingAttach); 258 }; 259 260 // BrowserPluginPropertyBinding ------------------------------------------------ 261 262 class BrowserPluginPropertyBinding { 263 public: 264 explicit BrowserPluginPropertyBinding(const char name[]) : name_(name) {} 265 virtual ~BrowserPluginPropertyBinding() {} 266 const std::string& name() const { return name_; } 267 bool MatchesName(NPIdentifier name) const { 268 return WebBindings::getStringIdentifier(name_.c_str()) == name; 269 } 270 virtual bool GetProperty(BrowserPluginBindings* bindings, 271 NPVariant* result) = 0; 272 virtual bool SetProperty(BrowserPluginBindings* bindings, 273 NPObject* np_obj, 274 const NPVariant* variant) = 0; 275 virtual void RemoveProperty(BrowserPluginBindings* bindings, 276 NPObject* np_obj) = 0; 277 // Updates the DOM Attribute value with the current property value. 278 void UpdateDOMAttribute(BrowserPluginBindings* bindings, 279 std::string new_value) { 280 bindings->instance()->UpdateDOMAttribute(name(), new_value); 281 } 282 private: 283 std::string name_; 284 285 DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBinding); 286 }; 287 288 class BrowserPluginPropertyBindingAllowTransparency 289 : public BrowserPluginPropertyBinding { 290 public: 291 BrowserPluginPropertyBindingAllowTransparency() 292 : BrowserPluginPropertyBinding( 293 browser_plugin::kAttributeAllowTransparency) { 294 } 295 virtual bool GetProperty(BrowserPluginBindings* bindings, 296 NPVariant* result) OVERRIDE { 297 bool allow_transparency = 298 bindings->instance()->GetAllowTransparencyAttribute(); 299 BOOLEAN_TO_NPVARIANT(allow_transparency, *result); 300 return true; 301 } 302 virtual bool SetProperty(BrowserPluginBindings* bindings, 303 NPObject* np_obj, 304 const NPVariant* variant) OVERRIDE { 305 std::string value = StringFromNPVariant(*variant); 306 if (!bindings->instance()->HasDOMAttribute(name())) { 307 UpdateDOMAttribute(bindings, value); 308 bindings->instance()->ParseAllowTransparencyAttribute(); 309 } else { 310 UpdateDOMAttribute(bindings, value); 311 } 312 return true; 313 } 314 virtual void RemoveProperty(BrowserPluginBindings* bindings, 315 NPObject* np_obj) OVERRIDE { 316 bindings->instance()->RemoveDOMAttribute(name()); 317 bindings->instance()->ParseAllowTransparencyAttribute(); 318 } 319 private: 320 DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingAllowTransparency); 321 }; 322 323 class BrowserPluginPropertyBindingAutoSize 324 : public BrowserPluginPropertyBinding { 325 public: 326 BrowserPluginPropertyBindingAutoSize() 327 : BrowserPluginPropertyBinding(browser_plugin::kAttributeAutoSize) { 328 } 329 virtual bool GetProperty(BrowserPluginBindings* bindings, 330 NPVariant* result) OVERRIDE { 331 bool auto_size = bindings->instance()->GetAutoSizeAttribute(); 332 BOOLEAN_TO_NPVARIANT(auto_size, *result); 333 return true; 334 } 335 virtual bool SetProperty(BrowserPluginBindings* bindings, 336 NPObject* np_obj, 337 const NPVariant* variant) OVERRIDE { 338 std::string value = StringFromNPVariant(*variant); 339 if (!bindings->instance()->HasDOMAttribute(name())) { 340 UpdateDOMAttribute(bindings, value); 341 bindings->instance()->ParseAutoSizeAttribute(); 342 } else { 343 UpdateDOMAttribute(bindings, value); 344 } 345 return true; 346 } 347 virtual void RemoveProperty(BrowserPluginBindings* bindings, 348 NPObject* np_obj) OVERRIDE { 349 bindings->instance()->RemoveDOMAttribute(name()); 350 bindings->instance()->ParseAutoSizeAttribute(); 351 } 352 private: 353 DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingAutoSize); 354 }; 355 356 class BrowserPluginPropertyBindingContentWindow 357 : public BrowserPluginPropertyBinding { 358 public: 359 BrowserPluginPropertyBindingContentWindow() 360 : BrowserPluginPropertyBinding(browser_plugin::kAttributeContentWindow) { 361 } 362 virtual bool GetProperty(BrowserPluginBindings* bindings, 363 NPVariant* result) OVERRIDE { 364 NPObject* obj = bindings->instance()->GetContentWindow(); 365 if (obj) { 366 result->type = NPVariantType_Object; 367 result->value.objectValue = WebBindings::retainObject(obj); 368 } 369 return true; 370 } 371 virtual bool SetProperty(BrowserPluginBindings* bindings, 372 NPObject* np_obj, 373 const NPVariant* variant) OVERRIDE { 374 return false; 375 } 376 virtual void RemoveProperty(BrowserPluginBindings* bindings, 377 NPObject* np_obj) OVERRIDE {} 378 private: 379 DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingContentWindow); 380 }; 381 382 class BrowserPluginPropertyBindingMaxHeight 383 : public BrowserPluginPropertyBinding { 384 public: 385 BrowserPluginPropertyBindingMaxHeight() 386 : BrowserPluginPropertyBinding(browser_plugin::kAttributeMaxHeight) { 387 } 388 virtual bool GetProperty(BrowserPluginBindings* bindings, 389 NPVariant* result) OVERRIDE { 390 int max_height = bindings->instance()->GetMaxHeightAttribute(); 391 INT32_TO_NPVARIANT(max_height, *result); 392 return true; 393 } 394 virtual bool SetProperty(BrowserPluginBindings* bindings, 395 NPObject* np_obj, 396 const NPVariant* variant) OVERRIDE { 397 int new_value = IntFromNPVariant(*variant); 398 if (bindings->instance()->GetMaxHeightAttribute() != new_value) { 399 UpdateDOMAttribute(bindings, base::IntToString(new_value)); 400 bindings->instance()->ParseSizeContraintsChanged(); 401 } 402 return true; 403 } 404 virtual void RemoveProperty(BrowserPluginBindings* bindings, 405 NPObject* np_obj) OVERRIDE { 406 bindings->instance()->RemoveDOMAttribute(name()); 407 bindings->instance()->ParseSizeContraintsChanged(); 408 } 409 private: 410 DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingMaxHeight); 411 }; 412 413 class BrowserPluginPropertyBindingMaxWidth 414 : public BrowserPluginPropertyBinding { 415 public: 416 BrowserPluginPropertyBindingMaxWidth() 417 : BrowserPluginPropertyBinding(browser_plugin::kAttributeMaxWidth) { 418 } 419 virtual bool GetProperty(BrowserPluginBindings* bindings, 420 NPVariant* result) OVERRIDE { 421 int max_width = bindings->instance()->GetMaxWidthAttribute(); 422 INT32_TO_NPVARIANT(max_width, *result); 423 return true; 424 } 425 virtual bool SetProperty(BrowserPluginBindings* bindings, 426 NPObject* np_obj, 427 const NPVariant* variant) OVERRIDE { 428 int new_value = IntFromNPVariant(*variant); 429 if (bindings->instance()->GetMaxWidthAttribute() != new_value) { 430 UpdateDOMAttribute(bindings, base::IntToString(new_value)); 431 bindings->instance()->ParseSizeContraintsChanged(); 432 } 433 return true; 434 } 435 virtual void RemoveProperty(BrowserPluginBindings* bindings, 436 NPObject* np_obj) OVERRIDE { 437 bindings->instance()->RemoveDOMAttribute(name()); 438 bindings->instance()->ParseSizeContraintsChanged(); 439 } 440 private: 441 DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingMaxWidth); 442 }; 443 444 class BrowserPluginPropertyBindingMinHeight 445 : public BrowserPluginPropertyBinding { 446 public: 447 BrowserPluginPropertyBindingMinHeight() 448 : BrowserPluginPropertyBinding(browser_plugin::kAttributeMinHeight) { 449 } 450 virtual bool GetProperty(BrowserPluginBindings* bindings, 451 NPVariant* result) OVERRIDE { 452 int min_height = bindings->instance()->GetMinHeightAttribute(); 453 INT32_TO_NPVARIANT(min_height, *result); 454 return true; 455 } 456 virtual bool SetProperty(BrowserPluginBindings* bindings, 457 NPObject* np_obj, 458 const NPVariant* variant) OVERRIDE { 459 int new_value = IntFromNPVariant(*variant); 460 if (bindings->instance()->GetMinHeightAttribute() != new_value) { 461 UpdateDOMAttribute(bindings, base::IntToString(new_value)); 462 bindings->instance()->ParseSizeContraintsChanged(); 463 } 464 return true; 465 } 466 virtual void RemoveProperty(BrowserPluginBindings* bindings, 467 NPObject* np_obj) OVERRIDE { 468 bindings->instance()->RemoveDOMAttribute(name()); 469 bindings->instance()->ParseSizeContraintsChanged(); 470 } 471 private: 472 DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingMinHeight); 473 }; 474 475 class BrowserPluginPropertyBindingMinWidth 476 : public BrowserPluginPropertyBinding { 477 public: 478 BrowserPluginPropertyBindingMinWidth() 479 : BrowserPluginPropertyBinding(browser_plugin::kAttributeMinWidth) { 480 } 481 virtual bool GetProperty(BrowserPluginBindings* bindings, 482 NPVariant* result) OVERRIDE { 483 int min_width = bindings->instance()->GetMinWidthAttribute(); 484 INT32_TO_NPVARIANT(min_width, *result); 485 return true; 486 } 487 virtual bool SetProperty(BrowserPluginBindings* bindings, 488 NPObject* np_obj, 489 const NPVariant* variant) OVERRIDE { 490 int new_value = IntFromNPVariant(*variant); 491 if (bindings->instance()->GetMinWidthAttribute() != new_value) { 492 UpdateDOMAttribute(bindings, base::IntToString(new_value)); 493 bindings->instance()->ParseSizeContraintsChanged(); 494 } 495 return true; 496 } 497 virtual void RemoveProperty(BrowserPluginBindings* bindings, 498 NPObject* np_obj) OVERRIDE { 499 bindings->instance()->RemoveDOMAttribute(name()); 500 bindings->instance()->ParseSizeContraintsChanged(); 501 } 502 private: 503 DISALLOW_COPY_AND_ASSIGN(BrowserPluginPropertyBindingMinWidth); 504 }; 505 506 507 // BrowserPluginBindings ------------------------------------------------------ 508 509 BrowserPluginBindings::BrowserPluginNPObject::BrowserPluginNPObject() { 510 } 511 512 BrowserPluginBindings::BrowserPluginNPObject::~BrowserPluginNPObject() { 513 } 514 515 BrowserPluginBindings::BrowserPluginBindings(BrowserPlugin* instance) 516 : instance_(instance), 517 np_object_(NULL), 518 weak_ptr_factory_(this) { 519 NPObject* obj = 520 WebBindings::createObject(instance->pluginNPP(), 521 &browser_plugin_message_class); 522 np_object_ = static_cast<BrowserPluginBindings::BrowserPluginNPObject*>(obj); 523 np_object_->message_channel = weak_ptr_factory_.GetWeakPtr(); 524 525 method_bindings_.push_back(new BrowserPluginBindingAttach); 526 527 property_bindings_.push_back( 528 new BrowserPluginPropertyBindingAllowTransparency); 529 property_bindings_.push_back(new BrowserPluginPropertyBindingAutoSize); 530 property_bindings_.push_back(new BrowserPluginPropertyBindingContentWindow); 531 property_bindings_.push_back(new BrowserPluginPropertyBindingMaxHeight); 532 property_bindings_.push_back(new BrowserPluginPropertyBindingMaxWidth); 533 property_bindings_.push_back(new BrowserPluginPropertyBindingMinHeight); 534 property_bindings_.push_back(new BrowserPluginPropertyBindingMinWidth); 535 } 536 537 BrowserPluginBindings::~BrowserPluginBindings() { 538 WebBindings::releaseObject(np_object_); 539 } 540 541 bool BrowserPluginBindings::HasMethod(NPIdentifier name) const { 542 for (BindingList::const_iterator iter = method_bindings_.begin(); 543 iter != method_bindings_.end(); 544 ++iter) { 545 if ((*iter)->MatchesName(name)) 546 return true; 547 } 548 return false; 549 } 550 551 bool BrowserPluginBindings::InvokeMethod(NPIdentifier name, 552 const NPVariant* args, 553 uint32 arg_count, 554 NPVariant* result) { 555 for (BindingList::iterator iter = method_bindings_.begin(); 556 iter != method_bindings_.end(); 557 ++iter) { 558 if ((*iter)->MatchesName(name) && (*iter)->arg_count() == arg_count) 559 return (*iter)->Invoke(this, args, result); 560 } 561 return false; 562 } 563 564 bool BrowserPluginBindings::HasProperty(NPIdentifier name) const { 565 for (PropertyBindingList::const_iterator iter = property_bindings_.begin(); 566 iter != property_bindings_.end(); 567 ++iter) { 568 if ((*iter)->MatchesName(name)) 569 return true; 570 } 571 return false; 572 } 573 574 bool BrowserPluginBindings::SetProperty(NPObject* np_obj, 575 NPIdentifier name, 576 const NPVariant* variant) { 577 for (PropertyBindingList::iterator iter = property_bindings_.begin(); 578 iter != property_bindings_.end(); 579 ++iter) { 580 if ((*iter)->MatchesName(name)) { 581 if ((*iter)->SetProperty(this, np_obj, variant)) { 582 return true; 583 } 584 break; 585 } 586 } 587 return false; 588 } 589 590 bool BrowserPluginBindings::RemoveProperty(NPObject* np_obj, 591 NPIdentifier name) { 592 for (PropertyBindingList::iterator iter = property_bindings_.begin(); 593 iter != property_bindings_.end(); 594 ++iter) { 595 if ((*iter)->MatchesName(name)) { 596 (*iter)->RemoveProperty(this, np_obj); 597 return true; 598 } 599 } 600 return false; 601 } 602 603 bool BrowserPluginBindings::GetProperty(NPIdentifier name, NPVariant* result) { 604 for (PropertyBindingList::iterator iter = property_bindings_.begin(); 605 iter != property_bindings_.end(); 606 ++iter) { 607 if ((*iter)->MatchesName(name)) 608 return (*iter)->GetProperty(this, result); 609 } 610 return false; 611 } 612 613 } // namespace content 614