1/* 2 * Copyright (C) 2010, 2012 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "PluginView.h" 28 29#include "NPRuntimeUtilities.h" 30#include "Plugin.h" 31#include "ShareableBitmap.h" 32#include "WebCoreArgumentCoders.h" 33#include "WebEvent.h" 34#include "WebPage.h" 35#include "WebPageProxyMessages.h" 36#include "WebProcess.h" 37#include <WebCore/BitmapImage.h> 38#include <WebCore/Chrome.h> 39#include <WebCore/CookieJar.h> 40#include <WebCore/Credential.h> 41#include <WebCore/CredentialStorage.h> 42#include <WebCore/DocumentLoader.h> 43#include <WebCore/EventHandler.h> 44#include <WebCore/FocusController.h> 45#include <WebCore/Frame.h> 46#include <WebCore/FrameLoadRequest.h> 47#include <WebCore/FrameLoader.h> 48#include <WebCore/FrameLoaderClient.h> 49#include <WebCore/FrameView.h> 50#include <WebCore/GraphicsContext.h> 51#include <WebCore/HTMLPlugInElement.h> 52#include <WebCore/HTMLPlugInImageElement.h> 53#include <WebCore/HostWindow.h> 54#include <WebCore/MIMETypeRegistry.h> 55#include <WebCore/MouseEvent.h> 56#include <WebCore/NetscapePlugInStreamLoader.h> 57#include <WebCore/NetworkingContext.h> 58#include <WebCore/Page.h> 59#include <WebCore/PageThrottler.h> 60#include <WebCore/PlatformMouseEvent.h> 61#include <WebCore/ProtectionSpace.h> 62#include <WebCore/ProxyServer.h> 63#include <WebCore/RenderEmbeddedObject.h> 64#include <WebCore/ResourceLoadScheduler.h> 65#include <WebCore/ScriptController.h> 66#include <WebCore/ScriptValue.h> 67#include <WebCore/ScrollView.h> 68#include <WebCore/SecurityOrigin.h> 69#include <WebCore/SecurityPolicy.h> 70#include <WebCore/Settings.h> 71#include <WebCore/UserGestureIndicator.h> 72#include <wtf/text/StringBuilder.h> 73 74using namespace JSC; 75using namespace WebCore; 76 77namespace WebKit { 78 79// This simulated mouse click delay in HTMLPlugInImageElement.cpp should generally be the same or shorter than this delay. 80static const double pluginSnapshotTimerDelay = 1.1; 81 82class PluginView::URLRequest : public RefCounted<URLRequest> { 83public: 84 static PassRefPtr<PluginView::URLRequest> create(uint64_t requestID, const FrameLoadRequest& request, bool allowPopups) 85 { 86 return adoptRef(new URLRequest(requestID, request, allowPopups)); 87 } 88 89 uint64_t requestID() const { return m_requestID; } 90 const String& target() const { return m_request.frameName(); } 91 const ResourceRequest & request() const { return m_request.resourceRequest(); } 92 bool allowPopups() const { return m_allowPopups; } 93 94private: 95 URLRequest(uint64_t requestID, const FrameLoadRequest& request, bool allowPopups) 96 : m_requestID(requestID) 97 , m_request(request) 98 , m_allowPopups(allowPopups) 99 { 100 } 101 102 uint64_t m_requestID; 103 FrameLoadRequest m_request; 104 bool m_allowPopups; 105}; 106 107class PluginView::Stream : public RefCounted<PluginView::Stream>, NetscapePlugInStreamLoaderClient { 108public: 109 static PassRefPtr<Stream> create(PluginView* pluginView, uint64_t streamID, const ResourceRequest& request) 110 { 111 return adoptRef(new Stream(pluginView, streamID, request)); 112 } 113 ~Stream(); 114 115 void start(); 116 void cancel(); 117 118 uint64_t streamID() const { return m_streamID; } 119 120private: 121 Stream(PluginView* pluginView, uint64_t streamID, const ResourceRequest& request) 122 : m_pluginView(pluginView) 123 , m_streamID(streamID) 124 , m_request(request) 125 , m_streamWasCancelled(false) 126 { 127 } 128 129 // NetscapePluginStreamLoaderClient 130 virtual void didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse&); 131 virtual void didReceiveData(NetscapePlugInStreamLoader*, const char*, int); 132 virtual void didFail(NetscapePlugInStreamLoader*, const ResourceError&); 133 virtual void didFinishLoading(NetscapePlugInStreamLoader*); 134 135 PluginView* m_pluginView; 136 uint64_t m_streamID; 137 const ResourceRequest m_request; 138 139 // True if the stream was explicitly cancelled by calling cancel(). 140 // (As opposed to being cancelled by the user hitting the stop button for example. 141 bool m_streamWasCancelled; 142 143 RefPtr<NetscapePlugInStreamLoader> m_loader; 144}; 145 146PluginView::Stream::~Stream() 147{ 148 ASSERT(!m_pluginView); 149} 150 151void PluginView::Stream::start() 152{ 153 ASSERT(m_pluginView->m_plugin); 154 ASSERT(!m_loader); 155 156 Frame* frame = m_pluginView->m_pluginElement->document()->frame(); 157 ASSERT(frame); 158 159 m_loader = resourceLoadScheduler()->schedulePluginStreamLoad(frame, this, m_request); 160} 161 162void PluginView::Stream::cancel() 163{ 164 ASSERT(m_loader); 165 166 m_streamWasCancelled = true; 167 m_loader->cancel(m_loader->cancelledError()); 168 m_loader = 0; 169} 170 171static String buildHTTPHeaders(const ResourceResponse& response, long long& expectedContentLength) 172{ 173 if (!response.isHTTP()) 174 return String(); 175 176 StringBuilder stringBuilder; 177 178 String statusLine = String::format("HTTP %d ", response.httpStatusCode()); 179 stringBuilder.append(statusLine); 180 stringBuilder.append(response.httpStatusText()); 181 stringBuilder.append('\n'); 182 183 HTTPHeaderMap::const_iterator end = response.httpHeaderFields().end(); 184 for (HTTPHeaderMap::const_iterator it = response.httpHeaderFields().begin(); it != end; ++it) { 185 stringBuilder.append(it->key); 186 stringBuilder.appendLiteral(": "); 187 stringBuilder.append(it->value); 188 stringBuilder.append('\n'); 189 } 190 191 String headers = stringBuilder.toString(); 192 193 // If the content is encoded (most likely compressed), then don't send its length to the plugin, 194 // which is only interested in the decoded length, not yet known at the moment. 195 // <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic. 196 String contentEncoding = response.httpHeaderField("Content-Encoding"); 197 if (!contentEncoding.isNull() && contentEncoding != "identity") 198 expectedContentLength = -1; 199 200 return headers; 201} 202 203void PluginView::Stream::didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse& response) 204{ 205 // Compute the stream related data from the resource response. 206 const KURL& responseURL = response.url(); 207 const String& mimeType = response.mimeType(); 208 long long expectedContentLength = response.expectedContentLength(); 209 210 String headers = buildHTTPHeaders(response, expectedContentLength); 211 212 uint32_t streamLength = 0; 213 if (expectedContentLength > 0) 214 streamLength = expectedContentLength; 215 216 m_pluginView->m_plugin->streamDidReceiveResponse(m_streamID, responseURL, streamLength, response.lastModifiedDate(), mimeType, headers, response.suggestedFilename()); 217} 218 219void PluginView::Stream::didReceiveData(NetscapePlugInStreamLoader*, const char* bytes, int length) 220{ 221 m_pluginView->m_plugin->streamDidReceiveData(m_streamID, bytes, length); 222} 223 224void PluginView::Stream::didFail(NetscapePlugInStreamLoader*, const ResourceError& error) 225{ 226 // Calling streamDidFail could cause us to be deleted, so we hold on to a reference here. 227 RefPtr<Stream> protect(this); 228 229 // We only want to call streamDidFail if the stream was not explicitly cancelled by the plug-in. 230 if (!m_streamWasCancelled) 231 m_pluginView->m_plugin->streamDidFail(m_streamID, error.isCancellation()); 232 233 m_pluginView->removeStream(this); 234 m_pluginView = 0; 235} 236 237void PluginView::Stream::didFinishLoading(NetscapePlugInStreamLoader*) 238{ 239 // Calling streamDidFinishLoading could cause us to be deleted, so we hold on to a reference here. 240 RefPtr<Stream> protectStream(this); 241 242#if ENABLE(NETSCAPE_PLUGIN_API) 243 // Protect the plug-in while we're calling into it. 244 NPRuntimeObjectMap::PluginProtector pluginProtector(&m_pluginView->m_npRuntimeObjectMap); 245#endif 246 m_pluginView->m_plugin->streamDidFinishLoading(m_streamID); 247 248 m_pluginView->removeStream(this); 249 m_pluginView = 0; 250} 251 252static inline WebPage* webPage(HTMLPlugInElement* pluginElement) 253{ 254 Frame* frame = pluginElement->document()->frame(); 255 ASSERT(frame); 256 257 WebFrameLoaderClient* webFrameLoaderClient = toWebFrameLoaderClient(frame->loader()->client()); 258 WebPage* webPage = webFrameLoaderClient ? webFrameLoaderClient->webFrame()->page() : 0; 259 ASSERT(webPage); 260 261 return webPage; 262} 263 264PassRefPtr<PluginView> PluginView::create(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<Plugin> plugin, const Plugin::Parameters& parameters) 265{ 266 return adoptRef(new PluginView(pluginElement, plugin, parameters)); 267} 268 269PluginView::PluginView(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<Plugin> plugin, const Plugin::Parameters& parameters) 270 : PluginViewBase(0) 271 , m_pluginElement(pluginElement) 272 , m_plugin(plugin) 273 , m_webPage(webPage(m_pluginElement.get())) 274 , m_parameters(parameters) 275 , m_isInitialized(false) 276 , m_isWaitingForSynchronousInitialization(false) 277 , m_isWaitingUntilMediaCanStart(false) 278 , m_isBeingDestroyed(false) 279 , m_pluginProcessHasCrashed(false) 280 , m_pendingURLRequestsTimer(RunLoop::main(), this, &PluginView::pendingURLRequestsTimerFired) 281#if ENABLE(NETSCAPE_PLUGIN_API) 282 , m_npRuntimeObjectMap(this) 283#endif 284 , m_manualStreamState(StreamStateInitial) 285 , m_pluginSnapshotTimer(this, &PluginView::pluginSnapshotTimerFired, pluginSnapshotTimerDelay) 286 , m_countSnapshotRetries(0) 287 , m_didReceiveUserInteraction(false) 288 , m_pageScaleFactor(1) 289{ 290 m_webPage->addPluginView(this); 291} 292 293PluginView::~PluginView() 294{ 295 if (m_webPage) 296 m_webPage->removePluginView(this); 297 298 ASSERT(!m_isBeingDestroyed); 299 300 if (m_isWaitingUntilMediaCanStart) 301 m_pluginElement->document()->removeMediaCanStartListener(this); 302 303 destroyPluginAndReset(); 304 305 // Null out the plug-in element explicitly so we'll crash earlier if we try to use 306 // the plug-in view after it's been destroyed. 307 m_pluginElement = nullptr; 308} 309 310void PluginView::destroyPluginAndReset() 311{ 312 // Cancel all pending frame loads. 313 for (FrameLoadMap::iterator it = m_pendingFrameLoads.begin(), end = m_pendingFrameLoads.end(); it != end; ++it) 314 it->key->setLoadListener(0); 315 316 if (m_plugin) { 317 m_isBeingDestroyed = true; 318 m_plugin->destroyPlugin(); 319 m_isBeingDestroyed = false; 320 321 m_pendingURLRequests.clear(); 322 m_pendingURLRequestsTimer.stop(); 323 324#if PLATFORM(MAC) 325 if (m_webPage) 326 pluginFocusOrWindowFocusChanged(false); 327#endif 328 } 329 330#if ENABLE(NETSCAPE_PLUGIN_API) 331 // Invalidate the object map. 332 m_npRuntimeObjectMap.invalidate(); 333#endif 334 335 cancelAllStreams(); 336} 337 338void PluginView::recreateAndInitialize(PassRefPtr<Plugin> plugin) 339{ 340 if (m_plugin) { 341 if (m_pluginSnapshotTimer.isActive()) 342 m_pluginSnapshotTimer.stop(); 343 destroyPluginAndReset(); 344 } 345 346 // Reset member variables to initial values. 347 m_plugin = plugin; 348 m_isInitialized = false; 349 m_isWaitingForSynchronousInitialization = false; 350 m_isWaitingUntilMediaCanStart = false; 351 m_isBeingDestroyed = false; 352 m_manualStreamState = StreamStateInitial; 353 m_transientPaintingSnapshot = nullptr; 354 355 initializePlugin(); 356} 357 358Frame* PluginView::frame() const 359{ 360 return m_pluginElement->document()->frame(); 361} 362 363void PluginView::manualLoadDidReceiveResponse(const ResourceResponse& response) 364{ 365 // The plug-in can be null here if it failed to initialize. 366 if (!m_plugin) 367 return; 368 369 if (!m_isInitialized) { 370 ASSERT(m_manualStreamState == StreamStateInitial); 371 m_manualStreamState = StreamStateHasReceivedResponse; 372 m_manualStreamResponse = response; 373 return; 374 } 375 376 // Compute the stream related data from the resource response. 377 const KURL& responseURL = response.url(); 378 const String& mimeType = response.mimeType(); 379 long long expectedContentLength = response.expectedContentLength(); 380 381 String headers = buildHTTPHeaders(response, expectedContentLength); 382 383 uint32_t streamLength = 0; 384 if (expectedContentLength > 0) 385 streamLength = expectedContentLength; 386 387 m_plugin->manualStreamDidReceiveResponse(responseURL, streamLength, response.lastModifiedDate(), mimeType, headers, response.suggestedFilename()); 388} 389 390void PluginView::manualLoadDidReceiveData(const char* bytes, int length) 391{ 392 // The plug-in can be null here if it failed to initialize. 393 if (!m_plugin) 394 return; 395 396 if (!m_isInitialized) { 397 ASSERT(m_manualStreamState == StreamStateHasReceivedResponse); 398 if (!m_manualStreamData) 399 m_manualStreamData = SharedBuffer::create(); 400 401 m_manualStreamData->append(bytes, length); 402 return; 403 } 404 405 m_plugin->manualStreamDidReceiveData(bytes, length); 406} 407 408void PluginView::manualLoadDidFinishLoading() 409{ 410 // The plug-in can be null here if it failed to initialize. 411 if (!m_plugin) 412 return; 413 414 if (!m_isInitialized) { 415 ASSERT(m_manualStreamState == StreamStateHasReceivedResponse); 416 m_manualStreamState = StreamStateFinished; 417 return; 418 } 419 420 m_plugin->manualStreamDidFinishLoading(); 421} 422 423void PluginView::manualLoadDidFail(const ResourceError& error) 424{ 425 // The plug-in can be null here if it failed to initialize. 426 if (!m_plugin) 427 return; 428 429 if (!m_isInitialized) { 430 m_manualStreamState = StreamStateFinished; 431 m_manualStreamError = error; 432 m_manualStreamData = nullptr; 433 return; 434 } 435 436 m_plugin->manualStreamDidFail(error.isCancellation()); 437} 438 439RenderBoxModelObject* PluginView::renderer() const 440{ 441 return toRenderBoxModelObject(m_pluginElement->renderer()); 442} 443 444void PluginView::pageScaleFactorDidChange() 445{ 446 viewGeometryDidChange(); 447} 448 449void PluginView::setPageScaleFactor(double scaleFactor, IntPoint) 450{ 451 m_pageScaleFactor = scaleFactor; 452 m_webPage->send(Messages::WebPageProxy::PageScaleFactorDidChange(scaleFactor)); 453 m_webPage->send(Messages::WebPageProxy::PageZoomFactorDidChange(scaleFactor)); 454 pageScaleFactorDidChange(); 455} 456 457double PluginView::pageScaleFactor() const 458{ 459 return m_pageScaleFactor; 460} 461 462bool PluginView::handlesPageScaleFactor() const 463{ 464 if (!m_plugin || !m_isInitialized) 465 return false; 466 467 return m_plugin->handlesPageScaleFactor(); 468} 469 470void PluginView::webPageDestroyed() 471{ 472 m_webPage = 0; 473} 474 475#if PLATFORM(MAC) 476void PluginView::setWindowIsVisible(bool windowIsVisible) 477{ 478 if (!m_isInitialized || !m_plugin) 479 return; 480 481 m_plugin->windowVisibilityChanged(windowIsVisible); 482} 483 484void PluginView::setWindowIsFocused(bool windowIsFocused) 485{ 486 if (!m_isInitialized || !m_plugin) 487 return; 488 489 m_plugin->windowFocusChanged(windowIsFocused); 490} 491 492void PluginView::setDeviceScaleFactor(float scaleFactor) 493{ 494 if (!m_isInitialized || !m_plugin) 495 return; 496 497 m_plugin->contentsScaleFactorChanged(scaleFactor); 498} 499 500void PluginView::windowAndViewFramesChanged(const FloatRect& windowFrameInScreenCoordinates, const FloatRect& viewFrameInWindowCoordinates) 501{ 502 if (!m_isInitialized || !m_plugin) 503 return; 504 505 m_plugin->windowAndViewFramesChanged(enclosingIntRect(windowFrameInScreenCoordinates), enclosingIntRect(viewFrameInWindowCoordinates)); 506} 507 508bool PluginView::sendComplexTextInput(uint64_t pluginComplexTextInputIdentifier, const String& textInput) 509{ 510 if (!m_plugin) 511 return false; 512 513 if (m_plugin->pluginComplexTextInputIdentifier() != pluginComplexTextInputIdentifier) 514 return false; 515 516 m_plugin->sendComplexTextInput(textInput); 517 return true; 518} 519 520void PluginView::setLayerHostingMode(LayerHostingMode layerHostingMode) 521{ 522 if (!m_plugin) 523 return; 524 525 if (!m_isInitialized) { 526 m_parameters.layerHostingMode = layerHostingMode; 527 return; 528 } 529 530 m_plugin->setLayerHostingMode(layerHostingMode); 531} 532 533NSObject *PluginView::accessibilityObject() const 534{ 535 if (!m_isInitialized || !m_plugin) 536 return 0; 537 538 return m_plugin->accessibilityObject(); 539} 540#endif 541 542void PluginView::initializePlugin() 543{ 544 if (m_isInitialized) 545 return; 546 547 if (!m_plugin) { 548 // We've already tried and failed to initialize the plug-in. 549 return; 550 } 551 552 if (Frame* frame = m_pluginElement->document()->frame()) { 553 if (Page* page = frame->page()) { 554 555 // We shouldn't initialize the plug-in right now, add a listener. 556 if (!page->canStartMedia()) { 557 if (m_isWaitingUntilMediaCanStart) 558 return; 559 560 m_isWaitingUntilMediaCanStart = true; 561 m_pluginElement->document()->addMediaCanStartListener(this); 562 return; 563 } 564 } 565 } 566 567 m_plugin->initialize(this, m_parameters); 568 569 // Plug-in initialization continued in didFailToInitializePlugin() or didInitializePlugin(). 570} 571 572void PluginView::didFailToInitializePlugin() 573{ 574 m_plugin = 0; 575 576 String frameURLString = frame()->loader()->documentLoader()->responseURL().string(); 577 String pageURLString = m_webPage->corePage()->mainFrame()->loader()->documentLoader()->responseURL().string(); 578 m_webPage->send(Messages::WebPageProxy::DidFailToInitializePlugin(m_parameters.mimeType, frameURLString, pageURLString)); 579} 580 581void PluginView::didInitializePlugin() 582{ 583 m_isInitialized = true; 584 585#if PLATFORM(MAC) 586 windowAndViewFramesChanged(m_webPage->windowFrameInScreenCoordinates(), m_webPage->viewFrameInWindowCoordinates()); 587#endif 588 589 viewGeometryDidChange(); 590 591 if (m_pluginElement->document()->focusedElement() == m_pluginElement) 592 m_plugin->setFocus(true); 593 594 redeliverManualStream(); 595 596#if PLATFORM(MAC) 597 if (m_pluginElement->displayState() < HTMLPlugInElement::Restarting) { 598 if (m_plugin->pluginLayer() && frame()) { 599 frame()->view()->enterCompositingMode(); 600 m_pluginElement->setNeedsStyleRecalc(SyntheticStyleChange); 601 } 602 if (frame() && !frame()->settings()->maximumPlugInSnapshotAttempts()) { 603 m_pluginElement->setDisplayState(HTMLPlugInElement::DisplayingSnapshot); 604 return; 605 } 606 m_pluginSnapshotTimer.restart(); 607 } else { 608 if (m_plugin->pluginLayer() && frame()) { 609 frame()->view()->enterCompositingMode(); 610 m_pluginElement->setNeedsStyleRecalc(SyntheticStyleChange); 611 } 612 if (m_pluginElement->displayState() == HTMLPlugInElement::RestartingWithPendingMouseClick) 613 m_pluginElement->dispatchPendingMouseClick(); 614 } 615 616 setWindowIsVisible(m_webPage->windowIsVisible()); 617 setWindowIsFocused(m_webPage->windowIsFocused()); 618#endif 619 620 if (wantsWheelEvents()) { 621 if (Frame* frame = m_pluginElement->document()->frame()) { 622 if (FrameView* frameView = frame->view()) 623 frameView->setNeedsLayout(); 624 } 625 } 626} 627 628#if PLATFORM(MAC) 629PlatformLayer* PluginView::platformLayer() const 630{ 631 // The plug-in can be null here if it failed to initialize. 632 if (!m_isInitialized || !m_plugin || m_pluginProcessHasCrashed) 633 return 0; 634 635 return m_plugin->pluginLayer(); 636} 637#endif 638 639JSObject* PluginView::scriptObject(JSGlobalObject* globalObject) 640{ 641 // If we're already waiting for synchronous initialization of the plugin, 642 // calls to scriptObject() are from the plug-in itself and need to return 0; 643 if (m_isWaitingForSynchronousInitialization) 644 return 0; 645 646 // We might not have started initialization of the plug-in yet, the plug-in might be in the middle 647 // of being initializing asynchronously, or initialization might have previously failed. 648 if (!m_isInitialized || !m_plugin) 649 return 0; 650 651#if ENABLE(NETSCAPE_PLUGIN_API) 652 NPObject* scriptableNPObject = m_plugin->pluginScriptableNPObject(); 653 if (!scriptableNPObject) 654 return 0; 655 656 JSObject* jsObject = m_npRuntimeObjectMap.getOrCreateJSObject(globalObject, scriptableNPObject); 657 releaseNPObject(scriptableNPObject); 658 659 return jsObject; 660#else 661 UNUSED_PARAM(globalObject); 662 return 0; 663#endif 664} 665 666void PluginView::storageBlockingStateChanged() 667{ 668 // The plug-in can be null here if it failed to initialize. 669 if (!m_isInitialized || !m_plugin) 670 return; 671 672 bool storageBlockingPolicy = !frame()->document()->securityOrigin()->canAccessPluginStorage(frame()->document()->topOrigin()); 673 674 m_plugin->storageBlockingStateChanged(storageBlockingPolicy); 675} 676 677void PluginView::privateBrowsingStateChanged(bool privateBrowsingEnabled) 678{ 679 // The plug-in can be null here if it failed to initialize. 680 if (!m_isInitialized || !m_plugin) 681 return; 682 683 m_plugin->privateBrowsingStateChanged(privateBrowsingEnabled); 684} 685 686bool PluginView::getFormValue(String& formValue) 687{ 688 // The plug-in can be null here if it failed to initialize. 689 if (!m_isInitialized || !m_plugin) 690 return false; 691 692 return m_plugin->getFormValue(formValue); 693} 694 695bool PluginView::scroll(ScrollDirection direction, ScrollGranularity granularity) 696{ 697 // The plug-in can be null here if it failed to initialize. 698 if (!m_isInitialized || !m_plugin) 699 return false; 700 701 return m_plugin->handleScroll(direction, granularity); 702} 703 704Scrollbar* PluginView::horizontalScrollbar() 705{ 706 // The plug-in can be null here if it failed to initialize. 707 if (!m_isInitialized || !m_plugin) 708 return 0; 709 710 return m_plugin->horizontalScrollbar(); 711} 712 713Scrollbar* PluginView::verticalScrollbar() 714{ 715 // The plug-in can be null here if it failed to initialize. 716 if (!m_isInitialized || !m_plugin) 717 return 0; 718 719 return m_plugin->verticalScrollbar(); 720} 721 722bool PluginView::wantsWheelEvents() 723{ 724 // The plug-in can be null here if it failed to initialize. 725 if (!m_isInitialized || !m_plugin) 726 return 0; 727 728 return m_plugin->wantsWheelEvents(); 729} 730 731void PluginView::setFrameRect(const WebCore::IntRect& rect) 732{ 733 Widget::setFrameRect(rect); 734 viewGeometryDidChange(); 735} 736 737void PluginView::paint(GraphicsContext* context, const IntRect& /*dirtyRect*/) 738{ 739 if (!m_plugin || !m_isInitialized || m_pluginElement->displayState() < HTMLPlugInElement::Restarting) 740 return; 741 742 if (context->paintingDisabled()) { 743 if (context->updatingControlTints()) 744 m_plugin->updateControlTints(context); 745 return; 746 } 747 748 // FIXME: We should try to intersect the dirty rect with the plug-in's clip rect here. 749 IntRect paintRect = IntRect(IntPoint(), frameRect().size()); 750 751 if (paintRect.isEmpty()) 752 return; 753 754 if (m_transientPaintingSnapshot) { 755 m_transientPaintingSnapshot->paint(*context, contentsScaleFactor(), frameRect().location(), m_transientPaintingSnapshot->bounds()); 756 return; 757 } 758 759 GraphicsContextStateSaver stateSaver(*context); 760 761 // Translate the coordinate system so that the origin is in the top-left corner of the plug-in. 762 context->translate(frameRect().location().x(), frameRect().location().y()); 763 764 m_plugin->paint(context, paintRect); 765} 766 767void PluginView::frameRectsChanged() 768{ 769 Widget::frameRectsChanged(); 770 viewGeometryDidChange(); 771} 772 773void PluginView::clipRectChanged() 774{ 775 viewGeometryDidChange(); 776} 777 778void PluginView::setParent(ScrollView* scrollView) 779{ 780 Widget::setParent(scrollView); 781 782 if (scrollView) 783 initializePlugin(); 784} 785 786unsigned PluginView::countFindMatches(const String& target, WebCore::FindOptions options, unsigned maxMatchCount) 787{ 788 if (!m_isInitialized || !m_plugin) 789 return 0; 790 791 return m_plugin->countFindMatches(target, options, maxMatchCount); 792} 793 794bool PluginView::findString(const String& target, WebCore::FindOptions options, unsigned maxMatchCount) 795{ 796 if (!m_isInitialized || !m_plugin) 797 return false; 798 799 return m_plugin->findString(target, options, maxMatchCount); 800} 801 802String PluginView::getSelectionString() const 803{ 804 if (!m_isInitialized || !m_plugin) 805 return String(); 806 807 return m_plugin->getSelectionString(); 808} 809 810PassOwnPtr<WebEvent> PluginView::createWebEvent(MouseEvent* event) const 811{ 812 WebEvent::Type type = WebEvent::NoType; 813 unsigned clickCount = 1; 814 if (event->type() == eventNames().mousedownEvent) 815 type = WebEvent::MouseDown; 816 else if (event->type() == eventNames().mouseupEvent) 817 type = WebEvent::MouseUp; 818 else if (event->type() == eventNames().mouseoverEvent) { 819 type = WebEvent::MouseMove; 820 clickCount = 0; 821 } else if (event->type() == eventNames().clickEvent) 822 return nullptr; 823 else 824 ASSERT_NOT_REACHED(); 825 826 WebMouseEvent::Button button = WebMouseEvent::NoButton; 827 switch (event->button()) { 828 case WebCore::LeftButton: 829 button = WebMouseEvent::LeftButton; 830 break; 831 case WebCore::MiddleButton: 832 button = WebMouseEvent::MiddleButton; 833 break; 834 case WebCore::RightButton: 835 button = WebMouseEvent::RightButton; 836 break; 837 default: 838 ASSERT_NOT_REACHED(); 839 break; 840 } 841 842 unsigned modifiers = 0; 843 if (event->shiftKey()) 844 modifiers |= WebEvent::ShiftKey; 845 if (event->ctrlKey()) 846 modifiers |= WebEvent::ControlKey; 847 if (event->altKey()) 848 modifiers |= WebEvent::AltKey; 849 if (event->metaKey()) 850 modifiers |= WebEvent::MetaKey; 851 852 return adoptPtr(new WebMouseEvent(type, button, m_plugin->convertToRootView(IntPoint(event->offsetX(), event->offsetY())), event->screenLocation(), 0, 0, 0, clickCount, static_cast<WebEvent::Modifiers>(modifiers), 0)); 853} 854 855void PluginView::handleEvent(Event* event) 856{ 857 if (!m_isInitialized || !m_plugin) 858 return; 859 860 const WebEvent* currentEvent = WebPage::currentEvent(); 861 OwnPtr<WebEvent> simulatedWebEvent; 862 if (event->isMouseEvent() && toMouseEvent(event)->isSimulated()) { 863 simulatedWebEvent = createWebEvent(toMouseEvent(event)); 864 currentEvent = simulatedWebEvent.get(); 865 } 866 if (!currentEvent) 867 return; 868 869 bool didHandleEvent = false; 870 871 if ((event->type() == eventNames().mousemoveEvent && currentEvent->type() == WebEvent::MouseMove) 872 || (event->type() == eventNames().mousedownEvent && currentEvent->type() == WebEvent::MouseDown) 873 || (event->type() == eventNames().mouseupEvent && currentEvent->type() == WebEvent::MouseUp)) { 874 // FIXME: Clicking in a scroll bar should not change focus. 875 if (currentEvent->type() == WebEvent::MouseDown) { 876 focusPluginElement(); 877 frame()->eventHandler()->setCapturingMouseEventsNode(m_pluginElement.get()); 878 } else if (currentEvent->type() == WebEvent::MouseUp) 879 frame()->eventHandler()->setCapturingMouseEventsNode(0); 880 881 didHandleEvent = m_plugin->handleMouseEvent(static_cast<const WebMouseEvent&>(*currentEvent)); 882 if (event->type() != eventNames().mousemoveEvent) 883 pluginDidReceiveUserInteraction(); 884 } else if (event->type() == eventNames().mousewheelEvent && currentEvent->type() == WebEvent::Wheel && m_plugin->wantsWheelEvents()) { 885 didHandleEvent = m_plugin->handleWheelEvent(static_cast<const WebWheelEvent&>(*currentEvent)); 886 pluginDidReceiveUserInteraction(); 887 } else if (event->type() == eventNames().mouseoverEvent && currentEvent->type() == WebEvent::MouseMove) 888 didHandleEvent = m_plugin->handleMouseEnterEvent(static_cast<const WebMouseEvent&>(*currentEvent)); 889 else if (event->type() == eventNames().mouseoutEvent && currentEvent->type() == WebEvent::MouseMove) 890 didHandleEvent = m_plugin->handleMouseLeaveEvent(static_cast<const WebMouseEvent&>(*currentEvent)); 891 else if (event->type() == eventNames().contextmenuEvent && currentEvent->type() == WebEvent::MouseDown) { 892 didHandleEvent = m_plugin->handleContextMenuEvent(static_cast<const WebMouseEvent&>(*currentEvent)); 893 pluginDidReceiveUserInteraction(); 894 } else if ((event->type() == eventNames().keydownEvent && currentEvent->type() == WebEvent::KeyDown) 895 || (event->type() == eventNames().keyupEvent && currentEvent->type() == WebEvent::KeyUp)) { 896 didHandleEvent = m_plugin->handleKeyboardEvent(static_cast<const WebKeyboardEvent&>(*currentEvent)); 897 pluginDidReceiveUserInteraction(); 898 } 899 900 if (didHandleEvent) 901 event->setDefaultHandled(); 902} 903 904bool PluginView::handleEditingCommand(const String& commandName, const String& argument) 905{ 906 if (!m_isInitialized || !m_plugin) 907 return false; 908 909 return m_plugin->handleEditingCommand(commandName, argument); 910} 911 912bool PluginView::isEditingCommandEnabled(const String& commandName) 913{ 914 if (!m_isInitialized || !m_plugin) 915 return false; 916 917 return m_plugin->isEditingCommandEnabled(commandName); 918} 919 920bool PluginView::shouldAllowScripting() 921{ 922 if (!m_isInitialized || !m_plugin) 923 return false; 924 925 return m_plugin->shouldAllowScripting(); 926} 927 928bool PluginView::shouldAllowNavigationFromDrags() const 929{ 930 if (!m_isInitialized || !m_plugin) 931 return false; 932 933 return m_plugin->shouldAllowNavigationFromDrags(); 934} 935 936bool PluginView::shouldNotAddLayer() const 937{ 938 return m_pluginElement->displayState() < HTMLPlugInElement::Restarting && !m_plugin->supportsSnapshotting(); 939} 940 941PassRefPtr<SharedBuffer> PluginView::liveResourceData() const 942{ 943 if (!m_isInitialized || !m_plugin) 944 return 0; 945 946 return m_plugin->liveResourceData(); 947} 948 949bool PluginView::performDictionaryLookupAtLocation(const WebCore::FloatPoint& point) 950{ 951 if (!m_isInitialized || !m_plugin) 952 return false; 953 954 return m_plugin->performDictionaryLookupAtLocation(point); 955} 956 957void PluginView::notifyWidget(WidgetNotification notification) 958{ 959 switch (notification) { 960 case WillPaintFlattened: 961 if (shouldCreateTransientPaintingSnapshot()) 962 m_transientPaintingSnapshot = m_plugin->snapshot(); 963 break; 964 case DidPaintFlattened: 965 m_transientPaintingSnapshot = nullptr; 966 break; 967 } 968} 969 970void PluginView::show() 971{ 972 bool wasVisible = isVisible(); 973 974 setSelfVisible(true); 975 976 if (!wasVisible) 977 viewVisibilityDidChange(); 978 979 Widget::show(); 980} 981 982void PluginView::hide() 983{ 984 bool wasVisible = isVisible(); 985 986 setSelfVisible(false); 987 988 if (wasVisible) 989 viewVisibilityDidChange(); 990 991 Widget::hide(); 992} 993 994bool PluginView::transformsAffectFrameRect() 995{ 996 return false; 997} 998 999void PluginView::viewGeometryDidChange() 1000{ 1001 if (!m_isInitialized || !m_plugin || !parent()) 1002 return; 1003 1004 ASSERT(frame()); 1005 float pageScaleFactor = frame()->page() ? frame()->page()->pageScaleFactor() : 1; 1006 1007 IntPoint scaledFrameRectLocation(frameRect().location().x() * pageScaleFactor, frameRect().location().y() * pageScaleFactor); 1008 IntPoint scaledLocationInRootViewCoordinates(parent()->contentsToRootView(scaledFrameRectLocation)); 1009 1010 // FIXME: We still don't get the right coordinates for transformed plugins. 1011 AffineTransform transform; 1012 transform.translate(scaledLocationInRootViewCoordinates.x(), scaledLocationInRootViewCoordinates.y()); 1013 transform.scale(pageScaleFactor); 1014 1015 // FIXME: The way we calculate this clip rect isn't correct. 1016 // But it is still important to distinguish between empty and non-empty rects so we can notify the plug-in when it becomes invisible. 1017 // Making the rect actually correct is covered by https://bugs.webkit.org/show_bug.cgi?id=95362 1018 IntRect clipRect = boundsRect(); 1019 1020 // FIXME: We can only get a semi-reliable answer from clipRectInWindowCoordinates() when the page is not scaled. 1021 // Fixing that is tracked in <rdar://problem/9026611> - Make the Widget hierarchy play nicely with transforms, for zoomed plug-ins and iframes 1022 if (pageScaleFactor == 1) { 1023 clipRect = clipRectInWindowCoordinates(); 1024 if (!clipRect.isEmpty()) 1025 clipRect = boundsRect(); 1026 } 1027 1028 m_plugin->geometryDidChange(size(), clipRect, transform); 1029} 1030 1031void PluginView::viewVisibilityDidChange() 1032{ 1033 if (!m_isInitialized || !m_plugin || !parent()) 1034 return; 1035 1036 m_plugin->visibilityDidChange(); 1037} 1038 1039IntRect PluginView::clipRectInWindowCoordinates() const 1040{ 1041 // Get the frame rect in window coordinates. 1042 IntRect frameRectInWindowCoordinates = parent()->contentsToWindow(frameRect()); 1043 1044 Frame* frame = this->frame(); 1045 1046 // Get the window clip rect for the plugin element (in window coordinates). 1047 IntRect windowClipRect = frame->view()->windowClipRectForFrameOwner(m_pluginElement.get(), true); 1048 1049 // Intersect the two rects to get the view clip rect in window coordinates. 1050 frameRectInWindowCoordinates.intersect(windowClipRect); 1051 1052 return frameRectInWindowCoordinates; 1053} 1054 1055void PluginView::focusPluginElement() 1056{ 1057 ASSERT(frame()); 1058 1059 if (Page* page = frame()->page()) 1060 page->focusController()->setFocusedElement(m_pluginElement.get(), frame()); 1061 else 1062 frame()->document()->setFocusedElement(m_pluginElement); 1063} 1064 1065void PluginView::pendingURLRequestsTimerFired() 1066{ 1067 ASSERT(!m_pendingURLRequests.isEmpty()); 1068 1069 RefPtr<URLRequest> urlRequest = m_pendingURLRequests.takeFirst(); 1070 1071 // If there are more requests to perform, reschedule the timer. 1072 if (!m_pendingURLRequests.isEmpty()) 1073 m_pendingURLRequestsTimer.startOneShot(0); 1074 1075 performURLRequest(urlRequest.get()); 1076} 1077 1078void PluginView::performURLRequest(URLRequest* request) 1079{ 1080 // This protector is needed to make sure the PluginView is not destroyed while it is still needed. 1081 RefPtr<PluginView> protect(this); 1082 1083 // First, check if this is a javascript: url. 1084 if (protocolIsJavaScript(request->request().url())) { 1085 performJavaScriptURLRequest(request); 1086 return; 1087 } 1088 1089 if (!request->target().isNull()) { 1090 performFrameLoadURLRequest(request); 1091 return; 1092 } 1093 1094 // This request is to load a URL and create a stream. 1095 RefPtr<Stream> stream = PluginView::Stream::create(this, request->requestID(), request->request()); 1096 addStream(stream.get()); 1097 stream->start(); 1098} 1099 1100void PluginView::performFrameLoadURLRequest(URLRequest* request) 1101{ 1102 ASSERT(!request->target().isNull()); 1103 1104 Frame* frame = m_pluginElement->document()->frame(); 1105 if (!frame) 1106 return; 1107 1108 if (!m_pluginElement->document()->securityOrigin()->canDisplay(request->request().url())) { 1109 // We can't load the request, send back a reply to the plug-in. 1110 m_plugin->frameDidFail(request->requestID(), false); 1111 return; 1112 } 1113 1114 UserGestureIndicator gestureIndicator(request->allowPopups() ? DefinitelyProcessingNewUserGesture : PossiblyProcessingUserGesture); 1115 1116 // First, try to find a target frame. 1117 Frame* targetFrame = frame->loader()->findFrameForNavigation(request->target()); 1118 if (!targetFrame) { 1119 // We did not find a target frame. Ask our frame to load the page. This may or may not create a popup window. 1120 FrameLoadRequest frameRequest(frame, request->request()); 1121 frameRequest.setFrameName(request->target()); 1122 frameRequest.setShouldCheckNewWindowPolicy(true); 1123 frame->loader()->load(frameRequest); 1124 1125 // FIXME: We don't know whether the window was successfully created here so we just assume that it worked. 1126 // It's better than not telling the plug-in anything. 1127 m_plugin->frameDidFinishLoading(request->requestID()); 1128 return; 1129 } 1130 1131 // Now ask the frame to load the request. 1132 targetFrame->loader()->load(FrameLoadRequest(targetFrame, request->request())); 1133 1134 WebFrameLoaderClient* webFrameLoaderClient = toWebFrameLoaderClient(targetFrame->loader()->client()); 1135 WebFrame* targetWebFrame = webFrameLoaderClient ? webFrameLoaderClient->webFrame() : 0; 1136 ASSERT(targetWebFrame); 1137 1138 if (WebFrame::LoadListener* loadListener = targetWebFrame->loadListener()) { 1139 // Check if another plug-in view or even this view is waiting for the frame to load. 1140 // If it is, tell it that the load was cancelled because it will be anyway. 1141 loadListener->didFailLoad(targetWebFrame, true); 1142 } 1143 1144 m_pendingFrameLoads.set(targetWebFrame, request); 1145 targetWebFrame->setLoadListener(this); 1146} 1147 1148void PluginView::performJavaScriptURLRequest(URLRequest* request) 1149{ 1150 ASSERT(protocolIsJavaScript(request->request().url())); 1151 1152 RefPtr<Frame> frame = m_pluginElement->document()->frame(); 1153 if (!frame) 1154 return; 1155 1156 String jsString = decodeURLEscapeSequences(request->request().url().string().substring(sizeof("javascript:") - 1)); 1157 1158 if (!request->target().isNull()) { 1159 // For security reasons, only allow JS requests to be made on the frame that contains the plug-in. 1160 if (frame->tree()->find(request->target()) != frame) { 1161 // Let the plug-in know that its frame load failed. 1162 m_plugin->frameDidFail(request->requestID(), false); 1163 return; 1164 } 1165 } 1166 1167 // Evaluate the JavaScript code. Note that running JavaScript here could cause the plug-in to be destroyed, so we 1168 // grab references to the plug-in here. 1169 RefPtr<Plugin> plugin = m_plugin; 1170 ScriptValue result = frame->script()->executeScript(jsString, request->allowPopups()); 1171 1172 // Check if evaluating the JavaScript destroyed the plug-in. 1173 if (!plugin->controller()) 1174 return; 1175 1176 // Don't notify the plug-in at all about targeted javascript: requests. This matches Mozilla and WebKit1. 1177 if (!request->target().isNull()) 1178 return; 1179 1180 ScriptState* scriptState = frame->script()->globalObject(pluginWorld())->globalExec(); 1181 String resultString; 1182 result.getString(scriptState, resultString); 1183 1184 // Send the result back to the plug-in. 1185 plugin->didEvaluateJavaScript(request->requestID(), resultString); 1186} 1187 1188void PluginView::addStream(Stream* stream) 1189{ 1190 ASSERT(!m_streams.contains(stream->streamID())); 1191 m_streams.set(stream->streamID(), stream); 1192} 1193 1194void PluginView::removeStream(Stream* stream) 1195{ 1196 ASSERT(m_streams.get(stream->streamID()) == stream); 1197 1198 m_streams.remove(stream->streamID()); 1199} 1200 1201void PluginView::cancelAllStreams() 1202{ 1203 Vector<RefPtr<Stream>> streams; 1204 copyValuesToVector(m_streams, streams); 1205 1206 for (size_t i = 0; i < streams.size(); ++i) 1207 streams[i]->cancel(); 1208 1209 // Cancelling a stream removes it from the m_streams map, so if we cancel all streams the map should be empty. 1210 ASSERT(m_streams.isEmpty()); 1211} 1212 1213void PluginView::redeliverManualStream() 1214{ 1215 if (m_manualStreamState == StreamStateInitial) { 1216 // Nothing to do. 1217 return; 1218 } 1219 1220 if (m_manualStreamState == StreamStateFailed) { 1221 manualLoadDidFail(m_manualStreamError); 1222 return; 1223 } 1224 1225 // Deliver the response. 1226 manualLoadDidReceiveResponse(m_manualStreamResponse); 1227 1228 // Deliver the data. 1229 if (m_manualStreamData) { 1230 const char* data; 1231 unsigned position = 0; 1232 1233 while (unsigned length = m_manualStreamData->getSomeData(data, position)) { 1234 manualLoadDidReceiveData(data, length); 1235 position += length; 1236 } 1237 1238 m_manualStreamData = nullptr; 1239 } 1240 1241 if (m_manualStreamState == StreamStateFinished) 1242 manualLoadDidFinishLoading(); 1243} 1244 1245void PluginView::invalidateRect(const IntRect& dirtyRect) 1246{ 1247 if (!parent() || !m_plugin || !m_isInitialized) 1248 return; 1249 1250#if PLATFORM(MAC) 1251 if (m_plugin->pluginLayer()) 1252 return; 1253#endif 1254 1255 if (m_pluginElement->displayState() < HTMLPlugInElement::Restarting) 1256 return; 1257 1258 RenderBoxModelObject* renderer = toRenderBoxModelObject(m_pluginElement->renderer()); 1259 if (!renderer) 1260 return; 1261 1262 IntRect contentRect(dirtyRect); 1263 contentRect.move(renderer->borderLeft() + renderer->paddingLeft(), renderer->borderTop() + renderer->paddingTop()); 1264 renderer->repaintRectangle(contentRect); 1265} 1266 1267void PluginView::setFocus(bool hasFocus) 1268{ 1269 Widget::setFocus(hasFocus); 1270 1271 if (!m_isInitialized || !m_plugin) 1272 return; 1273 1274 m_plugin->setFocus(hasFocus); 1275} 1276 1277void PluginView::mediaCanStart() 1278{ 1279 ASSERT(m_isWaitingUntilMediaCanStart); 1280 m_isWaitingUntilMediaCanStart = false; 1281 1282 initializePlugin(); 1283} 1284 1285bool PluginView::isPluginVisible() 1286{ 1287 return isVisible(); 1288} 1289 1290void PluginView::invalidate(const IntRect& dirtyRect) 1291{ 1292 invalidateRect(dirtyRect); 1293} 1294 1295String PluginView::userAgent() 1296{ 1297 Frame* frame = m_pluginElement->document()->frame(); 1298 if (!frame) 1299 return String(); 1300 1301 return frame->loader()->client()->userAgent(KURL()); 1302} 1303 1304void PluginView::loadURL(uint64_t requestID, const String& method, const String& urlString, const String& target, 1305 const HTTPHeaderMap& headerFields, const Vector<uint8_t>& httpBody, bool allowPopups) 1306{ 1307 FrameLoadRequest frameLoadRequest(m_pluginElement->document()->securityOrigin()); 1308 frameLoadRequest.resourceRequest().setHTTPMethod(method); 1309 frameLoadRequest.resourceRequest().setURL(m_pluginElement->document()->completeURL(urlString)); 1310 frameLoadRequest.resourceRequest().addHTTPHeaderFields(headerFields); 1311 frameLoadRequest.resourceRequest().setHTTPBody(FormData::create(httpBody.data(), httpBody.size())); 1312 frameLoadRequest.setFrameName(target); 1313 1314 String referrer = SecurityPolicy::generateReferrerHeader(frame()->document()->referrerPolicy(), frameLoadRequest.resourceRequest().url(), frame()->loader()->outgoingReferrer()); 1315 if (!referrer.isEmpty()) 1316 frameLoadRequest.resourceRequest().setHTTPReferrer(referrer); 1317 1318 m_pendingURLRequests.append(URLRequest::create(requestID, frameLoadRequest, allowPopups)); 1319 m_pendingURLRequestsTimer.startOneShot(0); 1320} 1321 1322void PluginView::cancelStreamLoad(uint64_t streamID) 1323{ 1324 // Keep a reference to the stream. Stream::cancel might remove the stream from the map, and thus 1325 // releasing its last reference. 1326 RefPtr<Stream> stream = m_streams.get(streamID); 1327 if (!stream) 1328 return; 1329 1330 // Cancelling the stream here will remove it from the map. 1331 stream->cancel(); 1332 ASSERT(!m_streams.contains(streamID)); 1333} 1334 1335void PluginView::cancelManualStreamLoad() 1336{ 1337 if (!frame()) 1338 return; 1339 1340 DocumentLoader* documentLoader = frame()->loader()->activeDocumentLoader(); 1341 ASSERT(documentLoader); 1342 1343 if (documentLoader->isLoadingMainResource()) 1344 documentLoader->cancelMainResourceLoad(frame()->loader()->cancelledError(m_parameters.url)); 1345} 1346 1347#if ENABLE(NETSCAPE_PLUGIN_API) 1348NPObject* PluginView::windowScriptNPObject() 1349{ 1350 if (!frame()) 1351 return 0; 1352 1353 if (!frame()->script()->canExecuteScripts(NotAboutToExecuteScript)) { 1354 // FIXME: Investigate if other browsers allow plug-ins to access JavaScript objects even if JavaScript is disabled. 1355 return 0; 1356 } 1357 1358 return m_npRuntimeObjectMap.getOrCreateNPObject(*pluginWorld()->vm(), frame()->script()->windowShell(pluginWorld())->window()); 1359} 1360 1361NPObject* PluginView::pluginElementNPObject() 1362{ 1363 if (!frame()) 1364 return 0; 1365 1366 if (!frame()->script()->canExecuteScripts(NotAboutToExecuteScript)) { 1367 // FIXME: Investigate if other browsers allow plug-ins to access JavaScript objects even if JavaScript is disabled. 1368 return 0; 1369 } 1370 1371 JSObject* object = frame()->script()->jsObjectForPluginElement(m_pluginElement.get()); 1372 ASSERT(object); 1373 1374 return m_npRuntimeObjectMap.getOrCreateNPObject(*pluginWorld()->vm(), object); 1375} 1376 1377bool PluginView::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result, bool allowPopups) 1378{ 1379 // FIXME: Is this check necessary? 1380 if (!m_pluginElement->document()->frame()) 1381 return false; 1382 1383 // Calling evaluate will run JavaScript that can potentially remove the plug-in element, so we need to 1384 // protect the plug-in view from destruction. 1385 NPRuntimeObjectMap::PluginProtector pluginProtector(&m_npRuntimeObjectMap); 1386 1387 UserGestureIndicator gestureIndicator(allowPopups ? DefinitelyProcessingNewUserGesture : PossiblyProcessingUserGesture); 1388 return m_npRuntimeObjectMap.evaluate(npObject, scriptString, result); 1389} 1390#endif 1391 1392void PluginView::setStatusbarText(const String& statusbarText) 1393{ 1394 if (!frame()) 1395 return; 1396 1397 Page* page = frame()->page(); 1398 if (!page) 1399 return; 1400 1401 page->chrome().setStatusbarText(frame(), statusbarText); 1402} 1403 1404bool PluginView::isAcceleratedCompositingEnabled() 1405{ 1406 if (!frame()) 1407 return false; 1408 1409 Settings* settings = frame()->settings(); 1410 if (!settings) 1411 return false; 1412 1413 // We know that some plug-ins can support snapshotting without needing 1414 // accelerated compositing. Since we're trying to snapshot them anyway, 1415 // put them into normal compositing mode. A side benefit is that this might 1416 // allow the entire page to stay in that mode. 1417 if (m_pluginElement->displayState() < HTMLPlugInElement::Restarting && m_parameters.mimeType == "application/x-shockwave-flash") 1418 return false; 1419 1420 return settings->acceleratedCompositingEnabled(); 1421} 1422 1423void PluginView::pluginProcessCrashed() 1424{ 1425 m_pluginProcessHasCrashed = true; 1426 1427 if (!m_pluginElement->renderer()) 1428 return; 1429 1430 // FIXME: The renderer could also be a RenderApplet, we should handle that. 1431 if (!m_pluginElement->renderer()->isEmbeddedObject()) 1432 return; 1433 1434 m_pluginElement->setNeedsStyleRecalc(SyntheticStyleChange); 1435 1436 RenderEmbeddedObject* renderer = toRenderEmbeddedObject(m_pluginElement->renderer()); 1437 renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginCrashed); 1438 1439 Widget::invalidate(); 1440} 1441 1442void PluginView::willSendEventToPlugin() 1443{ 1444 // If we're sending an event to a plug-in, we can't control how long the plug-in 1445 // takes to process it (e.g. it may display a context menu), so we tell the UI process 1446 // to stop the responsiveness timer in this case. 1447 m_webPage->send(Messages::WebPageProxy::StopResponsivenessTimer()); 1448} 1449 1450#if PLATFORM(MAC) 1451void PluginView::pluginFocusOrWindowFocusChanged(bool pluginHasFocusAndWindowHasFocus) 1452{ 1453 if (m_webPage) 1454 m_webPage->send(Messages::WebPageProxy::PluginFocusOrWindowFocusChanged(m_plugin->pluginComplexTextInputIdentifier(), pluginHasFocusAndWindowHasFocus)); 1455} 1456 1457void PluginView::setComplexTextInputState(PluginComplexTextInputState pluginComplexTextInputState) 1458{ 1459 if (m_webPage) 1460 m_webPage->send(Messages::WebPageProxy::SetPluginComplexTextInputState(m_plugin->pluginComplexTextInputIdentifier(), pluginComplexTextInputState)); 1461} 1462 1463mach_port_t PluginView::compositingRenderServerPort() 1464{ 1465 return WebProcess::shared().compositingRenderServerPort(); 1466} 1467 1468void PluginView::openPluginPreferencePane() 1469{ 1470 ASSERT_NOT_REACHED(); 1471} 1472 1473#endif 1474 1475float PluginView::contentsScaleFactor() 1476{ 1477 if (Page* page = frame() ? frame()->page() : 0) 1478 return page->deviceScaleFactor(); 1479 1480 return 1; 1481} 1482 1483String PluginView::proxiesForURL(const String& urlString) 1484{ 1485 const FrameLoader* frameLoader = frame() ? frame()->loader() : 0; 1486 const NetworkingContext* context = frameLoader ? frameLoader->networkingContext() : 0; 1487 Vector<ProxyServer> proxyServers = proxyServersForURL(KURL(KURL(), urlString), context); 1488 return toString(proxyServers); 1489} 1490 1491String PluginView::cookiesForURL(const String& urlString) 1492{ 1493 return cookies(m_pluginElement->document(), KURL(KURL(), urlString)); 1494} 1495 1496void PluginView::setCookiesForURL(const String& urlString, const String& cookieString) 1497{ 1498 setCookies(m_pluginElement->document(), KURL(KURL(), urlString), cookieString); 1499} 1500 1501bool PluginView::getAuthenticationInfo(const ProtectionSpace& protectionSpace, String& username, String& password) 1502{ 1503 Credential credential = CredentialStorage::get(protectionSpace); 1504 if (credential.isEmpty()) 1505 credential = CredentialStorage::getFromPersistentStorage(protectionSpace); 1506 1507 if (!credential.hasPassword()) 1508 return false; 1509 1510 username = credential.user(); 1511 password = credential.password(); 1512 1513 return true; 1514} 1515 1516bool PluginView::isPrivateBrowsingEnabled() 1517{ 1518 // If we can't get the real setting, we'll assume that private browsing is enabled. 1519 if (!frame()) 1520 return true; 1521 1522 if (!frame()->document()->securityOrigin()->canAccessPluginStorage(frame()->document()->topOrigin())) 1523 return true; 1524 1525 Settings* settings = frame()->settings(); 1526 if (!settings) 1527 return true; 1528 1529 return settings->privateBrowsingEnabled(); 1530} 1531 1532bool PluginView::asynchronousPluginInitializationEnabled() const 1533{ 1534 return m_webPage->asynchronousPluginInitializationEnabled(); 1535} 1536 1537bool PluginView::asynchronousPluginInitializationEnabledForAllPlugins() const 1538{ 1539 return m_webPage->asynchronousPluginInitializationEnabledForAllPlugins(); 1540} 1541 1542bool PluginView::artificialPluginInitializationDelayEnabled() const 1543{ 1544 return m_webPage->artificialPluginInitializationDelayEnabled(); 1545} 1546 1547void PluginView::protectPluginFromDestruction() 1548{ 1549 if (!m_isBeingDestroyed) 1550 ref(); 1551} 1552 1553static void derefPluginView(PluginView* pluginView) 1554{ 1555 pluginView->deref(); 1556} 1557 1558void PluginView::unprotectPluginFromDestruction() 1559{ 1560 if (m_isBeingDestroyed) 1561 return; 1562 1563 // A plug-in may ask us to evaluate JavaScript that removes the plug-in from the 1564 // page, but expect the object to still be alive when the call completes. Flash, 1565 // for example, may crash if the plug-in is destroyed and we return to code for 1566 // the destroyed object higher on the stack. To prevent this, if the plug-in has 1567 // only one remaining reference, call deref() asynchronously. 1568 if (hasOneRef()) 1569 RunLoop::main()->dispatch(bind(derefPluginView, this)); 1570 else 1571 deref(); 1572} 1573 1574void PluginView::didFinishLoad(WebFrame* webFrame) 1575{ 1576 RefPtr<URLRequest> request = m_pendingFrameLoads.take(webFrame); 1577 ASSERT(request); 1578 webFrame->setLoadListener(0); 1579 1580 m_plugin->frameDidFinishLoading(request->requestID()); 1581} 1582 1583void PluginView::didFailLoad(WebFrame* webFrame, bool wasCancelled) 1584{ 1585 RefPtr<URLRequest> request = m_pendingFrameLoads.take(webFrame); 1586 ASSERT(request); 1587 webFrame->setLoadListener(0); 1588 1589 m_plugin->frameDidFail(request->requestID(), wasCancelled); 1590} 1591 1592#if PLUGIN_ARCHITECTURE(X11) 1593uint64_t PluginView::createPluginContainer() 1594{ 1595 uint64_t windowID = 0; 1596 m_webPage->sendSync(Messages::WebPageProxy::CreatePluginContainer(), Messages::WebPageProxy::CreatePluginContainer::Reply(windowID)); 1597 return windowID; 1598} 1599 1600void PluginView::windowedPluginGeometryDidChange(const WebCore::IntRect& frameRect, const WebCore::IntRect& clipRect, uint64_t windowID) 1601{ 1602 m_webPage->send(Messages::WebPageProxy::WindowedPluginGeometryDidChange(frameRect, clipRect, windowID)); 1603} 1604#endif 1605 1606#if PLATFORM(MAC) 1607static bool isAlmostSolidColor(BitmapImage* bitmap) 1608{ 1609 CGImageRef image = bitmap->getCGImageRef(); 1610 ASSERT(CGImageGetBitsPerComponent(image) == 8); 1611 1612 CGBitmapInfo imageInfo = CGImageGetBitmapInfo(image); 1613 if (!(imageInfo & kCGBitmapByteOrder32Little) || (imageInfo & kCGBitmapAlphaInfoMask) != kCGImageAlphaPremultipliedFirst) { 1614 // FIXME: Consider being able to handle other pixel formats. 1615 ASSERT_NOT_REACHED(); 1616 return false; 1617 } 1618 1619 size_t width = CGImageGetWidth(image); 1620 size_t height = CGImageGetHeight(image); 1621 size_t bytesPerRow = CGImageGetBytesPerRow(image); 1622 1623 RetainPtr<CFDataRef> provider = adoptCF(CGDataProviderCopyData(CGImageGetDataProvider(image))); 1624 const UInt8* data = CFDataGetBytePtr(provider.get()); 1625 1626 // Overlay a grid of sampling dots on top of a grayscale version of the image. 1627 // For the interior points, calculate the difference in luminance among the sample point 1628 // and its surrounds points, scaled by transparency. 1629 const unsigned sampleRows = 7; 1630 const unsigned sampleCols = 7; 1631 // FIXME: Refine the proper number of samples, and accommodate different aspect ratios. 1632 if (width < sampleCols || height < sampleRows) 1633 return false; 1634 1635 // Ensure that the last row/column land on the image perimeter. 1636 const float strideWidth = static_cast<float>(width - 1) / (sampleCols - 1); 1637 const float strideHeight = static_cast<float>(height - 1) / (sampleRows - 1); 1638 float samples[sampleRows][sampleCols]; 1639 1640 // Find the luminance of the sample points. 1641 float y = 0; 1642 const UInt8* row = data; 1643 for (unsigned i = 0; i < sampleRows; ++i) { 1644 float x = 0; 1645 for (unsigned j = 0; j < sampleCols; ++j) { 1646 const UInt8* p0 = row + (static_cast<int>(x + .5)) * 4; 1647 // R G B A 1648 samples[i][j] = (0.2125 * *p0 + 0.7154 * *(p0+1) + 0.0721 * *(p0+2)) * *(p0+3) / 255; 1649 x += strideWidth; 1650 } 1651 y += strideHeight; 1652 row = data + (static_cast<int>(y + .5)) * bytesPerRow; 1653 } 1654 1655 // Determine the image score. 1656 float accumScore = 0; 1657 for (unsigned i = 1; i < sampleRows - 1; ++i) { 1658 for (unsigned j = 1; j < sampleCols - 1; ++j) { 1659 float diff = samples[i - 1][j] + samples[i + 1][j] + samples[i][j - 1] + samples[i][j + 1] - 4 * samples[i][j]; 1660 accumScore += diff * diff; 1661 } 1662 } 1663 1664 // The score for a given sample can be within the range of 0 and 255^2. 1665 return accumScore < 2500 * (sampleRows - 2) * (sampleCols - 2); 1666} 1667#endif 1668 1669void PluginView::pluginSnapshotTimerFired(DeferrableOneShotTimer<PluginView>*) 1670{ 1671 ASSERT(m_plugin); 1672 1673 if (m_plugin->supportsSnapshotting()) { 1674 // Snapshot might be 0 if plugin size is 0x0. 1675 RefPtr<ShareableBitmap> snapshot = m_plugin->snapshot(); 1676 RefPtr<Image> snapshotImage; 1677 if (snapshot) 1678 snapshotImage = snapshot->createImage(); 1679 m_pluginElement->updateSnapshot(snapshotImage.get()); 1680 1681#if PLATFORM(MAC) 1682 unsigned maximumSnapshotRetries = frame() ? frame()->settings()->maximumPlugInSnapshotAttempts() : 0; 1683 if (snapshotImage && isAlmostSolidColor(static_cast<BitmapImage*>(snapshotImage.get())) && m_countSnapshotRetries < maximumSnapshotRetries) { 1684 ++m_countSnapshotRetries; 1685 m_pluginSnapshotTimer.restart(); 1686 return; 1687 } 1688#endif 1689 } 1690 // Even if there is no snapshot we still set the state to DisplayingSnapshot 1691 // since we just want to display the default empty box. 1692 m_pluginElement->setDisplayState(HTMLPlugInElement::DisplayingSnapshot); 1693} 1694 1695void PluginView::beginSnapshottingRunningPlugin() 1696{ 1697 m_pluginSnapshotTimer.restart(); 1698} 1699 1700bool PluginView::shouldAlwaysAutoStart() const 1701{ 1702 if (!m_plugin) 1703 return PluginViewBase::shouldAlwaysAutoStart(); 1704 1705 if (MIMETypeRegistry::isJavaAppletMIMEType(m_parameters.mimeType)) 1706 return true; 1707 1708 return m_plugin->shouldAlwaysAutoStart(); 1709} 1710 1711void PluginView::pluginDidReceiveUserInteraction() 1712{ 1713 if (frame() && !frame()->settings()->plugInSnapshottingEnabled()) 1714 return; 1715 1716 if (m_didReceiveUserInteraction) 1717 return; 1718 1719 m_didReceiveUserInteraction = true; 1720 1721 WebCore::HTMLPlugInImageElement* plugInImageElement = toHTMLPlugInImageElement(m_pluginElement.get()); 1722 String pageOrigin = plugInImageElement->document()->page()->mainFrame()->document()->baseURL().host(); 1723 String pluginOrigin = plugInImageElement->loadedUrl().host(); 1724 String mimeType = plugInImageElement->loadedMimeType(); 1725 1726 WebProcess::shared().plugInDidReceiveUserInteraction(pageOrigin, pluginOrigin, mimeType); 1727} 1728 1729bool PluginView::shouldCreateTransientPaintingSnapshot() const 1730{ 1731 if (!m_plugin) 1732 return false; 1733 1734 if (!m_isInitialized) 1735 return false; 1736 1737 if (FrameView* frameView = frame()->view()) { 1738 if (frameView->paintBehavior() & (PaintBehaviorSelectionOnly | PaintBehaviorForceBlackText)) { 1739 // This paint behavior is used when drawing the find indicator and there's no need to 1740 // snapshot plug-ins, because they can never be painted as part of the find indicator. 1741 return false; 1742 } 1743 } 1744 1745 return true; 1746} 1747 1748} // namespace WebKit 1749