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#include "BridgeJSC.h"
32#include "Chrome.h"
33#include "ChromeClient.h"
34#include "Document.h"
35#include "DocumentLoader.h"
36#include "Element.h"
37#include "FloatPoint.h"
38#include "FocusController.h"
39#include "Frame.h"
40#include "FrameLoadRequest.h"
41#include "FrameLoader.h"
42#include "FrameTree.h"
43#include "FrameView.h"
44#include "GraphicsContext.h"
45#include "HTMLNames.h"
46#include "HTMLPlugInElement.h"
47#include "HostWindow.h"
48#include "IFrameShimSupport.h"
49#include "Image.h"
50#include "JSDOMBinding.h"
51#include "JSDOMWindowBase.h"
52#include "KeyboardEvent.h"
53#include "MouseEvent.h"
54#include "NotImplemented.h"
55#include "Page.h"
56#include "PlatformKeyboardEvent.h"
57#include "PlatformMouseEvent.h"
58#include "PluginDebug.h"
59#include "PluginMainThreadScheduler.h"
60#include "PluginPackage.h"
61#include "QWebPageClient.h"
62#include "RenderObject.h"
63#include "Settings.h"
64#include "npruntime_impl.h"
65#include "runtime_root.h"
66#include <QKeyEvent>
67#include <QPainter>
68#include <X11/X.h>
69#ifndef QT_NO_XRENDER
70#define Bool int
71#define Status int
72#include <X11/extensions/Xrender.h>
73#endif
74#include <runtime/JSCJSValue.h>
75#include <runtime/JSLock.h>
76
77#include "QtX11ImageConversion.h"
78#include <QGuiApplication>
79#include <QWindow>
80#include <qpa/qplatformnativeinterface.h>
81
82using JSC::ExecState;
83using JSC::Interpreter;
84using JSC::JSLock;
85using JSC::JSObject;
86
87using std::min;
88
89using namespace WTF;
90
91namespace WebCore {
92
93bool PluginView::s_isRunningUnderDRT = false;
94
95using namespace HTMLNames;
96
97struct X11Environment {
98    Display* display;
99    int screenID;
100    unsigned long rootWindowID;
101    int displayDepth;
102};
103
104static X11Environment x11Environment = { 0, 0, 0, 0 };
105
106static inline Display* x11Display() { return x11Environment.display; }
107static inline int x11Screen() { return x11Environment.screenID; }
108static inline unsigned long rootWindowID() { return x11Environment.rootWindowID; }
109static inline int displayDepth() { return x11Environment.displayDepth; }
110
111static inline void syncX()
112{
113    XSync(x11Display(), false);
114}
115
116QWebPageClient* PluginView::platformPageClient() const
117{
118    FrameView* view = m_parentFrame->view();
119    if (!view)
120        return 0;
121    HostWindow* hostWindow = view->hostWindow();
122    if (!hostWindow)
123        return 0;
124    return hostWindow->platformPageClient();
125}
126
127void PluginView::updatePluginWidget()
128{
129    if (!parent())
130        return;
131
132    ASSERT(parent()->isFrameView());
133    FrameView* frameView = toFrameView(parent());
134
135    IntRect oldWindowRect = m_windowRect;
136    IntRect oldClipRect = m_clipRect;
137
138    m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size());
139    m_clipRect = windowClipRect();
140    m_clipRect.move(-m_windowRect.x(), -m_windowRect.y());
141
142    if (m_windowRect == oldWindowRect && m_clipRect == oldClipRect)
143        return;
144
145    // The plugin had a zero width or height before but was resized, we need to show it again.
146    if (oldWindowRect.isEmpty())
147        show();
148
149    if (!m_isWindowed && m_windowRect.size() != oldWindowRect.size()) {
150        if (m_drawable)
151            XFreePixmap(x11Display(), m_drawable);
152
153        m_drawable = XCreatePixmap(x11Display(), rootWindowID(), m_windowRect.width(), m_windowRect.height(),
154                                   ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth);
155        syncX(); // make sure that the server knows about the Drawable
156    }
157
158    // do not call setNPWindowIfNeeded immediately, will be called on paint()
159    m_hasPendingGeometryChange = true;
160
161    // (i) in order to move/resize the plugin window at the same time as the
162    // rest of frame during e.g. scrolling, we set the window geometry
163    // in the paint() function, but as paint() isn't called when the
164    // plugin window is outside the frame which can be caused by a
165    // scroll, we need to move/resize immediately.
166    // (ii) if we are running layout tests from DRT, paint() won't ever get called
167    // so we need to call setNPWindowIfNeeded() if window geometry has changed
168    if (!m_windowRect.intersects(frameView->frameRect())
169        || (s_isRunningUnderDRT && platformPluginWidget() && (m_windowRect != oldWindowRect || m_clipRect != oldClipRect)))
170        setNPWindowIfNeeded();
171
172    if (!m_platformLayer) {
173        // Make sure we get repainted afterwards. This is necessary for downward
174        // scrolling to move the plugin widget properly.
175        // Note that we don't invalidate the frameRect() here. This is because QWebFrame::renderRelativeCoords()
176        // imitates ScrollView and adds the scroll offset back on to the rect we damage here (making the co-ordinates absolute
177        // to the frame again) before passing it to FrameView.
178        invalidate();
179    }
180}
181
182void PluginView::setFocus(bool focused)
183{
184    Widget::setFocus(focused);
185}
186
187void PluginView::show()
188{
189    Q_ASSERT(platformPluginWidget() == platformWidget());
190    Widget::show();
191}
192
193void PluginView::hide()
194{
195    Q_ASSERT(platformPluginWidget() == platformWidget());
196    Widget::hide();
197}
198
199static void setupGraphicsExposeEvent(Pixmap drawable, const QRect& exposedRect, XEvent& xevent)
200{
201    memset(&xevent, 0, sizeof(XEvent));
202    XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose;
203    exposeEvent.type = GraphicsExpose;
204    exposeEvent.display = x11Display();
205    exposeEvent.drawable = drawable;
206    exposeEvent.x = exposedRect.x();
207    exposeEvent.y = exposedRect.y();
208    exposeEvent.width = exposedRect.x() + exposedRect.width(); // flash bug? it thinks width is the right in transparent mode
209    exposeEvent.height = exposedRect.y() + exposedRect.height(); // flash bug? it thinks height is the bottom in transparent mode
210}
211
212void PluginView::paintUsingXPixmap(QPainter* painter, const QRect &exposedRect)
213{
214    bool shouldSyncX = m_pluginDisplay && m_pluginDisplay != x11Display();
215    XEvent xevent;
216
217    setupGraphicsExposeEvent(m_drawable, exposedRect, xevent);
218    dispatchNPEvent(xevent);
219
220    if (shouldSyncX)
221        XSync(m_pluginDisplay, false); // sync changes by plugin
222
223    XImage* xImage = XGetImage(x11Display(), m_drawable, exposedRect.x(), exposedRect.y(),
224                               exposedRect.width(), exposedRect.height(), ULONG_MAX, ZPixmap);
225    painter->drawImage(QPoint(exposedRect.x(), exposedRect.y()), qimageFromXImage(xImage));
226    XDestroyImage(xImage);
227}
228
229void PluginView::paint(GraphicsContext* context, const IntRect& rect)
230{
231    if (!m_isStarted) {
232        paintMissingPluginIcon(context, rect);
233        return;
234    }
235
236    if (context->paintingDisabled())
237        return;
238
239    setNPWindowIfNeeded();
240
241    if (m_isWindowed)
242        return;
243
244#if USE(ACCELERATED_COMPOSITING)
245    if (m_platformLayer)
246        return;
247#endif
248
249    if (!m_drawable)
250        return;
251
252    QPainter* painter = context->platformContext();
253    IntRect exposedRect(rect);
254    exposedRect.intersect(frameRect());
255    exposedRect.move(-frameRect().x(), -frameRect().y());
256
257    painter->translate(frameRect().x(), frameRect().y());
258    paintUsingXPixmap(painter, exposedRect);
259    painter->translate(-frameRect().x(), -frameRect().y());
260}
261
262// TODO: Unify across ports.
263bool PluginView::dispatchNPEvent(NPEvent& event)
264{
265    if (!m_plugin->pluginFuncs()->event)
266        return false;
267
268    bool shouldPop = false;
269
270    if (m_plugin->pluginFuncs()->version < NPVERS_HAS_POPUPS_ENABLED_STATE
271        && (event.type == ButtonRelease || event.type == 3 /*KeyRelease*/)) {
272        pushPopupsEnabledState(true);
273        shouldPop = true;
274    }
275
276    PluginView::setCurrentPluginView(this);
277    JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
278    setCallingPlugin(true);
279    bool accepted = !m_plugin->pluginFuncs()->event(m_instance, &event);
280    setCallingPlugin(false);
281    PluginView::setCurrentPluginView(0);
282
283    if (shouldPop)
284        popPopupsEnabledState();
285
286    return accepted;
287}
288
289void setSharedXEventFields(XEvent* xEvent, QWebPageClient* pageClient)
290{
291    xEvent->xany.serial = 0; // we are unaware of the last request processed by X Server
292    xEvent->xany.send_event = false;
293    xEvent->xany.display = x11Display();
294    // NOTE: event->xany.window doesn't always respond to the .window property of other XEvent's
295    // but does in the case of KeyPress, KeyRelease, ButtonPress, ButtonRelease, and MotionNotify
296    // events; thus, this is right:
297    QWindow* window = pageClient ? pageClient->ownerWindow() : 0;
298    xEvent->xany.window = window ? window->winId() : 0;
299}
300
301void PluginView::initXEvent(XEvent* xEvent)
302{
303    memset(xEvent, 0, sizeof(XEvent));
304
305    QWebPageClient* client = platformPageClient();
306    setSharedXEventFields(xEvent, client);
307}
308
309void PluginView::setXKeyEventSpecificFields(XEvent* xEvent, KeyboardEvent* event)
310{
311    const PlatformKeyboardEvent* keyEvent = event->keyEvent();
312
313    xEvent->type = (event->type() == eventNames().keydownEvent) ? 2 : 3; // ints as Qt unsets KeyPress and KeyRelease
314    xEvent->xkey.root = rootWindowID();
315    xEvent->xkey.subwindow = 0; // we have no child window
316    xEvent->xkey.time = event->timeStamp();
317    xEvent->xkey.state = keyEvent->nativeModifiers();
318    xEvent->xkey.keycode = keyEvent->nativeScanCode();
319
320    // We may not have a nativeScanCode() if the key event is from DRT's eventsender. In that
321    // case fetch the XEvent's keycode from the event's text. The only
322    // place this keycode will be used is in webkit_test_plugin_handle_event().
323    // FIXME: Create Qt API so that we can set the appropriate keycode in DRT EventSender instead.
324    if (s_isRunningUnderDRT && !xEvent->xkey.keycode) {
325        QKeyEvent* qKeyEvent = keyEvent->qtEvent();
326        ASSERT(qKeyEvent);
327        QString keyText = qKeyEvent->text().left(1);
328        xEvent->xkey.keycode = XKeysymToKeycode(x11Display(), XStringToKeysym(keyText.toUtf8().constData()));
329    }
330
331    xEvent->xkey.same_screen = true;
332
333    // NOTE: As the XEvents sent to the plug-in are synthesized and there is not a native window
334    // corresponding to the plug-in rectangle, some of the members of the XEvent structures are not
335    // set to their normal Xserver values. e.g. Key events don't have a position.
336    // source: https://developer.mozilla.org/en/NPEvent
337    xEvent->xkey.x = 0;
338    xEvent->xkey.y = 0;
339    xEvent->xkey.x_root = 0;
340    xEvent->xkey.y_root = 0;
341}
342
343void PluginView::handleKeyboardEvent(KeyboardEvent* event)
344{
345    if (m_isWindowed)
346        return;
347
348    if (event->type() != eventNames().keydownEvent && event->type() != eventNames().keyupEvent)
349        return;
350
351    XEvent npEvent;
352    initXEvent(&npEvent);
353    setXKeyEventSpecificFields(&npEvent, event);
354
355    if (dispatchNPEvent(npEvent))
356        event->setDefaultHandled();
357}
358
359static unsigned int inputEventState(MouseEvent* event)
360{
361    unsigned int state = 0;
362    if (event->ctrlKey())
363        state |= ControlMask;
364    if (event->shiftKey())
365        state |= ShiftMask;
366    if (event->altKey())
367        state |= Mod1Mask;
368    if (event->metaKey())
369        state |= Mod4Mask;
370    return state;
371}
372
373static void setXButtonEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos)
374{
375    XButtonEvent& xbutton = xEvent->xbutton;
376    xbutton.type = event->type() == eventNames().mousedownEvent ? ButtonPress : ButtonRelease;
377    xbutton.root = rootWindowID();
378    xbutton.subwindow = 0;
379    xbutton.time = event->timeStamp();
380    xbutton.x = postZoomPos.x();
381    xbutton.y = postZoomPos.y();
382    xbutton.x_root = event->screenX();
383    xbutton.y_root = event->screenY();
384    xbutton.state = inputEventState(event);
385    switch (event->button()) {
386    case MiddleButton:
387        xbutton.button = Button2;
388        break;
389    case RightButton:
390        xbutton.button = Button3;
391        break;
392    case LeftButton:
393    default:
394        xbutton.button = Button1;
395        break;
396    }
397    xbutton.same_screen = true;
398}
399
400static void setXMotionEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos)
401{
402    XMotionEvent& xmotion = xEvent->xmotion;
403    xmotion.type = MotionNotify;
404    xmotion.root = rootWindowID();
405    xmotion.subwindow = 0;
406    xmotion.time = event->timeStamp();
407    xmotion.x = postZoomPos.x();
408    xmotion.y = postZoomPos.y();
409    xmotion.x_root = event->screenX();
410    xmotion.y_root = event->screenY();
411    xmotion.state = inputEventState(event);
412    xmotion.is_hint = NotifyNormal;
413    xmotion.same_screen = true;
414}
415
416static void setXCrossingEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos)
417{
418    XCrossingEvent& xcrossing = xEvent->xcrossing;
419    xcrossing.type = event->type() == eventNames().mouseoverEvent ? EnterNotify : LeaveNotify;
420    xcrossing.root = rootWindowID();
421    xcrossing.subwindow = 0;
422    xcrossing.time = event->timeStamp();
423    xcrossing.x = postZoomPos.y();
424    xcrossing.y = postZoomPos.x();
425    xcrossing.x_root = event->screenX();
426    xcrossing.y_root = event->screenY();
427    xcrossing.state = inputEventState(event);
428    xcrossing.mode = NotifyNormal;
429    xcrossing.detail = NotifyDetailNone;
430    xcrossing.same_screen = true;
431    xcrossing.focus = false;
432}
433
434void PluginView::handleMouseEvent(MouseEvent* event)
435{
436    if (m_isWindowed)
437        return;
438
439    if (event->button() == RightButton && m_plugin->quirks().contains(PluginQuirkIgnoreRightClickInWindowlessMode))
440        return;
441
442    if (event->type() == eventNames().mousedownEvent) {
443        // Give focus to the plugin on click
444        if (Page* page = m_parentFrame->page())
445            page->focusController()->setActive(true);
446
447        focusPluginElement();
448    }
449
450    XEvent npEvent;
451    initXEvent(&npEvent);
452
453    IntPoint postZoomPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(event->absoluteLocation()));
454
455    if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent)
456        setXButtonEventSpecificFields(&npEvent, event, postZoomPos);
457    else if (event->type() == eventNames().mousemoveEvent)
458        setXMotionEventSpecificFields(&npEvent, event, postZoomPos);
459    else if (event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mouseoverEvent)
460        setXCrossingEventSpecificFields(&npEvent, event, postZoomPos);
461    else
462        return;
463
464    if (dispatchNPEvent(npEvent))
465        event->setDefaultHandled();
466}
467
468void PluginView::handleFocusInEvent()
469{
470    XEvent npEvent;
471    initXEvent(&npEvent);
472
473    XFocusChangeEvent& event = npEvent.xfocus;
474    event.type = 9; /* int as Qt unsets FocusIn */
475    event.mode = NotifyNormal;
476    event.detail = NotifyDetailNone;
477
478    dispatchNPEvent(npEvent);
479}
480
481void PluginView::handleFocusOutEvent()
482{
483    XEvent npEvent;
484    initXEvent(&npEvent);
485
486    XFocusChangeEvent& event = npEvent.xfocus;
487    event.type = 10; /* int as Qt unsets FocusOut */
488    event.mode = NotifyNormal;
489    event.detail = NotifyDetailNone;
490
491    dispatchNPEvent(npEvent);
492}
493
494void PluginView::setParent(ScrollView* parent)
495{
496    Widget::setParent(parent);
497
498    if (parent)
499        init();
500}
501
502void PluginView::setNPWindowRect(const IntRect&)
503{
504    if (!m_isWindowed)
505        setNPWindowIfNeeded();
506}
507
508void PluginView::setNPWindowIfNeeded()
509{
510    if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow)
511        return;
512
513    // If the plugin didn't load sucessfully, no point in calling setwindow
514    if (m_status != PluginStatusLoadedSuccessfully)
515        return;
516
517    // On Unix, only call plugin if it's full-page or windowed
518    if (m_mode != NP_FULL && m_mode != NP_EMBED)
519        return;
520
521    // Check if the platformPluginWidget still exists
522    if (m_isWindowed && !platformPluginWidget())
523        return;
524
525    if (!m_hasPendingGeometryChange)
526        return;
527    m_hasPendingGeometryChange = false;
528
529    m_npWindow.x = 0;
530    m_npWindow.y = 0;
531
532    // If the width or height are null, set the clipRect to null, indicating that
533    // the plugin is not visible/scrolled out.
534    if (!m_clipRect.width() || !m_clipRect.height()) {
535        m_npWindow.clipRect.left = 0;
536        m_npWindow.clipRect.right = 0;
537        m_npWindow.clipRect.top = 0;
538        m_npWindow.clipRect.bottom = 0;
539    } else {
540        // Clipping rectangle of the plug-in; the origin is the top left corner of the drawable or window.
541        m_npWindow.clipRect.left = m_npWindow.x + m_clipRect.x();
542        m_npWindow.clipRect.top = m_npWindow.y + m_clipRect.y();
543        m_npWindow.clipRect.right = m_npWindow.x + m_clipRect.x() + m_clipRect.width();
544        m_npWindow.clipRect.bottom = m_npWindow.y + m_clipRect.y() + m_clipRect.height();
545    }
546
547    if (m_plugin->quirks().contains(PluginQuirkDontCallSetWindowMoreThanOnce)) {
548        // FLASH WORKAROUND: Only set initially. Multiple calls to
549        // setNPWindow() cause the plugin to crash in windowed mode.
550        if (!m_isWindowed || m_npWindow.width == -1 || m_npWindow.height == -1) {
551            m_npWindow.width = m_windowRect.width();
552            m_npWindow.height = m_windowRect.height();
553        }
554    } else {
555        m_npWindow.width = m_windowRect.width();
556        m_npWindow.height = m_windowRect.height();
557    }
558
559    PluginView::setCurrentPluginView(this);
560    JSC::JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM());
561    setCallingPlugin(true);
562    m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
563    setCallingPlugin(false);
564    PluginView::setCurrentPluginView(0);
565}
566
567void PluginView::setParentVisible(bool visible)
568{
569    if (isParentVisible() == visible)
570        return;
571
572    Widget::setParentVisible(visible);
573}
574
575NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf)
576{
577    String filename(buf, len);
578
579    if (filename.startsWith("file:///"))
580        filename = filename.substring(8);
581
582    long long size;
583    if (!getFileSize(filename, size))
584        return NPERR_FILE_NOT_FOUND;
585
586    FILE* fileHandle = fopen((filename.utf8()).data(), "r");
587    if (!fileHandle)
588        return NPERR_FILE_NOT_FOUND;
589
590    buffer.resize(size);
591    int bytesRead = fread(buffer.data(), 1, size, fileHandle);
592
593    fclose(fileHandle);
594
595    if (bytesRead <= 0)
596        return NPERR_FILE_NOT_FOUND;
597
598    return NPERR_NO_ERROR;
599}
600
601bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result)
602{
603    switch (variable) {
604    case NPNVToolkit:
605        *static_cast<uint32_t*>(value) = 0;
606        *result = NPERR_NO_ERROR;
607        return true;
608
609    case NPNVSupportsXEmbedBool:
610        *static_cast<NPBool*>(value) = true;
611        *result = NPERR_NO_ERROR;
612        return true;
613
614    case NPNVjavascriptEnabledBool:
615        *static_cast<NPBool*>(value) = true;
616        *result = NPERR_NO_ERROR;
617        return true;
618
619    case NPNVSupportsWindowless:
620        *static_cast<NPBool*>(value) = true;
621        *result = NPERR_NO_ERROR;
622        return true;
623
624    default:
625        return false;
626    }
627}
628
629bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result)
630{
631    switch (variable) {
632    case NPNVxDisplay:
633        *reinterpret_cast<void**>(value) = x11Display();
634        *result = NPERR_NO_ERROR;
635        return true;
636
637    case NPNVxtAppContext:
638        *result = NPERR_GENERIC_ERROR;
639        return true;
640
641    case NPNVnetscapeWindow: {
642        QWebPageClient* client = platformPageClient();
643        QWindow* window = client ? client->ownerWindow() : 0;
644        *reinterpret_cast<XID*>(value) = window ? window->winId() : 0;
645        *result = NPERR_NO_ERROR;
646        return true;
647    }
648
649    case NPNVToolkit:
650        if (m_plugin->quirks().contains(PluginQuirkRequiresGtkToolKit)) {
651            *((uint32_t *)value) = 2;
652            *result = NPERR_NO_ERROR;
653            return true;
654        }
655        return false;
656
657    default:
658        return false;
659    }
660}
661
662void PluginView::invalidateRect(const IntRect& rect)
663{
664    invalidateWindowlessPluginRect(rect);
665}
666
667void PluginView::invalidateRect(NPRect* rect)
668{
669    if (!rect) {
670        invalidate();
671        return;
672    }
673    IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top);
674    invalidateRect(r);
675}
676
677void PluginView::invalidateRegion(NPRegion region)
678{
679    Q_UNUSED(region);
680    invalidate();
681}
682
683void PluginView::forceRedraw()
684{
685    invalidate();
686}
687
688static Display *getPluginDisplay()
689{
690    // The plugin toolkit might run using a different X connection. At the moment, we only
691    // support gdk based plugins (like flash) that use a different X connection.
692    // The code below has the same effect as this one:
693    // Display *gdkDisplay = gdk_x11_display_get_xdisplay(gdk_display_get_default());
694    QLibrary library(QLatin1String("libgdk-x11-2.0"), 0);
695    if (!library.load())
696        return 0;
697
698    typedef void *(*gdk_init_check_ptr)(void*, void*);
699    gdk_init_check_ptr gdk_init_check = (gdk_init_check_ptr)library.resolve("gdk_init_check");
700    if (!gdk_init_check)
701        return 0;
702
703    typedef void *(*gdk_display_get_default_ptr)();
704    gdk_display_get_default_ptr gdk_display_get_default = (gdk_display_get_default_ptr)library.resolve("gdk_display_get_default");
705    if (!gdk_display_get_default)
706        return 0;
707
708    typedef void *(*gdk_x11_display_get_xdisplay_ptr)(void *);
709    gdk_x11_display_get_xdisplay_ptr gdk_x11_display_get_xdisplay = (gdk_x11_display_get_xdisplay_ptr)library.resolve("gdk_x11_display_get_xdisplay");
710    if (!gdk_x11_display_get_xdisplay)
711        return 0;
712
713    gdk_init_check(0, 0);
714    return (Display*)gdk_x11_display_get_xdisplay(gdk_display_get_default());
715}
716
717static bool getVisualAndColormap(int depth, Visual*& visual, Colormap& colormap, bool forceARGB32)
718{
719    ASSERT(depth == 32 || !forceARGB32);
720
721    visual = 0;
722    colormap = 0;
723
724    if (forceARGB32)
725        return false;
726
727    int nvi;
728    XVisualInfo templ;
729    templ.screen  = x11Screen();
730    templ.depth   = depth;
731    templ.c_class = TrueColor;
732    XVisualInfo* xvi = XGetVisualInfo(x11Display(), VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nvi);
733    ASSERT(xvi || forceARGB32);
734    if (!xvi)
735        return false;
736
737    visual = xvi[0].visual;
738    ASSERT(visual);
739
740    XFree(xvi);
741
742    colormap = XCreateColormap(x11Display(), rootWindowID(), visual, AllocNone);
743    return true;
744}
745
746bool PluginView::platformStart()
747{
748    ASSERT(m_isStarted);
749    ASSERT(m_status == PluginStatusLoadedSuccessfully);
750
751    if (!x11Environment.display) {
752        Display* display;
753        display = static_cast<Display*>(QGuiApplication::platformNativeInterface()->nativeResourceForWindow("display", 0));
754        x11Environment.display = display;
755        x11Environment.screenID = XDefaultScreen(display);
756        x11Environment.displayDepth = XDefaultDepth(display, x11Environment.screenID);
757        x11Environment.rootWindowID = XDefaultRootWindow(display);
758    }
759
760    // Windowed mode is not supported with Qt5 yet.
761    if (m_isWindowed)
762        return false;
763    setPlatformWidget(0);
764    m_pluginDisplay = getPluginDisplay();
765
766    // If the width and the height are not zero we show the PluginView.
767    if (!frameRect().isEmpty())
768        show();
769
770    NPSetWindowCallbackStruct* wsi = new NPSetWindowCallbackStruct();
771    wsi->type = 0;
772
773    int depth = displayDepth();
774    bool found = getVisualAndColormap(depth, m_visual, m_colormap, /* forceARGB32 = */ false);
775    ASSERT_UNUSED(found, found);
776    wsi->depth = depth;
777
778    wsi->display = x11Display();
779    wsi->visual = m_visual;
780    wsi->colormap = m_colormap;
781
782    m_npWindow.type = NPWindowTypeDrawable;
783    m_npWindow.window = 0; // Not used?
784    m_npWindow.x = 0;
785    m_npWindow.y = 0;
786    m_npWindow.width = -1;
787    m_npWindow.height = -1;
788
789    m_npWindow.ws_info = wsi;
790
791    if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall))) {
792        updatePluginWidget();
793        setNPWindowIfNeeded();
794    }
795
796    return true;
797}
798
799void PluginView::platformDestroy()
800{
801    if (platformPluginWidget())
802        delete platformPluginWidget();
803
804    if (m_drawable)
805        XFreePixmap(x11Display(), m_drawable);
806
807    if (m_colormap)
808        XFreeColormap(x11Display(), m_colormap);
809}
810
811#if USE(ACCELERATED_COMPOSITING)
812PlatformLayer* PluginView::platformLayer() const
813{
814    return m_platformLayer.get();
815}
816#endif
817
818} // namespace WebCore
819