1 /* 2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Collabora Ltd. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include "config.h" 28 #include "PluginView.h" 29 30 #include "Bridge.h" 31 #include "Chrome.h" 32 #include "Document.h" 33 #include "DocumentLoader.h" 34 #include "Element.h" 35 #include "FrameLoader.h" 36 #include "FrameTree.h" 37 #include "Frame.h" 38 #include "FrameView.h" 39 #include "GraphicsContext.h" 40 #include "Image.h" 41 #include "HTMLNames.h" 42 #include "HTMLPlugInElement.h" 43 #include "KeyboardEvent.h" 44 #include "MIMETypeRegistry.h" 45 #include "MouseEvent.h" 46 #include "NotImplemented.h" 47 #include "Page.h" 48 #include "FocusController.h" 49 #include "PlatformMouseEvent.h" 50 #if OS(WINDOWS) && ENABLE(NETSCAPE_PLUGIN_API) 51 #include "PluginMessageThrottlerWin.h" 52 #endif 53 #include "PluginPackage.h" 54 #include "ScriptController.h" 55 #include "ScriptValue.h" 56 #include "SecurityOrigin.h" 57 #include "PluginDatabase.h" 58 #include "PluginDebug.h" 59 #include "PluginMainThreadScheduler.h" 60 #include "PluginPackage.h" 61 #include "RenderBox.h" 62 #include "RenderObject.h" 63 #include "npruntime_impl.h" 64 #include "Settings.h" 65 #include <wtf/ASCIICType.h> 66 67 #if defined(ANDROID_PLUGINS) 68 #include "TouchEvent.h" 69 #endif 70 71 // ANDROID 72 // TODO: Upstream to webkit.org 73 #if USE(JSC) 74 #include "JSDOMWindow.h" 75 #include "JSDOMBinding.h" 76 #include "c_instance.h" 77 #include "runtime_root.h" 78 #include <runtime/JSLock.h> 79 #include <runtime/JSValue.h> 80 81 using JSC::ExecState; 82 using JSC::JSLock; 83 using JSC::JSObject; 84 using JSC::JSValue; 85 using JSC::UString; 86 #endif 87 88 using std::min; 89 90 using namespace WTF; 91 92 namespace WebCore { 93 94 using namespace HTMLNames; 95 96 static int s_callingPlugin; 97 98 typedef HashMap<NPP, PluginView*> InstanceMap; 99 100 static InstanceMap& instanceMap() 101 { 102 static InstanceMap& map = *new InstanceMap; 103 return map; 104 } 105 106 static String scriptStringIfJavaScriptURL(const KURL& url) 107 { 108 if (!protocolIsJavaScript(url)) 109 return String(); 110 111 // This returns an unescaped string 112 return decodeURLEscapeSequences(url.string().substring(11)); 113 } 114 115 PluginView* PluginView::s_currentPluginView = 0; 116 117 void PluginView::popPopupsStateTimerFired(Timer<PluginView>*) 118 { 119 popPopupsEnabledState(); 120 } 121 122 IntRect PluginView::windowClipRect() const 123 { 124 // Start by clipping to our bounds. 125 IntRect clipRect(m_windowRect); 126 127 // Take our element and get the clip rect from the enclosing layer and frame view. 128 RenderLayer* layer = m_element->renderer()->enclosingLayer(); 129 FrameView* parentView = m_element->document()->view(); 130 clipRect.intersect(parentView->windowClipRectForLayer(layer, true)); 131 132 return clipRect; 133 } 134 135 void PluginView::setFrameRect(const IntRect& rect) 136 { 137 if (m_element->document()->printing()) 138 return; 139 140 if (rect != frameRect()) 141 Widget::setFrameRect(rect); 142 143 updatePluginWidget(); 144 145 #if OS(WINDOWS) || OS(SYMBIAN) 146 // On Windows and Symbian, always call plugin to change geometry. 147 setNPWindowRect(rect); 148 #elif defined(XP_UNIX) 149 // On Unix, multiple calls to setNPWindow() in windowed mode causes Flash to crash 150 if (m_mode == NP_FULL || !m_isWindowed) 151 setNPWindowRect(rect); 152 #endif 153 } 154 155 void PluginView::frameRectsChanged() 156 { 157 updatePluginWidget(); 158 } 159 160 void PluginView::handleEvent(Event* event) 161 { 162 if (!m_plugin || m_isWindowed) 163 return; 164 165 // Protect the plug-in from deletion while dispatching the event. 166 RefPtr<PluginView> protect(this); 167 168 if (event->isMouseEvent()) 169 handleMouseEvent(static_cast<MouseEvent*>(event)); 170 else if (event->isKeyboardEvent()) 171 handleKeyboardEvent(static_cast<KeyboardEvent*>(event)); 172 #if defined(ANDROID_PLUGINS) 173 else if (event->isTouchEvent()) 174 handleTouchEvent(static_cast<TouchEvent*>(event)); 175 else if (event->type() == eventNames().DOMFocusOutEvent) 176 handleFocusEvent(false); 177 else if (event->type() == eventNames().DOMFocusInEvent) 178 handleFocusEvent(true); 179 #endif 180 #if defined(XP_UNIX) && ENABLE(NETSCAPE_PLUGIN_API) 181 else if (event->type() == eventNames().DOMFocusOutEvent) 182 handleFocusOutEvent(); 183 else if (event->type() == eventNames().DOMFocusInEvent) 184 handleFocusInEvent(); 185 #endif 186 } 187 188 void PluginView::init() 189 { 190 if (m_haveInitialized) 191 return; 192 193 m_haveInitialized = true; 194 195 if (!m_plugin) { 196 ASSERT(m_status == PluginStatusCanNotFindPlugin); 197 return; 198 } 199 200 LOG(Plugins, "PluginView::init(): Initializing plug-in '%s'", m_plugin->name().utf8().data()); 201 202 if (!m_plugin->load()) { 203 m_plugin = 0; 204 m_status = PluginStatusCanNotLoadPlugin; 205 return; 206 } 207 208 if (!startOrAddToUnstartedList()) { 209 m_status = PluginStatusCanNotLoadPlugin; 210 return; 211 } 212 213 m_status = PluginStatusLoadedSuccessfully; 214 } 215 216 bool PluginView::startOrAddToUnstartedList() 217 { 218 if (!m_parentFrame->page()) 219 return false; 220 221 // We only delay starting the plug-in if we're going to kick off the load 222 // ourselves. Otherwise, the loader will try to deliver data before we've 223 // started the plug-in. 224 if (!m_loadManually && !m_parentFrame->page()->canStartPlugins()) { 225 m_parentFrame->page()->addUnstartedPlugin(this); 226 m_isWaitingToStart = true; 227 return true; 228 } 229 230 return start(); 231 } 232 233 234 bool PluginView::start() 235 { 236 if (m_isStarted) 237 return false; 238 239 m_isWaitingToStart = false; 240 241 PluginMainThreadScheduler::scheduler().registerPlugin(m_instance); 242 243 ASSERT(m_plugin); 244 ASSERT(m_plugin->pluginFuncs()->newp); 245 246 NPError npErr; 247 { 248 PluginView::setCurrentPluginView(this); 249 #if USE(JSC) 250 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 251 #endif 252 setCallingPlugin(true); 253 npErr = m_plugin->pluginFuncs()->newp((NPMIMEType)m_mimeType.utf8().data(), m_instance, m_mode, m_paramCount, m_paramNames, m_paramValues, NULL); 254 setCallingPlugin(false); 255 LOG_NPERROR(npErr); 256 PluginView::setCurrentPluginView(0); 257 } 258 259 if (npErr != NPERR_NO_ERROR) { 260 m_status = PluginStatusCanNotLoadPlugin; 261 PluginMainThreadScheduler::scheduler().unregisterPlugin(m_instance); 262 return false; 263 } 264 265 m_isStarted = true; 266 267 if (!m_url.isEmpty() && !m_loadManually) { 268 FrameLoadRequest frameLoadRequest; 269 frameLoadRequest.resourceRequest().setHTTPMethod("GET"); 270 frameLoadRequest.resourceRequest().setURL(m_url); 271 #ifdef ANDROID_PLUGINS 272 if (!SecurityOrigin::shouldHideReferrer( 273 m_url, m_parentFrame->loader()->outgoingReferrer())) 274 frameLoadRequest.resourceRequest().setHTTPReferrer( 275 m_parentFrame->loader()->outgoingReferrer()); 276 #endif 277 load(frameLoadRequest, false, 0); 278 } 279 280 m_status = PluginStatusLoadedSuccessfully; 281 282 if (!platformStart()) 283 m_status = PluginStatusCanNotLoadPlugin; 284 285 if (m_status != PluginStatusLoadedSuccessfully) 286 return false; 287 288 if (parentFrame()->page()) 289 parentFrame()->page()->didStartPlugin(this); 290 291 return true; 292 } 293 294 PluginView::~PluginView() 295 { 296 LOG(Plugins, "PluginView::~PluginView()"); 297 298 ASSERT(!m_lifeSupportTimer.isActive()); 299 300 instanceMap().remove(m_instance); 301 302 removeFromUnstartedListIfNecessary(); 303 304 stop(); 305 306 deleteAllValues(m_requests); 307 308 freeStringArray(m_paramNames, m_paramCount); 309 freeStringArray(m_paramValues, m_paramCount); 310 311 platformDestroy(); 312 313 m_parentFrame->script()->cleanupScriptObjectsForPlugin(this); 314 315 #if PLATFORM(ANDROID) 316 // Since we have no legacy plugins to check, we ignore the quirks check. 317 if (m_plugin) 318 #else 319 if (m_plugin && !(m_plugin->quirks().contains(PluginQuirkDontUnloadPlugin))) 320 #endif 321 m_plugin->unload(); 322 } 323 324 void PluginView::removeFromUnstartedListIfNecessary() 325 { 326 if (!m_isWaitingToStart) 327 return; 328 329 if (!m_parentFrame->page()) 330 return; 331 332 m_parentFrame->page()->removeUnstartedPlugin(this); 333 } 334 335 void PluginView::stop() 336 { 337 if (!m_isStarted) 338 return; 339 340 if (parentFrame()->page()) 341 parentFrame()->page()->didStopPlugin(this); 342 343 LOG(Plugins, "PluginView::stop(): Stopping plug-in '%s'", m_plugin->name().utf8().data()); 344 345 HashSet<RefPtr<PluginStream> > streams = m_streams; 346 HashSet<RefPtr<PluginStream> >::iterator end = streams.end(); 347 for (HashSet<RefPtr<PluginStream> >::iterator it = streams.begin(); it != end; ++it) { 348 (*it)->stop(); 349 disconnectStream((*it).get()); 350 } 351 352 ASSERT(m_streams.isEmpty()); 353 354 m_isStarted = false; 355 #if USE(JSC) 356 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 357 #endif 358 359 #if ENABLE(NETSCAPE_PLUGIN_API) 360 #ifdef XP_WIN 361 // Unsubclass the window 362 if (m_isWindowed) { 363 #if OS(WINCE) 364 WNDPROC currentWndProc = (WNDPROC)GetWindowLong(platformPluginWidget(), GWL_WNDPROC); 365 366 if (currentWndProc == PluginViewWndProc) 367 SetWindowLong(platformPluginWidget(), GWL_WNDPROC, (LONG)m_pluginWndProc); 368 #else 369 WNDPROC currentWndProc = (WNDPROC)GetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC); 370 371 if (currentWndProc == PluginViewWndProc) 372 SetWindowLongPtr(platformPluginWidget(), GWLP_WNDPROC, (LONG)m_pluginWndProc); 373 #endif 374 } 375 #endif // XP_WIN 376 #endif // ENABLE(NETSCAPE_PLUGIN_API) 377 378 #if !defined(XP_MACOSX) 379 // Clear the window 380 m_npWindow.window = 0; 381 382 if (m_plugin->pluginFuncs()->setwindow && !m_plugin->quirks().contains(PluginQuirkDontSetNullWindowHandleOnDestroy)) { 383 PluginView::setCurrentPluginView(this); 384 setCallingPlugin(true); 385 m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); 386 setCallingPlugin(false); 387 PluginView::setCurrentPluginView(0); 388 } 389 390 #ifdef XP_UNIX 391 if (m_isWindowed && m_npWindow.ws_info) 392 delete (NPSetWindowCallbackStruct *)m_npWindow.ws_info; 393 m_npWindow.ws_info = 0; 394 #endif 395 396 #endif // !defined(XP_MACOSX) 397 398 PluginMainThreadScheduler::scheduler().unregisterPlugin(m_instance); 399 400 NPSavedData* savedData = 0; 401 PluginView::setCurrentPluginView(this); 402 setCallingPlugin(true); 403 NPError npErr = m_plugin->pluginFuncs()->destroy(m_instance, &savedData); 404 setCallingPlugin(false); 405 LOG_NPERROR(npErr); 406 PluginView::setCurrentPluginView(0); 407 408 #if ENABLE(NETSCAPE_PLUGIN_API) 409 if (savedData) { 410 // TODO: Actually save this data instead of just discarding it 411 if (savedData->buf) 412 NPN_MemFree(savedData->buf); 413 NPN_MemFree(savedData); 414 } 415 #endif 416 417 m_instance->pdata = 0; 418 } 419 420 void PluginView::setCurrentPluginView(PluginView* pluginView) 421 { 422 s_currentPluginView = pluginView; 423 } 424 425 PluginView* PluginView::currentPluginView() 426 { 427 return s_currentPluginView; 428 } 429 430 static char* createUTF8String(const String& str) 431 { 432 CString cstr = str.utf8(); 433 char* result = reinterpret_cast<char*>(fastMalloc(cstr.length() + 1)); 434 435 strncpy(result, cstr.data(), cstr.length() + 1); 436 437 return result; 438 } 439 440 #if USE(JSC) 441 static bool getString(ScriptController* proxy, JSValue result, String& string) 442 { 443 if (!proxy || !result || result.isUndefined()) 444 return false; 445 JSLock lock(JSC::SilenceAssertionsOnly); 446 447 ExecState* exec = proxy->globalObject(pluginWorld())->globalExec(); 448 UString ustring = result.toString(exec); 449 exec->clearException(); 450 451 string = ustring; 452 return true; 453 } 454 #endif 455 456 void PluginView::performRequest(PluginRequest* request) 457 { 458 if (!m_isStarted) 459 return; 460 461 // don't let a plugin start any loads if it is no longer part of a document that is being 462 // displayed unless the loads are in the same frame as the plugin. 463 const String& targetFrameName = request->frameLoadRequest().frameName(); 464 if (m_parentFrame->loader()->documentLoader() != m_parentFrame->loader()->activeDocumentLoader() && 465 (targetFrameName.isNull() || m_parentFrame->tree()->find(targetFrameName) != m_parentFrame)) 466 return; 467 468 KURL requestURL = request->frameLoadRequest().resourceRequest().url(); 469 String jsString = scriptStringIfJavaScriptURL(requestURL); 470 471 if (jsString.isNull()) { 472 // if this is not a targeted request, create a stream for it. otherwise, 473 // just pass it off to the loader 474 if (targetFrameName.isEmpty()) { 475 RefPtr<PluginStream> stream = PluginStream::create(this, m_parentFrame.get(), request->frameLoadRequest().resourceRequest(), request->sendNotification(), request->notifyData(), plugin()->pluginFuncs(), instance(), m_plugin->quirks()); 476 m_streams.add(stream); 477 stream->start(); 478 } else { 479 // If the target frame is our frame, we could destroy the 480 // PluginView, so we protect it. <rdar://problem/6991251> 481 RefPtr<PluginView> protect(this); 482 483 m_parentFrame->loader()->load(request->frameLoadRequest().resourceRequest(), targetFrameName, false); 484 485 // FIXME: <rdar://problem/4807469> This should be sent when the document has finished loading 486 if (request->sendNotification()) { 487 PluginView::setCurrentPluginView(this); 488 #if USE(JSC) 489 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 490 #endif 491 setCallingPlugin(true); 492 m_plugin->pluginFuncs()->urlnotify(m_instance, requestURL.string().utf8().data(), NPRES_DONE, request->notifyData()); 493 setCallingPlugin(false); 494 PluginView::setCurrentPluginView(0); 495 } 496 } 497 return; 498 } 499 500 // Targeted JavaScript requests are only allowed on the frame that contains the JavaScript plugin 501 // and this has been made sure in ::load. 502 ASSERT(targetFrameName.isEmpty() || m_parentFrame->tree()->find(targetFrameName) == m_parentFrame); 503 504 // Executing a script can cause the plugin view to be destroyed, so we keep a reference to the parent frame. 505 RefPtr<Frame> parentFrame = m_parentFrame; 506 ScriptValue result = m_parentFrame->script()->executeScript(jsString, request->shouldAllowPopups()); 507 508 if (targetFrameName.isNull()) { 509 String resultString; 510 511 CString cstr; 512 #if USE(JSC) 513 if (getString(parentFrame->script(), result.jsValue(), resultString)) 514 cstr = resultString.utf8(); 515 #elif USE(V8) 516 // #if PLATFORM(ANDROID) 517 // TODO. When upstreaming this, we could re-visit whether the JSC getString function in this file 518 // could be removed, and this code re-factored to call ScriptValue::getString(ScriptState* scriptState, String& result) 519 // in both cases, thus getting rid of the #ifs 520 // #endif 521 if (result.getString(resultString)) 522 cstr = resultString.utf8(); 523 #endif 524 525 RefPtr<PluginStream> stream = PluginStream::create(this, m_parentFrame.get(), request->frameLoadRequest().resourceRequest(), request->sendNotification(), request->notifyData(), plugin()->pluginFuncs(), instance(), m_plugin->quirks()); 526 m_streams.add(stream); 527 stream->sendJavaScriptStream(requestURL, cstr); 528 } 529 } 530 531 void PluginView::requestTimerFired(Timer<PluginView>* timer) 532 { 533 ASSERT(timer == &m_requestTimer); 534 ASSERT(m_requests.size() > 0); 535 ASSERT(!m_isJavaScriptPaused); 536 537 PluginRequest* request = m_requests[0]; 538 m_requests.remove(0); 539 540 // Schedule a new request before calling performRequest since the call to 541 // performRequest can cause the plugin view to be deleted. 542 if (m_requests.size() > 0) 543 m_requestTimer.startOneShot(0); 544 545 performRequest(request); 546 delete request; 547 } 548 549 void PluginView::scheduleRequest(PluginRequest* request) 550 { 551 m_requests.append(request); 552 553 if (!m_isJavaScriptPaused) 554 m_requestTimer.startOneShot(0); 555 } 556 557 NPError PluginView::load(const FrameLoadRequest& frameLoadRequest, bool sendNotification, void* notifyData) 558 { 559 ASSERT(frameLoadRequest.resourceRequest().httpMethod() == "GET" || frameLoadRequest.resourceRequest().httpMethod() == "POST"); 560 561 KURL url = frameLoadRequest.resourceRequest().url(); 562 563 if (url.isEmpty()) 564 return NPERR_INVALID_URL; 565 566 // Don't allow requests to be made when the document loader is stopping all loaders. 567 if (m_parentFrame->loader()->documentLoader()->isStopping()) 568 return NPERR_GENERIC_ERROR; 569 570 const String& targetFrameName = frameLoadRequest.frameName(); 571 String jsString = scriptStringIfJavaScriptURL(url); 572 573 if (!jsString.isNull()) { 574 Settings* settings = m_parentFrame->settings(); 575 576 // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does. 577 if (!settings || !settings->isJavaScriptEnabled()) 578 return NPERR_GENERIC_ERROR; 579 580 // For security reasons, only allow JS requests to be made on the frame that contains the plug-in. 581 if (!targetFrameName.isNull() && m_parentFrame->tree()->find(targetFrameName) != m_parentFrame) 582 return NPERR_INVALID_PARAM; 583 } else if (!SecurityOrigin::canLoad(url, String(), m_parentFrame->document())) 584 return NPERR_GENERIC_ERROR; 585 586 PluginRequest* request = new PluginRequest(frameLoadRequest, sendNotification, notifyData, arePopupsAllowed()); 587 scheduleRequest(request); 588 589 return NPERR_NO_ERROR; 590 } 591 592 static KURL makeURL(const KURL& baseURL, const char* relativeURLString) 593 { 594 String urlString = relativeURLString; 595 596 // Strip return characters. 597 urlString.replace('\n', ""); 598 urlString.replace('\r', ""); 599 600 return KURL(baseURL, urlString); 601 } 602 603 NPError PluginView::getURLNotify(const char* url, const char* target, void* notifyData) 604 { 605 FrameLoadRequest frameLoadRequest; 606 607 frameLoadRequest.setFrameName(target); 608 frameLoadRequest.resourceRequest().setHTTPMethod("GET"); 609 frameLoadRequest.resourceRequest().setURL(makeURL(m_baseURL, url)); 610 #ifdef ANDROID_PLUGINS 611 if (!SecurityOrigin::shouldHideReferrer( 612 frameLoadRequest.resourceRequest().url(), m_url)) 613 frameLoadRequest.resourceRequest().setHTTPReferrer(m_url); 614 #endif 615 616 return load(frameLoadRequest, true, notifyData); 617 } 618 619 NPError PluginView::getURL(const char* url, const char* target) 620 { 621 FrameLoadRequest frameLoadRequest; 622 623 frameLoadRequest.setFrameName(target); 624 frameLoadRequest.resourceRequest().setHTTPMethod("GET"); 625 frameLoadRequest.resourceRequest().setURL(makeURL(m_baseURL, url)); 626 #ifdef ANDROID_PLUGINS 627 if (!SecurityOrigin::shouldHideReferrer( 628 frameLoadRequest.resourceRequest().url(), m_url)) 629 frameLoadRequest.resourceRequest().setHTTPReferrer(m_url); 630 #endif 631 632 return load(frameLoadRequest, false, 0); 633 } 634 635 NPError PluginView::postURLNotify(const char* url, const char* target, uint32 len, const char* buf, NPBool file, void* notifyData) 636 { 637 return handlePost(url, target, len, buf, file, notifyData, true, true); 638 } 639 640 NPError PluginView::postURL(const char* url, const char* target, uint32 len, const char* buf, NPBool file) 641 { 642 // As documented, only allow headers to be specified via NPP_PostURL when using a file. 643 return handlePost(url, target, len, buf, file, 0, false, file); 644 } 645 646 NPError PluginView::newStream(NPMIMEType type, const char* target, NPStream** stream) 647 { 648 notImplemented(); 649 // Unsupported 650 return NPERR_GENERIC_ERROR; 651 } 652 653 int32 PluginView::write(NPStream* stream, int32 len, void* buffer) 654 { 655 notImplemented(); 656 // Unsupported 657 return -1; 658 } 659 660 NPError PluginView::destroyStream(NPStream* stream, NPReason reason) 661 { 662 if (!stream || PluginStream::ownerForStream(stream) != m_instance) 663 return NPERR_INVALID_INSTANCE_ERROR; 664 665 PluginStream* browserStream = static_cast<PluginStream*>(stream->ndata); 666 browserStream->cancelAndDestroyStream(reason); 667 668 return NPERR_NO_ERROR; 669 } 670 671 void PluginView::status(const char* message) 672 { 673 if (Page* page = m_parentFrame->page()) 674 page->chrome()->setStatusbarText(m_parentFrame.get(), String(message)); 675 } 676 677 NPError PluginView::setValue(NPPVariable variable, void* value) 678 { 679 LOG(Plugins, "PluginView::setValue(%s): ", prettyNameForNPPVariable(variable, value).data()); 680 681 switch (variable) { 682 case NPPVpluginWindowBool: 683 m_isWindowed = value; 684 return NPERR_NO_ERROR; 685 case NPPVpluginTransparentBool: 686 m_isTransparent = value; 687 return NPERR_NO_ERROR; 688 #if defined(XP_MACOSX) 689 case NPPVpluginDrawingModel: { 690 // Can only set drawing model inside NPP_New() 691 if (this != currentPluginView()) 692 return NPERR_GENERIC_ERROR; 693 694 NPDrawingModel newDrawingModel = NPDrawingModel(uintptr_t(value)); 695 switch (newDrawingModel) { 696 case NPDrawingModelCoreGraphics: 697 m_drawingModel = newDrawingModel; 698 return NPERR_NO_ERROR; 699 #ifndef NP_NO_QUICKDRAW 700 case NPDrawingModelQuickDraw: 701 #endif 702 case NPDrawingModelCoreAnimation: 703 default: 704 LOG(Plugins, "Plugin asked for unsupported drawing model: %s", 705 prettyNameForDrawingModel(newDrawingModel)); 706 return NPERR_GENERIC_ERROR; 707 } 708 } 709 710 case NPPVpluginEventModel: { 711 // Can only set event model inside NPP_New() 712 if (this != currentPluginView()) 713 return NPERR_GENERIC_ERROR; 714 715 NPEventModel newEventModel = NPEventModel(uintptr_t(value)); 716 switch (newEventModel) { 717 #ifndef NP_NO_CARBON 718 case NPEventModelCarbon: 719 #endif 720 case NPEventModelCocoa: 721 m_eventModel = newEventModel; 722 return NPERR_NO_ERROR; 723 724 default: 725 LOG(Plugins, "Plugin asked for unsupported event model: %s", 726 prettyNameForEventModel(newEventModel)); 727 return NPERR_GENERIC_ERROR; 728 } 729 } 730 #endif // defined(XP_MACOSX) 731 732 default: 733 #ifdef PLUGIN_PLATFORM_SETVALUE 734 return platformSetValue(variable, value); 735 #else 736 notImplemented(); 737 return NPERR_GENERIC_ERROR; 738 #endif 739 } 740 } 741 742 void PluginView::invalidateTimerFired(Timer<PluginView>* timer) 743 { 744 ASSERT(timer == &m_invalidateTimer); 745 746 for (unsigned i = 0; i < m_invalidRects.size(); i++) 747 invalidateRect(m_invalidRects[i]); 748 m_invalidRects.clear(); 749 } 750 751 752 void PluginView::pushPopupsEnabledState(bool state) 753 { 754 m_popupStateStack.append(state); 755 } 756 757 void PluginView::popPopupsEnabledState() 758 { 759 m_popupStateStack.removeLast(); 760 } 761 762 bool PluginView::arePopupsAllowed() const 763 { 764 if (!m_popupStateStack.isEmpty()) 765 return m_popupStateStack.last(); 766 767 return false; 768 } 769 770 void PluginView::setJavaScriptPaused(bool paused) 771 { 772 if (m_isJavaScriptPaused == paused) 773 return; 774 m_isJavaScriptPaused = paused; 775 776 if (m_isJavaScriptPaused) 777 m_requestTimer.stop(); 778 else if (!m_requests.isEmpty()) 779 m_requestTimer.startOneShot(0); 780 } 781 782 783 #if USE(JSC) 784 PassRefPtr<JSC::Bindings::Instance> PluginView::bindingInstance() 785 { 786 #if ENABLE(NETSCAPE_PLUGIN_API) 787 NPObject* object = 0; 788 789 if (!m_isStarted || !m_plugin || !m_plugin->pluginFuncs()->getvalue) 790 return 0; 791 792 // On Windows, calling Java's NPN_GetValue can allow the message loop to 793 // run, allowing loading to take place or JavaScript to run. Protect the 794 // PluginView from destruction. <rdar://problem/6978804> 795 RefPtr<PluginView> protect(this); 796 797 NPError npErr; 798 { 799 PluginView::setCurrentPluginView(this); 800 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 801 setCallingPlugin(true); 802 npErr = m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginScriptableNPObject, &object); 803 setCallingPlugin(false); 804 PluginView::setCurrentPluginView(0); 805 } 806 807 if (hasOneRef()) { 808 // The renderer for the PluginView was destroyed during the above call, and 809 // the PluginView will be destroyed when this function returns, so we 810 // return null. 811 return 0; 812 } 813 814 if (npErr != NPERR_NO_ERROR || !object) 815 return 0; 816 817 RefPtr<JSC::Bindings::RootObject> root = m_parentFrame->script()->createRootObject(this); 818 RefPtr<JSC::Bindings::Instance> instance = JSC::Bindings::CInstance::create(object, root.release()); 819 820 _NPN_ReleaseObject(object); 821 822 return instance.release(); 823 #else 824 return 0; 825 #endif // NETSCAPE_PLUGIN_API 826 } 827 #endif // JSC 828 829 #if USE(V8) 830 // This is really JS engine independent 831 NPObject* PluginView::getNPObject() { 832 #if ENABLE(NETSCAPE_PLUGIN_API) 833 if (!m_plugin || !m_plugin->pluginFuncs()->getvalue) 834 return 0; 835 836 NPObject* object = 0; 837 838 NPError npErr; 839 { 840 PluginView::setCurrentPluginView(this); 841 setCallingPlugin(true); 842 npErr = m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginScriptableNPObject, &object); 843 setCallingPlugin(false); 844 PluginView::setCurrentPluginView(0); 845 } 846 847 if (npErr != NPERR_NO_ERROR || !object) 848 return 0; 849 850 // Bindings::CInstance (used in JSC version) retains the object, so in ~PluginView() it calls 851 // cleanupScriptObjectsForPlugin() to releases the object. To maintain the reference count, 852 // don't call _NPN_ReleaseObject(object) here. 853 return object; 854 #else 855 return 0; 856 #endif // NETSCAPE_PLUGIN_API 857 } 858 #endif // V8 859 860 void PluginView::disconnectStream(PluginStream* stream) 861 { 862 ASSERT(m_streams.contains(stream)); 863 864 m_streams.remove(stream); 865 } 866 867 void PluginView::setParameters(const Vector<String>& paramNames, const Vector<String>& paramValues) 868 { 869 ASSERT(paramNames.size() == paramValues.size()); 870 871 unsigned size = paramNames.size(); 872 unsigned paramCount = 0; 873 874 m_paramNames = reinterpret_cast<char**>(fastMalloc(sizeof(char*) * size)); 875 m_paramValues = reinterpret_cast<char**>(fastMalloc(sizeof(char*) * size)); 876 877 for (unsigned i = 0; i < size; i++) { 878 if (m_plugin->quirks().contains(PluginQuirkRemoveWindowlessVideoParam) && equalIgnoringCase(paramNames[i], "windowlessvideo")) 879 continue; 880 881 if (paramNames[i] == "pluginspage") 882 m_pluginsPage = paramValues[i]; 883 884 m_paramNames[paramCount] = createUTF8String(paramNames[i]); 885 m_paramValues[paramCount] = createUTF8String(paramValues[i]); 886 887 paramCount++; 888 } 889 890 m_paramCount = paramCount; 891 } 892 893 PluginView::PluginView(Frame* parentFrame, const IntSize& size, PluginPackage* plugin, Element* element, const KURL& url, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType, bool loadManually) 894 : m_parentFrame(parentFrame) 895 , m_plugin(plugin) 896 , m_element(element) 897 , m_isStarted(false) 898 , m_url(url) 899 , m_baseURL(m_parentFrame->loader()->completeURL(m_parentFrame->document()->baseURL().string())) 900 , m_status(PluginStatusLoadedSuccessfully) 901 , m_requestTimer(this, &PluginView::requestTimerFired) 902 , m_invalidateTimer(this, &PluginView::invalidateTimerFired) 903 , m_popPopupsStateTimer(this, &PluginView::popPopupsStateTimerFired) 904 , m_lifeSupportTimer(this, &PluginView::lifeSupportTimerFired) 905 , m_mode(loadManually ? NP_FULL : NP_EMBED) 906 , m_paramNames(0) 907 , m_paramValues(0) 908 , m_mimeType(mimeType) 909 #if defined(XP_MACOSX) 910 , m_isWindowed(false) 911 #else 912 , m_isWindowed(true) 913 #endif 914 , m_isTransparent(false) 915 , m_haveInitialized(false) 916 , m_isWaitingToStart(false) 917 #if defined(XP_UNIX) 918 , m_needsXEmbed(false) 919 #endif 920 #if OS(WINDOWS) && ENABLE(NETSCAPE_PLUGIN_API) 921 , m_pluginWndProc(0) 922 , m_lastMessage(0) 923 , m_isCallingPluginWndProc(false) 924 , m_wmPrintHDC(0) 925 , m_haveUpdatedPluginWidget(false) 926 #endif 927 #if (PLATFORM(QT) && OS(WINDOWS)) || defined(XP_MACOSX) 928 , m_window(0) 929 #endif 930 #if defined(XP_MACOSX) 931 , m_drawingModel(NPDrawingModel(-1)) 932 , m_eventModel(NPEventModel(-1)) 933 , m_contextRef(0) 934 , m_fakeWindow(0) 935 #endif 936 #if defined(XP_UNIX) && ENABLE(NETSCAPE_PLUGIN_API) 937 , m_hasPendingGeometryChange(true) 938 , m_drawable(0) 939 , m_visual(0) 940 , m_colormap(0) 941 , m_pluginDisplay(0) 942 #endif 943 , m_loadManually(loadManually) 944 , m_manualStream(0) 945 , m_isJavaScriptPaused(false) 946 , m_isHalted(false) 947 , m_hasBeenHalted(false) 948 { 949 #if defined(ANDROID_PLUGINS) 950 platformInit(); 951 #endif 952 953 if (!m_plugin) { 954 m_status = PluginStatusCanNotFindPlugin; 955 return; 956 } 957 958 m_instance = &m_instanceStruct; 959 m_instance->ndata = this; 960 m_instance->pdata = 0; 961 962 instanceMap().add(m_instance, this); 963 964 setParameters(paramNames, paramValues); 965 966 memset(&m_npWindow, 0, sizeof(m_npWindow)); 967 #if defined(XP_MACOSX) 968 memset(&m_npCgContext, 0, sizeof(m_npCgContext)); 969 #endif 970 971 resize(size); 972 } 973 974 void PluginView::focusPluginElement() 975 { 976 // Focus the plugin 977 if (Page* page = m_parentFrame->page()) 978 page->focusController()->setFocusedFrame(m_parentFrame); 979 m_parentFrame->document()->setFocusedNode(m_element); 980 } 981 982 void PluginView::didReceiveResponse(const ResourceResponse& response) 983 { 984 if (m_status != PluginStatusLoadedSuccessfully) 985 return; 986 987 ASSERT(m_loadManually); 988 ASSERT(!m_manualStream); 989 990 m_manualStream = PluginStream::create(this, m_parentFrame.get(), m_parentFrame->loader()->activeDocumentLoader()->request(), false, 0, plugin()->pluginFuncs(), instance(), m_plugin->quirks()); 991 m_manualStream->setLoadManually(true); 992 993 m_manualStream->didReceiveResponse(0, response); 994 } 995 996 void PluginView::didReceiveData(const char* data, int length) 997 { 998 if (m_status != PluginStatusLoadedSuccessfully) 999 return; 1000 1001 ASSERT(m_loadManually); 1002 ASSERT(m_manualStream); 1003 1004 m_manualStream->didReceiveData(0, data, length); 1005 } 1006 1007 void PluginView::didFinishLoading() 1008 { 1009 if (m_status != PluginStatusLoadedSuccessfully) 1010 return; 1011 1012 ASSERT(m_loadManually); 1013 ASSERT(m_manualStream); 1014 1015 m_manualStream->didFinishLoading(0); 1016 } 1017 1018 void PluginView::didFail(const ResourceError& error) 1019 { 1020 if (m_status != PluginStatusLoadedSuccessfully) 1021 return; 1022 1023 ASSERT(m_loadManually); 1024 ASSERT(m_manualStream); 1025 1026 m_manualStream->didFail(0, error); 1027 } 1028 1029 void PluginView::setCallingPlugin(bool b) const 1030 { 1031 if (!m_plugin->quirks().contains(PluginQuirkHasModalMessageLoop)) 1032 return; 1033 1034 if (b) 1035 ++s_callingPlugin; 1036 else 1037 --s_callingPlugin; 1038 1039 ASSERT(s_callingPlugin >= 0); 1040 } 1041 1042 bool PluginView::isCallingPlugin() 1043 { 1044 return s_callingPlugin > 0; 1045 } 1046 1047 PassRefPtr<PluginView> PluginView::create(Frame* parentFrame, const IntSize& size, Element* element, const KURL& url, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType, bool loadManually) 1048 { 1049 // if we fail to find a plugin for this MIME type, findPlugin will search for 1050 // a plugin by the file extension and update the MIME type, so pass a mutable String 1051 String mimeTypeCopy = mimeType; 1052 PluginPackage* plugin = PluginDatabase::installedPlugins()->findPlugin(url, mimeTypeCopy); 1053 1054 // No plugin was found, try refreshing the database and searching again 1055 if (!plugin && PluginDatabase::installedPlugins()->refresh()) { 1056 mimeTypeCopy = mimeType; 1057 plugin = PluginDatabase::installedPlugins()->findPlugin(url, mimeTypeCopy); 1058 } 1059 1060 return adoptRef(new PluginView(parentFrame, size, plugin, element, url, paramNames, paramValues, mimeTypeCopy, loadManually)); 1061 } 1062 1063 void PluginView::freeStringArray(char** stringArray, int length) 1064 { 1065 if (!stringArray) 1066 return; 1067 1068 for (int i = 0; i < length; i++) 1069 fastFree(stringArray[i]); 1070 1071 fastFree(stringArray); 1072 } 1073 1074 static inline bool startsWithBlankLine(const Vector<char>& buffer) 1075 { 1076 return buffer.size() > 0 && buffer[0] == '\n'; 1077 } 1078 1079 static inline int locationAfterFirstBlankLine(const Vector<char>& buffer) 1080 { 1081 const char* bytes = buffer.data(); 1082 unsigned length = buffer.size(); 1083 1084 for (unsigned i = 0; i < length - 4; i++) { 1085 // Support for Acrobat. It sends "\n\n". 1086 if (bytes[i] == '\n' && bytes[i + 1] == '\n') 1087 return i + 2; 1088 1089 // Returns the position after 2 CRLF's or 1 CRLF if it is the first line. 1090 if (bytes[i] == '\r' && bytes[i + 1] == '\n') { 1091 i += 2; 1092 if (i == 2) 1093 return i; 1094 else if (bytes[i] == '\n') 1095 // Support for Director. It sends "\r\n\n" (3880387). 1096 return i + 1; 1097 else if (bytes[i] == '\r' && bytes[i + 1] == '\n') 1098 // Support for Flash. It sends "\r\n\r\n" (3758113). 1099 return i + 2; 1100 } 1101 } 1102 1103 return -1; 1104 } 1105 1106 static inline const char* findEOL(const char* bytes, unsigned length) 1107 { 1108 // According to the HTTP specification EOL is defined as 1109 // a CRLF pair. Unfortunately, some servers will use LF 1110 // instead. Worse yet, some servers will use a combination 1111 // of both (e.g. <header>CRLFLF<body>), so findEOL needs 1112 // to be more forgiving. It will now accept CRLF, LF or 1113 // CR. 1114 // 1115 // It returns NULL if EOLF is not found or it will return 1116 // a pointer to the first terminating character. 1117 for (unsigned i = 0; i < length; i++) { 1118 if (bytes[i] == '\n') 1119 return bytes + i; 1120 if (bytes[i] == '\r') { 1121 // Check to see if spanning buffer bounds 1122 // (CRLF is across reads). If so, wait for 1123 // next read. 1124 if (i + 1 == length) 1125 break; 1126 1127 return bytes + i; 1128 } 1129 } 1130 1131 return 0; 1132 } 1133 1134 static inline String capitalizeRFC822HeaderFieldName(const String& name) 1135 { 1136 bool capitalizeCharacter = true; 1137 String result; 1138 1139 for (unsigned i = 0; i < name.length(); i++) { 1140 UChar c; 1141 1142 if (capitalizeCharacter && name[i] >= 'a' && name[i] <= 'z') 1143 c = toASCIIUpper(name[i]); 1144 else if (!capitalizeCharacter && name[i] >= 'A' && name[i] <= 'Z') 1145 c = toASCIILower(name[i]); 1146 else 1147 c = name[i]; 1148 1149 if (name[i] == '-') 1150 capitalizeCharacter = true; 1151 else 1152 capitalizeCharacter = false; 1153 1154 result.append(c); 1155 } 1156 1157 return result; 1158 } 1159 1160 static inline HTTPHeaderMap parseRFC822HeaderFields(const Vector<char>& buffer, unsigned length) 1161 { 1162 const char* bytes = buffer.data(); 1163 const char* eol; 1164 String lastKey; 1165 HTTPHeaderMap headerFields; 1166 1167 // Loop ove rlines until we're past the header, or we can't find any more end-of-lines 1168 while ((eol = findEOL(bytes, length))) { 1169 const char* line = bytes; 1170 int lineLength = eol - bytes; 1171 1172 // Move bytes to the character after the terminator as returned by findEOL. 1173 bytes = eol + 1; 1174 if ((*eol == '\r') && (*bytes == '\n')) 1175 bytes++; // Safe since findEOL won't return a spanning CRLF. 1176 1177 length -= (bytes - line); 1178 if (lineLength == 0) 1179 // Blank line; we're at the end of the header 1180 break; 1181 else if (*line == ' ' || *line == '\t') { 1182 // Continuation of the previous header 1183 if (lastKey.isNull()) { 1184 // malformed header; ignore it and continue 1185 continue; 1186 } else { 1187 // Merge the continuation of the previous header 1188 String currentValue = headerFields.get(lastKey); 1189 String newValue(line, lineLength); 1190 1191 headerFields.set(lastKey, currentValue + newValue); 1192 } 1193 } else { 1194 // Brand new header 1195 const char* colon; 1196 for (colon = line; *colon != ':' && colon != eol; colon++) { 1197 // empty loop 1198 } 1199 if (colon == eol) 1200 // malformed header; ignore it and continue 1201 continue; 1202 else { 1203 lastKey = capitalizeRFC822HeaderFieldName(String(line, colon - line)); 1204 String value; 1205 1206 for (colon++; colon != eol; colon++) { 1207 if (*colon != ' ' && *colon != '\t') 1208 break; 1209 } 1210 if (colon == eol) 1211 value = ""; 1212 else 1213 value = String(colon, eol - colon); 1214 1215 String oldValue = headerFields.get(lastKey); 1216 if (!oldValue.isNull()) { 1217 String tmp = oldValue; 1218 tmp += ", "; 1219 tmp += value; 1220 value = tmp; 1221 } 1222 1223 headerFields.set(lastKey, value); 1224 } 1225 } 1226 } 1227 1228 return headerFields; 1229 } 1230 1231 NPError PluginView::handlePost(const char* url, const char* target, uint32 len, const char* buf, bool file, void* notifyData, bool sendNotification, bool allowHeaders) 1232 { 1233 if (!url || !len || !buf) 1234 return NPERR_INVALID_PARAM; 1235 1236 FrameLoadRequest frameLoadRequest; 1237 1238 HTTPHeaderMap headerFields; 1239 Vector<char> buffer; 1240 1241 if (file) { 1242 NPError readResult = handlePostReadFile(buffer, len, buf); 1243 if(readResult != NPERR_NO_ERROR) 1244 return readResult; 1245 } else { 1246 buffer.resize(len); 1247 memcpy(buffer.data(), buf, len); 1248 } 1249 1250 const char* postData = buffer.data(); 1251 int postDataLength = buffer.size(); 1252 1253 if (allowHeaders) { 1254 if (startsWithBlankLine(buffer)) { 1255 postData++; 1256 postDataLength--; 1257 } else { 1258 int location = locationAfterFirstBlankLine(buffer); 1259 if (location != -1) { 1260 // If the blank line is somewhere in the middle of the buffer, everything before is the header 1261 headerFields = parseRFC822HeaderFields(buffer, location); 1262 unsigned dataLength = buffer.size() - location; 1263 1264 // Sometimes plugins like to set Content-Length themselves when they post, 1265 // but WebFoundation does not like that. So we will remove the header 1266 // and instead truncate the data to the requested length. 1267 String contentLength = headerFields.get("Content-Length"); 1268 1269 if (!contentLength.isNull()) 1270 dataLength = min(contentLength.toInt(), (int)dataLength); 1271 headerFields.remove("Content-Length"); 1272 1273 postData += location; 1274 postDataLength = dataLength; 1275 } 1276 } 1277 } 1278 1279 frameLoadRequest.resourceRequest().setHTTPMethod("POST"); 1280 frameLoadRequest.resourceRequest().setURL(makeURL(m_baseURL, url)); 1281 frameLoadRequest.resourceRequest().addHTTPHeaderFields(headerFields); 1282 frameLoadRequest.resourceRequest().setHTTPBody(FormData::create(postData, postDataLength)); 1283 frameLoadRequest.setFrameName(target); 1284 1285 return load(frameLoadRequest, sendNotification, notifyData); 1286 } 1287 1288 #ifdef PLUGIN_SCHEDULE_TIMER 1289 uint32 PluginView::scheduleTimer(NPP instance, uint32 interval, bool repeat, 1290 void (*timerFunc)(NPP, uint32 timerID)) 1291 { 1292 return m_timerList.schedule(instance, interval, repeat, timerFunc); 1293 } 1294 1295 void PluginView::unscheduleTimer(NPP instance, uint32 timerID) 1296 { 1297 m_timerList.unschedule(instance, timerID); 1298 } 1299 #endif 1300 1301 void PluginView::invalidateWindowlessPluginRect(const IntRect& rect) 1302 { 1303 if (!isVisible()) 1304 return; 1305 1306 if (!m_element->renderer()) 1307 return; 1308 RenderBox* renderer = toRenderBox(m_element->renderer()); 1309 1310 IntRect dirtyRect = rect; 1311 dirtyRect.move(renderer->borderLeft() + renderer->paddingLeft(), renderer->borderTop() + renderer->paddingTop()); 1312 renderer->repaintRectangle(dirtyRect); 1313 } 1314 1315 void PluginView::paintMissingPluginIcon(GraphicsContext* context, const IntRect& rect) 1316 { 1317 static RefPtr<Image> nullPluginImage; 1318 if (!nullPluginImage) 1319 nullPluginImage = Image::loadPlatformResource("nullPlugin"); 1320 1321 IntRect imageRect(frameRect().x(), frameRect().y(), nullPluginImage->width(), nullPluginImage->height()); 1322 1323 int xOffset = (frameRect().width() - imageRect.width()) / 2; 1324 int yOffset = (frameRect().height() - imageRect.height()) / 2; 1325 1326 imageRect.move(xOffset, yOffset); 1327 1328 if (!rect.intersects(imageRect)) 1329 return; 1330 1331 context->save(); 1332 context->clip(windowClipRect()); 1333 context->drawImage(nullPluginImage.get(), DeviceColorSpace, imageRect.location()); 1334 context->restore(); 1335 } 1336 1337 static const char* MozillaUserAgent = "Mozilla/5.0 (" 1338 #if defined(XP_MACOSX) 1339 "Macintosh; U; Intel Mac OS X;" 1340 #elif defined(XP_WIN) 1341 "Windows; U; Windows NT 5.1;" 1342 #elif defined(XP_UNIX) 1343 // The Gtk port uses X11 plugins in Mac. 1344 #if OS(DARWIN) && PLATFORM(GTK) 1345 "X11; U; Intel Mac OS X;" 1346 #else 1347 "X11; U; Linux i686;" 1348 #endif 1349 #endif 1350 " en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0"; 1351 1352 const char* PluginView::userAgent() 1353 { 1354 #if !PLATFORM(ANDROID) 1355 if (m_plugin->quirks().contains(PluginQuirkWantsMozillaUserAgent)) 1356 return MozillaUserAgent; 1357 #endif 1358 1359 if (m_userAgent.isNull()) 1360 m_userAgent = m_parentFrame->loader()->userAgent(m_url).utf8(); 1361 1362 return m_userAgent.data(); 1363 } 1364 1365 #if ENABLE(NETSCAPE_PLUGIN_API) 1366 const char* PluginView::userAgentStatic() 1367 { 1368 return MozillaUserAgent; 1369 } 1370 #endif 1371 1372 1373 Node* PluginView::node() const 1374 { 1375 return m_element; 1376 } 1377 1378 String PluginView::pluginName() const 1379 { 1380 return m_plugin->name(); 1381 } 1382 1383 void PluginView::lifeSupportTimerFired(Timer<PluginView>*) 1384 { 1385 deref(); 1386 } 1387 1388 void PluginView::keepAlive() 1389 { 1390 if (m_lifeSupportTimer.isActive()) 1391 return; 1392 1393 ref(); 1394 m_lifeSupportTimer.startOneShot(0); 1395 } 1396 1397 void PluginView::keepAlive(NPP instance) 1398 { 1399 PluginView* view = instanceMap().get(instance); 1400 if (!view) 1401 return; 1402 1403 view->keepAlive(); 1404 } 1405 1406 } // namespace WebCore 1407