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