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        invalidate();
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            // update() will schedule a repaint of the widget so ensure
817            // its knowledge of its position on the page is up to date.
818            platformWidget()->setGeometry(m_windowRect);
819            platformWidget()->update(rect);
820        }
821        return;
822    }
823
824    invalidateWindowlessPluginRect(rect);
825}
826
827void PluginView::invalidateRect(NPRect* rect)
828{
829    if (!rect) {
830        invalidate();
831        return;
832    }
833    IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top);
834    invalidateRect(r);
835}
836
837void PluginView::invalidateRegion(NPRegion region)
838{
839    Q_UNUSED(region);
840    invalidate();
841}
842
843void PluginView::forceRedraw()
844{
845    invalidate();
846}
847
848static Display *getPluginDisplay()
849{
850    // The plugin toolkit might run using a different X connection. At the moment, we only
851    // support gdk based plugins (like flash) that use a different X connection.
852    // The code below has the same effect as this one:
853    // Display *gdkDisplay = gdk_x11_display_get_xdisplay(gdk_display_get_default());
854    QLibrary library(QLatin1String("libgdk-x11-2.0"), 0);
855    if (!library.load())
856        return 0;
857
858    typedef void *(*gdk_display_get_default_ptr)();
859    gdk_display_get_default_ptr gdk_display_get_default = (gdk_display_get_default_ptr)library.resolve("gdk_display_get_default");
860    if (!gdk_display_get_default)
861        return 0;
862
863    typedef void *(*gdk_x11_display_get_xdisplay_ptr)(void *);
864    gdk_x11_display_get_xdisplay_ptr gdk_x11_display_get_xdisplay = (gdk_x11_display_get_xdisplay_ptr)library.resolve("gdk_x11_display_get_xdisplay");
865    if (!gdk_x11_display_get_xdisplay)
866        return 0;
867
868    return (Display*)gdk_x11_display_get_xdisplay(gdk_display_get_default());
869}
870
871static void getVisualAndColormap(int depth, Visual **visual, Colormap *colormap)
872{
873    *visual = 0;
874    *colormap = 0;
875
876#ifndef QT_NO_XRENDER
877    static const bool useXRender = qgetenv("QT_X11_NO_XRENDER").isNull(); // Should also check for XRender >= 0.5
878#else
879    static const bool useXRender = false;
880#endif
881
882    if (!useXRender && depth == 32)
883        return;
884
885    int nvi;
886    XVisualInfo templ;
887    templ.screen  = QX11Info::appScreen();
888    templ.depth   = depth;
889    templ.c_class = TrueColor;
890    XVisualInfo* xvi = XGetVisualInfo(QX11Info::display(), VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nvi);
891
892    if (!xvi)
893        return;
894
895#ifndef QT_NO_XRENDER
896    if (depth == 32) {
897        for (int idx = 0; idx < nvi; ++idx) {
898            XRenderPictFormat* format = XRenderFindVisualFormat(QX11Info::display(), xvi[idx].visual);
899            if (format->type == PictTypeDirect && format->direct.alphaMask) {
900                 *visual = xvi[idx].visual;
901                 break;
902            }
903         }
904    } else
905#endif // QT_NO_XRENDER
906        *visual = xvi[0].visual;
907
908    XFree(xvi);
909
910    if (*visual)
911        *colormap = XCreateColormap(QX11Info::display(), QX11Info::appRootWindow(), *visual, AllocNone);
912}
913
914bool PluginView::platformStart()
915{
916    ASSERT(m_isStarted);
917    ASSERT(m_status == PluginStatusLoadedSuccessfully);
918
919    if (m_plugin->pluginFuncs()->getvalue) {
920        PluginView::setCurrentPluginView(this);
921#if USE(JSC)
922        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
923#endif
924        setCallingPlugin(true);
925        m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed);
926        setCallingPlugin(false);
927        PluginView::setCurrentPluginView(0);
928    }
929
930    if (m_isWindowed) {
931        QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient();
932        if (m_needsXEmbed && client) {
933            setPlatformWidget(new PluginContainerQt(this, client->ownerWidget()));
934            // sync our XEmbed container window creation before sending the xid to plugins.
935            QApplication::syncX();
936        } else {
937            notImplemented();
938            m_status = PluginStatusCanNotLoadPlugin;
939            return false;
940        }
941    } else {
942        setPlatformWidget(0);
943        m_pluginDisplay = getPluginDisplay();
944
945#if USE(ACCELERATED_COMPOSITING) && !USE(TEXTURE_MAPPER)
946        if (m_parentFrame->page()->chrome()->client()->allowsAcceleratedCompositing()
947            && m_parentFrame->page()->settings()
948            && m_parentFrame->page()->settings()->acceleratedCompositingEnabled()) {
949            m_platformLayer = new PluginGraphicsLayerQt(this);
950            // Trigger layer computation in RenderLayerCompositor
951            m_element->setNeedsStyleRecalc(SyntheticStyleChange);
952        }
953#endif
954    }
955
956    // If the width and the height are not zero we show the PluginView.
957    if (!frameRect().isEmpty())
958        show();
959
960    NPSetWindowCallbackStruct* wsi = new NPSetWindowCallbackStruct();
961    wsi->type = 0;
962
963    if (m_isWindowed) {
964        const QX11Info* x11Info = &platformPluginWidget()->x11Info();
965
966        wsi->display = x11Info->display();
967        wsi->visual = (Visual*)x11Info->visual();
968        wsi->depth = x11Info->depth();
969        wsi->colormap = x11Info->colormap();
970
971        m_npWindow.type = NPWindowTypeWindow;
972        m_npWindow.window = (void*)platformPluginWidget()->winId();
973        m_npWindow.width = -1;
974        m_npWindow.height = -1;
975    } else {
976        const QX11Info* x11Info = &QApplication::desktop()->x11Info();
977
978        if (x11Info->depth() == 32 || !m_plugin->quirks().contains(PluginQuirkRequiresDefaultScreenDepth)) {
979            getVisualAndColormap(32, &m_visual, &m_colormap);
980            wsi->depth = 32;
981        }
982
983        if (!m_visual) {
984            getVisualAndColormap(x11Info->depth(), &m_visual, &m_colormap);
985            wsi->depth = x11Info->depth();
986        }
987
988        wsi->display = x11Info->display();
989        wsi->visual = m_visual;
990        wsi->colormap = m_colormap;
991
992        m_npWindow.type = NPWindowTypeDrawable;
993        m_npWindow.window = 0; // Not used?
994        m_npWindow.x = 0;
995        m_npWindow.y = 0;
996        m_npWindow.width = -1;
997        m_npWindow.height = -1;
998    }
999
1000    m_npWindow.ws_info = wsi;
1001
1002    if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall))) {
1003        updatePluginWidget();
1004        setNPWindowIfNeeded();
1005    }
1006
1007    return true;
1008}
1009
1010void PluginView::platformDestroy()
1011{
1012    if (platformPluginWidget())
1013        delete platformPluginWidget();
1014
1015    if (m_drawable)
1016        XFreePixmap(QX11Info::display(), m_drawable);
1017
1018    if (m_colormap)
1019        XFreeColormap(QX11Info::display(), m_colormap);
1020}
1021
1022void PluginView::halt()
1023{
1024}
1025
1026void PluginView::restart()
1027{
1028}
1029
1030#if USE(ACCELERATED_COMPOSITING)
1031PlatformLayer* PluginView::platformLayer() const
1032{
1033    return m_platformLayer.get();
1034}
1035#endif
1036
1037} // namespace WebCore
1038