PluginViewQt.cpp revision 2bde8e466a4451c7319e3a072d118917957d6554
1/* 2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Collabora Ltd. All rights reserved. 4 * Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "config.h" 29#include "PluginView.h" 30 31#if USE(JSC) 32#include "BridgeJSC.h" 33#endif 34#include "Chrome.h" 35#include "ChromeClient.h" 36#include "Document.h" 37#include "DocumentLoader.h" 38#include "Element.h" 39#include "FloatPoint.h" 40#include "FocusController.h" 41#include "Frame.h" 42#include "FrameLoadRequest.h" 43#include "FrameLoader.h" 44#include "FrameTree.h" 45#include "FrameView.h" 46#include "GraphicsContext.h" 47#include "HTMLNames.h" 48#include "HTMLPlugInElement.h" 49#include "HostWindow.h" 50#include "IFrameShimSupport.h" 51#include "Image.h" 52#if USE(JSC) 53#include "JSDOMBinding.h" 54#endif 55#include "KeyboardEvent.h" 56#include "MouseEvent.h" 57#include "NotImplemented.h" 58#include "Page.h" 59#include "PlatformMouseEvent.h" 60#include "PlatformKeyboardEvent.h" 61#include "PluginContainerQt.h" 62#include "PluginDebug.h" 63#include "PluginPackage.h" 64#include "PluginMainThreadScheduler.h" 65#include "QWebPageClient.h" 66#include "RenderLayer.h" 67#include "ScriptController.h" 68#include "Settings.h" 69#include "npruntime_impl.h" 70#include "qwebpage_p.h" 71#if USE(JSC) 72#include "runtime_root.h" 73#endif 74 75#include <QApplication> 76#include <QDesktopWidget> 77#include <QGraphicsWidget> 78#include <QKeyEvent> 79#include <QPainter> 80#include <QStyleOptionGraphicsItem> 81#include <QWidget> 82#include <QX11Info> 83#include <X11/X.h> 84#ifndef QT_NO_XRENDER 85#define Bool int 86#define Status int 87#include <X11/extensions/Xrender.h> 88#endif 89#include <runtime/JSLock.h> 90#include <runtime/JSValue.h> 91 92using JSC::ExecState; 93#if USE(JSC) 94using JSC::Interpreter; 95#endif 96using JSC::JSLock; 97using JSC::JSObject; 98using JSC::UString; 99 100using std::min; 101 102using namespace WTF; 103 104namespace WebCore { 105 106using namespace HTMLNames; 107 108#if USE(ACCELERATED_COMPOSITING) 109// Qt's GraphicsLayer (GraphicsLayerQt) requires layers to be QGraphicsWidgets 110class PluginGraphicsLayerQt : public QGraphicsWidget { 111public: 112 PluginGraphicsLayerQt(PluginView* view) : m_view(view) { } 113 ~PluginGraphicsLayerQt() { } 114 115 void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0) 116 { 117 Q_UNUSED(widget); 118 m_view->paintUsingXPixmap(painter, option->exposedRect.toRect()); 119 } 120 121private: 122 PluginView* m_view; 123}; 124#endif 125 126void PluginView::updatePluginWidget() 127{ 128 if (!parent()) 129 return; 130 131 ASSERT(parent()->isFrameView()); 132 FrameView* frameView = static_cast<FrameView*>(parent()); 133 134 IntRect oldWindowRect = m_windowRect; 135 IntRect oldClipRect = m_clipRect; 136 137 m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size()); 138 m_clipRect = windowClipRect(); 139 m_clipRect.move(-m_windowRect.x(), -m_windowRect.y()); 140 141 if (m_windowRect == oldWindowRect && m_clipRect == oldClipRect) 142 return; 143 144 // The plugin had a zero width or height before but was resized, we need to show it again. 145 if (oldWindowRect.isEmpty()) 146 show(); 147 148 if (!m_isWindowed && m_windowRect.size() != oldWindowRect.size()) { 149#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) 150 // On Maemo5, Flash always renders to 16-bit buffer 151 if (m_renderToImage) 152 m_image = QImage(m_windowRect.width(), m_windowRect.height(), QImage::Format_RGB16); 153 else 154#endif 155 { 156 if (m_drawable) 157 XFreePixmap(QX11Info::display(), m_drawable); 158 159 m_drawable = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), m_windowRect.width(), m_windowRect.height(), 160 ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth); 161 QApplication::syncX(); // make sure that the server knows about the Drawable 162 } 163 } 164 165 // do not call setNPWindowIfNeeded immediately, will be called on paint() 166 m_hasPendingGeometryChange = true; 167 168 // (i) in order to move/resize the plugin window at the same time as the 169 // rest of frame during e.g. scrolling, we set the window geometry 170 // in the paint() function, but as paint() isn't called when the 171 // plugin window is outside the frame which can be caused by a 172 // scroll, we need to move/resize immediately. 173 // (ii) if we are running layout tests from DRT, paint() won't ever get called 174 // so we need to call setNPWindowIfNeeded() if window geometry has changed 175 if (!m_windowRect.intersects(frameView->frameRect()) 176 || (QWebPagePrivate::drtRun && platformPluginWidget() && (m_windowRect != oldWindowRect || m_clipRect != oldClipRect))) 177 setNPWindowIfNeeded(); 178 179 if (!m_platformLayer) { 180 // Make sure we get repainted afterwards. This is necessary for downward 181 // scrolling to move the plugin widget properly. 182 // Note that we don't invalidate the frameRect() here. This is because QWebFrame::renderRelativeCoords() 183 // imitates ScrollView and adds the scroll offset back on to the rect we damage here (making the co-ordinates absolute 184 // to the frame again) before passing it to FrameView. 185 frameView->invalidateRect(m_windowRect); 186 } 187} 188 189void PluginView::setFocus(bool focused) 190{ 191 if (platformPluginWidget()) { 192 if (focused) 193 platformPluginWidget()->setFocus(Qt::OtherFocusReason); 194 } else { 195 Widget::setFocus(focused); 196 } 197} 198 199void PluginView::show() 200{ 201 Q_ASSERT(platformPluginWidget() == platformWidget()); 202 Widget::show(); 203} 204 205void PluginView::hide() 206{ 207 Q_ASSERT(platformPluginWidget() == platformWidget()); 208 Widget::hide(); 209} 210 211#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) 212void PluginView::paintUsingImageSurfaceExtension(QPainter* painter, const IntRect& exposedRect) 213{ 214 NPImageExpose imageExpose; 215 QPoint offset; 216 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); 217 const bool surfaceHasUntransformedContents = client && qobject_cast<QWidget*>(client->pluginParent()); 218 219 QPaintDevice* surface = QPainter::redirected(painter->device(), &offset); 220 221 // If the surface is a QImage, we can render directly into it 222 if (surfaceHasUntransformedContents && surface && surface->devType() == QInternal::Image) { 223 QImage* image = static_cast<QImage*>(surface); 224 offset = -offset; // negating the offset gives us the offset of the view within the surface 225 imageExpose.data = reinterpret_cast<char*>(image->bits()); 226 imageExpose.dataSize.width = image->width(); 227 imageExpose.dataSize.height = image->height(); 228 imageExpose.stride = image->bytesPerLine(); 229 imageExpose.depth = image->depth(); // this is guaranteed to be 16 on Maemo5 230 imageExpose.translateX = offset.x() + m_windowRect.x(); 231 imageExpose.translateY = offset.y() + m_windowRect.y(); 232 imageExpose.scaleX = 1; 233 imageExpose.scaleY = 1; 234 } else { 235 if (m_isTransparent) { 236 // On Maemo5, Flash expects the buffer to contain the contents that are below it. 237 // We don't support transparency for non-raster graphicssystem, so clean the image 238 // before giving to Flash. 239 QPainter imagePainter(&m_image); 240 imagePainter.fillRect(exposedRect, Qt::white); 241 } 242 243 imageExpose.data = reinterpret_cast<char*>(m_image.bits()); 244 imageExpose.dataSize.width = m_image.width(); 245 imageExpose.dataSize.height = m_image.height(); 246 imageExpose.stride = m_image.bytesPerLine(); 247 imageExpose.depth = m_image.depth(); 248 imageExpose.translateX = 0; 249 imageExpose.translateY = 0; 250 imageExpose.scaleX = 1; 251 imageExpose.scaleY = 1; 252 } 253 imageExpose.x = exposedRect.x(); 254 imageExpose.y = exposedRect.y(); 255 imageExpose.width = exposedRect.width(); 256 imageExpose.height = exposedRect.height(); 257 258 XEvent xevent; 259 memset(&xevent, 0, sizeof(XEvent)); 260 XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose; 261 exposeEvent.type = GraphicsExpose; 262 exposeEvent.display = 0; 263 exposeEvent.drawable = reinterpret_cast<XID>(&imageExpose); 264 exposeEvent.x = exposedRect.x(); 265 exposeEvent.y = exposedRect.y(); 266 exposeEvent.width = exposedRect.width(); 267 exposeEvent.height = exposedRect.height(); 268 269 dispatchNPEvent(xevent); 270 271 if (!surfaceHasUntransformedContents || !surface || surface->devType() != QInternal::Image) 272 painter->drawImage(QPoint(frameRect().x() + exposedRect.x(), frameRect().y() + exposedRect.y()), m_image, exposedRect); 273} 274#endif 275 276void PluginView::paintUsingXPixmap(QPainter* painter, const QRect &exposedRect) 277{ 278 QPixmap qtDrawable = QPixmap::fromX11Pixmap(m_drawable, QPixmap::ExplicitlyShared); 279 const int drawableDepth = ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth; 280 ASSERT(drawableDepth == qtDrawable.depth()); 281 const bool syncX = m_pluginDisplay && m_pluginDisplay != QX11Info::display(); 282 283 // When printing, Qt uses a QPicture to capture the output in preview mode. The 284 // QPicture holds a reference to the X Pixmap. As a result, the print preview would 285 // update itself when the X Pixmap changes. To prevent this, we create a copy. 286 if (m_element->document()->printing()) 287 qtDrawable = qtDrawable.copy(); 288 289 if (m_isTransparent && drawableDepth != 32) { 290 // Attempt content propagation for drawable with no alpha by copying over from the backing store 291 QPoint offset; 292 QPaintDevice* backingStoreDevice = QPainter::redirected(painter->device(), &offset); 293 offset = -offset; // negating the offset gives us the offset of the view within the backing store pixmap 294 295 const bool hasValidBackingStore = backingStoreDevice && backingStoreDevice->devType() == QInternal::Pixmap; 296 QPixmap* backingStorePixmap = static_cast<QPixmap*>(backingStoreDevice); 297 298 // We cannot grab contents from the backing store when painting on QGraphicsView items 299 // (because backing store contents are already transformed). What we really mean to do 300 // here is to check if we are painting on QWebView, but let's be a little permissive :) 301 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); 302 const bool backingStoreHasUntransformedContents = client && qobject_cast<QWidget*>(client->pluginParent()); 303 304 if (hasValidBackingStore && backingStorePixmap->depth() == drawableDepth 305 && backingStoreHasUntransformedContents) { 306 GC gc = XDefaultGC(QX11Info::display(), QX11Info::appScreen()); 307 XCopyArea(QX11Info::display(), backingStorePixmap->handle(), m_drawable, gc, 308 offset.x() + m_windowRect.x() + exposedRect.x(), offset.y() + m_windowRect.y() + exposedRect.y(), 309 exposedRect.width(), exposedRect.height(), exposedRect.x(), exposedRect.y()); 310 } else { // no backing store, clean the pixmap because the plugin thinks its transparent 311 QPainter painter(&qtDrawable); 312 painter.fillRect(exposedRect, Qt::white); 313 } 314 315 if (syncX) 316 QApplication::syncX(); 317 } 318 319 XEvent xevent; 320 memset(&xevent, 0, sizeof(XEvent)); 321 XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose; 322 exposeEvent.type = GraphicsExpose; 323 exposeEvent.display = QX11Info::display(); 324 exposeEvent.drawable = qtDrawable.handle(); 325 exposeEvent.x = exposedRect.x(); 326 exposeEvent.y = exposedRect.y(); 327 exposeEvent.width = exposedRect.x() + exposedRect.width(); // flash bug? it thinks width is the right in transparent mode 328 exposeEvent.height = exposedRect.y() + exposedRect.height(); // flash bug? it thinks height is the bottom in transparent mode 329 330 dispatchNPEvent(xevent); 331 332 if (syncX) 333 XSync(m_pluginDisplay, false); // sync changes by plugin 334 335 painter->drawPixmap(QPoint(exposedRect.x(), exposedRect.y()), qtDrawable, exposedRect); 336} 337 338void PluginView::paint(GraphicsContext* context, const IntRect& rect) 339{ 340 if (!m_isStarted) { 341 paintMissingPluginIcon(context, rect); 342 return; 343 } 344 345 if (context->paintingDisabled()) 346 return; 347 348 setNPWindowIfNeeded(); 349 350 if (m_isWindowed) 351 return; 352 353#if USE(ACCELERATED_COMPOSITING) 354 if (m_platformLayer) 355 return; 356#endif 357 358 if (!m_drawable 359#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) 360 && m_image.isNull() 361#endif 362 ) 363 return; 364 365 QPainter* painter = context->platformContext(); 366 IntRect exposedRect(rect); 367 exposedRect.intersect(frameRect()); 368 exposedRect.move(-frameRect().x(), -frameRect().y()); 369 370#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) 371 if (!m_image.isNull()) { 372 paintUsingImageSurfaceExtension(painter, exposedRect); 373 return; 374 } 375#endif 376 377 painter->translate(frameRect().x(), frameRect().y()); 378 paintUsingXPixmap(painter, exposedRect); 379 painter->translate(-frameRect().x(), -frameRect().y()); 380} 381 382// TODO: Unify across ports. 383bool PluginView::dispatchNPEvent(NPEvent& event) 384{ 385 if (!m_plugin->pluginFuncs()->event) 386 return false; 387 388 PluginView::setCurrentPluginView(this); 389#if USE(JSC) 390 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 391#endif 392 setCallingPlugin(true); 393 bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event); 394 setCallingPlugin(false); 395 PluginView::setCurrentPluginView(0); 396 397 return accepted; 398} 399 400void setSharedXEventFields(XEvent* xEvent, QWidget* ownerWidget) 401{ 402 xEvent->xany.serial = 0; // we are unaware of the last request processed by X Server 403 xEvent->xany.send_event = false; 404 xEvent->xany.display = QX11Info::display(); 405 // NOTE: event->xany.window doesn't always respond to the .window property of other XEvent's 406 // but does in the case of KeyPress, KeyRelease, ButtonPress, ButtonRelease, and MotionNotify 407 // events; thus, this is right: 408 xEvent->xany.window = ownerWidget ? ownerWidget->window()->handle() : 0; 409} 410 411void PluginView::initXEvent(XEvent* xEvent) 412{ 413 memset(xEvent, 0, sizeof(XEvent)); 414 415 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); 416 QWidget* ownerWidget = client ? client->ownerWidget() : 0; 417 setSharedXEventFields(xEvent, ownerWidget); 418} 419 420void setXKeyEventSpecificFields(XEvent* xEvent, KeyboardEvent* event) 421{ 422 const PlatformKeyboardEvent* keyEvent = event->keyEvent(); 423 424 xEvent->type = (event->type() == eventNames().keydownEvent) ? 2 : 3; // ints as Qt unsets KeyPress and KeyRelease 425 xEvent->xkey.root = QX11Info::appRootWindow(); 426 xEvent->xkey.subwindow = 0; // we have no child window 427 xEvent->xkey.time = event->timeStamp(); 428 xEvent->xkey.state = keyEvent->nativeModifiers(); 429 xEvent->xkey.keycode = keyEvent->nativeScanCode(); 430 431 // We may not have a nativeScanCode() if the key event is from DRT's eventsender. In that 432 // case fetch the XEvent's keycode from the event's text. The only 433 // place this keycode will be used is in webkit_test_plugin_handle_event(). 434 // FIXME: Create Qt API so that we can set the appropriate keycode in DRT EventSender instead. 435 if (QWebPagePrivate::drtRun && !xEvent->xkey.keycode) { 436 QKeyEvent* qKeyEvent = keyEvent->qtEvent(); 437 ASSERT(qKeyEvent); 438 QString keyText = qKeyEvent->text().left(1); 439 xEvent->xkey.keycode = XKeysymToKeycode(QX11Info::display(), XStringToKeysym(keyText.toUtf8().constData())); 440 } 441 442 xEvent->xkey.same_screen = true; 443 444 // NOTE: As the XEvents sent to the plug-in are synthesized and there is not a native window 445 // corresponding to the plug-in rectangle, some of the members of the XEvent structures are not 446 // set to their normal Xserver values. e.g. Key events don't have a position. 447 // source: https://developer.mozilla.org/en/NPEvent 448 xEvent->xkey.x = 0; 449 xEvent->xkey.y = 0; 450 xEvent->xkey.x_root = 0; 451 xEvent->xkey.y_root = 0; 452} 453 454void PluginView::handleKeyboardEvent(KeyboardEvent* event) 455{ 456 if (m_isWindowed) 457 return; 458 459 if (event->type() != eventNames().keydownEvent && event->type() != eventNames().keyupEvent) 460 return; 461 462 XEvent npEvent; 463 initXEvent(&npEvent); 464 setXKeyEventSpecificFields(&npEvent, event); 465 466 if (!dispatchNPEvent(npEvent)) 467 event->setDefaultHandled(); 468} 469 470static unsigned int inputEventState(MouseEvent* event) 471{ 472 unsigned int state = 0; 473 if (event->ctrlKey()) 474 state |= ControlMask; 475 if (event->shiftKey()) 476 state |= ShiftMask; 477 if (event->altKey()) 478 state |= Mod1Mask; 479 if (event->metaKey()) 480 state |= Mod4Mask; 481 return state; 482} 483 484static void setXButtonEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos) 485{ 486 XButtonEvent& xbutton = xEvent->xbutton; 487 xbutton.type = event->type() == eventNames().mousedownEvent ? ButtonPress : ButtonRelease; 488 xbutton.root = QX11Info::appRootWindow(); 489 xbutton.subwindow = 0; 490 xbutton.time = event->timeStamp(); 491 xbutton.x = postZoomPos.x(); 492 xbutton.y = postZoomPos.y(); 493 xbutton.x_root = event->screenX(); 494 xbutton.y_root = event->screenY(); 495 xbutton.state = inputEventState(event); 496 switch (event->button()) { 497 case MiddleButton: 498 xbutton.button = Button2; 499 break; 500 case RightButton: 501 xbutton.button = Button3; 502 break; 503 case LeftButton: 504 default: 505 xbutton.button = Button1; 506 break; 507 } 508 xbutton.same_screen = true; 509} 510 511static void setXMotionEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos) 512{ 513 XMotionEvent& xmotion = xEvent->xmotion; 514 xmotion.type = MotionNotify; 515 xmotion.root = QX11Info::appRootWindow(); 516 xmotion.subwindow = 0; 517 xmotion.time = event->timeStamp(); 518 xmotion.x = postZoomPos.x(); 519 xmotion.y = postZoomPos.y(); 520 xmotion.x_root = event->screenX(); 521 xmotion.y_root = event->screenY(); 522 xmotion.state = inputEventState(event); 523 xmotion.is_hint = NotifyNormal; 524 xmotion.same_screen = true; 525} 526 527static void setXCrossingEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos) 528{ 529 XCrossingEvent& xcrossing = xEvent->xcrossing; 530 xcrossing.type = event->type() == eventNames().mouseoverEvent ? EnterNotify : LeaveNotify; 531 xcrossing.root = QX11Info::appRootWindow(); 532 xcrossing.subwindow = 0; 533 xcrossing.time = event->timeStamp(); 534 xcrossing.x = postZoomPos.y(); 535 xcrossing.y = postZoomPos.x(); 536 xcrossing.x_root = event->screenX(); 537 xcrossing.y_root = event->screenY(); 538 xcrossing.state = inputEventState(event); 539 xcrossing.mode = NotifyNormal; 540 xcrossing.detail = NotifyDetailNone; 541 xcrossing.same_screen = true; 542 xcrossing.focus = false; 543} 544 545void PluginView::handleMouseEvent(MouseEvent* event) 546{ 547 if (m_isWindowed) 548 return; 549 550 if (event->button() == RightButton && m_plugin->quirks().contains(PluginQuirkIgnoreRightClickInWindowlessMode)) 551 return; 552 553 if (event->type() == eventNames().mousedownEvent) { 554 // Give focus to the plugin on click 555 if (Page* page = m_parentFrame->page()) 556 page->focusController()->setActive(true); 557 558 focusPluginElement(); 559 } 560 561 XEvent npEvent; 562 initXEvent(&npEvent); 563 564 IntPoint postZoomPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(event->absoluteLocation())); 565 566 if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent) 567 setXButtonEventSpecificFields(&npEvent, event, postZoomPos); 568 else if (event->type() == eventNames().mousemoveEvent) 569 setXMotionEventSpecificFields(&npEvent, event, postZoomPos); 570 else if (event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mouseoverEvent) 571 setXCrossingEventSpecificFields(&npEvent, event, postZoomPos); 572 else 573 return; 574 575 if (!dispatchNPEvent(npEvent)) 576 event->setDefaultHandled(); 577} 578 579void PluginView::handleFocusInEvent() 580{ 581 XEvent npEvent; 582 initXEvent(&npEvent); 583 584 XFocusChangeEvent& event = npEvent.xfocus; 585 event.type = 9; /* int as Qt unsets FocusIn */ 586 event.mode = NotifyNormal; 587 event.detail = NotifyDetailNone; 588 589 dispatchNPEvent(npEvent); 590} 591 592void PluginView::handleFocusOutEvent() 593{ 594 XEvent npEvent; 595 initXEvent(&npEvent); 596 597 XFocusChangeEvent& event = npEvent.xfocus; 598 event.type = 10; /* int as Qt unsets FocusOut */ 599 event.mode = NotifyNormal; 600 event.detail = NotifyDetailNone; 601 602 dispatchNPEvent(npEvent); 603} 604 605void PluginView::setParent(ScrollView* parent) 606{ 607 Widget::setParent(parent); 608 609 if (parent) 610 init(); 611} 612 613void PluginView::setNPWindowRect(const IntRect&) 614{ 615 if (!m_isWindowed) 616 setNPWindowIfNeeded(); 617} 618 619void PluginView::setNPWindowIfNeeded() 620{ 621 if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow) 622 return; 623 624 // If the plugin didn't load sucessfully, no point in calling setwindow 625 if (m_status != PluginStatusLoadedSuccessfully) 626 return; 627 628 // On Unix, only call plugin if it's full-page or windowed 629 if (m_mode != NP_FULL && m_mode != NP_EMBED) 630 return; 631 632 // Check if the platformPluginWidget still exists 633 if (m_isWindowed && !platformPluginWidget()) 634 return; 635 636 if (!m_hasPendingGeometryChange) 637 return; 638 m_hasPendingGeometryChange = false; 639 640 if (m_isWindowed) { 641 platformPluginWidget()->setGeometry(m_windowRect); 642 643 // Cut out areas of the plugin occluded by iframe shims 644 Vector<IntRect> cutOutRects; 645 QRegion clipRegion = QRegion(m_clipRect); 646 getPluginOcclusions(m_element, this->parent(), frameRect(), cutOutRects); 647 for (size_t i = 0; i < cutOutRects.size(); i++) { 648 cutOutRects[i].move(-frameRect().x(), -frameRect().y()); 649 clipRegion = clipRegion.subtracted(QRegion(cutOutRects[i])); 650 } 651 // if setMask is set with an empty QRegion, no clipping will 652 // be performed, so in that case we hide the plugin view 653 platformPluginWidget()->setVisible(!clipRegion.isEmpty()); 654 platformPluginWidget()->setMask(clipRegion); 655 656 m_npWindow.x = m_windowRect.x(); 657 m_npWindow.y = m_windowRect.y(); 658 } else { 659 m_npWindow.x = 0; 660 m_npWindow.y = 0; 661 } 662 663 // If the width or height are null, set the clipRect to null, indicating that 664 // the plugin is not visible/scrolled out. 665 if (!m_clipRect.width() || !m_clipRect.height()) { 666 m_npWindow.clipRect.left = 0; 667 m_npWindow.clipRect.right = 0; 668 m_npWindow.clipRect.top = 0; 669 m_npWindow.clipRect.bottom = 0; 670 } else { 671 // Clipping rectangle of the plug-in; the origin is the top left corner of the drawable or window. 672 m_npWindow.clipRect.left = m_npWindow.x + m_clipRect.x(); 673 m_npWindow.clipRect.top = m_npWindow.y + m_clipRect.y(); 674 m_npWindow.clipRect.right = m_npWindow.x + m_clipRect.x() + m_clipRect.width(); 675 m_npWindow.clipRect.bottom = m_npWindow.y + m_clipRect.y() + m_clipRect.height(); 676 } 677 678 if (m_plugin->quirks().contains(PluginQuirkDontCallSetWindowMoreThanOnce)) { 679 // FLASH WORKAROUND: Only set initially. Multiple calls to 680 // setNPWindow() cause the plugin to crash in windowed mode. 681 if (!m_isWindowed || m_npWindow.width == -1 || m_npWindow.height == -1) { 682 m_npWindow.width = m_windowRect.width(); 683 m_npWindow.height = m_windowRect.height(); 684 } 685 } else { 686 m_npWindow.width = m_windowRect.width(); 687 m_npWindow.height = m_windowRect.height(); 688 } 689 690 PluginView::setCurrentPluginView(this); 691#if USE(JSC) 692 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 693#endif 694 setCallingPlugin(true); 695 m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); 696 setCallingPlugin(false); 697 PluginView::setCurrentPluginView(0); 698} 699 700void PluginView::setParentVisible(bool visible) 701{ 702 if (isParentVisible() == visible) 703 return; 704 705 Widget::setParentVisible(visible); 706 707 if (isSelfVisible() && platformPluginWidget()) 708 platformPluginWidget()->setVisible(visible); 709} 710 711NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf) 712{ 713 String filename(buf, len); 714 715 if (filename.startsWith("file:///")) 716 filename = filename.substring(8); 717 718 long long size; 719 if (!getFileSize(filename, size)) 720 return NPERR_FILE_NOT_FOUND; 721 722 FILE* fileHandle = fopen((filename.utf8()).data(), "r"); 723 if (!fileHandle) 724 return NPERR_FILE_NOT_FOUND; 725 726 buffer.resize(size); 727 int bytesRead = fread(buffer.data(), 1, size, fileHandle); 728 729 fclose(fileHandle); 730 731 if (bytesRead <= 0) 732 return NPERR_FILE_NOT_FOUND; 733 734 return NPERR_NO_ERROR; 735} 736 737bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result) 738{ 739 switch (variable) { 740 case NPNVToolkit: 741 *static_cast<uint32_t*>(value) = 0; 742 *result = NPERR_NO_ERROR; 743 return true; 744 745 case NPNVSupportsXEmbedBool: 746 *static_cast<NPBool*>(value) = true; 747 *result = NPERR_NO_ERROR; 748 return true; 749 750 case NPNVjavascriptEnabledBool: 751 *static_cast<NPBool*>(value) = true; 752 *result = NPERR_NO_ERROR; 753 return true; 754 755 case NPNVSupportsWindowless: 756 *static_cast<NPBool*>(value) = true; 757 *result = NPERR_NO_ERROR; 758 return true; 759 760#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) 761 case NPNVSupportsWindowlessLocal: 762 *static_cast<NPBool*>(value) = true; 763 *result = NPERR_NO_ERROR; 764 return true; 765#endif 766 767 default: 768 return false; 769 } 770} 771 772bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result) 773{ 774 switch (variable) { 775 case NPNVxDisplay: 776 *(void **)value = QX11Info::display(); 777 *result = NPERR_NO_ERROR; 778 return true; 779 780 case NPNVxtAppContext: 781 *result = NPERR_GENERIC_ERROR; 782 return true; 783 784 case NPNVnetscapeWindow: { 785 void* w = reinterpret_cast<void*>(value); 786 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); 787 *((XID *)w) = client ? client->ownerWidget()->window()->winId() : 0; 788 *result = NPERR_NO_ERROR; 789 return true; 790 } 791 792 case NPNVToolkit: 793 if (m_plugin->quirks().contains(PluginQuirkRequiresGtkToolKit)) { 794 *((uint32_t *)value) = 2; 795 *result = NPERR_NO_ERROR; 796 return true; 797 } 798 return false; 799 800 default: 801 return false; 802 } 803} 804 805void PluginView::invalidateRect(const IntRect& rect) 806{ 807#if USE(ACCELERATED_COMPOSITING) && !USE(TEXTURE_MAPPER) 808 if (m_platformLayer) { 809 m_platformLayer->update(QRectF(rect)); 810 return; 811 } 812#endif 813 814 if (m_isWindowed) { 815 if (platformWidget()) 816 platformWidget()->update(rect); 817 return; 818 } 819 820 invalidateWindowlessPluginRect(rect); 821} 822 823void PluginView::invalidateRect(NPRect* rect) 824{ 825 if (!rect) { 826 invalidate(); 827 return; 828 } 829 IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top); 830 invalidateRect(r); 831} 832 833void PluginView::invalidateRegion(NPRegion region) 834{ 835 Q_UNUSED(region); 836 invalidate(); 837} 838 839void PluginView::forceRedraw() 840{ 841 invalidate(); 842} 843 844static Display *getPluginDisplay() 845{ 846 // The plugin toolkit might run using a different X connection. At the moment, we only 847 // support gdk based plugins (like flash) that use a different X connection. 848 // The code below has the same effect as this one: 849 // Display *gdkDisplay = gdk_x11_display_get_xdisplay(gdk_display_get_default()); 850 QLibrary library(QLatin1String("libgdk-x11-2.0"), 0); 851 if (!library.load()) 852 return 0; 853 854 typedef void *(*gdk_display_get_default_ptr)(); 855 gdk_display_get_default_ptr gdk_display_get_default = (gdk_display_get_default_ptr)library.resolve("gdk_display_get_default"); 856 if (!gdk_display_get_default) 857 return 0; 858 859 typedef void *(*gdk_x11_display_get_xdisplay_ptr)(void *); 860 gdk_x11_display_get_xdisplay_ptr gdk_x11_display_get_xdisplay = (gdk_x11_display_get_xdisplay_ptr)library.resolve("gdk_x11_display_get_xdisplay"); 861 if (!gdk_x11_display_get_xdisplay) 862 return 0; 863 864 return (Display*)gdk_x11_display_get_xdisplay(gdk_display_get_default()); 865} 866 867static void getVisualAndColormap(int depth, Visual **visual, Colormap *colormap) 868{ 869 *visual = 0; 870 *colormap = 0; 871 872#ifndef QT_NO_XRENDER 873 static const bool useXRender = qgetenv("QT_X11_NO_XRENDER").isNull(); // Should also check for XRender >= 0.5 874#else 875 static const bool useXRender = false; 876#endif 877 878 if (!useXRender && depth == 32) 879 return; 880 881 int nvi; 882 XVisualInfo templ; 883 templ.screen = QX11Info::appScreen(); 884 templ.depth = depth; 885 templ.c_class = TrueColor; 886 XVisualInfo* xvi = XGetVisualInfo(QX11Info::display(), VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nvi); 887 888 if (!xvi) 889 return; 890 891#ifndef QT_NO_XRENDER 892 if (depth == 32) { 893 for (int idx = 0; idx < nvi; ++idx) { 894 XRenderPictFormat* format = XRenderFindVisualFormat(QX11Info::display(), xvi[idx].visual); 895 if (format->type == PictTypeDirect && format->direct.alphaMask) { 896 *visual = xvi[idx].visual; 897 break; 898 } 899 } 900 } else 901#endif // QT_NO_XRENDER 902 *visual = xvi[0].visual; 903 904 XFree(xvi); 905 906 if (*visual) 907 *colormap = XCreateColormap(QX11Info::display(), QX11Info::appRootWindow(), *visual, AllocNone); 908} 909 910bool PluginView::platformStart() 911{ 912 ASSERT(m_isStarted); 913 ASSERT(m_status == PluginStatusLoadedSuccessfully); 914 915 if (m_plugin->pluginFuncs()->getvalue) { 916 PluginView::setCurrentPluginView(this); 917#if USE(JSC) 918 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 919#endif 920 setCallingPlugin(true); 921 m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed); 922 setCallingPlugin(false); 923 PluginView::setCurrentPluginView(0); 924 } 925 926 if (m_isWindowed) { 927 QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient(); 928 if (m_needsXEmbed && client) { 929 setPlatformWidget(new PluginContainerQt(this, client->ownerWidget())); 930 // sync our XEmbed container window creation before sending the xid to plugins. 931 QApplication::syncX(); 932 } else { 933 notImplemented(); 934 m_status = PluginStatusCanNotLoadPlugin; 935 return false; 936 } 937 } else { 938 setPlatformWidget(0); 939 m_pluginDisplay = getPluginDisplay(); 940 941#if USE(ACCELERATED_COMPOSITING) && !USE(TEXTURE_MAPPER) 942 if (m_parentFrame->page()->chrome()->client()->allowsAcceleratedCompositing() 943 && m_parentFrame->page()->settings() 944 && m_parentFrame->page()->settings()->acceleratedCompositingEnabled()) { 945 m_platformLayer = new PluginGraphicsLayerQt(this); 946 // Trigger layer computation in RenderLayerCompositor 947 m_element->setNeedsStyleRecalc(SyntheticStyleChange); 948 } 949#endif 950 } 951 952 // If the width and the height are not zero we show the PluginView. 953 if (!frameRect().isEmpty()) 954 show(); 955 956 NPSetWindowCallbackStruct* wsi = new NPSetWindowCallbackStruct(); 957 wsi->type = 0; 958 959 if (m_isWindowed) { 960 const QX11Info* x11Info = &platformPluginWidget()->x11Info(); 961 962 wsi->display = x11Info->display(); 963 wsi->visual = (Visual*)x11Info->visual(); 964 wsi->depth = x11Info->depth(); 965 wsi->colormap = x11Info->colormap(); 966 967 m_npWindow.type = NPWindowTypeWindow; 968 m_npWindow.window = (void*)platformPluginWidget()->winId(); 969 m_npWindow.width = -1; 970 m_npWindow.height = -1; 971 } else { 972 const QX11Info* x11Info = &QApplication::desktop()->x11Info(); 973 974 if (x11Info->depth() == 32 || !m_plugin->quirks().contains(PluginQuirkRequiresDefaultScreenDepth)) { 975 getVisualAndColormap(32, &m_visual, &m_colormap); 976 wsi->depth = 32; 977 } 978 979 if (!m_visual) { 980 getVisualAndColormap(x11Info->depth(), &m_visual, &m_colormap); 981 wsi->depth = x11Info->depth(); 982 } 983 984 wsi->display = x11Info->display(); 985 wsi->visual = m_visual; 986 wsi->colormap = m_colormap; 987 988 m_npWindow.type = NPWindowTypeDrawable; 989 m_npWindow.window = 0; // Not used? 990 m_npWindow.x = 0; 991 m_npWindow.y = 0; 992 m_npWindow.width = -1; 993 m_npWindow.height = -1; 994 } 995 996 m_npWindow.ws_info = wsi; 997 998 if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall))) { 999 updatePluginWidget(); 1000 setNPWindowIfNeeded(); 1001 } 1002 1003 return true; 1004} 1005 1006void PluginView::platformDestroy() 1007{ 1008 if (platformPluginWidget()) 1009 delete platformPluginWidget(); 1010 1011 if (m_drawable) 1012 XFreePixmap(QX11Info::display(), m_drawable); 1013 1014 if (m_colormap) 1015 XFreeColormap(QX11Info::display(), m_colormap); 1016} 1017 1018void PluginView::halt() 1019{ 1020} 1021 1022void PluginView::restart() 1023{ 1024} 1025 1026#if USE(ACCELERATED_COMPOSITING) 1027PlatformLayer* PluginView::platformLayer() const 1028{ 1029 return m_platformLayer.get(); 1030} 1031#endif 1032 1033} // namespace WebCore 1034