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/child/npapi/plugin_host.h" 6 7 #include "base/command_line.h" 8 #include "base/file_util.h" 9 #include "base/logging.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/strings/string_piece.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/sys_string_conversions.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "build/build_config.h" 16 #include "content/child/npapi/plugin_instance.h" 17 #include "content/child/npapi/plugin_lib.h" 18 #include "content/child/npapi/plugin_stream_url.h" 19 #include "content/child/npapi/webplugin_delegate.h" 20 #include "content/public/common/content_switches.h" 21 #include "content/public/common/webplugininfo.h" 22 #include "net/base/net_util.h" 23 #include "third_party/WebKit/public/web/WebBindings.h" 24 #include "third_party/WebKit/public/web/WebKit.h" 25 #include "third_party/npapi/bindings/npruntime.h" 26 #include "ui/gl/gl_implementation.h" 27 #include "ui/gl/gl_surface.h" 28 #include "webkit/common/user_agent/user_agent.h" 29 30 #if defined(OS_MACOSX) 31 #include "base/mac/mac_util.h" 32 #endif 33 34 using blink::WebBindings; 35 36 // Declarations for stub implementations of deprecated functions, which are no 37 // longer listed in npapi.h. 38 extern "C" { 39 void* NPN_GetJavaEnv(); 40 void* NPN_GetJavaPeer(NPP); 41 } 42 43 namespace content { 44 45 // Finds a PluginInstance from an NPP. 46 // The caller must take a reference if needed. 47 static PluginInstance* FindInstance(NPP id) { 48 if (id == NULL) { 49 return NULL; 50 } 51 return reinterpret_cast<PluginInstance*>(id->ndata); 52 } 53 54 #if defined(OS_MACOSX) 55 // Returns true if Core Animation plugins are supported. This requires that the 56 // OS supports shared accelerated surfaces via IOSurface. This is true on Snow 57 // Leopard and higher. 58 static bool SupportsCoreAnimationPlugins() { 59 if (CommandLine::ForCurrentProcess()->HasSwitch( 60 switches::kDisableCoreAnimationPlugins)) 61 return false; 62 // We also need to be running with desktop GL and not the software 63 // OSMesa renderer in order to share accelerated surfaces between 64 // processes. 65 gfx::GLImplementation implementation = gfx::GetGLImplementation(); 66 if (implementation == gfx::kGLImplementationNone) { 67 // Not initialized yet. 68 if (!gfx::GLSurface::InitializeOneOff()) { 69 return false; 70 } 71 implementation = gfx::GetGLImplementation(); 72 } 73 return (implementation == gfx::kGLImplementationDesktopGL); 74 } 75 #endif 76 77 PluginHost::PluginHost() { 78 InitializeHostFuncs(); 79 } 80 81 PluginHost::~PluginHost() { 82 } 83 84 PluginHost *PluginHost::Singleton() { 85 CR_DEFINE_STATIC_LOCAL(scoped_refptr<PluginHost>, singleton, ()); 86 if (singleton.get() == NULL) { 87 singleton = new PluginHost(); 88 } 89 90 DCHECK(singleton.get() != NULL); 91 return singleton.get(); 92 } 93 94 void PluginHost::InitializeHostFuncs() { 95 memset(&host_funcs_, 0, sizeof(host_funcs_)); 96 host_funcs_.size = sizeof(host_funcs_); 97 host_funcs_.version = (NP_VERSION_MAJOR << 8) | (NP_VERSION_MINOR); 98 99 // The "basic" functions 100 host_funcs_.geturl = &NPN_GetURL; 101 host_funcs_.posturl = &NPN_PostURL; 102 host_funcs_.requestread = &NPN_RequestRead; 103 host_funcs_.newstream = &NPN_NewStream; 104 host_funcs_.write = &NPN_Write; 105 host_funcs_.destroystream = &NPN_DestroyStream; 106 host_funcs_.status = &NPN_Status; 107 host_funcs_.uagent = &NPN_UserAgent; 108 host_funcs_.memalloc = &NPN_MemAlloc; 109 host_funcs_.memfree = &NPN_MemFree; 110 host_funcs_.memflush = &NPN_MemFlush; 111 host_funcs_.reloadplugins = &NPN_ReloadPlugins; 112 113 // Stubs for deprecated Java functions 114 host_funcs_.getJavaEnv = &NPN_GetJavaEnv; 115 host_funcs_.getJavaPeer = &NPN_GetJavaPeer; 116 117 // Advanced functions we implement 118 host_funcs_.geturlnotify = &NPN_GetURLNotify; 119 host_funcs_.posturlnotify = &NPN_PostURLNotify; 120 host_funcs_.getvalue = &NPN_GetValue; 121 host_funcs_.setvalue = &NPN_SetValue; 122 host_funcs_.invalidaterect = &NPN_InvalidateRect; 123 host_funcs_.invalidateregion = &NPN_InvalidateRegion; 124 host_funcs_.forceredraw = &NPN_ForceRedraw; 125 126 // These come from the Javascript Engine 127 host_funcs_.getstringidentifier = WebBindings::getStringIdentifier; 128 host_funcs_.getstringidentifiers = WebBindings::getStringIdentifiers; 129 host_funcs_.getintidentifier = WebBindings::getIntIdentifier; 130 host_funcs_.identifierisstring = WebBindings::identifierIsString; 131 host_funcs_.utf8fromidentifier = WebBindings::utf8FromIdentifier; 132 host_funcs_.intfromidentifier = WebBindings::intFromIdentifier; 133 host_funcs_.createobject = WebBindings::createObject; 134 host_funcs_.retainobject = WebBindings::retainObject; 135 host_funcs_.releaseobject = WebBindings::releaseObject; 136 host_funcs_.invoke = WebBindings::invoke; 137 host_funcs_.invokeDefault = WebBindings::invokeDefault; 138 host_funcs_.evaluate = WebBindings::evaluate; 139 host_funcs_.getproperty = WebBindings::getProperty; 140 host_funcs_.setproperty = WebBindings::setProperty; 141 host_funcs_.removeproperty = WebBindings::removeProperty; 142 host_funcs_.hasproperty = WebBindings::hasProperty; 143 host_funcs_.hasmethod = WebBindings::hasMethod; 144 host_funcs_.releasevariantvalue = WebBindings::releaseVariantValue; 145 host_funcs_.setexception = WebBindings::setException; 146 host_funcs_.pushpopupsenabledstate = NPN_PushPopupsEnabledState; 147 host_funcs_.poppopupsenabledstate = NPN_PopPopupsEnabledState; 148 host_funcs_.enumerate = WebBindings::enumerate; 149 host_funcs_.pluginthreadasynccall = NPN_PluginThreadAsyncCall; 150 host_funcs_.construct = WebBindings::construct; 151 host_funcs_.getvalueforurl = NPN_GetValueForURL; 152 host_funcs_.setvalueforurl = NPN_SetValueForURL; 153 host_funcs_.getauthenticationinfo = NPN_GetAuthenticationInfo; 154 host_funcs_.scheduletimer = NPN_ScheduleTimer; 155 host_funcs_.unscheduletimer = NPN_UnscheduleTimer; 156 host_funcs_.popupcontextmenu = NPN_PopUpContextMenu; 157 host_funcs_.convertpoint = NPN_ConvertPoint; 158 host_funcs_.handleevent = NPN_HandleEvent; 159 host_funcs_.unfocusinstance = NPN_UnfocusInstance; 160 host_funcs_.urlredirectresponse = NPN_URLRedirectResponse; 161 } 162 163 void PluginHost::PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides) { 164 // When running in the plugin process, we need to patch the NPN functions 165 // that the plugin calls to interact with NPObjects that we give. Otherwise 166 // the plugin will call the v8 NPN functions, which won't work since we have 167 // an NPObjectProxy and not a real v8 implementation. 168 if (overrides->invoke) 169 host_funcs_.invoke = overrides->invoke; 170 171 if (overrides->invokeDefault) 172 host_funcs_.invokeDefault = overrides->invokeDefault; 173 174 if (overrides->evaluate) 175 host_funcs_.evaluate = overrides->evaluate; 176 177 if (overrides->getproperty) 178 host_funcs_.getproperty = overrides->getproperty; 179 180 if (overrides->setproperty) 181 host_funcs_.setproperty = overrides->setproperty; 182 183 if (overrides->removeproperty) 184 host_funcs_.removeproperty = overrides->removeproperty; 185 186 if (overrides->hasproperty) 187 host_funcs_.hasproperty = overrides->hasproperty; 188 189 if (overrides->hasmethod) 190 host_funcs_.hasmethod = overrides->hasmethod; 191 192 if (overrides->setexception) 193 host_funcs_.setexception = overrides->setexception; 194 195 if (overrides->enumerate) 196 host_funcs_.enumerate = overrides->enumerate; 197 } 198 199 bool PluginHost::SetPostData(const char* buf, 200 uint32 length, 201 std::vector<std::string>* names, 202 std::vector<std::string>* values, 203 std::vector<char>* body) { 204 // Use a state table to do the parsing. Whitespace must be 205 // trimmed after the fact if desired. In our case, we actually 206 // don't care about the whitespace, because we're just going to 207 // pass this back into another POST. This function strips out the 208 // "Content-length" header and does not append it to the request. 209 210 // 211 // This parser takes action only on state changes. 212 // 213 // Transition table: 214 // : \n NULL Other 215 // 0 GetHeader 1 2 4 0 216 // 1 GetValue 1 0 3 1 217 // 2 GetData 2 2 3 2 218 // 3 DONE 219 // 4 ERR 220 // 221 enum { INPUT_COLON=0, INPUT_NEWLINE, INPUT_NULL, INPUT_OTHER }; 222 enum { GETNAME, GETVALUE, GETDATA, DONE, ERR }; 223 int statemachine[3][4] = { { GETVALUE, GETDATA, GETDATA, GETNAME }, 224 { GETVALUE, GETNAME, DONE, GETVALUE }, 225 { GETDATA, GETDATA, DONE, GETDATA } }; 226 std::string name, value; 227 const char* ptr = static_cast<const char*>(buf); 228 const char* start = ptr; 229 int state = GETNAME; // initial state 230 bool done = false; 231 bool err = false; 232 do { 233 int input; 234 235 // Translate the current character into an input 236 // for the state table. 237 switch (*ptr) { 238 case ':' : 239 input = INPUT_COLON; 240 break; 241 case '\n': 242 input = INPUT_NEWLINE; 243 break; 244 case 0 : 245 input = INPUT_NULL; 246 break; 247 default : 248 input = INPUT_OTHER; 249 break; 250 } 251 252 int newstate = statemachine[state][input]; 253 254 // Take action based on the new state. 255 if (state != newstate) { 256 switch (newstate) { 257 case GETNAME: 258 // Got a value. 259 value = std::string(start, ptr - start); 260 TrimWhitespace(value, TRIM_ALL, &value); 261 // If the name field is empty, we'll skip this header 262 // but we won't error out. 263 if (!name.empty() && name != "content-length") { 264 names->push_back(name); 265 values->push_back(value); 266 } 267 start = ptr + 1; 268 break; 269 case GETVALUE: 270 // Got a header. 271 name = StringToLowerASCII(std::string(start, ptr - start)); 272 TrimWhitespace(name, TRIM_ALL, &name); 273 start = ptr + 1; 274 break; 275 case GETDATA: { 276 // Finished headers, now get body 277 if (*ptr) 278 start = ptr + 1; 279 size_t previous_size = body->size(); 280 size_t new_body_size = length - static_cast<int>(start - buf); 281 body->resize(previous_size + new_body_size); 282 if (!body->empty()) 283 memcpy(&body->front() + previous_size, start, new_body_size); 284 done = true; 285 break; 286 } 287 case ERR: 288 // error 289 err = true; 290 done = true; 291 break; 292 } 293 } 294 state = newstate; 295 ptr++; 296 } while (!done); 297 298 return !err; 299 } 300 301 } // namespace content 302 303 extern "C" { 304 305 using content::FindInstance; 306 using content::PluginHost; 307 using content::PluginInstance; 308 using content::WebPlugin; 309 310 // Allocates memory from the host's memory space. 311 void* NPN_MemAlloc(uint32_t size) { 312 // Note: We must use the same allocator/deallocator 313 // that is used by the javascript library, as some of the 314 // JS APIs will pass memory to the plugin which the plugin 315 // will attempt to free. 316 return malloc(size); 317 } 318 319 // Deallocates memory from the host's memory space 320 void NPN_MemFree(void* ptr) { 321 if (ptr != NULL && ptr != reinterpret_cast<void*>(-1)) 322 free(ptr); 323 } 324 325 // Requests that the host free a specified amount of memory. 326 uint32_t NPN_MemFlush(uint32_t size) { 327 // This is not relevant on Windows; MAC specific 328 return size; 329 } 330 331 // This is for dynamic discovery of new plugins. 332 // Should force a re-scan of the plugins directory to load new ones. 333 void NPN_ReloadPlugins(NPBool reload_pages) { 334 blink::resetPluginCache(reload_pages ? true : false); 335 } 336 337 // Requests a range of bytes for a seekable stream. 338 NPError NPN_RequestRead(NPStream* stream, NPByteRange* range_list) { 339 if (!stream || !range_list) 340 return NPERR_GENERIC_ERROR; 341 342 scoped_refptr<PluginInstance> plugin( 343 reinterpret_cast<PluginInstance*>(stream->ndata)); 344 if (!plugin.get()) 345 return NPERR_GENERIC_ERROR; 346 347 plugin->RequestRead(stream, range_list); 348 return NPERR_NO_ERROR; 349 } 350 351 // Generic form of GetURL for common code between GetURL and GetURLNotify. 352 static NPError GetURLNotify(NPP id, 353 const char* url, 354 const char* target, 355 bool notify, 356 void* notify_data) { 357 if (!url) 358 return NPERR_INVALID_URL; 359 360 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 361 if (!plugin.get()) { 362 return NPERR_GENERIC_ERROR; 363 } 364 365 plugin->RequestURL(url, "GET", target, NULL, 0, notify, notify_data); 366 return NPERR_NO_ERROR; 367 } 368 369 // Requests creation of a new stream with the contents of the 370 // specified URL; gets notification of the result. 371 NPError NPN_GetURLNotify(NPP id, 372 const char* url, 373 const char* target, 374 void* notify_data) { 375 // This is identical to NPN_GetURL, but after finishing, the 376 // browser will call NPP_URLNotify to inform the plugin that 377 // it has completed. 378 379 // According to the NPAPI documentation, if target == _self 380 // or a parent to _self, the browser should return NPERR_INVALID_PARAM, 381 // because it can't notify the plugin once deleted. This is 382 // absolutely false; firefox doesn't do this, and Flash relies on 383 // being able to use this. 384 385 // Also according to the NPAPI documentation, we should return 386 // NPERR_INVALID_URL if the url requested is not valid. However, 387 // this would require that we synchronously start fetching the 388 // URL. That just isn't practical. As such, there really is 389 // no way to return this error. From looking at the Firefox 390 // implementation, it doesn't look like Firefox does this either. 391 392 return GetURLNotify(id, url, target, true, notify_data); 393 } 394 395 NPError NPN_GetURL(NPP id, const char* url, const char* target) { 396 // Notes: 397 // Request from the Plugin to fetch content either for the plugin 398 // or to be placed into a browser window. 399 // 400 // If target == null, the browser fetches content and streams to plugin. 401 // otherwise, the browser loads content into an existing browser frame. 402 // If the target is the window/frame containing the plugin, the plugin 403 // may be destroyed. 404 // If the target is _blank, a mailto: or news: url open content in a new 405 // browser window 406 // If the target is _self, no other instance of the plugin is created. The 407 // plugin continues to operate in its own window 408 409 return GetURLNotify(id, url, target, false, 0); 410 } 411 412 // Generic form of PostURL for common code between PostURL and PostURLNotify. 413 static NPError PostURLNotify(NPP id, 414 const char* url, 415 const char* target, 416 uint32_t len, 417 const char* buf, 418 NPBool file, 419 bool notify, 420 void* notify_data) { 421 if (!url) 422 return NPERR_INVALID_URL; 423 424 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 425 if (!plugin.get()) { 426 NOTREACHED(); 427 return NPERR_GENERIC_ERROR; 428 } 429 430 std::string post_file_contents; 431 432 if (file) { 433 // Post data to be uploaded from a file. This can be handled in two 434 // ways. 435 // 1. Read entire file and send the contents as if it was a post data 436 // specified in the argument 437 // 2. Send just the file details and read them in the browser at the 438 // time of sending the request. 439 // Approach 2 is more efficient but complicated. Approach 1 has a major 440 // drawback of sending potentially large data over two IPC hops. In a way 441 // 'large data over IPC' problem exists as it is in case of plugin giving 442 // the data directly instead of in a file. 443 // Currently we are going with the approach 1 to get the feature working. 444 // We can optimize this later with approach 2. 445 446 // TODO(joshia): Design a scheme to send a file descriptor instead of 447 // entire file contents across. 448 449 // Security alert: 450 // --------------- 451 // Here we are blindly uploading whatever file requested by a plugin. 452 // This is risky as someone could exploit a plugin to send private 453 // data in arbitrary locations. 454 // A malicious (non-sandboxed) plugin has unfeterred access to OS 455 // resources and can do this anyway without using browser's HTTP stack. 456 // FWIW, Firefox and Safari don't perform any security checks. 457 458 if (!buf) 459 return NPERR_FILE_NOT_FOUND; 460 461 std::string file_path_ascii(buf); 462 base::FilePath file_path; 463 static const char kFileUrlPrefix[] = "file:"; 464 if (StartsWithASCII(file_path_ascii, kFileUrlPrefix, false)) { 465 GURL file_url(file_path_ascii); 466 DCHECK(file_url.SchemeIsFile()); 467 net::FileURLToFilePath(file_url, &file_path); 468 } else { 469 file_path = base::FilePath::FromUTF8Unsafe(file_path_ascii); 470 } 471 472 base::PlatformFileInfo post_file_info; 473 if (!base::GetFileInfo(file_path, &post_file_info) || 474 post_file_info.is_directory) 475 return NPERR_FILE_NOT_FOUND; 476 477 if (!base::ReadFileToString(file_path, &post_file_contents)) 478 return NPERR_FILE_NOT_FOUND; 479 480 buf = post_file_contents.c_str(); 481 len = post_file_contents.size(); 482 } 483 484 // The post data sent by a plugin contains both headers 485 // and post data. Example: 486 // Content-type: text/html 487 // Content-length: 200 488 // 489 // <200 bytes of content here> 490 // 491 // Unfortunately, our stream needs these broken apart, 492 // so we need to parse the data and set headers and data 493 // separately. 494 plugin->RequestURL(url, "POST", target, buf, len, notify, notify_data); 495 return NPERR_NO_ERROR; 496 } 497 498 NPError NPN_PostURLNotify(NPP id, 499 const char* url, 500 const char* target, 501 uint32_t len, 502 const char* buf, 503 NPBool file, 504 void* notify_data) { 505 return PostURLNotify(id, url, target, len, buf, file, true, notify_data); 506 } 507 508 NPError NPN_PostURL(NPP id, 509 const char* url, 510 const char* target, 511 uint32_t len, 512 const char* buf, 513 NPBool file) { 514 // POSTs data to an URL, either from a temp file or a buffer. 515 // If file is true, buf contains a temp file (which host will delete after 516 // completing), and len contains the length of the filename. 517 // If file is false, buf contains the data to send, and len contains the 518 // length of the buffer 519 // 520 // If target is null, 521 // server response is returned to the plugin 522 // If target is _current, _self, or _top, 523 // server response is written to the plugin window and plugin is unloaded. 524 // If target is _new or _blank, 525 // server response is written to a new browser window 526 // If target is an existing frame, 527 // server response goes to that frame. 528 // 529 // For protocols other than FTP 530 // file uploads must be line-end converted from \r\n to \n 531 // 532 // Note: you cannot specify headers (even a blank line) in a memory buffer, 533 // use NPN_PostURLNotify 534 535 return PostURLNotify(id, url, target, len, buf, file, false, 0); 536 } 537 538 NPError NPN_NewStream(NPP id, 539 NPMIMEType type, 540 const char* target, 541 NPStream** stream) { 542 // Requests creation of a new data stream produced by the plugin, 543 // consumed by the browser. 544 // 545 // Browser should put this stream into a window target. 546 // 547 // TODO: implement me 548 DVLOG(1) << "NPN_NewStream is not implemented yet."; 549 return NPERR_GENERIC_ERROR; 550 } 551 552 int32_t NPN_Write(NPP id, NPStream* stream, int32_t len, void* buffer) { 553 // Writes data to an existing Plugin-created stream. 554 555 // TODO: implement me 556 DVLOG(1) << "NPN_Write is not implemented yet."; 557 return NPERR_GENERIC_ERROR; 558 } 559 560 NPError NPN_DestroyStream(NPP id, NPStream* stream, NPReason reason) { 561 // Destroys a stream (could be created by plugin or browser). 562 // 563 // Reasons: 564 // NPRES_DONE - normal completion 565 // NPRES_USER_BREAK - user terminated 566 // NPRES_NETWORK_ERROR - network error (all errors fit here?) 567 // 568 // 569 570 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 571 if (plugin.get() == NULL) { 572 NOTREACHED(); 573 return NPERR_GENERIC_ERROR; 574 } 575 576 return plugin->NPP_DestroyStream(stream, reason); 577 } 578 579 const char* NPN_UserAgent(NPP id) { 580 #if defined(OS_WIN) 581 // Flash passes in a null id during the NP_initialize call. We need to 582 // default to the Mozilla user agent if we don't have an NPP instance or 583 // else Flash won't request windowless mode. 584 bool use_mozilla_user_agent = true; 585 if (id) { 586 scoped_refptr<PluginInstance> plugin = FindInstance(id); 587 if (plugin.get() && !plugin->use_mozilla_user_agent()) 588 use_mozilla_user_agent = false; 589 } 590 591 if (use_mozilla_user_agent) 592 return "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) " 593 "Gecko/20061103 Firefox/2.0a1"; 594 #endif 595 596 return webkit_glue::GetUserAgent(GURL()).c_str(); 597 } 598 599 void NPN_Status(NPP id, const char* message) { 600 // Displays a message on the status line of the browser window. 601 602 // TODO: implement me 603 DVLOG(1) << "NPN_Status is not implemented yet."; 604 } 605 606 void NPN_InvalidateRect(NPP id, NPRect *invalidRect) { 607 // Invalidates specified drawing area prior to repainting or refreshing a 608 // windowless plugin 609 610 // Before a windowless plugin can refresh part of its drawing area, it must 611 // first invalidate it. This function causes the NPP_HandleEvent method to 612 // pass an update event or a paint message to the plug-in. After calling 613 // this method, the plug-in recieves a paint message asynchronously. 614 615 // The browser redraws invalid areas of the document and any windowless 616 // plug-ins at regularly timed intervals. To force a paint message, the 617 // plug-in can call NPN_ForceRedraw after calling this method. 618 619 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 620 if (plugin.get() && plugin->webplugin()) { 621 if (invalidRect) { 622 #if defined(OS_WIN) 623 if (!plugin->windowless()) { 624 RECT rect = {0}; 625 rect.left = invalidRect->left; 626 rect.right = invalidRect->right; 627 rect.top = invalidRect->top; 628 rect.bottom = invalidRect->bottom; 629 ::InvalidateRect(plugin->window_handle(), &rect, false); 630 return; 631 } 632 #endif 633 gfx::Rect rect(invalidRect->left, 634 invalidRect->top, 635 invalidRect->right - invalidRect->left, 636 invalidRect->bottom - invalidRect->top); 637 plugin->webplugin()->InvalidateRect(rect); 638 } else { 639 plugin->webplugin()->Invalidate(); 640 } 641 } 642 } 643 644 void NPN_InvalidateRegion(NPP id, NPRegion invalidRegion) { 645 // Invalidates a specified drawing region prior to repainting 646 // or refreshing a window-less plugin. 647 // 648 // Similar to NPN_InvalidateRect. 649 650 // TODO: this is overkill--add platform-specific region handling (at the 651 // very least, fetch the region's bounding box and pass it to InvalidateRect). 652 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 653 DCHECK(plugin.get() != NULL); 654 if (plugin.get() && plugin->webplugin()) 655 plugin->webplugin()->Invalidate(); 656 } 657 658 void NPN_ForceRedraw(NPP id) { 659 // Forces repaint for a windowless plug-in. 660 // 661 // We deliberately do not implement this; we don't want plugins forcing 662 // synchronous paints. 663 } 664 665 NPError NPN_GetValue(NPP id, NPNVariable variable, void* value) { 666 // Allows the plugin to query the browser for information 667 // 668 // Variables: 669 // NPNVxDisplay (unix only) 670 // NPNVxtAppContext (unix only) 671 // NPNVnetscapeWindow (win only) - Gets the native window on which the 672 // plug-in drawing occurs, returns HWND 673 // NPNVjavascriptEnabledBool: tells whether Javascript is enabled 674 // NPNVasdEnabledBool: tells whether SmartUpdate is enabled 675 // NPNVOfflineBool: tells whether offline-mode is enabled 676 677 NPError rv = NPERR_GENERIC_ERROR; 678 679 switch (static_cast<int>(variable)) { 680 case NPNVWindowNPObject: { 681 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 682 if (!plugin.get()) { 683 NOTREACHED(); 684 return NPERR_INVALID_INSTANCE_ERROR; 685 } 686 NPObject *np_object = plugin->webplugin()->GetWindowScriptNPObject(); 687 // Return value is expected to be retained, as 688 // described here: 689 // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> 690 if (np_object) { 691 WebBindings::retainObject(np_object); 692 void **v = (void **)value; 693 *v = np_object; 694 rv = NPERR_NO_ERROR; 695 } else { 696 NOTREACHED(); 697 } 698 break; 699 } 700 case NPNVPluginElementNPObject: { 701 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 702 if (!plugin.get()) { 703 NOTREACHED(); 704 return NPERR_INVALID_INSTANCE_ERROR; 705 } 706 NPObject *np_object = plugin->webplugin()->GetPluginElement(); 707 // Return value is expected to be retained, as 708 // described here: 709 // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> 710 if (np_object) { 711 WebBindings::retainObject(np_object); 712 void** v = static_cast<void**>(value); 713 *v = np_object; 714 rv = NPERR_NO_ERROR; 715 } else { 716 NOTREACHED(); 717 } 718 break; 719 } 720 #if !defined(OS_MACOSX) // OS X doesn't have windowed plugins. 721 case NPNVnetscapeWindow: { 722 scoped_refptr<PluginInstance> plugin = FindInstance(id); 723 if (!plugin.get()) { 724 NOTREACHED(); 725 return NPERR_INVALID_INSTANCE_ERROR; 726 } 727 gfx::PluginWindowHandle handle = plugin->window_handle(); 728 *((void**)value) = (void*)handle; 729 rv = NPERR_NO_ERROR; 730 break; 731 } 732 #endif 733 case NPNVjavascriptEnabledBool: { 734 // yes, JS is enabled. 735 *((void**)value) = (void*)1; 736 rv = NPERR_NO_ERROR; 737 break; 738 } 739 #if defined(TOOLKIT_GTK) 740 case NPNVToolkit: 741 // Tell them we are GTK2. (The alternative is GTK 1.2.) 742 *reinterpret_cast<int*>(value) = NPNVGtk2; 743 rv = NPERR_NO_ERROR; 744 break; 745 746 case NPNVSupportsXEmbedBool: 747 *reinterpret_cast<NPBool*>(value) = true; 748 rv = NPERR_NO_ERROR; 749 break; 750 #endif 751 case NPNVSupportsWindowless: { 752 NPBool* supports_windowless = reinterpret_cast<NPBool*>(value); 753 *supports_windowless = true; 754 rv = NPERR_NO_ERROR; 755 break; 756 } 757 case NPNVprivateModeBool: { 758 NPBool* private_mode = reinterpret_cast<NPBool*>(value); 759 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 760 if (!plugin.get()) { 761 NOTREACHED(); 762 return NPERR_INVALID_INSTANCE_ERROR; 763 } 764 *private_mode = plugin->webplugin()->IsOffTheRecord(); 765 rv = NPERR_NO_ERROR; 766 break; 767 } 768 #if defined(OS_MACOSX) 769 case NPNVpluginDrawingModel: { 770 // return the drawing model that was negotiated when we initialized. 771 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 772 if (!plugin.get()) { 773 NOTREACHED(); 774 return NPERR_INVALID_INSTANCE_ERROR; 775 } 776 *reinterpret_cast<int*>(value) = plugin->drawing_model(); 777 rv = NPERR_NO_ERROR; 778 break; 779 } 780 case NPNVsupportsCoreGraphicsBool: 781 case NPNVsupportsCocoaBool: { 782 // These drawing and event models are always supported. 783 NPBool* supports_model = reinterpret_cast<NPBool*>(value); 784 *supports_model = true; 785 rv = NPERR_NO_ERROR; 786 break; 787 } 788 case NPNVsupportsInvalidatingCoreAnimationBool: 789 case NPNVsupportsCoreAnimationBool: { 790 NPBool* supports_model = reinterpret_cast<NPBool*>(value); 791 *supports_model = content::SupportsCoreAnimationPlugins(); 792 rv = NPERR_NO_ERROR; 793 break; 794 } 795 #ifndef NP_NO_CARBON 796 case NPNVsupportsCarbonBool: 797 #endif 798 #ifndef NP_NO_QUICKDRAW 799 case NPNVsupportsQuickDrawBool: 800 #endif 801 case NPNVsupportsOpenGLBool: { 802 // These models are never supported. OpenGL was never widely supported, 803 // and QuickDraw and Carbon have been deprecated for quite some time. 804 NPBool* supports_model = reinterpret_cast<NPBool*>(value); 805 *supports_model = false; 806 rv = NPERR_NO_ERROR; 807 break; 808 } 809 case NPNVsupportsCompositingCoreAnimationPluginsBool: { 810 NPBool* supports_compositing = reinterpret_cast<NPBool*>(value); 811 *supports_compositing = content::SupportsCoreAnimationPlugins(); 812 rv = NPERR_NO_ERROR; 813 break; 814 } 815 case NPNVsupportsUpdatedCocoaTextInputBool: { 816 // We support the clarifications to the Cocoa IME event spec. 817 NPBool* supports_update = reinterpret_cast<NPBool*>(value); 818 *supports_update = true; 819 rv = NPERR_NO_ERROR; 820 break; 821 } 822 #endif // OS_MACOSX 823 default: 824 DVLOG(1) << "NPN_GetValue(" << variable << ") is not implemented yet."; 825 break; 826 } 827 return rv; 828 } 829 830 NPError NPN_SetValue(NPP id, NPPVariable variable, void* value) { 831 // Allows the plugin to set various modes 832 833 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 834 if (!plugin.get()) { 835 NOTREACHED(); 836 return NPERR_INVALID_INSTANCE_ERROR; 837 } 838 switch(variable) { 839 case NPPVpluginWindowBool: { 840 // Sets windowless mode for display of the plugin 841 // Note: the documentation at 842 // http://developer.mozilla.org/en/docs/NPN_SetValue is wrong. When 843 // value is NULL, the mode is set to true. This is the same way Mozilla 844 // works. 845 plugin->set_windowless(value == 0); 846 return NPERR_NO_ERROR; 847 } 848 case NPPVpluginTransparentBool: { 849 // Sets transparent mode for display of the plugin 850 // 851 // Transparent plugins require the browser to paint the background 852 // before having the plugin paint. By default, windowless plugins 853 // are transparent. Making a windowless plugin opaque means that 854 // the plugin does not require the browser to paint the background. 855 bool mode = (value != 0); 856 plugin->set_transparent(mode); 857 return NPERR_NO_ERROR; 858 } 859 case NPPVjavascriptPushCallerBool: 860 // Specifies whether you are pushing or popping the JSContext off. 861 // the stack 862 // TODO: implement me 863 DVLOG(1) << "NPN_SetValue(NPPVJavascriptPushCallerBool) is not " 864 "implemented."; 865 return NPERR_GENERIC_ERROR; 866 case NPPVpluginKeepLibraryInMemory: 867 // Tells browser that plugin library should live longer than usual. 868 // TODO: implement me 869 DVLOG(1) << "NPN_SetValue(NPPVpluginKeepLibraryInMemory) is not " 870 "implemented."; 871 return NPERR_GENERIC_ERROR; 872 #if defined(OS_MACOSX) 873 case NPPVpluginDrawingModel: { 874 intptr_t model = reinterpret_cast<intptr_t>(value); 875 if (model == NPDrawingModelCoreGraphics || 876 ((model == NPDrawingModelInvalidatingCoreAnimation || 877 model == NPDrawingModelCoreAnimation) && 878 content::SupportsCoreAnimationPlugins())) { 879 plugin->set_drawing_model(static_cast<NPDrawingModel>(model)); 880 return NPERR_NO_ERROR; 881 } 882 return NPERR_GENERIC_ERROR; 883 } 884 case NPPVpluginEventModel: { 885 // Only the Cocoa event model is supported. 886 intptr_t model = reinterpret_cast<intptr_t>(value); 887 if (model == NPEventModelCocoa) { 888 plugin->set_event_model(static_cast<NPEventModel>(model)); 889 return NPERR_NO_ERROR; 890 } 891 return NPERR_GENERIC_ERROR; 892 } 893 #endif 894 default: 895 // TODO: implement me 896 DVLOG(1) << "NPN_SetValue(" << variable << ") is not implemented."; 897 break; 898 } 899 900 NOTREACHED(); 901 return NPERR_GENERIC_ERROR; 902 } 903 904 void* NPN_GetJavaEnv() { 905 // TODO: implement me 906 DVLOG(1) << "NPN_GetJavaEnv is not implemented."; 907 return NULL; 908 } 909 910 void* NPN_GetJavaPeer(NPP) { 911 // TODO: implement me 912 DVLOG(1) << "NPN_GetJavaPeer is not implemented."; 913 return NULL; 914 } 915 916 void NPN_PushPopupsEnabledState(NPP id, NPBool enabled) { 917 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 918 if (plugin.get()) 919 plugin->PushPopupsEnabledState(enabled ? true : false); 920 } 921 922 void NPN_PopPopupsEnabledState(NPP id) { 923 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 924 if (plugin.get()) 925 plugin->PopPopupsEnabledState(); 926 } 927 928 void NPN_PluginThreadAsyncCall(NPP id, 929 void (*func)(void*), 930 void* user_data) { 931 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 932 if (plugin.get()) 933 plugin->PluginThreadAsyncCall(func, user_data); 934 } 935 936 NPError NPN_GetValueForURL(NPP id, 937 NPNURLVariable variable, 938 const char* url, 939 char** value, 940 uint32_t* len) { 941 if (!id) 942 return NPERR_INVALID_PARAM; 943 944 if (!url || !*url || !len) 945 return NPERR_INVALID_URL; 946 947 *len = 0; 948 std::string result; 949 950 switch (variable) { 951 case NPNURLVProxy: { 952 result = "DIRECT"; 953 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 954 if (!plugin.get()) 955 return NPERR_GENERIC_ERROR; 956 957 WebPlugin* webplugin = plugin->webplugin(); 958 if (!webplugin) 959 return NPERR_GENERIC_ERROR; 960 961 if (!webplugin->FindProxyForUrl(GURL(std::string(url)), &result)) 962 return NPERR_GENERIC_ERROR; 963 break; 964 } 965 case NPNURLVCookie: { 966 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 967 if (!plugin.get()) 968 return NPERR_GENERIC_ERROR; 969 970 WebPlugin* webplugin = plugin->webplugin(); 971 if (!webplugin) 972 return NPERR_GENERIC_ERROR; 973 974 // Bypass third-party cookie blocking by using the url as the 975 // first_party_for_cookies. 976 GURL cookies_url((std::string(url))); 977 result = webplugin->GetCookies(cookies_url, cookies_url); 978 break; 979 } 980 default: 981 return NPERR_GENERIC_ERROR; 982 } 983 984 // Allocate this using the NPAPI allocator. The plugin will call 985 // NPN_Free to free this. 986 *value = static_cast<char*>(NPN_MemAlloc(result.length() + 1)); 987 base::strlcpy(*value, result.c_str(), result.length() + 1); 988 *len = result.length(); 989 990 return NPERR_NO_ERROR; 991 } 992 993 NPError NPN_SetValueForURL(NPP id, 994 NPNURLVariable variable, 995 const char* url, 996 const char* value, 997 uint32_t len) { 998 if (!id) 999 return NPERR_INVALID_PARAM; 1000 1001 if (!url || !*url) 1002 return NPERR_INVALID_URL; 1003 1004 switch (variable) { 1005 case NPNURLVCookie: { 1006 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 1007 if (!plugin.get()) 1008 return NPERR_GENERIC_ERROR; 1009 1010 WebPlugin* webplugin = plugin->webplugin(); 1011 if (!webplugin) 1012 return NPERR_GENERIC_ERROR; 1013 1014 std::string cookie(value, len); 1015 GURL cookies_url((std::string(url))); 1016 webplugin->SetCookie(cookies_url, cookies_url, cookie); 1017 return NPERR_NO_ERROR; 1018 } 1019 case NPNURLVProxy: 1020 // We don't support setting proxy values, fall through... 1021 break; 1022 default: 1023 // Fall through and return an error... 1024 break; 1025 } 1026 1027 return NPERR_GENERIC_ERROR; 1028 } 1029 1030 NPError NPN_GetAuthenticationInfo(NPP id, 1031 const char* protocol, 1032 const char* host, 1033 int32_t port, 1034 const char* scheme, 1035 const char* realm, 1036 char** username, 1037 uint32_t* ulen, 1038 char** password, 1039 uint32_t* plen) { 1040 if (!id || !protocol || !host || !scheme || !realm || !username || 1041 !ulen || !password || !plen) 1042 return NPERR_INVALID_PARAM; 1043 1044 // TODO: implement me (bug 23928) 1045 return NPERR_GENERIC_ERROR; 1046 } 1047 1048 uint32_t NPN_ScheduleTimer(NPP id, 1049 uint32_t interval, 1050 NPBool repeat, 1051 void (*func)(NPP id, uint32_t timer_id)) { 1052 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 1053 if (!plugin.get()) 1054 return 0; 1055 1056 return plugin->ScheduleTimer(interval, repeat, func); 1057 } 1058 1059 void NPN_UnscheduleTimer(NPP id, uint32_t timer_id) { 1060 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 1061 if (plugin.get()) 1062 plugin->UnscheduleTimer(timer_id); 1063 } 1064 1065 NPError NPN_PopUpContextMenu(NPP id, NPMenu* menu) { 1066 if (!menu) 1067 return NPERR_INVALID_PARAM; 1068 1069 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 1070 if (plugin.get()) { 1071 return plugin->PopUpContextMenu(menu); 1072 } 1073 NOTREACHED(); 1074 return NPERR_GENERIC_ERROR; 1075 } 1076 1077 NPBool NPN_ConvertPoint(NPP id, double sourceX, double sourceY, 1078 NPCoordinateSpace sourceSpace, 1079 double *destX, double *destY, 1080 NPCoordinateSpace destSpace) { 1081 scoped_refptr<PluginInstance> plugin(FindInstance(id)); 1082 if (plugin.get()) { 1083 return plugin->ConvertPoint( 1084 sourceX, sourceY, sourceSpace, destX, destY, destSpace); 1085 } 1086 NOTREACHED(); 1087 return false; 1088 } 1089 1090 NPBool NPN_HandleEvent(NPP id, void *event, NPBool handled) { 1091 // TODO: Implement advanced key handling: http://crbug.com/46578 1092 NOTIMPLEMENTED(); 1093 return false; 1094 } 1095 1096 NPBool NPN_UnfocusInstance(NPP id, NPFocusDirection direction) { 1097 // TODO: Implement advanced key handling: http://crbug.com/46578 1098 NOTIMPLEMENTED(); 1099 return false; 1100 } 1101 1102 void NPN_URLRedirectResponse(NPP instance, void* notify_data, NPBool allow) { 1103 scoped_refptr<PluginInstance> plugin(FindInstance(instance)); 1104 if (plugin.get()) { 1105 plugin->URLRedirectResponse(!!allow, notify_data); 1106 } 1107 } 1108 1109 } // extern "C" 1110