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