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