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 56 using namespace JSC; 57 using namespace WebCore; 58 59 namespace WebKit { 60 61 class PluginView::URLRequest : public RefCounted<URLRequest> { 62 public: 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 73 private: 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 86 class PluginView::Stream : public RefCounted<PluginView::Stream>, NetscapePlugInStreamLoaderClient { 87 public: 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 99 private: 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 125 PluginView::Stream::~Stream() 126 { 127 ASSERT(!m_pluginView); 128 } 129 130 void 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 140 void 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 149 static 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 182 void 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 198 void PluginView::Stream::didReceiveData(NetscapePlugInStreamLoader*, const char* bytes, int length) 199 { 200 m_pluginView->m_plugin->streamDidReceiveData(m_streamID, bytes, length); 201 } 202 203 void 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 216 void 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 229 static 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 240 PassRefPtr<PluginView> PluginView::create(PassRefPtr<HTMLPlugInElement> pluginElement, PassRefPtr<Plugin> plugin, const Plugin::Parameters& parameters) 241 { 242 return adoptRef(new PluginView(pluginElement, plugin, parameters)); 243 } 244 245 PluginView::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 263 PluginView::~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 295 Frame* PluginView::frame() 296 { 297 return m_pluginElement->document()->frame(); 298 } 299 300 void 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 327 void 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 345 void 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 360 void 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) 377 void PluginView::setWindowIsVisible(bool windowIsVisible) 378 { 379 if (!m_plugin) 380 return; 381 382 // FIXME: Implement. 383 } 384 385 void PluginView::setWindowIsFocused(bool windowIsFocused) 386 { 387 if (!m_isInitialized || !m_plugin) 388 return; 389 390 m_plugin->windowFocusChanged(windowIsFocused); 391 } 392 393 void 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 401 bool 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 415 void 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) 468 PlatformLayer* 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 478 JSObject* 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 494 void 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 503 void PluginView::setFrameRect(const WebCore::IntRect& rect) 504 { 505 Widget::setFrameRect(rect); 506 viewGeometryDidChange(); 507 } 508 509 void PluginView::setBoundsSize(const WebCore::IntSize& size) 510 { 511 Widget::setBoundsSize(size); 512 m_boundsSize = size; 513 viewGeometryDidChange(); 514 } 515 516 void 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 541 void PluginView::frameRectsChanged() 542 { 543 Widget::frameRectsChanged(); 544 viewGeometryDidChange(); 545 } 546 547 void PluginView::setParent(ScrollView* scrollView) 548 { 549 Widget::setParent(scrollView); 550 551 if (scrollView) 552 initializePlugin(); 553 } 554 555 void 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 593 void 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 606 void 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 617 IntRect 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 634 void 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 643 void 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 656 void 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 675 void 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 715 void 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 766 void PluginView::addStream(Stream* stream) 767 { 768 ASSERT(!m_streams.contains(stream->streamID())); 769 m_streams.set(stream->streamID(), stream); 770 } 771 772 void PluginView::removeStream(Stream* stream) 773 { 774 ASSERT(m_streams.get(stream->streamID()) == stream); 775 776 m_streams.remove(stream->streamID()); 777 } 778 779 void 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 791 void 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 823 void 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 838 void 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 848 void PluginView::mediaCanStart() 849 { 850 ASSERT(m_isWaitingUntilMediaCanStart); 851 m_isWaitingUntilMediaCanStart = false; 852 853 initializePlugin(); 854 } 855 856 void PluginView::invalidate(const IntRect& dirtyRect) 857 { 858 invalidateRect(dirtyRect); 859 } 860 861 String 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 870 void 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 884 void 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 897 void 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 909 NPObject* 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 920 NPObject* 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 932 bool 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 952 void 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 964 bool 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 976 void 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) 992 HWND PluginView::nativeParentWindow() 993 { 994 return m_webPage->nativeWindow(); 995 } 996 #endif 997 998 #if PLATFORM(MAC) 999 void PluginView::setComplexTextInputEnabled(bool complexTextInputEnabled) 1000 { 1001 m_webPage->send(Messages::WebPageProxy::SetComplexTextInputEnabled(m_plugin->pluginComplexTextInputIdentifier(), complexTextInputEnabled)); 1002 } 1003 1004 mach_port_t PluginView::compositingRenderServerPort() 1005 { 1006 return WebProcess::shared().compositingRenderServerPort(); 1007 } 1008 1009 #endif 1010 1011 String 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 1019 String PluginView::cookiesForURL(const String& urlString) 1020 { 1021 return cookies(m_pluginElement->document(), KURL(KURL(), urlString)); 1022 } 1023 1024 void PluginView::setCookiesForURL(const String& urlString, const String& cookieString) 1025 { 1026 setCookies(m_pluginElement->document(), KURL(KURL(), urlString), cookieString); 1027 } 1028 1029 bool 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 1042 void PluginView::protectPluginFromDestruction() 1043 { 1044 if (!m_isBeingDestroyed) 1045 ref(); 1046 } 1047 1048 void PluginView::unprotectPluginFromDestruction() 1049 { 1050 if (!m_isBeingDestroyed) 1051 deref(); 1052 } 1053 1054 void 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 1063 void 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