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