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 "Chrome.h" 35 #include "ChromeClientImpl.h" 36 #include "PluginLayerChromium.h" 37 #include "WebClipboard.h" 38 #include "WebCursorInfo.h" 39 #include "WebDataSourceImpl.h" 40 #include "WebElement.h" 41 #include "WebInputEvent.h" 42 #include "WebInputEventConversion.h" 43 #include "WebKit.h" 44 #include "WebKitClient.h" 45 #include "WebPlugin.h" 46 #include "WebRect.h" 47 #include "WebString.h" 48 #include "WebURL.h" 49 #include "WebURLError.h" 50 #include "WebURLRequest.h" 51 #include "WebVector.h" 52 #include "WebViewImpl.h" 53 #include "WrappedResourceResponse.h" 54 55 #include "EventNames.h" 56 #include "FocusController.h" 57 #include "FormState.h" 58 #include "Frame.h" 59 #include "FrameLoadRequest.h" 60 #include "FrameView.h" 61 #include "GraphicsContext.h" 62 #include "HostWindow.h" 63 #include "HTMLFormElement.h" 64 #include "HTMLNames.h" 65 #include "HTMLPlugInElement.h" 66 #include "IFrameShimSupport.h" 67 #include "KeyboardCodes.h" 68 #include "KeyboardEvent.h" 69 #include "MouseEvent.h" 70 #include "Page.h" 71 #include "RenderBox.h" 72 #include "ScrollView.h" 73 #include "UserGestureIndicator.h" 74 #include "WheelEvent.h" 75 76 #if WEBKIT_USING_SKIA 77 #include "PlatformContextSkia.h" 78 #endif 79 80 using namespace WebCore; 81 82 namespace WebKit { 83 84 // Public methods -------------------------------------------------------------- 85 86 void WebPluginContainerImpl::setFrameRect(const IntRect& frameRect) 87 { 88 Widget::setFrameRect(frameRect); 89 reportGeometry(); 90 } 91 92 void WebPluginContainerImpl::paint(GraphicsContext* gc, const IntRect& damageRect) 93 { 94 if (gc->paintingDisabled()) 95 return; 96 97 if (!parent()) 98 return; 99 100 // Don't paint anything if the plugin doesn't intersect the damage rect. 101 if (!frameRect().intersects(damageRect)) 102 return; 103 104 gc->save(); 105 106 ASSERT(parent()->isFrameView()); 107 ScrollView* view = parent(); 108 109 // The plugin is positioned in window coordinates, so it needs to be painted 110 // in window coordinates. 111 IntPoint origin = view->windowToContents(IntPoint(0, 0)); 112 gc->translate(static_cast<float>(origin.x()), static_cast<float>(origin.y())); 113 114 #if WEBKIT_USING_SKIA 115 WebCanvas* canvas = gc->platformContext()->canvas(); 116 #elif WEBKIT_USING_CG 117 WebCanvas* canvas = gc->platformContext(); 118 #endif 119 120 IntRect windowRect = 121 IntRect(view->contentsToWindow(damageRect.location()), damageRect.size()); 122 m_webPlugin->paint(canvas, windowRect); 123 124 gc->restore(); 125 } 126 127 void WebPluginContainerImpl::invalidateRect(const IntRect& rect) 128 { 129 if (!parent()) 130 return; 131 132 RenderBox* renderer = toRenderBox(m_element->renderer()); 133 134 IntRect dirtyRect = rect; 135 dirtyRect.move(renderer->borderLeft() + renderer->paddingLeft(), 136 renderer->borderTop() + renderer->paddingTop()); 137 renderer->repaintRectangle(dirtyRect); 138 } 139 140 void WebPluginContainerImpl::setFocus(bool focused) 141 { 142 Widget::setFocus(focused); 143 m_webPlugin->updateFocus(focused); 144 } 145 146 void WebPluginContainerImpl::show() 147 { 148 setSelfVisible(true); 149 m_webPlugin->updateVisibility(true); 150 151 Widget::show(); 152 } 153 154 void WebPluginContainerImpl::hide() 155 { 156 setSelfVisible(false); 157 m_webPlugin->updateVisibility(false); 158 159 Widget::hide(); 160 } 161 162 void WebPluginContainerImpl::handleEvent(Event* event) 163 { 164 if (!m_webPlugin->acceptsInputEvents()) 165 return; 166 167 RefPtr<WebPluginContainerImpl> protector(this); 168 // The events we pass are defined at: 169 // http://devedge-temp.mozilla.org/library/manuals/2002/plugin/1.0/structures5.html#1000000 170 // Don't take the documentation as truth, however. There are many cases 171 // where mozilla behaves differently than the spec. 172 if (event->isMouseEvent()) 173 handleMouseEvent(static_cast<MouseEvent*>(event)); 174 else if (event->isWheelEvent()) 175 handleWheelEvent(static_cast<WheelEvent*>(event)); 176 else if (event->isKeyboardEvent()) 177 handleKeyboardEvent(static_cast<KeyboardEvent*>(event)); 178 179 // FIXME: it would be cleaner if Widget::handleEvent returned true/false and 180 // HTMLPluginElement called setDefaultHandled or defaultEventHandler. 181 if (!event->defaultHandled()) 182 m_element->Node::defaultEventHandler(event); 183 } 184 185 void WebPluginContainerImpl::frameRectsChanged() 186 { 187 Widget::frameRectsChanged(); 188 reportGeometry(); 189 } 190 191 void WebPluginContainerImpl::widgetPositionsUpdated() 192 { 193 Widget::widgetPositionsUpdated(); 194 reportGeometry(); 195 } 196 197 void WebPluginContainerImpl::setParentVisible(bool parentVisible) 198 { 199 // We override this function to make sure that geometry updates are sent 200 // over to the plugin. For e.g. when a plugin is instantiated it does not 201 // have a valid parent. As a result the first geometry update from webkit 202 // is ignored. This function is called when the plugin eventually gets a 203 // parent. 204 205 if (isParentVisible() == parentVisible) 206 return; // No change. 207 208 Widget::setParentVisible(parentVisible); 209 if (!isSelfVisible()) 210 return; // This widget has explicitely been marked as not visible. 211 212 m_webPlugin->updateVisibility(isVisible()); 213 } 214 215 void WebPluginContainerImpl::setParent(ScrollView* view) 216 { 217 // We override this function so that if the plugin is windowed, we can call 218 // NPP_SetWindow at the first possible moment. This ensures that 219 // NPP_SetWindow is called before the manual load data is sent to a plugin. 220 // If this order is reversed, Flash won't load videos. 221 222 Widget::setParent(view); 223 if (view) 224 reportGeometry(); 225 } 226 227 bool WebPluginContainerImpl::supportsPaginatedPrint() const 228 { 229 return m_webPlugin->supportsPaginatedPrint(); 230 } 231 232 int WebPluginContainerImpl::printBegin(const IntRect& printableArea, 233 int printerDPI) const 234 { 235 return m_webPlugin->printBegin(printableArea, printerDPI); 236 } 237 238 bool WebPluginContainerImpl::printPage(int pageNumber, 239 WebCore::GraphicsContext* gc) 240 { 241 gc->save(); 242 #if WEBKIT_USING_SKIA 243 WebCanvas* canvas = gc->platformContext()->canvas(); 244 #elif WEBKIT_USING_CG 245 WebCanvas* canvas = gc->platformContext(); 246 #endif 247 bool ret = m_webPlugin->printPage(pageNumber, canvas); 248 gc->restore(); 249 return ret; 250 } 251 252 void WebPluginContainerImpl::printEnd() 253 { 254 return m_webPlugin->printEnd(); 255 } 256 257 void WebPluginContainerImpl::copy() 258 { 259 if (!m_webPlugin->hasSelection()) 260 return; 261 262 webKitClient()->clipboard()->writeHTML(m_webPlugin->selectionAsMarkup(), WebURL(), m_webPlugin->selectionAsText(), false); 263 } 264 265 WebElement WebPluginContainerImpl::element() 266 { 267 return WebElement(m_element); 268 } 269 270 void WebPluginContainerImpl::invalidate() 271 { 272 Widget::invalidate(); 273 } 274 275 void WebPluginContainerImpl::invalidateRect(const WebRect& rect) 276 { 277 invalidateRect(static_cast<IntRect>(rect)); 278 } 279 280 void WebPluginContainerImpl::scrollRect(int dx, int dy, const WebRect& rect) 281 { 282 Widget* parentWidget = parent(); 283 if (parentWidget->isFrameView()) { 284 FrameView* parentFrameView = static_cast<FrameView*>(parentWidget); 285 if (!parentFrameView->isOverlapped()) { 286 IntRect damageRect = convertToContainingWindow(static_cast<IntRect>(rect)); 287 IntSize scrollDelta(dx, dy); 288 // scroll() only uses the second rectangle, clipRect, and ignores the first 289 // rectangle. 290 parent()->hostWindow()->scroll(scrollDelta, damageRect, damageRect); 291 return; 292 } 293 } 294 295 // Use slow scrolling instead. 296 invalidateRect(rect); 297 } 298 299 void WebPluginContainerImpl::reportGeometry() 300 { 301 if (!parent()) 302 return; 303 304 IntRect windowRect, clipRect; 305 Vector<IntRect> cutOutRects; 306 calculateGeometry(frameRect(), windowRect, clipRect, cutOutRects); 307 308 m_webPlugin->updateGeometry(windowRect, clipRect, cutOutRects, isVisible()); 309 } 310 311 void WebPluginContainerImpl::setBackingTextureId(unsigned id) 312 { 313 #if USE(ACCELERATED_COMPOSITING) 314 unsigned currId = m_platformLayer->textureId(); 315 if (currId == id) 316 return; 317 318 m_platformLayer->setTextureId(id); 319 // If anyone of the IDs is zero we need to switch between hardware 320 // and software compositing. This is done by triggering a style recalc 321 // on the container element. 322 if (!(currId * id)) 323 m_element->setNeedsStyleRecalc(WebCore::SyntheticStyleChange); 324 #endif 325 } 326 327 void WebPluginContainerImpl::commitBackingTexture() 328 { 329 #if USE(ACCELERATED_COMPOSITING) 330 if (platformLayer()) 331 platformLayer()->setNeedsDisplay(); 332 #endif 333 } 334 335 void WebPluginContainerImpl::clearScriptObjects() 336 { 337 Frame* frame = m_element->document()->frame(); 338 if (!frame) 339 return; 340 frame->script()->cleanupScriptObjectsForPlugin(this); 341 } 342 343 NPObject* WebPluginContainerImpl::scriptableObjectForElement() 344 { 345 return m_element->getNPObject(); 346 } 347 348 WebString WebPluginContainerImpl::executeScriptURL(const WebURL& url, bool popupsAllowed) 349 { 350 Frame* frame = m_element->document()->frame(); 351 if (!frame) 352 return WebString(); 353 354 const KURL& kurl = url; 355 ASSERT(kurl.protocolIs("javascript")); 356 357 String script = decodeURLEscapeSequences( 358 kurl.string().substring(strlen("javascript:"))); 359 360 ScriptValue result = frame->script()->executeScript(script, popupsAllowed); 361 362 // Failure is reported as a null string. 363 String resultStr; 364 result.getString(resultStr); 365 return resultStr; 366 } 367 368 void WebPluginContainerImpl::loadFrameRequest( 369 const WebURLRequest& request, const WebString& target, bool notifyNeeded, void* notifyData) 370 { 371 Frame* frame = m_element->document()->frame(); 372 if (!frame) 373 return; // FIXME: send a notification in this case? 374 375 if (notifyNeeded) { 376 // FIXME: This is a bit of hack to allow us to observe completion of 377 // our frame request. It would be better to evolve FrameLoader to 378 // support a completion callback instead. 379 WebPluginLoadObserver* observer = 380 new WebPluginLoadObserver(this, request.url(), notifyData); 381 m_pluginLoadObservers.append(observer); 382 WebDataSourceImpl::setNextPluginLoadObserver(observer); 383 } 384 385 FrameLoadRequest frameRequest(frame->document()->securityOrigin(), 386 request.toResourceRequest(), target); 387 388 UserGestureIndicator gestureIndicator(request.hasUserGesture() ? 389 DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture); 390 391 frame->loader()->loadFrameRequest( 392 frameRequest, 393 false, // lock history 394 false, // lock back forward list 395 0, // event 396 0, // form state 397 SendReferrer); 398 } 399 400 void WebPluginContainerImpl::zoomLevelChanged(double zoomLevel) 401 { 402 WebViewImpl* view = WebViewImpl::fromPage(m_element->document()->frame()->page()); 403 view->fullFramePluginZoomLevelChanged(zoomLevel); 404 } 405 406 void WebPluginContainerImpl::didReceiveResponse(const ResourceResponse& response) 407 { 408 // Make sure that the plugin receives window geometry before data, or else 409 // plugins misbehave. 410 frameRectsChanged(); 411 412 WrappedResourceResponse urlResponse(response); 413 m_webPlugin->didReceiveResponse(urlResponse); 414 } 415 416 void WebPluginContainerImpl::didReceiveData(const char *data, int dataLength) 417 { 418 m_webPlugin->didReceiveData(data, dataLength); 419 } 420 421 void WebPluginContainerImpl::didFinishLoading() 422 { 423 m_webPlugin->didFinishLoading(); 424 } 425 426 void WebPluginContainerImpl::didFailLoading(const ResourceError& error) 427 { 428 m_webPlugin->didFailLoading(error); 429 } 430 431 NPObject* WebPluginContainerImpl::scriptableObject() 432 { 433 return m_webPlugin->scriptableObject(); 434 } 435 436 void WebPluginContainerImpl::willDestroyPluginLoadObserver(WebPluginLoadObserver* observer) 437 { 438 size_t pos = m_pluginLoadObservers.find(observer); 439 if (pos == notFound) 440 return; 441 m_pluginLoadObservers.remove(pos); 442 } 443 444 #if USE(ACCELERATED_COMPOSITING) 445 WebCore::LayerChromium* WebPluginContainerImpl::platformLayer() const 446 { 447 return m_platformLayer->textureId() ? m_platformLayer.get() : 0; 448 } 449 #endif 450 451 // Private methods ------------------------------------------------------------- 452 453 WebPluginContainerImpl::WebPluginContainerImpl(WebCore::HTMLPlugInElement* element, WebPlugin* webPlugin) 454 : WebCore::PluginViewBase(0) 455 , m_element(element) 456 , m_webPlugin(webPlugin) 457 #if USE(ACCELERATED_COMPOSITING) 458 , m_platformLayer(PluginLayerChromium::create(0)) 459 #endif 460 { 461 } 462 463 WebPluginContainerImpl::~WebPluginContainerImpl() 464 { 465 for (size_t i = 0; i < m_pluginLoadObservers.size(); ++i) 466 m_pluginLoadObservers[i]->clearPluginContainer(); 467 m_webPlugin->destroy(); 468 } 469 470 void WebPluginContainerImpl::handleMouseEvent(MouseEvent* event) 471 { 472 ASSERT(parent()->isFrameView()); 473 474 // We cache the parent FrameView here as the plugin widget could be deleted 475 // in the call to HandleEvent. See http://b/issue?id=1362948 476 FrameView* parentView = static_cast<FrameView*>(parent()); 477 478 WebMouseEventBuilder webEvent(this, *event); 479 if (webEvent.type == WebInputEvent::Undefined) 480 return; 481 482 if (event->type() == eventNames().mousedownEvent) { 483 Frame* containingFrame = parentView->frame(); 484 if (Page* currentPage = containingFrame->page()) 485 currentPage->focusController()->setFocusedNode(m_element, containingFrame); 486 else 487 containingFrame->document()->setFocusedNode(m_element); 488 } 489 490 WebCursorInfo cursorInfo; 491 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo)) 492 event->setDefaultHandled(); 493 494 // A windowless plugin can change the cursor in response to a mouse move 495 // event. We need to reflect the changed cursor in the frame view as the 496 // mouse is moved in the boundaries of the windowless plugin. 497 Page* page = parentView->frame()->page(); 498 if (!page) 499 return; 500 ChromeClientImpl* chromeClient = 501 static_cast<ChromeClientImpl*>(page->chrome()->client()); 502 chromeClient->setCursorForPlugin(cursorInfo); 503 } 504 505 void WebPluginContainerImpl::handleWheelEvent(WheelEvent* event) 506 { 507 WebMouseWheelEventBuilder webEvent(this, *event); 508 if (webEvent.type == WebInputEvent::Undefined) 509 return; 510 511 WebCursorInfo cursorInfo; 512 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo)) 513 event->setDefaultHandled(); 514 } 515 516 void WebPluginContainerImpl::handleKeyboardEvent(KeyboardEvent* event) 517 { 518 WebKeyboardEventBuilder webEvent(*event); 519 if (webEvent.type == WebInputEvent::Undefined) 520 return; 521 522 if (webEvent.type == WebInputEvent::KeyDown) { 523 #if defined(OS_MACOSX) 524 if (webEvent.modifiers == WebInputEvent::MetaKey 525 #else 526 if (webEvent.modifiers == WebInputEvent::ControlKey 527 #endif 528 && webEvent.windowsKeyCode == VKEY_C 529 // Only copy if there's a selection, so that we only ever do this 530 // for Pepper plugins that support copying. Windowless NPAPI 531 // plugins will get the event as before. 532 && m_webPlugin->hasSelection()) { 533 copy(); 534 event->setDefaultHandled(); 535 return; 536 } 537 } 538 539 const WebInputEvent* currentInputEvent = WebViewImpl::currentInputEvent(); 540 541 // Copy stashed info over, and only copy here in order not to interfere 542 // the ctrl-c logic above. 543 if (currentInputEvent 544 && WebInputEvent::isKeyboardEventType(currentInputEvent->type)) { 545 webEvent.modifiers |= currentInputEvent->modifiers & 546 (WebInputEvent::CapsLockOn | WebInputEvent::NumLockOn); 547 } 548 549 WebCursorInfo cursorInfo; 550 if (m_webPlugin->handleInputEvent(webEvent, cursorInfo)) 551 event->setDefaultHandled(); 552 } 553 554 void WebPluginContainerImpl::calculateGeometry(const IntRect& frameRect, 555 IntRect& windowRect, 556 IntRect& clipRect, 557 Vector<IntRect>& cutOutRects) 558 { 559 windowRect = IntRect( 560 parent()->contentsToWindow(frameRect.location()), frameRect.size()); 561 562 // Calculate a clip-rect so that we don't overlap the scrollbars, etc. 563 clipRect = windowClipRect(); 564 clipRect.move(-windowRect.x(), -windowRect.y()); 565 566 getPluginOcclusions(m_element, this->parent(), frameRect, cutOutRects); 567 // Convert to the plugin position. 568 for (size_t i = 0; i < cutOutRects.size(); i++) 569 cutOutRects[i].move(-frameRect.x(), -frameRect.y()); 570 } 571 572 WebCore::IntRect WebPluginContainerImpl::windowClipRect() const 573 { 574 // Start by clipping to our bounds. 575 IntRect clipRect = 576 convertToContainingWindow(IntRect(0, 0, width(), height())); 577 578 // document()->renderer() can be 0 when we receive messages from the 579 // plugins while we are destroying a frame. 580 if (m_element->renderer()->document()->renderer()) { 581 // Take our element and get the clip rect from the enclosing layer and 582 // frame view. 583 RenderLayer* layer = m_element->renderer()->enclosingLayer(); 584 clipRect.intersect( 585 m_element->document()->view()->windowClipRectForLayer(layer, true)); 586 } 587 588 return clipRect; 589 } 590 591 } // namespace WebKit 592