1 /* 2 * Copyright (C) 2009 Google 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 are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "WebPluginContainerImpl.h" 33 34 #include "ChromeClientImpl.h" 35 #include "ScrollbarGroup.h" 36 #include "WebDataSourceImpl.h" 37 #include "WebElement.h" 38 #include "WebInputEvent.h" 39 #include "WebInputEventConversion.h" 40 #include "WebPlugin.h" 41 #include "WebViewClient.h" 42 #include "WebViewImpl.h" 43 #include "core/page/Chrome.h" 44 #include "core/page/EventHandler.h" 45 #include "platform/exported/WrappedResourceResponse.h" 46 47 #include "HTMLNames.h" 48 #include "WebPrintParams.h" 49 #include "bindings/v8/ScriptController.h" 50 #include "core/dom/Clipboard.h" 51 #include "core/events/GestureEvent.h" 52 #include "core/events/KeyboardEvent.h" 53 #include "core/events/MouseEvent.h" 54 #include "core/events/ThreadLocalEventNames.h" 55 #include "core/events/TouchEvent.h" 56 #include "core/events/WheelEvent.h" 57 #include "core/html/HTMLFormElement.h" 58 #include "core/html/HTMLPlugInElement.h" 59 #include "core/loader/FormState.h" 60 #include "core/loader/FrameLoadRequest.h" 61 #include "core/page/FocusController.h" 62 #include "core/frame/Frame.h" 63 #include "core/frame/FrameView.h" 64 #include "core/page/Page.h" 65 #include "core/page/scrolling/ScrollingCoordinator.h" 66 #include "core/platform/chromium/ChromiumDataObject.h" 67 #include "core/plugins/PluginOcclusionSupport.h" 68 #include "core/rendering/HitTestResult.h" 69 #include "core/rendering/RenderBox.h" 70 #include "platform/HostWindow.h" 71 #include "platform/KeyboardCodes.h" 72 #include "platform/PlatformGestureEvent.h" 73 #include "platform/UserGestureIndicator.h" 74 #include "platform/graphics/GraphicsContext.h" 75 #include "platform/graphics/GraphicsLayer.h" 76 #include "platform/scroll/ScrollAnimator.h" 77 #include "platform/scroll/ScrollView.h" 78 #include "platform/scroll/ScrollbarTheme.h" 79 #include "public/platform/Platform.h" 80 #include "public/platform/WebClipboard.h" 81 #include "public/platform/WebCompositorSupport.h" 82 #include "public/platform/WebCursorInfo.h" 83 #include "public/platform/WebDragData.h" 84 #include "public/platform/WebExternalTextureLayer.h" 85 #include "public/platform/WebRect.h" 86 #include "public/platform/WebString.h" 87 #include "public/platform/WebURL.h" 88 #include "public/platform/WebURLError.h" 89 #include "public/platform/WebURLRequest.h" 90 #include "public/platform/WebVector.h" 91 92 using namespace WebCore; 93 94 namespace blink { 95 96 // Public methods -------------------------------------------------------------- 97 98 void WebPluginContainerImpl::setFrameRect(const IntRect& frameRect) 99 { 100 Widget::setFrameRect(frameRect); 101 reportGeometry(); 102 } 103 104 void WebPluginContainerImpl::paint(GraphicsContext* gc, const IntRect& damageRect) 105 { 106 if (gc->updatingControlTints() && m_scrollbarGroup) { 107 // See comment in FrameView::updateControlTints(). 108 if (m_scrollbarGroup->horizontalScrollbar()) 109 m_scrollbarGroup->horizontalScrollbar()->invalidate(); 110 if (m_scrollbarGroup->verticalScrollbar()) 111 m_scrollbarGroup->verticalScrollbar()->invalidate(); 112 } 113 114 if (gc->paintingDisabled()) 115 return; 116 117 if (!parent()) 118 return; 119 120 // Don't paint anything if the plugin doesn't intersect the damage rect. 121 if (!frameRect().intersects(damageRect)) 122 return; 123 124 gc->save(); 125 126 ASSERT(parent()->isFrameView()); 127 ScrollView* view = toScrollView(parent()); 128 129 // The plugin is positioned in window coordinates, so it needs to be painted 130 // in window coordinates. 131 IntPoint origin = view->contentsToWindow(IntPoint(0, 0)); 132 gc->translate(static_cast<float>(-origin.x()), static_cast<float>(-origin.y())); 133 134 WebCanvas* canvas = gc->canvas(); 135 136 IntRect windowRect = view->contentsToWindow(damageRect); 137 m_webPlugin->paint(canvas, windowRect); 138 139 gc->restore(); 140 } 141 142 void WebPluginContainerImpl::invalidateRect(const IntRect& rect) 143 { 144 if (!parent()) 145 return; 146 147 RenderBox* renderer = toRenderBox(m_element->renderer()); 148 149 IntRect dirtyRect = rect; 150 dirtyRect.move(renderer->borderLeft() + renderer->paddingLeft(), 151 renderer->borderTop() + renderer->paddingTop()); 152 renderer->repaintRectangle(dirtyRect); 153 } 154 155 void WebPluginContainerImpl::setFocus(bool focused) 156 { 157 Widget::setFocus(focused); 158 m_webPlugin->updateFocus(focused); 159 } 160 161 void WebPluginContainerImpl::show() 162 { 163 setSelfVisible(true); 164 m_webPlugin->updateVisibility(true); 165 166 Widget::show(); 167 } 168 169 void WebPluginContainerImpl::hide() 170 { 171 setSelfVisible(false); 172 m_webPlugin->updateVisibility(false); 173 174 Widget::hide(); 175 } 176 177 void WebPluginContainerImpl::handleEvent(Event* event) 178 { 179 if (!m_webPlugin->acceptsInputEvents()) 180 return; 181 182 const WebInputEvent* currentInputEvent = WebViewImpl::currentInputEvent(); 183 UserGestureIndicator gestureIndicator(currentInputEvent && WebInputEvent::isUserGestureEventType(currentInputEvent->type) ? DefinitelyProcessingNewUserGesture : PossiblyProcessingUserGesture); 184 185 RefPtr<WebPluginContainerImpl> protector(this); 186 // The events we pass are defined at: 187 // http://devedge-temp.mozilla.org/library/manuals/2002/plugin/1.0/structures5.html#1000000 188 // Don't take the documentation as truth, however. There are many cases 189 // where mozilla behaves differently than the spec. 190 if (event->isMouseEvent()) 191 handleMouseEvent(toMouseEvent(event)); 192 else if (event->isWheelEvent()) 193 handleWheelEvent(toWheelEvent(event)); 194 else if (event->isKeyboardEvent()) 195 handleKeyboardEvent(toKeyboardEvent(event)); 196 else if (event->isTouchEvent()) 197 handleTouchEvent(toTouchEvent(event)); 198 else if (event->isGestureEvent()) 199 handleGestureEvent(toGestureEvent(event)); 200 201 // FIXME: it would be cleaner if Widget::handleEvent returned true/false and 202 // HTMLPluginElement called setDefaultHandled or defaultEventHandler. 203 if (!event->defaultHandled()) 204 m_element->Node::defaultEventHandler(event); 205 } 206 207 void WebPluginContainerImpl::frameRectsChanged() 208 { 209 Widget::frameRectsChanged(); 210 reportGeometry(); 211 } 212 213 void WebPluginContainerImpl::widgetPositionsUpdated() 214 { 215 Widget::widgetPositionsUpdated(); 216 reportGeometry(); 217 } 218 219 void WebPluginContainerImpl::clipRectChanged() 220 { 221 reportGeometry(); 222 } 223 224 void WebPluginContainerImpl::eventListenersRemoved() 225 { 226 // We're no longer registered to receive touch events, so don't try to remove 227 // the touch event handlers in our destructor. 228 m_touchEventRequestType = TouchEventRequestTypeNone; 229 } 230 231 void WebPluginContainerImpl::setParentVisible(bool parentVisible) 232 { 233 // We override this function to make sure that geometry updates are sent 234 // over to the plugin. For e.g. when a plugin is instantiated it does not 235 // have a valid parent. As a result the first geometry update from webkit 236 // is ignored. This function is called when the plugin eventually gets a 237 // parent. 238 239 if (isParentVisible() == parentVisible) 240 return; // No change. 241 242 Widget::setParentVisible(parentVisible); 243 if (!isSelfVisible()) 244 return; // This widget has explicitely been marked as not visible. 245 246 m_webPlugin->updateVisibility(isVisible()); 247 } 248 249 void WebPluginContainerImpl::setParent(Widget* widget) 250 { 251 // We override this function so that if the plugin is windowed, we can call 252 // NPP_SetWindow at the first possible moment. This ensures that 253 // NPP_SetWindow is called before the manual load data is sent to a plugin. 254 // If this order is reversed, Flash won't load videos. 255 256 Widget::setParent(widget); 257 if (widget) 258 reportGeometry(); 259 } 260 261 void WebPluginContainerImpl::setPlugin(WebPlugin* plugin) 262 { 263 if (plugin != m_webPlugin) { 264 m_element->resetInstance(); 265 m_webPlugin = plugin; 266 } 267 } 268 269 float WebPluginContainerImpl::deviceScaleFactor() 270 { 271 Page* page = m_element->document().page(); 272 if (!page) 273 return 1.0; 274 return page->deviceScaleFactor(); 275 } 276 277 float WebPluginContainerImpl::pageScaleFactor() 278 { 279 Page* page = m_element->document().page(); 280 if (!page) 281 return 1.0; 282 return page->pageScaleFactor(); 283 } 284 285 float WebPluginContainerImpl::pageZoomFactor() 286 { 287 Frame* frame = m_element->document().frame(); 288 if (!frame) 289 return 1.0; 290 return frame->pageZoomFactor(); 291 } 292 293 void WebPluginContainerImpl::setWebLayer(WebLayer* layer) 294 { 295 if (m_webLayer == layer) 296 return; 297 298 // If anyone of the layers is null we need to switch between hardware 299 // and software compositing. 300 if (!m_webLayer || !layer) 301 m_element->scheduleLayerUpdate(); 302 if (m_webLayer) 303 GraphicsLayer::unregisterContentsLayer(m_webLayer); 304 if (layer) 305 GraphicsLayer::registerContentsLayer(layer); 306 m_webLayer = layer; 307 } 308 309 bool WebPluginContainerImpl::supportsPaginatedPrint() const 310 { 311 return m_webPlugin->supportsPaginatedPrint(); 312 } 313 314 bool WebPluginContainerImpl::isPrintScalingDisabled() const 315 { 316 return m_webPlugin->isPrintScalingDisabled(); 317 } 318 319 int WebPluginContainerImpl::printBegin(const WebPrintParams& printParams) const 320 { 321 return m_webPlugin->printBegin(printParams); 322 } 323 324 bool WebPluginContainerImpl::printPage(int pageNumber, 325 WebCore::GraphicsContext* gc) 326 { 327 gc->save(); 328 WebCanvas* canvas = gc->canvas(); 329 bool ret = m_webPlugin->printPage(pageNumber, canvas); 330 gc->restore(); 331 return ret; 332 } 333 334 void WebPluginContainerImpl::printEnd() 335 { 336 m_webPlugin->printEnd(); 337 } 338 339 void WebPluginContainerImpl::copy() 340 { 341 if (!m_webPlugin->hasSelection()) 342 return; 343 344 blink::Platform::current()->clipboard()->writeHTML(m_webPlugin->selectionAsMarkup(), WebURL(), m_webPlugin->selectionAsText(), false); 345 } 346 347 bool WebPluginContainerImpl::executeEditCommand(const WebString& name) 348 { 349 if (m_webPlugin->executeEditCommand(name)) 350 return true; 351 352 if (name != "Copy") 353 return false; 354 355 copy(); 356 return true; 357 } 358 359 bool WebPluginContainerImpl::executeEditCommand(const WebString& name, const WebString& value) 360 { 361 return m_webPlugin->executeEditCommand(name, value); 362 } 363 364 WebElement WebPluginContainerImpl::element() 365 { 366 return WebElement(m_element); 367 } 368 369 void WebPluginContainerImpl::invalidate() 370 { 371 Widget::invalidate(); 372 } 373 374 void WebPluginContainerImpl::invalidateRect(const WebRect& rect) 375 { 376 invalidateRect(static_cast<IntRect>(rect)); 377 } 378 379 void WebPluginContainerImpl::scrollRect(int dx, int dy, const WebRect& rect) 380 { 381 Widget* parentWidget = parent(); 382 if (parentWidget->isFrameView()) { 383 FrameView* parentFrameView = toFrameView(parentWidget); 384 if (!parentFrameView->isOverlapped()) { 385 IntRect damageRect = convertToContainingWindow(static_cast<IntRect>(rect)); 386 IntSize scrollDelta(dx, dy); 387 // scroll() only uses the second rectangle, clipRect, and ignores the first 388 // rectangle. 389 parent()->hostWindow()->scroll(scrollDelta, damageRect, damageRect); 390 return; 391 } 392 } 393 394 // Use slow scrolling instead. 395 invalidateRect(rect); 396 } 397 398 void WebPluginContainerImpl::reportGeometry() 399 { 400 if (!parent()) 401 return; 402 403 IntRect windowRect, clipRect; 404 Vector<IntRect> cutOutRects; 405 calculateGeometry(frameRect(), windowRect, clipRect, cutOutRects); 406 407 m_webPlugin->updateGeometry(windowRect, clipRect, cutOutRects, isVisible()); 408 409 if (m_scrollbarGroup) { 410 m_scrollbarGroup->scrollAnimator()->contentsResized(); 411 m_scrollbarGroup->setFrameRect(frameRect()); 412 } 413 } 414 415 void WebPluginContainerImpl::allowScriptObjects() 416 { 417 } 418 419 void WebPluginContainerImpl::clearScriptObjects() 420 { 421 Frame* frame = m_element->document().frame(); 422 if (!frame) 423 return; 424 frame->script().cleanupScriptObjectsForPlugin(this); 425 } 426 427 NPObject* WebPluginContainerImpl::scriptableObjectForElement() 428 { 429 return m_element->getNPObject(); 430 } 431 432 WebString WebPluginContainerImpl::executeScriptURL(const WebURL& url, bool popupsAllowed) 433 { 434 Frame* frame = m_element->document().frame(); 435 if (!frame) 436 return WebString(); 437 438 const KURL& kurl = url; 439 ASSERT(kurl.protocolIs("javascript")); 440 441 String script = decodeURLEscapeSequences( 442 kurl.string().substring(strlen("javascript:"))); 443 444 UserGestureIndicator gestureIndicator(popupsAllowed ? DefinitelyProcessingNewUserGesture : PossiblyProcessingUserGesture); 445 ScriptValue result = frame->script().executeScriptInMainWorldAndReturnValue(ScriptSourceCode(script)); 446 447 // Failure is reported as a null string. 448 String resultStr; 449 result.getString(resultStr); 450 return resultStr; 451 } 452 453 void WebPluginContainerImpl::loadFrameRequest(const WebURLRequest& request, const WebString& target, bool notifyNeeded, void* notifyData) 454 { 455 Frame* frame = m_element->document().frame(); 456 if (!frame || !frame->loader().documentLoader()) 457 return; // FIXME: send a notification in this case? 458 459 if (notifyNeeded) { 460 // FIXME: This is a bit of hack to allow us to observe completion of 461 // our frame request. It would be better to evolve FrameLoader to 462 // support a completion callback instead. 463 OwnPtr<WebPluginLoadObserver> observer = adoptPtr(new WebPluginLoadObserver(this, request.url(), notifyData)); 464 // FIXME: Calling get here is dangerous! What if observer is freed? 465 m_pluginLoadObservers.append(observer.get()); 466 WebDataSourceImpl::setNextPluginLoadObserver(observer.release()); 467 } 468 469 FrameLoadRequest frameRequest(frame->document(), request.toResourceRequest(), target); 470 UserGestureIndicator gestureIndicator(request.hasUserGesture() ? DefinitelyProcessingNewUserGesture : PossiblyProcessingUserGesture); 471 frame->loader().load(frameRequest); 472 } 473 474 void WebPluginContainerImpl::zoomLevelChanged(double zoomLevel) 475 { 476 WebViewImpl* view = WebViewImpl::fromPage(m_element->document().frame()->page()); 477 view->fullFramePluginZoomLevelChanged(zoomLevel); 478 } 479 480 bool WebPluginContainerImpl::isRectTopmost(const WebRect& rect) 481 { 482 Frame* frame = m_element->document().frame(); 483 if (!frame) 484 return false; 485 486 // hitTestResultAtPoint() takes a padding rectangle. 487 // FIXME: We'll be off by 1 when the width or height is even. 488 IntRect documentRect(x() + rect.x, y() + rect.y, rect.width, rect.height); 489 LayoutPoint center = documentRect.center(); 490 // Make the rect we're checking (the point surrounded by padding rects) contained inside the requested rect. (Note that -1/2 is 0.) 491 LayoutSize padding((documentRect.width() - 1) / 2, (documentRect.height() - 1) / 2); 492 HitTestResult result = frame->eventHandler().hitTestResultAtPoint(center, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent, padding); 493 const HitTestResult::NodeSet& nodes = result.rectBasedTestResult(); 494 if (nodes.size() != 1) 495 return false; 496 return nodes.first().get() == m_element; 497 } 498 499 void WebPluginContainerImpl::requestTouchEventType(TouchEventRequestType requestType) 500 { 501 if (m_touchEventRequestType == requestType) 502 return; 503 504 if (requestType != TouchEventRequestTypeNone && m_touchEventRequestType == TouchEventRequestTypeNone) 505 m_element->document().didAddTouchEventHandler(m_element); 506 else if (requestType == TouchEventRequestTypeNone && m_touchEventRequestType != TouchEventRequestTypeNone) 507 m_element->document().didRemoveTouchEventHandler(m_element); 508 m_touchEventRequestType = requestType; 509 } 510 511 void WebPluginContainerImpl::setWantsWheelEvents(bool wantsWheelEvents) 512 { 513 if (m_wantsWheelEvents == wantsWheelEvents) 514 return; 515 m_wantsWheelEvents = wantsWheelEvents; 516 if (Page* page = m_element->document().page()) { 517 if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) { 518 if (parent() && parent()->isFrameView()) 519 scrollingCoordinator->notifyLayoutUpdated(); 520 } 521 } 522 } 523 524 WebPoint WebPluginContainerImpl::windowToLocalPoint(const WebPoint& point) 525 { 526 ScrollView* view = toScrollView(parent()); 527 if (!view) 528 return point; 529 WebPoint windowPoint = view->windowToContents(point); 530 return roundedIntPoint(m_element->renderer()->absoluteToLocal(LayoutPoint(windowPoint), UseTransforms)); 531 } 532 533 WebPoint WebPluginContainerImpl::localToWindowPoint(const WebPoint& point) 534 { 535 ScrollView* view = toScrollView(parent()); 536 if (!view) 537 return point; 538 IntPoint absolutePoint = roundedIntPoint(m_element->renderer()->localToAbsolute(LayoutPoint(point), UseTransforms)); 539 return view->contentsToWindow(absolutePoint); 540 } 541 542 void WebPluginContainerImpl::didReceiveResponse(const ResourceResponse& response) 543 { 544 // Make sure that the plugin receives window geometry before data, or else 545 // plugins misbehave. 546 frameRectsChanged(); 547 548 WrappedResourceResponse urlResponse(response); 549 m_webPlugin->didReceiveResponse(urlResponse); 550 } 551 552 void WebPluginContainerImpl::didReceiveData(const char *data, int dataLength) 553 { 554 m_webPlugin->didReceiveData(data, dataLength); 555 } 556 557 void WebPluginContainerImpl::didFinishLoading() 558 { 559 m_webPlugin->didFinishLoading(); 560 } 561 562 void WebPluginContainerImpl::didFailLoading(const ResourceError& error) 563 { 564 m_webPlugin->didFailLoading(error); 565 } 566 567 WebLayer* WebPluginContainerImpl::platformLayer() const 568 { 569 return m_webLayer; 570 } 571 572 NPObject* WebPluginContainerImpl::scriptableObject() 573 { 574 return m_webPlugin->scriptableObject(); 575 } 576 577 bool WebPluginContainerImpl::getFormValue(String& value) 578 { 579 WebString webValue; 580 if (m_webPlugin->getFormValue(webValue)) { 581 value = webValue; 582 return true; 583 } 584 return false; 585 } 586 587 bool WebPluginContainerImpl::supportsKeyboardFocus() const 588 { 589 return m_webPlugin->supportsKeyboardFocus(); 590 } 591 592 bool WebPluginContainerImpl::supportsInputMethod() const 593 { 594 return m_webPlugin->supportsInputMethod(); 595 } 596 597 bool WebPluginContainerImpl::canProcessDrag() const 598 { 599 return m_webPlugin->canProcessDrag(); 600 } 601 602 bool WebPluginContainerImpl::wantsWheelEvents() 603 { 604 return m_wantsWheelEvents; 605 } 606 607 void WebPluginContainerImpl::willDestroyPluginLoadObserver(WebPluginLoadObserver* observer) 608 { 609 size_t pos = m_pluginLoadObservers.find(observer); 610 if (pos == kNotFound) 611 return; 612 m_pluginLoadObservers.remove(pos); 613 } 614 615 ScrollbarGroup* WebPluginContainerImpl::scrollbarGroup() 616 { 617 if (!m_scrollbarGroup) 618 m_scrollbarGroup = adoptPtr(new ScrollbarGroup(m_element->document().frame()->view(), frameRect())); 619 return m_scrollbarGroup.get(); 620 } 621 622 void WebPluginContainerImpl::willStartLiveResize() 623 { 624 if (m_scrollbarGroup) 625 m_scrollbarGroup->willStartLiveResize(); 626 } 627 628 void WebPluginContainerImpl::willEndLiveResize() 629 { 630 if (m_scrollbarGroup) 631 m_scrollbarGroup->willEndLiveResize(); 632 } 633 634 bool WebPluginContainerImpl::paintCustomOverhangArea(GraphicsContext* context, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect) 635 { 636 context->save(); 637 context->setFillColor(Color(0xCC, 0xCC, 0xCC)); 638 context->fillRect(intersection(horizontalOverhangArea, dirtyRect)); 639 context->fillRect(intersection(verticalOverhangArea, dirtyRect)); 640 context->restore(); 641 return true; 642 } 643 644 // Private methods ------------------------------------------------------------- 645 646 WebPluginContainerImpl::WebPluginContainerImpl(WebCore::HTMLPlugInElement* element, WebPlugin* webPlugin) 647 : m_element(element) 648 , m_webPlugin(webPlugin) 649 , m_webLayer(0) 650 , m_touchEventRequestType(TouchEventRequestTypeNone) 651 , m_wantsWheelEvents(false) 652 { 653 } 654 655 WebPluginContainerImpl::~WebPluginContainerImpl() 656 { 657 if (m_touchEventRequestType != TouchEventRequestTypeNone) 658 m_element->document().didRemoveTouchEventHandler(m_element); 659 660 for (size_t i = 0; i < m_pluginLoadObservers.size(); ++i) 661 m_pluginLoadObservers[i]->clearPluginContainer(); 662 m_webPlugin->destroy(); 663 if (m_webLayer) 664 GraphicsLayer::unregisterContentsLayer(m_webLayer); 665 } 666 667 void WebPluginContainerImpl::handleMouseEvent(MouseEvent* event) 668 { 669 ASSERT(parent()->isFrameView()); 670 671 if (event->isDragEvent()) { 672 if (m_webPlugin->canProcessDrag()) 673 handleDragEvent(event); 674 return; 675 } 676 677 // We cache the parent FrameView here as the plugin widget could be deleted 678 // in the call to HandleEvent. See http://b/issue?id=1362948 679 FrameView* parentView = toFrameView(parent()); 680 681 WebMouseEventBuilder webEvent(this, m_element->renderer(), *event); 682 if (webEvent.type == WebInputEvent::Undefined) 683 return; 684 685 if (event->type() == EventTypeNames::mousedown) 686 focusPlugin(); 687 688 if (m_scrollbarGroup) { 689 // This needs to be set before the other callbacks in this scope, since 690 // the scroll animator class might query the position in response. 691 m_scrollbarGroup->setLastMousePosition(IntPoint(event->x(), event->y())); 692 if (event->type() == EventTypeNames::mousemove) 693 m_scrollbarGroup->scrollAnimator()->mouseMovedInContentArea(); 694 else if (event->type() == EventTypeNames::mouseover) 695 m_scrollbarGroup->scrollAnimator()->mouseEnteredContentArea(); 696 else if (event->type() == EventTypeNames::mouseout) 697 m_scrollbarGroup->scrollAnimator()->mouseExitedContentArea(); 698 } 699 700 WebCursorInfo cursorInfo; 701 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo)) 702 event->setDefaultHandled(); 703 704 // A windowless plugin can change the cursor in response to a mouse move 705 // event. We need to reflect the changed cursor in the frame view as the 706 // mouse is moved in the boundaries of the windowless plugin. 707 Page* page = parentView->frame().page(); 708 if (!page) 709 return; 710 ChromeClientImpl* chromeClient = toChromeClientImpl(page->chrome().client()); 711 chromeClient->setCursorForPlugin(cursorInfo); 712 } 713 714 void WebPluginContainerImpl::handleDragEvent(MouseEvent* event) 715 { 716 ASSERT(event->isDragEvent()); 717 718 WebDragStatus dragStatus = WebDragStatusUnknown; 719 if (event->type() == EventTypeNames::dragenter) 720 dragStatus = WebDragStatusEnter; 721 else if (event->type() == EventTypeNames::dragleave) 722 dragStatus = WebDragStatusLeave; 723 else if (event->type() == EventTypeNames::dragover) 724 dragStatus = WebDragStatusOver; 725 else if (event->type() == EventTypeNames::drop) 726 dragStatus = WebDragStatusDrop; 727 728 if (dragStatus == WebDragStatusUnknown) 729 return; 730 731 Clipboard* clipboard = event->dataTransfer(); 732 WebDragData dragData = clipboard->dataObject(); 733 WebDragOperationsMask dragOperationMask = static_cast<WebDragOperationsMask>(clipboard->sourceOperation()); 734 WebPoint dragScreenLocation(event->screenX(), event->screenY()); 735 WebPoint dragLocation(event->absoluteLocation().x() - location().x(), event->absoluteLocation().y() - location().y()); 736 737 m_webPlugin->handleDragStatusUpdate(dragStatus, dragData, dragOperationMask, dragLocation, dragScreenLocation); 738 } 739 740 void WebPluginContainerImpl::handleWheelEvent(WheelEvent* event) 741 { 742 WebMouseWheelEventBuilder webEvent(this, m_element->renderer(), *event); 743 if (webEvent.type == WebInputEvent::Undefined) 744 return; 745 746 WebCursorInfo cursorInfo; 747 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo)) 748 event->setDefaultHandled(); 749 } 750 751 void WebPluginContainerImpl::handleKeyboardEvent(KeyboardEvent* event) 752 { 753 WebKeyboardEventBuilder webEvent(*event); 754 if (webEvent.type == WebInputEvent::Undefined) 755 return; 756 757 if (webEvent.type == WebInputEvent::KeyDown) { 758 #if OS(MACOSX) 759 if (webEvent.modifiers == WebInputEvent::MetaKey 760 #else 761 if (webEvent.modifiers == WebInputEvent::ControlKey 762 #endif 763 && webEvent.windowsKeyCode == VKEY_C 764 // Only copy if there's a selection, so that we only ever do this 765 // for Pepper plugins that support copying. Windowless NPAPI 766 // plugins will get the event as before. 767 && m_webPlugin->hasSelection()) { 768 copy(); 769 event->setDefaultHandled(); 770 return; 771 } 772 } 773 774 const WebInputEvent* currentInputEvent = WebViewImpl::currentInputEvent(); 775 776 // Copy stashed info over, and only copy here in order not to interfere 777 // the ctrl-c logic above. 778 if (currentInputEvent 779 && WebInputEvent::isKeyboardEventType(currentInputEvent->type)) { 780 webEvent.modifiers |= currentInputEvent->modifiers & 781 (WebInputEvent::CapsLockOn | WebInputEvent::NumLockOn); 782 } 783 784 // Give the client a chance to issue edit comamnds. 785 WebViewImpl* view = WebViewImpl::fromPage(m_element->document().frame()->page()); 786 if (m_webPlugin->supportsEditCommands() && view->client()) 787 view->client()->handleCurrentKeyboardEvent(); 788 789 WebCursorInfo cursorInfo; 790 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo)) 791 event->setDefaultHandled(); 792 } 793 794 void WebPluginContainerImpl::handleTouchEvent(TouchEvent* event) 795 { 796 switch (m_touchEventRequestType) { 797 case TouchEventRequestTypeNone: 798 return; 799 case TouchEventRequestTypeRaw: { 800 WebTouchEventBuilder webEvent(this, m_element->renderer(), *event); 801 if (webEvent.type == WebInputEvent::Undefined) 802 return; 803 804 if (event->type() == EventTypeNames::touchstart) 805 focusPlugin(); 806 807 WebCursorInfo cursorInfo; 808 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo)) 809 event->setDefaultHandled(); 810 // FIXME: Can a plugin change the cursor from a touch-event callback? 811 return; 812 } 813 case TouchEventRequestTypeSynthesizedMouse: 814 synthesizeMouseEventIfPossible(event); 815 return; 816 } 817 } 818 819 static inline bool gestureScrollHelper(ScrollbarGroup* scrollbarGroup, ScrollDirection positiveDirection, ScrollDirection negativeDirection, float delta) 820 { 821 if (!delta) 822 return false; 823 float absDelta = delta > 0 ? delta : -delta; 824 return scrollbarGroup->scroll(delta < 0 ? negativeDirection : positiveDirection, ScrollByPrecisePixel, absDelta); 825 } 826 827 void WebPluginContainerImpl::handleGestureEvent(GestureEvent* event) 828 { 829 WebGestureEventBuilder webEvent(this, m_element->renderer(), *event); 830 if (webEvent.type == WebInputEvent::Undefined) 831 return; 832 WebCursorInfo cursorInfo; 833 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo)) { 834 event->setDefaultHandled(); 835 return; 836 } 837 838 if (webEvent.type == WebInputEvent::GestureScrollUpdate || webEvent.type == WebInputEvent::GestureScrollUpdateWithoutPropagation) { 839 if (!m_scrollbarGroup) 840 return; 841 if (gestureScrollHelper(m_scrollbarGroup.get(), ScrollLeft, ScrollRight, webEvent.data.scrollUpdate.deltaX)) 842 event->setDefaultHandled(); 843 if (gestureScrollHelper(m_scrollbarGroup.get(), ScrollUp, ScrollDown, webEvent.data.scrollUpdate.deltaY)) 844 event->setDefaultHandled(); 845 } 846 // FIXME: Can a plugin change the cursor from a touch-event callback? 847 } 848 849 void WebPluginContainerImpl::synthesizeMouseEventIfPossible(TouchEvent* event) 850 { 851 WebMouseEventBuilder webEvent(this, m_element->renderer(), *event); 852 if (webEvent.type == WebInputEvent::Undefined) 853 return; 854 855 WebCursorInfo cursorInfo; 856 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo)) 857 event->setDefaultHandled(); 858 } 859 860 void WebPluginContainerImpl::focusPlugin() 861 { 862 Frame& containingFrame = toFrameView(parent())->frame(); 863 if (Page* currentPage = containingFrame.page()) 864 currentPage->focusController().setFocusedElement(m_element, &containingFrame); 865 else 866 containingFrame.document()->setFocusedElement(m_element); 867 } 868 869 void WebPluginContainerImpl::calculateGeometry(const IntRect& frameRect, 870 IntRect& windowRect, 871 IntRect& clipRect, 872 Vector<IntRect>& cutOutRects) 873 { 874 windowRect = toScrollView(parent())->contentsToWindow(frameRect); 875 876 // Calculate a clip-rect so that we don't overlap the scrollbars, etc. 877 clipRect = windowClipRect(); 878 clipRect.move(-windowRect.x(), -windowRect.y()); 879 880 getPluginOcclusions(m_element, this->parent(), frameRect, cutOutRects); 881 // Convert to the plugin position. 882 for (size_t i = 0; i < cutOutRects.size(); i++) 883 cutOutRects[i].move(-frameRect.x(), -frameRect.y()); 884 } 885 886 WebCore::IntRect WebPluginContainerImpl::windowClipRect() const 887 { 888 // Start by clipping to our bounds. 889 IntRect clipRect = 890 convertToContainingWindow(IntRect(0, 0, width(), height())); 891 892 // document().renderer() can be 0 when we receive messages from the 893 // plugins while we are destroying a frame. 894 // FIXME: Can we just check m_element->document().isActive() ? 895 if (m_element->renderer()->document().renderer()) { 896 // Take our element and get the clip rect from the enclosing layer and 897 // frame view. 898 clipRect.intersect( 899 m_element->document().view()->windowClipRectForFrameOwner(m_element, true)); 900 } 901 902 return clipRect; 903 } 904 905 } // namespace blink 906