1/*
2 * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
3 * Copyright (C) 2008 Collabora Ltd. All rights reserved.
4 * Copyright (C) 2009, 2010 Kakai, Inc. <brian@kakai.com>
5 * Copyright (C) 2010 Igalia S.L.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "PluginView.h"
31
32#include "BridgeJSC.h"
33#include "Document.h"
34#include "DocumentLoader.h"
35#include "Element.h"
36#include "FocusController.h"
37#include "FrameLoader.h"
38#include "FrameLoadRequest.h"
39#include "FrameTree.h"
40#include "Frame.h"
41#include "FrameView.h"
42#include "GraphicsContext.h"
43#include "GtkVersioning.h"
44#include "HTMLNames.h"
45#include "HTMLPlugInElement.h"
46#include "HostWindow.h"
47#include "Image.h"
48#include "KeyboardEvent.h"
49#include "MouseEvent.h"
50#include "NotImplemented.h"
51#include "Page.h"
52#include "PlatformContextCairo.h"
53#include "PlatformKeyboardEvent.h"
54#include "PlatformMouseEvent.h"
55#include "PluginDebug.h"
56#include "PluginMainThreadScheduler.h"
57#include "PluginPackage.h"
58#include "RenderLayer.h"
59#include "Settings.h"
60#include "JSDOMBinding.h"
61#include "ScriptController.h"
62#include "npruntime_impl.h"
63#include "runtime_root.h"
64#include <runtime/JSLock.h>
65#include <runtime/JSValue.h>
66
67#ifdef GTK_API_VERSION_2
68#include <gdkconfig.h>
69#endif
70#include <gtk/gtk.h>
71
72#if defined(XP_UNIX)
73#include "RefPtrCairo.h"
74#include "gtk2xtbin.h"
75#define Bool int // this got undefined somewhere
76#define Status int // ditto
77#include <X11/extensions/Xrender.h>
78#include <cairo/cairo-xlib.h>
79#include <gdk/gdkx.h>
80#elif defined(GDK_WINDOWING_WIN32)
81#include "PluginMessageThrottlerWin.h"
82#include <gdk/gdkwin32.h>
83#endif
84
85using JSC::ExecState;
86using JSC::Interpreter;
87using JSC::JSLock;
88using JSC::JSObject;
89using JSC::UString;
90
91using std::min;
92
93using namespace WTF;
94
95namespace WebCore {
96
97using namespace HTMLNames;
98
99bool PluginView::dispatchNPEvent(NPEvent& event)
100{
101    // sanity check
102    if (!m_plugin->pluginFuncs()->event)
103        return false;
104
105    PluginView::setCurrentPluginView(this);
106    JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
107    setCallingPlugin(true);
108
109    bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event);
110
111    setCallingPlugin(false);
112    PluginView::setCurrentPluginView(0);
113    return accepted;
114}
115
116#if defined(XP_UNIX)
117static Window getRootWindow(Frame* parentFrame)
118{
119    GtkWidget* parentWidget = parentFrame->view()->hostWindow()->platformPageClient();
120    GdkScreen* gscreen = gtk_widget_get_screen(parentWidget);
121    return GDK_WINDOW_XWINDOW(gdk_screen_get_root_window(gscreen));
122}
123#endif
124
125void PluginView::updatePluginWidget()
126{
127    if (!parent())
128        return;
129
130    ASSERT(parent()->isFrameView());
131    FrameView* frameView = static_cast<FrameView*>(parent());
132
133    IntRect oldWindowRect = m_windowRect;
134    IntRect oldClipRect = m_clipRect;
135
136    m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size());
137    m_clipRect = windowClipRect();
138    m_clipRect.move(-m_windowRect.x(), -m_windowRect.y());
139
140    if (m_windowRect == oldWindowRect && m_clipRect == oldClipRect)
141        return;
142
143#if defined(XP_UNIX)
144    if (!m_isWindowed) {
145        Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
146        if (m_drawable)
147            XFreePixmap(display, m_drawable);
148
149        m_drawable = XCreatePixmap(display, getRootWindow(m_parentFrame.get()),
150                                   m_windowRect.width(), m_windowRect.height(),
151                                   ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth);
152        XSync(display, false); // make sure that the server knows about the Drawable
153    }
154#endif
155
156    setNPWindowIfNeeded();
157}
158
159void PluginView::setFocus(bool focused)
160{
161    ASSERT(platformPluginWidget() == platformWidget());
162    Widget::setFocus(focused);
163}
164
165void PluginView::show()
166{
167    ASSERT(platformPluginWidget() == platformWidget());
168    Widget::show();
169}
170
171void PluginView::hide()
172{
173    ASSERT(platformPluginWidget() == platformWidget());
174    Widget::hide();
175}
176
177void PluginView::paint(GraphicsContext* context, const IntRect& rect)
178{
179    if (!m_isStarted) {
180        paintMissingPluginIcon(context, rect);
181        return;
182    }
183
184    if (context->paintingDisabled())
185        return;
186
187    setNPWindowIfNeeded();
188
189    if (m_isWindowed)
190        return;
191
192#if defined(XP_UNIX)
193    if (!m_drawable)
194        return;
195
196    Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
197    const bool syncX = m_pluginDisplay && m_pluginDisplay != display;
198
199    IntRect exposedRect(rect);
200    exposedRect.intersect(frameRect());
201    exposedRect.move(-frameRect().x(), -frameRect().y());
202
203    RefPtr<cairo_surface_t> drawableSurface = adoptRef(cairo_xlib_surface_create(display,
204                                                       m_drawable,
205                                                       m_visual,
206                                                       m_windowRect.width(),
207                                                       m_windowRect.height()));
208
209    if (m_isTransparent) {
210        // If we have a 32 bit drawable and the plugin wants transparency,
211        // we'll clear the exposed area to transparent first.  Otherwise,
212        // we'd end up with junk in there from the last paint, or, worse,
213        // uninitialized data.
214        RefPtr<cairo_t> cr = adoptRef(cairo_create(drawableSurface.get()));
215
216        if (!(cairo_surface_get_content(drawableSurface.get()) & CAIRO_CONTENT_ALPHA)) {
217            // Attempt to fake it when we don't have an alpha channel on our
218            // pixmap.  If that's not possible, at least clear the window to
219            // avoid drawing artifacts.
220
221            // This Would not work without double buffering, but we always use it.
222            cairo_set_source_surface(cr.get(), cairo_get_group_target(context->platformContext()->cr()),
223                                     -m_windowRect.x(), -m_windowRect.y());
224            cairo_set_operator(cr.get(), CAIRO_OPERATOR_SOURCE);
225        } else
226            cairo_set_operator(cr.get(), CAIRO_OPERATOR_CLEAR);
227
228        cairo_rectangle(cr.get(), exposedRect.x(), exposedRect.y(),
229                        exposedRect.width(), exposedRect.height());
230        cairo_fill(cr.get());
231    }
232
233    XEvent xevent;
234    memset(&xevent, 0, sizeof(XEvent));
235    XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose;
236    exposeEvent.type = GraphicsExpose;
237    exposeEvent.display = display;
238    exposeEvent.drawable = m_drawable;
239    exposeEvent.x = exposedRect.x();
240    exposeEvent.y = exposedRect.y();
241    exposeEvent.width = exposedRect.x() + exposedRect.width(); // flash bug? it thinks width is the right in transparent mode
242    exposeEvent.height = exposedRect.y() + exposedRect.height(); // flash bug? it thinks height is the bottom in transparent mode
243
244    dispatchNPEvent(xevent);
245
246    if (syncX)
247        XSync(m_pluginDisplay, false); // sync changes by plugin
248
249    cairo_t* cr = context->platformContext()->cr();
250    cairo_save(cr);
251
252    cairo_set_source_surface(cr, drawableSurface.get(), frameRect().x(), frameRect().y());
253
254    cairo_rectangle(cr,
255                    frameRect().x() + exposedRect.x(), frameRect().y() + exposedRect.y(),
256                    exposedRect.width(), exposedRect.height());
257    cairo_clip(cr);
258
259    if (m_isTransparent)
260        cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
261    else
262        cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
263    cairo_paint(cr);
264
265    cairo_restore(cr);
266#endif // defined(XP_UNIX)
267}
268
269void PluginView::handleKeyboardEvent(KeyboardEvent* event)
270{
271    JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
272
273    if (m_isWindowed)
274        return;
275
276    if (event->type() != eventNames().keydownEvent && event->type() != eventNames().keyupEvent)
277        return;
278
279    NPEvent xEvent;
280#if defined(XP_UNIX)
281    initXEvent(&xEvent);
282    GdkEventKey* gdkEvent = event->keyEvent()->gdkEventKey();
283
284    xEvent.type = (event->type() == eventNames().keydownEvent) ? 2 : 3; // KeyPress/Release get unset somewhere
285    xEvent.xkey.root = getRootWindow(m_parentFrame.get());
286    xEvent.xkey.subwindow = 0; // we have no child window
287    xEvent.xkey.time = event->timeStamp();
288    xEvent.xkey.state = gdkEvent->state; // GdkModifierType mirrors xlib state masks
289    xEvent.xkey.keycode = gdkEvent->hardware_keycode;
290    xEvent.xkey.same_screen = true;
291
292    // NOTE: As the XEvents sent to the plug-in are synthesized and there is not a native window
293    // corresponding to the plug-in rectangle, some of the members of the XEvent structures are not
294    // set to their normal Xserver values. e.g. Key events don't have a position.
295    // source: https://developer.mozilla.org/en/NPEvent
296    xEvent.xkey.x = 0;
297    xEvent.xkey.y = 0;
298    xEvent.xkey.x_root = 0;
299    xEvent.xkey.y_root = 0;
300#endif
301
302    if (!dispatchNPEvent(xEvent))
303        event->setDefaultHandled();
304}
305
306#if defined(XP_UNIX)
307static unsigned int inputEventState(MouseEvent* event)
308{
309    unsigned int state = 0;
310    if (event->ctrlKey())
311        state |= ControlMask;
312    if (event->shiftKey())
313        state |= ShiftMask;
314    if (event->altKey())
315        state |= Mod1Mask;
316    if (event->metaKey())
317        state |= Mod4Mask;
318    return state;
319}
320
321void PluginView::initXEvent(XEvent* xEvent)
322{
323    memset(xEvent, 0, sizeof(XEvent));
324
325    xEvent->xany.serial = 0; // we are unaware of the last request processed by X Server
326    xEvent->xany.send_event = false;
327    GtkWidget* widget = m_parentFrame->view()->hostWindow()->platformPageClient();
328    xEvent->xany.display = GDK_DISPLAY_XDISPLAY(gtk_widget_get_display(widget));
329
330    // Mozilla also sends None here for windowless plugins. See nsObjectFrame.cpp in the Mozilla sources.
331    // This method also sets up FocusIn and FocusOut events for windows plugins, but Mozilla doesn't
332    // even send these types of events to windowed plugins. In the future, it may be good to only
333    // send them to windowless plugins.
334    xEvent->xany.window = None;
335}
336
337static void setXButtonEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos, Frame* parentFrame)
338{
339    XButtonEvent& xbutton = xEvent->xbutton;
340    xbutton.type = event->type() == eventNames().mousedownEvent ? ButtonPress : ButtonRelease;
341    xbutton.root = getRootWindow(parentFrame);
342    xbutton.subwindow = 0;
343    xbutton.time = event->timeStamp();
344    xbutton.x = postZoomPos.x();
345    xbutton.y = postZoomPos.y();
346    xbutton.x_root = event->screenX();
347    xbutton.y_root = event->screenY();
348    xbutton.state = inputEventState(event);
349    switch (event->button()) {
350    case MiddleButton:
351        xbutton.button = Button2;
352        break;
353    case RightButton:
354        xbutton.button = Button3;
355        break;
356    case LeftButton:
357    default:
358        xbutton.button = Button1;
359        break;
360    }
361    xbutton.same_screen = true;
362}
363
364static void setXMotionEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos, Frame* parentFrame)
365{
366    XMotionEvent& xmotion = xEvent->xmotion;
367    xmotion.type = MotionNotify;
368    xmotion.root = getRootWindow(parentFrame);
369    xmotion.subwindow = 0;
370    xmotion.time = event->timeStamp();
371    xmotion.x = postZoomPos.x();
372    xmotion.y = postZoomPos.y();
373    xmotion.x_root = event->screenX();
374    xmotion.y_root = event->screenY();
375    xmotion.state = inputEventState(event);
376    xmotion.is_hint = NotifyNormal;
377    xmotion.same_screen = true;
378}
379
380static void setXCrossingEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos, Frame* parentFrame)
381{
382    XCrossingEvent& xcrossing = xEvent->xcrossing;
383    xcrossing.type = event->type() == eventNames().mouseoverEvent ? EnterNotify : LeaveNotify;
384    xcrossing.root = getRootWindow(parentFrame);
385    xcrossing.subwindow = 0;
386    xcrossing.time = event->timeStamp();
387    xcrossing.x = postZoomPos.y();
388    xcrossing.y = postZoomPos.x();
389    xcrossing.x_root = event->screenX();
390    xcrossing.y_root = event->screenY();
391    xcrossing.state = inputEventState(event);
392    xcrossing.mode = NotifyNormal;
393    xcrossing.detail = NotifyDetailNone;
394    xcrossing.same_screen = true;
395    xcrossing.focus = false;
396}
397#endif
398
399void PluginView::handleMouseEvent(MouseEvent* event)
400{
401    JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
402
403    if (m_isWindowed)
404        return;
405
406    if (event->type() == eventNames().mousedownEvent) {
407        if (Page* page = m_parentFrame->page())
408            page->focusController()->setActive(true);
409        focusPluginElement();
410    }
411
412    NPEvent xEvent;
413#if defined(XP_UNIX)
414    initXEvent(&xEvent);
415
416    IntPoint postZoomPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(event->absoluteLocation()));
417
418    if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent)
419        setXButtonEventSpecificFields(&xEvent, event, postZoomPos, m_parentFrame.get());
420    else if (event->type() == eventNames().mousemoveEvent)
421        setXMotionEventSpecificFields(&xEvent, event, postZoomPos, m_parentFrame.get());
422    else if (event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mouseoverEvent) {
423        setXCrossingEventSpecificFields(&xEvent, event, postZoomPos, m_parentFrame.get());
424
425        // This is a work-around for plugins which change the cursor. When that happens we
426        // get out of sync with GDK somehow. Resetting the cursor here seems to fix the issue.
427        if (event->type() == eventNames().mouseoutEvent)
428            gdk_window_set_cursor(gtk_widget_get_window(m_parentFrame->view()->hostWindow()->platformPageClient()), 0);
429    }
430    else
431        return;
432#endif
433
434    if (!dispatchNPEvent(xEvent))
435        event->setDefaultHandled();
436}
437
438#if defined(XP_UNIX)
439void PluginView::handleFocusInEvent()
440{
441    XEvent npEvent;
442    initXEvent(&npEvent);
443
444    XFocusChangeEvent& event = npEvent.xfocus;
445    event.type = 9; // FocusIn gets unset somewhere
446    event.mode = NotifyNormal;
447    event.detail = NotifyDetailNone;
448
449    dispatchNPEvent(npEvent);
450}
451
452void PluginView::handleFocusOutEvent()
453{
454    XEvent npEvent;
455    initXEvent(&npEvent);
456
457    XFocusChangeEvent& event = npEvent.xfocus;
458    event.type = 10; // FocusOut gets unset somewhere
459    event.mode = NotifyNormal;
460    event.detail = NotifyDetailNone;
461
462    dispatchNPEvent(npEvent);
463}
464#endif
465
466void PluginView::setParent(ScrollView* parent)
467{
468    Widget::setParent(parent);
469
470    if (parent)
471        init();
472}
473
474void PluginView::setNPWindowRect(const IntRect&)
475{
476    if (!m_isWindowed)
477        setNPWindowIfNeeded();
478}
479
480void PluginView::setNPWindowIfNeeded()
481{
482    if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow)
483        return;
484
485    // If the plugin didn't load sucessfully, no point in calling setwindow
486    if (m_status != PluginStatusLoadedSuccessfully)
487        return;
488
489    // On Unix, only call plugin's setwindow if it's full-page or windowed
490    if (m_mode != NP_FULL && m_mode != NP_EMBED)
491        return;
492
493    // Check if the platformPluginWidget still exists
494    if (m_isWindowed && !platformPluginWidget())
495        return;
496
497    if (m_isWindowed) {
498        m_npWindow.x = m_windowRect.x();
499        m_npWindow.y = m_windowRect.y();
500        m_npWindow.width = m_windowRect.width();
501        m_npWindow.height = m_windowRect.height();
502
503        m_npWindow.clipRect.left = max(0, m_clipRect.x());
504        m_npWindow.clipRect.top = max(0, m_clipRect.y());
505        m_npWindow.clipRect.right = m_clipRect.x() + m_clipRect.width();
506        m_npWindow.clipRect.bottom = m_clipRect.y() + m_clipRect.height();
507    } else {
508        m_npWindow.x = 0;
509        m_npWindow.y = 0;
510        m_npWindow.width = m_windowRect.width();
511        m_npWindow.height = m_windowRect.height();
512
513        m_npWindow.clipRect.left = 0;
514        m_npWindow.clipRect.top = 0;
515        m_npWindow.clipRect.right = 0;
516        m_npWindow.clipRect.bottom = 0;
517    }
518
519    PluginView::setCurrentPluginView(this);
520    JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
521    setCallingPlugin(true);
522    m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
523    setCallingPlugin(false);
524    PluginView::setCurrentPluginView(0);
525
526    if (!m_isWindowed)
527        return;
528
529#if defined(XP_UNIX)
530    // GtkXtBin will call gtk_widget_size_allocate, so we don't need to do it here.
531    if (!m_needsXEmbed) {
532        gtk_xtbin_set_position(GTK_XTBIN(platformPluginWidget()), m_windowRect.x(), m_windowRect.y());
533        gtk_xtbin_resize(platformPluginWidget(), m_windowRect.width(), m_windowRect.height());
534        return;
535    }
536#endif
537
538    GtkAllocation allocation = { m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height() };
539
540    // If the window has not been embedded yet (the plug added), we delay setting its allocation until
541    // that point. This fixes issues with some Java plugin instances not rendering immediately.
542    if (!m_plugAdded) {
543        m_delayedAllocation = allocation;
544        return;
545    }
546    gtk_widget_size_allocate(platformPluginWidget(), &allocation);
547}
548
549void PluginView::setParentVisible(bool visible)
550{
551    if (isParentVisible() == visible)
552        return;
553
554    Widget::setParentVisible(visible);
555
556    if (isSelfVisible() && platformPluginWidget()) {
557        if (visible)
558            gtk_widget_show(platformPluginWidget());
559        else
560            gtk_widget_hide(platformPluginWidget());
561    }
562}
563
564NPError PluginView::handlePostReadFile(Vector<char>& outputBuffer, uint32_t filenameLength, const char* filenameBuffer)
565{
566    // There doesn't seem to be any documentation about what encoding the filename
567    // is in, but most ports seem to assume UTF-8 here and the test plugin is definitely
568    // sending the path in UTF-8 encoding.
569    CString filename(filenameBuffer, filenameLength);
570
571    GRefPtr<GFile> file = adoptGRef(g_file_new_for_commandline_arg(filename.data()));
572    if (g_file_query_file_type(file.get(), G_FILE_QUERY_INFO_NONE, 0) != G_FILE_TYPE_REGULAR)
573        return NPERR_FILE_NOT_FOUND;
574
575    GRefPtr<GFileInfo> fileInfo = adoptGRef(g_file_query_info(file.get(),
576                                                              G_FILE_ATTRIBUTE_STANDARD_SIZE,
577                                                              G_FILE_QUERY_INFO_NONE,
578                                                              0, 0));
579    if (!fileInfo)
580        return NPERR_FILE_NOT_FOUND;
581
582    GRefPtr<GFileInputStream> inputStream = adoptGRef(g_file_read(file.get(), 0, 0));
583    if (!inputStream)
584        return NPERR_FILE_NOT_FOUND;
585
586    outputBuffer.resize(g_file_info_get_size(fileInfo.get()));
587    gsize bytesRead = 0;
588    if (!g_input_stream_read_all(G_INPUT_STREAM(inputStream.get()),
589                                 outputBuffer.data(), outputBuffer.size(), &bytesRead, 0, 0))
590        return NPERR_FILE_NOT_FOUND;
591
592    return NPERR_NO_ERROR;
593}
594
595bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result)
596{
597    switch (variable) {
598    case NPNVToolkit:
599#if defined(XP_UNIX)
600        *static_cast<uint32_t*>(value) = 2;
601#else
602        *static_cast<uint32_t*>(value) = 0;
603#endif
604        *result = NPERR_NO_ERROR;
605        return true;
606
607    case NPNVSupportsXEmbedBool:
608#if defined(XP_UNIX)
609        *static_cast<NPBool*>(value) = true;
610#else
611        *static_cast<NPBool*>(value) = false;
612#endif
613        *result = NPERR_NO_ERROR;
614        return true;
615
616    case NPNVjavascriptEnabledBool:
617        *static_cast<NPBool*>(value) = true;
618        *result = NPERR_NO_ERROR;
619        return true;
620
621    case NPNVSupportsWindowless:
622#if defined(XP_UNIX)
623        *static_cast<NPBool*>(value) = true;
624#else
625        *static_cast<NPBool*>(value) = false;
626#endif
627        *result = NPERR_NO_ERROR;
628        return true;
629
630    default:
631        return false;
632    }
633}
634
635bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result)
636{
637    switch (variable) {
638    case NPNVxDisplay:
639#if defined(XP_UNIX)
640        if (m_needsXEmbed)
641            *(void **)value = (void *)GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
642        else
643            *(void **)value = (void *)GTK_XTBIN(platformPluginWidget())->xtclient.xtdisplay;
644        *result = NPERR_NO_ERROR;
645#else
646        *result = NPERR_GENERIC_ERROR;
647#endif
648        return true;
649
650#if defined(XP_UNIX)
651    case NPNVxtAppContext:
652        if (!m_needsXEmbed) {
653            *(void **)value = XtDisplayToApplicationContext (GTK_XTBIN(platformPluginWidget())->xtclient.xtdisplay);
654
655            *result = NPERR_NO_ERROR;
656        } else
657            *result = NPERR_GENERIC_ERROR;
658        return true;
659#endif
660
661        case NPNVnetscapeWindow: {
662            GdkWindow* gdkWindow = gtk_widget_get_window(m_parentFrame->view()->hostWindow()->platformPageClient());
663#if defined(XP_UNIX)
664            *static_cast<Window*>(value) = GDK_WINDOW_XWINDOW(gdk_window_get_toplevel(gdkWindow));
665#elif defined(GDK_WINDOWING_WIN32)
666            *static_cast<HGDIOBJ*>(value) = GDK_WINDOW_HWND(gdkWindow);
667#endif
668            *result = NPERR_NO_ERROR;
669            return true;
670        }
671
672    default:
673        return false;
674    }
675}
676
677void PluginView::invalidateRect(const IntRect& rect)
678{
679    if (m_isWindowed) {
680        gtk_widget_queue_draw_area(GTK_WIDGET(platformPluginWidget()), rect.x(), rect.y(), rect.width(), rect.height());
681        return;
682    }
683
684    invalidateWindowlessPluginRect(rect);
685}
686
687void PluginView::invalidateRect(NPRect* rect)
688{
689    if (!rect) {
690        invalidate();
691        return;
692    }
693
694    IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top);
695    invalidateWindowlessPluginRect(r);
696}
697
698void PluginView::invalidateRegion(NPRegion)
699{
700    // TODO: optimize
701    invalidate();
702}
703
704void PluginView::forceRedraw()
705{
706    if (m_isWindowed)
707        gtk_widget_queue_draw(platformPluginWidget());
708    else
709        gtk_widget_queue_draw(m_parentFrame->view()->hostWindow()->platformPageClient());
710}
711
712#ifndef GDK_WINDOWING_WIN32
713static Display* getPluginDisplay()
714{
715    // The plugin toolkit might have a different X connection open.  Since we're
716    // a gdk/gtk app, we'll (probably?) have the same X connection as any gdk-based
717    // plugins, so we can return that.  We might want to add other implementations here
718    // later.
719
720#if defined(XP_UNIX)
721    return GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
722#else
723    return 0;
724#endif
725}
726#endif
727
728#if defined(XP_UNIX)
729static void getVisualAndColormap(int depth, Visual** visual, Colormap* colormap)
730{
731    *visual = 0;
732    *colormap = 0;
733
734    Display* display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
735    int rmaj, rmin;
736    if (depth == 32 && (!XRenderQueryVersion(display, &rmaj, &rmin) || (rmaj == 0 && rmin < 5)))
737        return;
738
739    XVisualInfo templ;
740    templ.screen  = gdk_screen_get_number(gdk_screen_get_default());
741    templ.depth   = depth;
742    templ.c_class = TrueColor;
743    int nVisuals;
744    XVisualInfo* visualInfo = XGetVisualInfo(display, VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nVisuals);
745
746    if (!nVisuals)
747        return;
748
749    if (depth == 32) {
750        for (int idx = 0; idx < nVisuals; ++idx) {
751            XRenderPictFormat* format = XRenderFindVisualFormat(display, visualInfo[idx].visual);
752            if (format->type == PictTypeDirect && format->direct.alphaMask) {
753                 *visual = visualInfo[idx].visual;
754                 break;
755            }
756         }
757    } else
758        *visual = visualInfo[0].visual;
759
760    XFree(visualInfo);
761
762    if (*visual)
763        *colormap = XCreateColormap(display, GDK_ROOT_WINDOW(), *visual, AllocNone);
764}
765#endif
766
767gboolean PluginView::plugRemovedCallback(GtkSocket* socket, PluginView* view)
768{
769    view->m_plugAdded = false;
770    return TRUE;
771}
772
773void PluginView::plugAddedCallback(GtkSocket* socket, PluginView* view)
774{
775    ASSERT(socket);
776    ASSERT(view);
777
778    view->m_plugAdded = true;
779    if (!view->m_delayedAllocation.isEmpty()) {
780        GtkAllocation allocation(view->m_delayedAllocation);
781        gtk_widget_size_allocate(GTK_WIDGET(socket), &allocation);
782        view->m_delayedAllocation.setSize(IntSize());
783    }
784}
785
786bool PluginView::platformStart()
787{
788    ASSERT(m_isStarted);
789    ASSERT(m_status == PluginStatusLoadedSuccessfully);
790
791#if defined(XP_UNIX)
792    if (m_plugin->pluginFuncs()->getvalue) {
793        PluginView::setCurrentPluginView(this);
794        JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
795        setCallingPlugin(true);
796        m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed);
797        setCallingPlugin(false);
798        PluginView::setCurrentPluginView(0);
799    }
800#endif
801
802    if (m_isWindowed) {
803        GtkWidget* pageClient = m_parentFrame->view()->hostWindow()->platformPageClient();
804#if defined(XP_UNIX)
805        if (m_needsXEmbed) {
806            // If our parent is not anchored the startup process will
807            // fail miserably for XEmbed plugins a bit later on when
808            // we try to get the ID of our window (since realize will
809            // fail), so let's just abort here.
810            if (!gtk_widget_get_parent(pageClient))
811                return false;
812
813            m_plugAdded = false;
814            setPlatformWidget(gtk_socket_new());
815            gtk_container_add(GTK_CONTAINER(pageClient), platformPluginWidget());
816            g_signal_connect(platformPluginWidget(), "plug-added", G_CALLBACK(PluginView::plugAddedCallback), this);
817            g_signal_connect(platformPluginWidget(), "plug-removed", G_CALLBACK(PluginView::plugRemovedCallback), this);
818        } else
819            setPlatformWidget(gtk_xtbin_new(pageClient, 0));
820#else
821        setPlatformWidget(gtk_socket_new());
822        gtk_container_add(GTK_CONTAINER(pageClient), platformPluginWidget());
823#endif
824    } else {
825        setPlatformWidget(0);
826#if defined(XP_UNIX)
827        m_pluginDisplay = getPluginDisplay();
828#endif
829    }
830
831    show();
832
833#if defined(XP_UNIX)
834        NPSetWindowCallbackStruct* ws = new NPSetWindowCallbackStruct();
835        ws->type = 0;
836#endif
837
838    if (m_isWindowed) {
839        m_npWindow.type = NPWindowTypeWindow;
840#if defined(XP_UNIX)
841        if (m_needsXEmbed) {
842            GtkWidget* widget = platformPluginWidget();
843            gtk_widget_realize(widget);
844            m_npWindow.window = (void*)gtk_socket_get_id(GTK_SOCKET(platformPluginWidget()));
845            GdkWindow* window = gtk_widget_get_window(widget);
846            ws->display = GDK_WINDOW_XDISPLAY(window);
847            ws->visual = GDK_VISUAL_XVISUAL(gdk_window_get_visual(window));
848            ws->depth = gdk_visual_get_depth(gdk_window_get_visual(window));
849            ws->colormap = XCreateColormap(ws->display, GDK_ROOT_WINDOW(), ws->visual, AllocNone);
850        } else {
851            m_npWindow.window = (void*)GTK_XTBIN(platformPluginWidget())->xtwindow;
852            ws->display = GTK_XTBIN(platformPluginWidget())->xtdisplay;
853            ws->visual = GTK_XTBIN(platformPluginWidget())->xtclient.xtvisual;
854            ws->depth = GTK_XTBIN(platformPluginWidget())->xtclient.xtdepth;
855            ws->colormap = GTK_XTBIN(platformPluginWidget())->xtclient.xtcolormap;
856        }
857        XFlush (ws->display);
858#elif defined(GDK_WINDOWING_WIN32)
859        m_npWindow.window = (void*)GDK_WINDOW_HWND(gtk_widget_get_window(platformPluginWidget()));
860#endif
861    } else {
862        m_npWindow.type = NPWindowTypeDrawable;
863        m_npWindow.window = 0; // Not used?
864
865#if defined(XP_UNIX)
866        GdkScreen* gscreen = gdk_screen_get_default();
867        GdkVisual* gvisual = gdk_screen_get_system_visual(gscreen);
868
869        if (gdk_visual_get_depth(gvisual) == 32 || !m_plugin->quirks().contains(PluginQuirkRequiresDefaultScreenDepth)) {
870            getVisualAndColormap(32, &m_visual, &m_colormap);
871            ws->depth = 32;
872        }
873
874        if (!m_visual) {
875            getVisualAndColormap(gdk_visual_get_depth(gvisual), &m_visual, &m_colormap);
876            ws->depth = gdk_visual_get_depth(gvisual);
877        }
878
879        ws->display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
880        ws->visual = m_visual;
881        ws->colormap = m_colormap;
882
883        m_npWindow.x = 0;
884        m_npWindow.y = 0;
885        m_npWindow.width = -1;
886        m_npWindow.height = -1;
887#else
888        notImplemented();
889        m_status = PluginStatusCanNotLoadPlugin;
890        return false;
891#endif
892    }
893
894#if defined(XP_UNIX)
895    m_npWindow.ws_info = ws;
896#endif
897
898    // TODO remove in favor of null events, like mac port?
899    if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall)))
900        updatePluginWidget(); // was: setNPWindowIfNeeded(), but this doesn't produce 0x0 rects at first go
901
902    return true;
903}
904
905void PluginView::platformDestroy()
906{
907#if defined(XP_UNIX)
908    if (m_drawable) {
909        XFreePixmap(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), m_drawable);
910        m_drawable = 0;
911    }
912#endif
913}
914
915void PluginView::halt()
916{
917}
918
919void PluginView::restart()
920{
921}
922
923} // namespace WebCore
924