1/* 2 * Copyright (C) 2010 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 "WebEvent.h" 33#include "WebPage.h" 34#include "WebPageProxyMessages.h" 35#include "WebProcess.h" 36#include <WebCore/Chrome.h> 37#include <WebCore/CookieJar.h> 38#include <WebCore/DocumentLoader.h> 39#include <WebCore/Event.h> 40#include <WebCore/FocusController.h> 41#include <WebCore/FrameLoadRequest.h> 42#include <WebCore/FrameLoaderClient.h> 43#include <WebCore/FrameView.h> 44#include <WebCore/GraphicsContext.h> 45#include <WebCore/HTMLPlugInElement.h> 46#include <WebCore/HostWindow.h> 47#include <WebCore/NetscapePlugInStreamLoader.h> 48#include <WebCore/NetworkingContext.h> 49#include <WebCore/ProxyServer.h> 50#include <WebCore/RenderEmbeddedObject.h> 51#include <WebCore/RenderLayer.h> 52#include <WebCore/ResourceLoadScheduler.h> 53#include <WebCore/ScrollView.h> 54#include <WebCore/Settings.h> 55 56using namespace JSC; 57using namespace WebCore; 58 59namespace WebKit { 60 61class PluginView::URLRequest : public RefCounted<URLRequest> { 62public: 63 static PassRefPtr<PluginView::URLRequest> create(uint64_t requestID, const FrameLoadRequest& request, bool allowPopups) 64 { 65 return adoptRef(new URLRequest(requestID, request, allowPopups)); 66 } 67 68 uint64_t requestID() const { return m_requestID; } 69 const String& target() const { return m_request.frameName(); } 70 const ResourceRequest & request() const { return m_request.resourceRequest(); } 71 bool allowPopups() const { return m_allowPopups; } 72 73private: 74 URLRequest(uint64_t requestID, const FrameLoadRequest& request, bool allowPopups) 75 : m_requestID(requestID) 76 , m_request(request) 77 , m_allowPopups(allowPopups) 78 { 79 } 80 81 uint64_t m_requestID; 82 FrameLoadRequest m_request; 83 bool m_allowPopups; 84}; 85 86class PluginView::Stream : public RefCounted<PluginView::Stream>, NetscapePlugInStreamLoaderClient { 87public: 88 static PassRefPtr<Stream> create(PluginView* pluginView, uint64_t streamID, const ResourceRequest& request) 89 { 90 return adoptRef(new Stream(pluginView, streamID, request)); 91 } 92 ~Stream(); 93 94 void start(); 95 void cancel(); 96 97 uint64_t streamID() const { return m_streamID; } 98 99private: 100 Stream(PluginView* pluginView, uint64_t streamID, const ResourceRequest& request) 101 : m_pluginView(pluginView) 102 , m_streamID(streamID) 103 , m_request(request) 104 , m_streamWasCancelled(false) 105 { 106 } 107 108 // NetscapePluginStreamLoaderClient 109 virtual void didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse&); 110 virtual void didReceiveData(NetscapePlugInStreamLoader*, const char*, int); 111 virtual void didFail(NetscapePlugInStreamLoader*, const ResourceError&); 112 virtual void didFinishLoading(NetscapePlugInStreamLoader*); 113 114 PluginView* m_pluginView; 115 uint64_t m_streamID; 116 const ResourceRequest m_request; 117 118 // True if the stream was explicitly cancelled by calling cancel(). 119 // (As opposed to being cancelled by the user hitting the stop button for example. 120 bool m_streamWasCancelled; 121 122 RefPtr<NetscapePlugInStreamLoader> m_loader; 123}; 124 125PluginView::Stream::~Stream() 126{ 127 ASSERT(!m_pluginView); 128} 129 130void PluginView::Stream::start() 131{ 132 ASSERT(!m_loader); 133 134 Frame* frame = m_pluginView->m_pluginElement->document()->frame(); 135 ASSERT(frame); 136 137 m_loader = resourceLoadScheduler()->schedulePluginStreamLoad(frame, this, m_request); 138} 139 140void PluginView::Stream::cancel() 141{ 142 ASSERT(m_loader); 143 144 m_streamWasCancelled = true; 145 m_loader->cancel(m_loader->cancelledError()); 146 m_loader = 0; 147} 148 149static String buildHTTPHeaders(const ResourceResponse& response, long long& expectedContentLength) 150{ 151 if (!response.isHTTP()) 152 return String(); 153 154 Vector<UChar> stringBuilder; 155 String separator(": "); 156 157 String statusLine = String::format("HTTP %d ", response.httpStatusCode()); 158 stringBuilder.append(statusLine.characters(), statusLine.length()); 159 stringBuilder.append(response.httpStatusText().characters(), response.httpStatusText().length()); 160 stringBuilder.append('\n'); 161 162 HTTPHeaderMap::const_iterator end = response.httpHeaderFields().end(); 163 for (HTTPHeaderMap::const_iterator it = response.httpHeaderFields().begin(); it != end; ++it) { 164 stringBuilder.append(it->first.characters(), it->first.length()); 165 stringBuilder.append(separator.characters(), separator.length()); 166 stringBuilder.append(it->second.characters(), it->second.length()); 167 stringBuilder.append('\n'); 168 } 169 170 String headers = String::adopt(stringBuilder); 171 172 // If the content is encoded (most likely compressed), then don't send its length to the plugin, 173 // which is only interested in the decoded length, not yet known at the moment. 174 // <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic. 175 String contentEncoding = response.httpHeaderField("Content-Encoding"); 176 if (!contentEncoding.isNull() && contentEncoding != "identity") 177 expectedContentLength = -1; 178 179 return headers; 180} 181 182void PluginView::Stream::didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse& response) 183{ 184 // Compute the stream related data from the resource response. 185 const KURL& responseURL = response.url(); 186 const String& mimeType = response.mimeType(); 187 long long expectedContentLength = response.expectedContentLength(); 188 189 String headers = buildHTTPHeaders(response, expectedContentLength); 190 191 uint32_t streamLength = 0; 192 if (expectedContentLength > 0) 193 streamLength = expectedContentLength; 194 195 m_pluginView->m_plugin->streamDidReceiveResponse(m_streamID, responseURL, streamLength, response.lastModifiedDate(), mimeType, headers); 196} 197 198void PluginView::Stream::didReceiveData(NetscapePlugInStreamLoader*, const char* bytes, int length) 199{ 200 m_pluginView->m_plugin->streamDidReceiveData(m_streamID, bytes, length); 201} 202 203void PluginView::Stream::didFail(NetscapePlugInStreamLoader*, const ResourceError& error) 204{ 205 // Calling streamDidFail could cause us to be deleted, so we hold on to a reference here. 206 RefPtr<Stream> protect(this); 207 208 // We only want to call streamDidFail if the stream was not explicitly cancelled by the plug-in. 209 if (!m_streamWasCancelled) 210 m_pluginView->m_plugin->streamDidFail(m_streamID, error.isCancellation()); 211 212 m_pluginView->removeStream(this); 213 m_pluginView = 0; 214} 215 216void PluginView::Stream::didFinishLoading(NetscapePlugInStreamLoader*) 217{ 218 // Calling streamDidFinishLoading could cause us to be deleted, so we hold on to a reference here. 219 RefPtr<Stream> protectStream(this); 220 221 // Protect the plug-in while we're calling into it. 222 NPRuntimeObjectMap::PluginProtector pluginProtector(&m_pluginView->m_npRuntimeObjectMap); 223 m_pluginView->m_plugin->streamDidFinishLoading(m_streamID); 224 225 m_pluginView->removeStream(this); 226 m_pluginView = 0; 227} 228 229static inline WebPage* webPage(HTMLPlugInElement* pluginElement) 230{ 231 Frame* frame = pluginElement->document()->frame(); 232 ASSERT(frame); 233 234 WebPage* webPage = static_cast<WebFrameLoaderClient*>(frame->loader()->client())->webFrame()->page(); 235 ASSERT(webPage); 236 237 return webPage; 238} 239 240PassRefPtr<PluginView> PluginView::create(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<Plugin> plugin, const Plugin::Parameters& parameters) 241{ 242 return adoptRef(new PluginView(pluginElement, plugin, parameters)); 243} 244 245PluginView::PluginView(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<Plugin> plugin, const Plugin::Parameters& parameters) 246 : PluginViewBase(0) 247 , m_pluginElement(pluginElement) 248 , m_plugin(plugin) 249 , m_webPage(webPage(m_pluginElement.get())) 250 , m_parameters(parameters) 251 , m_isInitialized(false) 252 , m_isWaitingUntilMediaCanStart(false) 253 , m_isBeingDestroyed(false) 254 , m_pendingURLRequestsTimer(RunLoop::main(), this, &PluginView::pendingURLRequestsTimerFired) 255 , m_npRuntimeObjectMap(this) 256 , m_manualStreamState(StreamStateInitial) 257{ 258#if PLATFORM(MAC) 259 m_webPage->addPluginView(this); 260#endif 261} 262 263PluginView::~PluginView() 264{ 265#if PLATFORM(MAC) 266 m_webPage->removePluginView(this); 267#endif 268 269 ASSERT(!m_isBeingDestroyed); 270 271 if (m_isWaitingUntilMediaCanStart) 272 m_pluginElement->document()->removeMediaCanStartListener(this); 273 274 // Cancel all pending frame loads. 275 FrameLoadMap::iterator end = m_pendingFrameLoads.end(); 276 for (FrameLoadMap::iterator it = m_pendingFrameLoads.begin(), end = m_pendingFrameLoads.end(); it != end; ++it) 277 it->first->setLoadListener(0); 278 279 if (m_plugin && m_isInitialized) { 280 m_isBeingDestroyed = true; 281 m_plugin->destroy(); 282 m_isBeingDestroyed = false; 283 } 284 285 // Invalidate the object map. 286 m_npRuntimeObjectMap.invalidate(); 287 288 cancelAllStreams(); 289 290 // Null out the plug-in element explicitly so we'll crash earlier if we try to use 291 // the plug-in view after it's been destroyed. 292 m_pluginElement = nullptr; 293} 294 295Frame* PluginView::frame() 296{ 297 return m_pluginElement->document()->frame(); 298} 299 300void PluginView::manualLoadDidReceiveResponse(const ResourceResponse& response) 301{ 302 // The plug-in can be null here if it failed to initialize. 303 if (!m_plugin) 304 return; 305 306 if (!m_isInitialized) { 307 ASSERT(m_manualStreamState == StreamStateInitial); 308 m_manualStreamState = StreamStateHasReceivedResponse; 309 m_manualStreamResponse = response; 310 return; 311 } 312 313 // Compute the stream related data from the resource response. 314 const KURL& responseURL = response.url(); 315 const String& mimeType = response.mimeType(); 316 long long expectedContentLength = response.expectedContentLength(); 317 318 String headers = buildHTTPHeaders(response, expectedContentLength); 319 320 uint32_t streamLength = 0; 321 if (expectedContentLength > 0) 322 streamLength = expectedContentLength; 323 324 m_plugin->manualStreamDidReceiveResponse(responseURL, streamLength, response.lastModifiedDate(), mimeType, headers); 325} 326 327void PluginView::manualLoadDidReceiveData(const char* bytes, int length) 328{ 329 // The plug-in can be null here if it failed to initialize. 330 if (!m_plugin) 331 return; 332 333 if (!m_isInitialized) { 334 ASSERT(m_manualStreamState == StreamStateHasReceivedResponse); 335 if (!m_manualStreamData) 336 m_manualStreamData = SharedBuffer::create(); 337 338 m_manualStreamData->append(bytes, length); 339 return; 340 } 341 342 m_plugin->manualStreamDidReceiveData(bytes, length); 343} 344 345void PluginView::manualLoadDidFinishLoading() 346{ 347 // The plug-in can be null here if it failed to initialize. 348 if (!m_plugin) 349 return; 350 351 if (!m_isInitialized) { 352 ASSERT(m_manualStreamState == StreamStateHasReceivedResponse); 353 m_manualStreamState = StreamStateFinished; 354 return; 355 } 356 357 m_plugin->manualStreamDidFinishLoading(); 358} 359 360void PluginView::manualLoadDidFail(const ResourceError& error) 361{ 362 // The plug-in can be null here if it failed to initialize. 363 if (!m_plugin) 364 return; 365 366 if (!m_isInitialized) { 367 m_manualStreamState = StreamStateFinished; 368 m_manualStreamError = error; 369 m_manualStreamData = nullptr; 370 return; 371 } 372 373 m_plugin->manualStreamDidFail(error.isCancellation()); 374} 375 376#if PLATFORM(MAC) 377void PluginView::setWindowIsVisible(bool windowIsVisible) 378{ 379 if (!m_plugin) 380 return; 381 382 // FIXME: Implement. 383} 384 385void PluginView::setWindowIsFocused(bool windowIsFocused) 386{ 387 if (!m_isInitialized || !m_plugin) 388 return; 389 390 m_plugin->windowFocusChanged(windowIsFocused); 391} 392 393void PluginView::windowAndViewFramesChanged(const IntRect& windowFrameInScreenCoordinates, const IntRect& viewFrameInWindowCoordinates) 394{ 395 if (!m_isInitialized || !m_plugin) 396 return; 397 398 m_plugin->windowAndViewFramesChanged(windowFrameInScreenCoordinates, viewFrameInWindowCoordinates); 399} 400 401bool PluginView::sendComplexTextInput(uint64_t pluginComplexTextInputIdentifier, const String& textInput) 402{ 403 if (!m_plugin) 404 return false; 405 406 if (m_plugin->pluginComplexTextInputIdentifier() != pluginComplexTextInputIdentifier) 407 return false; 408 409 m_plugin->sendComplexTextInput(textInput); 410 return true; 411} 412 413#endif 414 415void PluginView::initializePlugin() 416{ 417 if (m_isInitialized) 418 return; 419 420 if (!m_plugin) { 421 // We've already tried and failed to initialize the plug-in. 422 return; 423 } 424 425 if (Frame* frame = m_pluginElement->document()->frame()) { 426 if (Page* page = frame->page()) { 427 428 // We shouldn't initialize the plug-in right now, add a listener. 429 if (!page->canStartMedia()) { 430 if (m_isWaitingUntilMediaCanStart) 431 return; 432 433 m_isWaitingUntilMediaCanStart = true; 434 m_pluginElement->document()->addMediaCanStartListener(this); 435 return; 436 } 437 } 438 } 439 440 if (!m_plugin->initialize(this, m_parameters)) { 441 // We failed to initialize the plug-in. 442 m_plugin = 0; 443 444 return; 445 } 446 447 m_isInitialized = true; 448 449 viewGeometryDidChange(); 450 451 redeliverManualStream(); 452 453#if PLATFORM(MAC) 454 if (m_plugin->pluginLayer()) { 455 if (frame()) { 456 frame()->view()->enterCompositingMode(); 457 m_pluginElement->setNeedsStyleRecalc(SyntheticStyleChange); 458 } 459 } 460 461 windowAndViewFramesChanged(m_webPage->windowFrameInScreenCoordinates(), m_webPage->viewFrameInWindowCoordinates()); 462 setWindowIsVisible(m_webPage->windowIsVisible()); 463 setWindowIsFocused(m_webPage->windowIsFocused()); 464#endif 465} 466 467#if PLATFORM(MAC) 468PlatformLayer* PluginView::platformLayer() const 469{ 470 // The plug-in can be null here if it failed to initialize. 471 if (!m_isInitialized || !m_plugin) 472 return 0; 473 474 return m_plugin->pluginLayer(); 475} 476#endif 477 478JSObject* PluginView::scriptObject(JSGlobalObject* globalObject) 479{ 480 // The plug-in can be null here if it failed to initialize. 481 if (!m_isInitialized || !m_plugin) 482 return 0; 483 484 NPObject* scriptableNPObject = m_plugin->pluginScriptableNPObject(); 485 if (!scriptableNPObject) 486 return 0; 487 488 JSObject* jsObject = m_npRuntimeObjectMap.getOrCreateJSObject(globalObject, scriptableNPObject); 489 releaseNPObject(scriptableNPObject); 490 491 return jsObject; 492} 493 494void PluginView::privateBrowsingStateChanged(bool privateBrowsingEnabled) 495{ 496 // The plug-in can be null here if it failed to initialize. 497 if (!m_isInitialized || !m_plugin) 498 return; 499 500 m_plugin->privateBrowsingStateChanged(privateBrowsingEnabled); 501} 502 503void PluginView::setFrameRect(const WebCore::IntRect& rect) 504{ 505 Widget::setFrameRect(rect); 506 viewGeometryDidChange(); 507} 508 509void PluginView::setBoundsSize(const WebCore::IntSize& size) 510{ 511 Widget::setBoundsSize(size); 512 m_boundsSize = size; 513 viewGeometryDidChange(); 514} 515 516void PluginView::paint(GraphicsContext* context, const IntRect& dirtyRect) 517{ 518 if (context->paintingDisabled() || !m_plugin || !m_isInitialized) 519 return; 520 521 IntRect dirtyRectInWindowCoordinates = parent()->contentsToWindow(dirtyRect); 522 IntRect paintRectInWindowCoordinates = intersection(dirtyRectInWindowCoordinates, clipRectInWindowCoordinates()); 523 if (paintRectInWindowCoordinates.isEmpty()) 524 return; 525 526 if (m_snapshot) 527 m_snapshot->paint(*context, frameRect().location(), m_snapshot->bounds()); 528 else { 529 // The plugin is given a frame rect which is parent()->contentsToWindow(frameRect()), 530 // and un-translates by the its origin when painting. The current CTM reflects 531 // this widget's frame is its parent (the document), so we have to offset the CTM by 532 // the document's window coordinates. 533 IntPoint documentOriginInWindowCoordinates = parent()->contentsToWindow(IntPoint()); 534 context->save(); 535 context->translate(-documentOriginInWindowCoordinates.x(), -documentOriginInWindowCoordinates.y()); 536 m_plugin->paint(context, paintRectInWindowCoordinates); 537 context->restore(); 538 } 539} 540 541void PluginView::frameRectsChanged() 542{ 543 Widget::frameRectsChanged(); 544 viewGeometryDidChange(); 545} 546 547void PluginView::setParent(ScrollView* scrollView) 548{ 549 Widget::setParent(scrollView); 550 551 if (scrollView) 552 initializePlugin(); 553} 554 555void PluginView::handleEvent(Event* event) 556{ 557 if (!m_isInitialized || !m_plugin) 558 return; 559 560 const WebEvent* currentEvent = WebPage::currentEvent(); 561 if (!currentEvent) 562 return; 563 564 bool didHandleEvent = false; 565 566 if ((event->type() == eventNames().mousemoveEvent && currentEvent->type() == WebEvent::MouseMove) 567 || (event->type() == eventNames().mousedownEvent && currentEvent->type() == WebEvent::MouseDown) 568 || (event->type() == eventNames().mouseupEvent && currentEvent->type() == WebEvent::MouseUp)) { 569 // We have a mouse event. 570 if (currentEvent->type() == WebEvent::MouseDown) 571 focusPluginElement(); 572 573 didHandleEvent = m_plugin->handleMouseEvent(static_cast<const WebMouseEvent&>(*currentEvent)); 574 } else if (event->type() == eventNames().mousewheelEvent && currentEvent->type() == WebEvent::Wheel) { 575 // We have a wheel event. 576 didHandleEvent = m_plugin->handleWheelEvent(static_cast<const WebWheelEvent&>(*currentEvent)); 577 } else if (event->type() == eventNames().mouseoverEvent && currentEvent->type() == WebEvent::MouseMove) { 578 // We have a mouse enter event. 579 didHandleEvent = m_plugin->handleMouseEnterEvent(static_cast<const WebMouseEvent&>(*currentEvent)); 580 } else if (event->type() == eventNames().mouseoutEvent && currentEvent->type() == WebEvent::MouseMove) { 581 // We have a mouse leave event. 582 didHandleEvent = m_plugin->handleMouseLeaveEvent(static_cast<const WebMouseEvent&>(*currentEvent)); 583 } else if ((event->type() == eventNames().keydownEvent && currentEvent->type() == WebEvent::KeyDown) 584 || (event->type() == eventNames().keyupEvent && currentEvent->type() == WebEvent::KeyUp)) { 585 // We have a keyboard event. 586 didHandleEvent = m_plugin->handleKeyboardEvent(static_cast<const WebKeyboardEvent&>(*currentEvent)); 587 } 588 589 if (didHandleEvent) 590 event->setDefaultHandled(); 591} 592 593void PluginView::notifyWidget(WidgetNotification notification) 594{ 595 switch (notification) { 596 case WillPaintFlattened: 597 if (m_plugin && m_isInitialized) 598 m_snapshot = m_plugin->snapshot(); 599 break; 600 case DidPaintFlattened: 601 m_snapshot = nullptr; 602 break; 603 } 604} 605 606void PluginView::viewGeometryDidChange() 607{ 608 if (!m_isInitialized || !m_plugin || !parent()) 609 return; 610 611 // Get the frame rect in window coordinates. 612 IntRect frameRectInWindowCoordinates = parent()->contentsToWindow(frameRect()); 613 frameRectInWindowCoordinates.setSize(m_boundsSize); 614 m_plugin->geometryDidChange(frameRectInWindowCoordinates, clipRectInWindowCoordinates()); 615} 616 617IntRect PluginView::clipRectInWindowCoordinates() const 618{ 619 ASSERT(parent()); 620 621 // Get the frame rect in window coordinates. 622 IntRect frameRectInWindowCoordinates = parent()->contentsToWindow(frameRect()); 623 frameRectInWindowCoordinates.setSize(m_boundsSize); 624 625 // Get the window clip rect for the enclosing layer (in window coordinates). 626 RenderLayer* layer = m_pluginElement->renderer()->enclosingLayer(); 627 FrameView* parentView = m_pluginElement->document()->frame()->view(); 628 IntRect windowClipRect = parentView->windowClipRectForLayer(layer, true); 629 630 // Intersect the two rects to get the view clip rect in window coordinates. 631 return intersection(frameRectInWindowCoordinates, windowClipRect); 632} 633 634void PluginView::focusPluginElement() 635{ 636 ASSERT(frame()); 637 638 if (Page* page = frame()->page()) 639 page->focusController()->setFocusedFrame(frame()); 640 frame()->document()->setFocusedNode(m_pluginElement); 641} 642 643void PluginView::pendingURLRequestsTimerFired() 644{ 645 ASSERT(!m_pendingURLRequests.isEmpty()); 646 647 RefPtr<URLRequest> urlRequest = m_pendingURLRequests.takeFirst(); 648 649 // If there are more requests to perform, reschedule the timer. 650 if (!m_pendingURLRequests.isEmpty()) 651 m_pendingURLRequestsTimer.startOneShot(0); 652 653 performURLRequest(urlRequest.get()); 654} 655 656void PluginView::performURLRequest(URLRequest* request) 657{ 658 // First, check if this is a javascript: url. 659 if (protocolIsJavaScript(request->request().url())) { 660 performJavaScriptURLRequest(request); 661 return; 662 } 663 664 if (!request->target().isNull()) { 665 performFrameLoadURLRequest(request); 666 return; 667 } 668 669 // This request is to load a URL and create a stream. 670 RefPtr<Stream> stream = PluginView::Stream::create(this, request->requestID(), request->request()); 671 addStream(stream.get()); 672 stream->start(); 673} 674 675void PluginView::performFrameLoadURLRequest(URLRequest* request) 676{ 677 ASSERT(!request->target().isNull()); 678 679 Frame* frame = m_pluginElement->document()->frame(); 680 if (!frame) 681 return; 682 683 if (!m_pluginElement->document()->securityOrigin()->canDisplay(request->request().url())) { 684 // We can't load the request, send back a reply to the plug-in. 685 m_plugin->frameDidFail(request->requestID(), false); 686 return; 687 } 688 689 // First, try to find a target frame. 690 Frame* targetFrame = frame->loader()->findFrameForNavigation(request->target()); 691 if (!targetFrame) { 692 // We did not find a target frame. Ask our frame to load the page. This may or may not create a popup window. 693 frame->loader()->load(request->request(), request->target(), false); 694 695 // FIXME: We don't know whether the window was successfully created here so we just assume that it worked. 696 // It's better than not telling the plug-in anything. 697 m_plugin->frameDidFinishLoading(request->requestID()); 698 return; 699 } 700 701 // Now ask the frame to load the request. 702 targetFrame->loader()->load(request->request(), false); 703 704 WebFrame* targetWebFrame = static_cast<WebFrameLoaderClient*>(targetFrame->loader()->client())->webFrame(); 705 if (WebFrame::LoadListener* loadListener = targetWebFrame->loadListener()) { 706 // Check if another plug-in view or even this view is waiting for the frame to load. 707 // If it is, tell it that the load was cancelled because it will be anyway. 708 loadListener->didFailLoad(targetWebFrame, true); 709 } 710 711 m_pendingFrameLoads.set(targetWebFrame, request); 712 targetWebFrame->setLoadListener(this); 713} 714 715void PluginView::performJavaScriptURLRequest(URLRequest* request) 716{ 717 ASSERT(protocolIsJavaScript(request->request().url())); 718 719 RefPtr<Frame> frame = m_pluginElement->document()->frame(); 720 if (!frame) 721 return; 722 723 String jsString = decodeURLEscapeSequences(request->request().url().string().substring(sizeof("javascript:") - 1)); 724 725 if (!request->target().isNull()) { 726 // For security reasons, only allow JS requests to be made on the frame that contains the plug-in. 727 if (frame->tree()->find(request->target()) != frame) { 728 // Let the plug-in know that its frame load failed. 729 m_plugin->frameDidFail(request->requestID(), false); 730 return; 731 } 732 } 733 734 // Evaluate the JavaScript code. Note that running JavaScript here could cause the plug-in to be destroyed, so we 735 // grab references to the plug-in here. 736 RefPtr<Plugin> plugin = m_plugin; 737 738 bool oldAllowPopups = frame->script()->allowPopupsFromPlugin(); 739 frame->script()->setAllowPopupsFromPlugin(request->allowPopups()); 740 741 ScriptValue result = frame->script()->executeScript(jsString); 742 743 frame->script()->setAllowPopupsFromPlugin(oldAllowPopups); 744 745 // Check if evaluating the JavaScript destroyed the plug-in. 746 if (!plugin->controller()) 747 return; 748 749 ScriptState* scriptState = frame->script()->globalObject(pluginWorld())->globalExec(); 750 String resultString; 751 result.getString(scriptState, resultString); 752 753 if (!request->target().isNull()) { 754 // Just send back whether the frame load succeeded or not. 755 if (resultString.isNull()) 756 m_plugin->frameDidFail(request->requestID(), false); 757 else 758 m_plugin->frameDidFinishLoading(request->requestID()); 759 return; 760 } 761 762 // Send the result back to the plug-in. 763 plugin->didEvaluateJavaScript(request->requestID(), decodeURLEscapeSequences(request->request().url()), resultString); 764} 765 766void PluginView::addStream(Stream* stream) 767{ 768 ASSERT(!m_streams.contains(stream->streamID())); 769 m_streams.set(stream->streamID(), stream); 770} 771 772void PluginView::removeStream(Stream* stream) 773{ 774 ASSERT(m_streams.get(stream->streamID()) == stream); 775 776 m_streams.remove(stream->streamID()); 777} 778 779void PluginView::cancelAllStreams() 780{ 781 Vector<RefPtr<Stream> > streams; 782 copyValuesToVector(m_streams, streams); 783 784 for (size_t i = 0; i < streams.size(); ++i) 785 streams[i]->cancel(); 786 787 // Cancelling a stream removes it from the m_streams map, so if we cancel all streams the map should be empty. 788 ASSERT(m_streams.isEmpty()); 789} 790 791void PluginView::redeliverManualStream() 792{ 793 if (m_manualStreamState == StreamStateInitial) { 794 // Nothing to do. 795 return; 796 } 797 798 if (m_manualStreamState == StreamStateFailed) { 799 manualLoadDidFail(m_manualStreamError); 800 return; 801 } 802 803 // Deliver the response. 804 manualLoadDidReceiveResponse(m_manualStreamResponse); 805 806 // Deliver the data. 807 if (m_manualStreamData) { 808 const char* data; 809 unsigned position = 0; 810 811 while (unsigned length = m_manualStreamData->getSomeData(data, position)) { 812 manualLoadDidReceiveData(data, length); 813 position += length; 814 } 815 816 m_manualStreamData = nullptr; 817 } 818 819 if (m_manualStreamState == StreamStateFinished) 820 manualLoadDidFinishLoading(); 821} 822 823void PluginView::invalidateRect(const IntRect& dirtyRect) 824{ 825 if (!parent() || !m_plugin || !m_isInitialized) 826 return; 827 828#if PLATFORM(MAC) 829 if (m_plugin->pluginLayer()) 830 return; 831#endif 832 833 IntRect dirtyRectInWindowCoordinates = convertToContainingWindow(dirtyRect); 834 835 parent()->hostWindow()->invalidateContentsAndWindow(intersection(dirtyRectInWindowCoordinates, clipRectInWindowCoordinates()), false); 836} 837 838void PluginView::setFocus(bool hasFocus) 839{ 840 Widget::setFocus(hasFocus); 841 842 if (!m_isInitialized || !m_plugin) 843 return; 844 845 m_plugin->setFocus(hasFocus); 846} 847 848void PluginView::mediaCanStart() 849{ 850 ASSERT(m_isWaitingUntilMediaCanStart); 851 m_isWaitingUntilMediaCanStart = false; 852 853 initializePlugin(); 854} 855 856void PluginView::invalidate(const IntRect& dirtyRect) 857{ 858 invalidateRect(dirtyRect); 859} 860 861String PluginView::userAgent() 862{ 863 Frame* frame = m_pluginElement->document()->frame(); 864 if (!frame) 865 return String(); 866 867 return frame->loader()->client()->userAgent(KURL()); 868} 869 870void PluginView::loadURL(uint64_t requestID, const String& method, const String& urlString, const String& target, 871 const HTTPHeaderMap& headerFields, const Vector<uint8_t>& httpBody, bool allowPopups) 872{ 873 FrameLoadRequest frameLoadRequest(m_pluginElement->document()->securityOrigin()); 874 frameLoadRequest.resourceRequest().setHTTPMethod(method); 875 frameLoadRequest.resourceRequest().setURL(m_pluginElement->document()->completeURL(urlString)); 876 frameLoadRequest.resourceRequest().addHTTPHeaderFields(headerFields); 877 frameLoadRequest.resourceRequest().setHTTPBody(FormData::create(httpBody.data(), httpBody.size())); 878 frameLoadRequest.setFrameName(target); 879 880 m_pendingURLRequests.append(URLRequest::create(requestID, frameLoadRequest, allowPopups)); 881 m_pendingURLRequestsTimer.startOneShot(0); 882} 883 884void PluginView::cancelStreamLoad(uint64_t streamID) 885{ 886 // Keep a reference to the stream. Stream::cancel might remove the stream from the map, and thus 887 // releasing its last reference. 888 RefPtr<Stream> stream = m_streams.get(streamID).get(); 889 if (!stream) 890 return; 891 892 // Cancelling the stream here will remove it from the map. 893 stream->cancel(); 894 ASSERT(!m_streams.contains(streamID)); 895} 896 897void PluginView::cancelManualStreamLoad() 898{ 899 if (!frame()) 900 return; 901 902 DocumentLoader* documentLoader = frame()->loader()->activeDocumentLoader(); 903 ASSERT(documentLoader); 904 905 if (documentLoader->isLoadingMainResource()) 906 documentLoader->cancelMainResourceLoad(frame()->loader()->cancelledError(m_parameters.url)); 907} 908 909NPObject* PluginView::windowScriptNPObject() 910{ 911 if (!frame()) 912 return 0; 913 914 // FIXME: Handle JavaScript being disabled. 915 ASSERT(frame()->script()->canExecuteScripts(NotAboutToExecuteScript)); 916 917 return m_npRuntimeObjectMap.getOrCreateNPObject(*pluginWorld()->globalData(), frame()->script()->windowShell(pluginWorld())->window()); 918} 919 920NPObject* PluginView::pluginElementNPObject() 921{ 922 if (!frame()) 923 return 0; 924 925 // FIXME: Handle JavaScript being disabled. 926 JSObject* object = frame()->script()->jsObjectForPluginElement(m_pluginElement.get()); 927 ASSERT(object); 928 929 return m_npRuntimeObjectMap.getOrCreateNPObject(*pluginWorld()->globalData(), object); 930} 931 932bool PluginView::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result, bool allowPopups) 933{ 934 RefPtr<Frame> frame = m_pluginElement->document()->frame(); 935 if (!frame) 936 return false; 937 938 bool oldAllowPopups = frame->script()->allowPopupsFromPlugin(); 939 frame->script()->setAllowPopupsFromPlugin(allowPopups); 940 941 // Calling evaluate will run JavaScript that can potentially remove the plug-in element, so we need to 942 // protect the plug-in view from destruction. 943 NPRuntimeObjectMap::PluginProtector pluginProtector(&m_npRuntimeObjectMap); 944 945 bool returnValue = m_npRuntimeObjectMap.evaluate(npObject, scriptString, result); 946 947 frame->script()->setAllowPopupsFromPlugin(oldAllowPopups); 948 949 return returnValue; 950} 951 952void PluginView::setStatusbarText(const String& statusbarText) 953{ 954 if (!frame()) 955 return; 956 957 Page* page = frame()->page(); 958 if (!page) 959 return; 960 961 page->chrome()->setStatusbarText(frame(), statusbarText); 962} 963 964bool PluginView::isAcceleratedCompositingEnabled() 965{ 966 if (!frame()) 967 return false; 968 969 Settings* settings = frame()->settings(); 970 if (!settings) 971 return false; 972 973 return settings->acceleratedCompositingEnabled(); 974} 975 976void PluginView::pluginProcessCrashed() 977{ 978 if (!m_pluginElement->renderer()) 979 return; 980 981 // FIXME: The renderer could also be a RenderApplet, we should handle that. 982 if (!m_pluginElement->renderer()->isEmbeddedObject()) 983 return; 984 985 RenderEmbeddedObject* renderer = toRenderEmbeddedObject(m_pluginElement->renderer()); 986 renderer->setShowsCrashedPluginIndicator(); 987 988 invalidateRect(frameRect()); 989} 990 991#if PLATFORM(WIN) 992HWND PluginView::nativeParentWindow() 993{ 994 return m_webPage->nativeWindow(); 995} 996#endif 997 998#if PLATFORM(MAC) 999void PluginView::setComplexTextInputEnabled(bool complexTextInputEnabled) 1000{ 1001 m_webPage->send(Messages::WebPageProxy::SetComplexTextInputEnabled(m_plugin->pluginComplexTextInputIdentifier(), complexTextInputEnabled)); 1002} 1003 1004mach_port_t PluginView::compositingRenderServerPort() 1005{ 1006 return WebProcess::shared().compositingRenderServerPort(); 1007} 1008 1009#endif 1010 1011String PluginView::proxiesForURL(const String& urlString) 1012{ 1013 const FrameLoader* frameLoader = frame() ? frame()->loader() : 0; 1014 const NetworkingContext* context = frameLoader ? frameLoader->networkingContext() : 0; 1015 Vector<ProxyServer> proxyServers = proxyServersForURL(KURL(KURL(), urlString), context); 1016 return toString(proxyServers); 1017} 1018 1019String PluginView::cookiesForURL(const String& urlString) 1020{ 1021 return cookies(m_pluginElement->document(), KURL(KURL(), urlString)); 1022} 1023 1024void PluginView::setCookiesForURL(const String& urlString, const String& cookieString) 1025{ 1026 setCookies(m_pluginElement->document(), KURL(KURL(), urlString), cookieString); 1027} 1028 1029bool PluginView::isPrivateBrowsingEnabled() 1030{ 1031 // If we can't get the real setting, we'll assume that private browsing is enabled. 1032 if (!frame()) 1033 return true; 1034 1035 Settings* settings = frame()->settings(); 1036 if (!settings) 1037 return true; 1038 1039 return settings->privateBrowsingEnabled(); 1040} 1041 1042void PluginView::protectPluginFromDestruction() 1043{ 1044 if (!m_isBeingDestroyed) 1045 ref(); 1046} 1047 1048void PluginView::unprotectPluginFromDestruction() 1049{ 1050 if (!m_isBeingDestroyed) 1051 deref(); 1052} 1053 1054void PluginView::didFinishLoad(WebFrame* webFrame) 1055{ 1056 RefPtr<URLRequest> request = m_pendingFrameLoads.take(webFrame); 1057 ASSERT(request); 1058 webFrame->setLoadListener(0); 1059 1060 m_plugin->frameDidFinishLoading(request->requestID()); 1061} 1062 1063void PluginView::didFailLoad(WebFrame* webFrame, bool wasCancelled) 1064{ 1065 RefPtr<URLRequest> request = m_pendingFrameLoads.take(webFrame); 1066 ASSERT(request); 1067 webFrame->setLoadListener(0); 1068 1069 m_plugin->frameDidFail(request->requestID(), wasCancelled); 1070} 1071 1072} // namespace WebKit 1073