WebCore:
[WebKit-https.git] / WebCore / plugins / gtk / PluginViewGtk.cpp
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 "Bridge.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 "Page.h"
51 #include "PlatformKeyboardEvent.h"
52 #include "PlatformMouseEvent.h"
53 #include "PluginDebug.h"
54 #include "PluginMainThreadScheduler.h"
55 #include "PluginPackage.h"
56 #include "RenderLayer.h"
57 #include "Settings.h"
58 #include "JSDOMBinding.h"
59 #include "ScriptController.h"
60 #include "npruntime_impl.h"
61 #include "runtime_root.h"
62 #include <runtime/JSLock.h>
63 #include <runtime/JSValue.h>
64
65 #include <gdkconfig.h>
66 #include <gtk/gtk.h>
67
68 #if defined(XP_UNIX)
69 #include "gtk2xtbin.h"
70 #define Bool int // this got undefined somewhere
71 #define Status int // ditto
72 #include <X11/extensions/Xrender.h>
73 #include <cairo/cairo-xlib.h>
74 #include <gdk/gdkx.h>
75 #elif defined(GDK_WINDOWING_WIN32)
76 #include "PluginMessageThrottlerWin.h"
77 #include <gdk/gdkwin32.h>
78 #endif
79
80 using JSC::ExecState;
81 using JSC::Interpreter;
82 using JSC::JSLock;
83 using JSC::JSObject;
84 using JSC::UString;
85
86 using std::min;
87
88 using namespace WTF;
89
90 namespace WebCore {
91
92 using namespace HTMLNames;
93
94 bool PluginView::dispatchNPEvent(NPEvent& event)
95 {
96     // sanity check
97     if (!m_plugin->pluginFuncs()->event)
98         return false;
99
100     PluginView::setCurrentPluginView(this);
101     JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
102     setCallingPlugin(true);
103
104     bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event);
105
106     setCallingPlugin(false);
107     PluginView::setCurrentPluginView(0);
108     return accepted;
109 }
110
111 #if defined(XP_UNIX)
112 static Window getRootWindow(Frame* parentFrame)
113 {
114     GtkWidget* parentWidget = parentFrame->view()->hostWindow()->platformPageClient();
115     GdkScreen* gscreen = gtk_widget_get_screen(parentWidget);
116     return GDK_WINDOW_XWINDOW(gdk_screen_get_root_window(gscreen));
117 }
118 #endif
119
120 void PluginView::updatePluginWidget()
121 {
122     if (!parent())
123         return;
124
125     ASSERT(parent()->isFrameView());
126     FrameView* frameView = static_cast<FrameView*>(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 defined(XP_UNIX)
139     if (!m_isWindowed) {
140         if (m_drawable)
141             XFreePixmap(GDK_DISPLAY(), m_drawable);
142             
143         m_drawable = XCreatePixmap(GDK_DISPLAY(), getRootWindow(m_parentFrame.get()),
144                                    m_windowRect.width(), m_windowRect.height(),
145                                    ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth);
146         XSync(GDK_DISPLAY(), False); // make sure that the server knows about the Drawable
147     }
148 #endif
149
150     setNPWindowIfNeeded();
151 }
152
153 void PluginView::setFocus(bool focused)
154 {
155     ASSERT(platformPluginWidget() == platformWidget());
156     Widget::setFocus(focused);
157 }
158
159 void PluginView::show()
160 {
161     ASSERT(platformPluginWidget() == platformWidget());
162     Widget::show();
163 }
164
165 void PluginView::hide()
166 {
167     ASSERT(platformPluginWidget() == platformWidget());
168     Widget::hide();
169 }
170
171 void PluginView::paint(GraphicsContext* context, const IntRect& rect)
172 {
173     if (!m_isStarted) {
174         paintMissingPluginIcon(context, rect);
175         return;
176     }
177
178     if (context->paintingDisabled())
179         return;
180
181     setNPWindowIfNeeded();
182
183     if (m_isWindowed)
184         return;
185
186 #if defined(XP_UNIX)
187     if (!m_drawable)
188         return;
189
190     const bool syncX = m_pluginDisplay && m_pluginDisplay != GDK_DISPLAY();
191
192     IntRect exposedRect(rect);
193     exposedRect.intersect(frameRect());
194     exposedRect.move(-frameRect().x(), -frameRect().y());
195
196     Window dummyW;
197     int dummyI;
198     unsigned int dummyUI, actualDepth = 0;
199     XGetGeometry(GDK_DISPLAY(), m_drawable, &dummyW, &dummyI, &dummyI,
200                  &dummyUI, &dummyUI, &dummyUI, &actualDepth);
201
202     const unsigned int drawableDepth = ((NPSetWindowCallbackStruct*)m_npWindow.ws_info)->depth;
203     ASSERT(drawableDepth == actualDepth);
204
205     cairo_surface_t* drawableSurface = cairo_xlib_surface_create(GDK_DISPLAY(),
206                                                                  m_drawable,
207                                                                  m_visual,
208                                                                  m_windowRect.width(),
209                                                                  m_windowRect.height());
210
211     if (m_isTransparent && drawableDepth != 32) {
212         // Attempt to fake it when we don't have an alpha channel on our
213         // pixmap.  If that's not possible, at least clear the window to
214         // avoid drawing artifacts.
215         GtkWidget* widget = m_parentFrame->view()->hostWindow()->platformPageClient();
216         GdkDrawable* gdkBackingStore = 0;
217         gint xoff = 0, yoff = 0;
218
219         gdk_window_get_internal_paint_info(gtk_widget_get_window(widget), &gdkBackingStore, &xoff, &yoff);
220
221         GC gc = XDefaultGC(GDK_DISPLAY(), gdk_screen_get_number(gdk_screen_get_default()));
222         if (gdkBackingStore) {
223             XCopyArea(GDK_DISPLAY(), GDK_DRAWABLE_XID(gdkBackingStore), m_drawable, gc,
224                       m_windowRect.x() + exposedRect.x() - xoff,
225                       m_windowRect.y() + exposedRect.y() - yoff,
226                       exposedRect.width(), exposedRect.height(),
227                       exposedRect.x(), exposedRect.y());
228         } else {
229             // no valid backing store; clear to the background color
230             XFillRectangle(GDK_DISPLAY(), m_drawable, gc,
231                            exposedRect.x(), exposedRect.y(),
232                            exposedRect.width(), exposedRect.height());
233         }
234     } else if (m_isTransparent) {
235         // If we have a 32 bit drawable and the plugin wants transparency,
236         // we'll clear the exposed area to transparent first.  Otherwise,
237         // we'd end up with junk in there from the last paint, or, worse,
238         // uninitialized data.
239         cairo_t* crFill = cairo_create(drawableSurface);
240
241         cairo_set_operator(crFill, CAIRO_OPERATOR_SOURCE);
242         cairo_pattern_t* fill = cairo_pattern_create_rgba(0., 0., 0., 0.);
243         cairo_set_source(crFill, fill);
244
245         cairo_rectangle(crFill, exposedRect.x(), exposedRect.y(),
246                         exposedRect.width(), exposedRect.height());
247         cairo_clip(crFill);
248         cairo_paint(crFill);
249
250         cairo_destroy(crFill);
251         cairo_pattern_destroy(fill);
252     }
253
254     XEvent xevent;
255     memset(&xevent, 0, sizeof(XEvent));
256     XGraphicsExposeEvent& exposeEvent = xevent.xgraphicsexpose;
257     exposeEvent.type = GraphicsExpose;
258     exposeEvent.display = GDK_DISPLAY();
259     exposeEvent.drawable = m_drawable;
260     exposeEvent.x = exposedRect.x();
261     exposeEvent.y = exposedRect.y();
262     exposeEvent.width = exposedRect.x() + exposedRect.width(); // flash bug? it thinks width is the right in transparent mode
263     exposeEvent.height = exposedRect.y() + exposedRect.height(); // flash bug? it thinks height is the bottom in transparent mode
264
265     dispatchNPEvent(xevent);
266
267     if (syncX)
268         XSync(m_pluginDisplay, False); // sync changes by plugin
269
270     cairo_t* cr = context->platformContext();
271     cairo_save(cr);
272
273     cairo_set_source_surface(cr, drawableSurface, frameRect().x(), frameRect().y());
274
275     cairo_rectangle(cr,
276                     frameRect().x() + exposedRect.x(), frameRect().y() + exposedRect.y(),
277                     exposedRect.width(), exposedRect.height());
278     cairo_clip(cr);
279
280     if (m_isTransparent)
281         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
282     else
283         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
284     cairo_paint(cr);
285
286     cairo_restore(cr);
287     cairo_surface_destroy(drawableSurface);
288 #endif // defined(XP_UNIX)
289 }
290
291 void PluginView::handleKeyboardEvent(KeyboardEvent* event)
292 {
293     JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
294
295     if (m_isWindowed)
296         return;
297
298     if (event->type() != eventNames().keydownEvent && event->type() != eventNames().keyupEvent)
299         return;
300
301     NPEvent xEvent;
302 #if defined(XP_UNIX)
303     initXEvent(&xEvent);
304     GdkEventKey* gdkEvent = event->keyEvent()->gdkEventKey();
305
306     xEvent.type = (event->type() == eventNames().keydownEvent) ? 2 : 3; // KeyPress/Release get unset somewhere
307     xEvent.xkey.root = getRootWindow(m_parentFrame.get());
308     xEvent.xkey.subwindow = 0; // we have no child window
309     xEvent.xkey.time = event->timeStamp();
310     xEvent.xkey.state = gdkEvent->state; // GdkModifierType mirrors xlib state masks
311     xEvent.xkey.keycode = gdkEvent->keyval;
312     xEvent.xkey.same_screen = true;
313
314     // NOTE: As the XEvents sent to the plug-in are synthesized and there is not a native window
315     // corresponding to the plug-in rectangle, some of the members of the XEvent structures are not
316     // set to their normal Xserver values. e.g. Key events don't have a position.
317     // source: https://developer.mozilla.org/en/NPEvent
318     xEvent.xkey.x = 0;
319     xEvent.xkey.y = 0;
320     xEvent.xkey.x_root = 0;
321     xEvent.xkey.y_root = 0;
322 #endif
323
324     if (!dispatchNPEvent(xEvent))
325         event->setDefaultHandled();
326 }
327
328 #if defined(XP_UNIX)
329 static unsigned int inputEventState(MouseEvent* event)
330 {
331     unsigned int state = 0;
332     if (event->ctrlKey())
333         state |= ControlMask;
334     if (event->shiftKey())
335         state |= ShiftMask;
336     if (event->altKey())
337         state |= Mod1Mask;
338     if (event->metaKey())
339         state |= Mod4Mask;
340     return state;
341 }
342
343 void PluginView::initXEvent(XEvent* xEvent)
344 {
345     memset(xEvent, 0, sizeof(XEvent));
346
347     xEvent->xany.serial = 0; // we are unaware of the last request processed by X Server
348     xEvent->xany.send_event = false;
349     xEvent->xany.display = GDK_DISPLAY();
350     // NOTE: event->xany.window doesn't always correspond to the .window property of other XEvent's
351     // but does in the case of KeyPress, KeyRelease, ButtonPress, ButtonRelease, and MotionNotify
352     // events; thus, this is right:
353     GtkWidget* widget = m_parentFrame->view()->hostWindow()->platformPageClient();
354     xEvent->xany.window = widget ? GDK_WINDOW_XWINDOW(gtk_widget_get_window(widget)) : 0;
355 }
356
357 static void setXButtonEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos, Frame* parentFrame)
358 {
359     XButtonEvent& xbutton = xEvent->xbutton;
360     xbutton.type = event->type() == eventNames().mousedownEvent ? ButtonPress : ButtonRelease;
361     xbutton.root = getRootWindow(parentFrame);
362     xbutton.subwindow = 0;
363     xbutton.time = event->timeStamp();
364     xbutton.x = postZoomPos.x();
365     xbutton.y = postZoomPos.y();
366     xbutton.x_root = event->screenX();
367     xbutton.y_root = event->screenY();
368     xbutton.state = inputEventState(event);
369     switch (event->button()) {
370     case MiddleButton:
371         xbutton.button = Button2;
372         break;
373     case RightButton:
374         xbutton.button = Button3;
375         break;
376     case LeftButton:
377     default:
378         xbutton.button = Button1;
379         break;
380     }
381     xbutton.same_screen = true;
382 }
383
384 static void setXMotionEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos, Frame* parentFrame)
385 {
386     XMotionEvent& xmotion = xEvent->xmotion;
387     xmotion.type = MotionNotify;
388     xmotion.root = getRootWindow(parentFrame);
389     xmotion.subwindow = 0;
390     xmotion.time = event->timeStamp();
391     xmotion.x = postZoomPos.x();
392     xmotion.y = postZoomPos.y();
393     xmotion.x_root = event->screenX();
394     xmotion.y_root = event->screenY();
395     xmotion.state = inputEventState(event);
396     xmotion.is_hint = NotifyNormal;
397     xmotion.same_screen = true;
398 }
399
400 static void setXCrossingEventSpecificFields(XEvent* xEvent, MouseEvent* event, const IntPoint& postZoomPos, Frame* parentFrame)
401 {
402     XCrossingEvent& xcrossing = xEvent->xcrossing;
403     xcrossing.type = event->type() == eventNames().mouseoverEvent ? EnterNotify : LeaveNotify;
404     xcrossing.root = getRootWindow(parentFrame);
405     xcrossing.subwindow = 0;
406     xcrossing.time = event->timeStamp();
407     xcrossing.x = postZoomPos.y();
408     xcrossing.y = postZoomPos.x();
409     xcrossing.x_root = event->screenX();
410     xcrossing.y_root = event->screenY();
411     xcrossing.state = inputEventState(event);
412     xcrossing.mode = NotifyNormal;
413     xcrossing.detail = NotifyDetailNone;
414     xcrossing.same_screen = true;
415     xcrossing.focus = false;
416 }
417 #endif
418
419 void PluginView::handleMouseEvent(MouseEvent* event)
420 {
421     JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
422
423     if (m_isWindowed)
424         return;
425
426     if (event->type() == eventNames().mousedownEvent) {
427         if (Page* page = m_parentFrame->page())
428             page->focusController()->setActive(true);
429         focusPluginElement();
430     }
431
432     NPEvent xEvent;
433 #if defined(XP_UNIX)
434     initXEvent(&xEvent);
435
436     IntPoint postZoomPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(event->absoluteLocation()));
437
438     if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent)
439         setXButtonEventSpecificFields(&xEvent, event, postZoomPos, m_parentFrame.get());
440     else if (event->type() == eventNames().mousemoveEvent)
441         setXMotionEventSpecificFields(&xEvent, event, postZoomPos, m_parentFrame.get());
442     else if (event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mouseoverEvent)
443         setXCrossingEventSpecificFields(&xEvent, event, postZoomPos, m_parentFrame.get());
444     else
445         return;
446 #endif
447
448     if (!dispatchNPEvent(xEvent))
449         event->setDefaultHandled();
450 }
451
452 #if defined(XP_UNIX)
453 void PluginView::handleFocusInEvent()
454 {
455     XEvent npEvent;
456     initXEvent(&npEvent);
457
458     XFocusChangeEvent& event = npEvent.xfocus;
459     event.type = 9; // FocusIn gets unset somewhere
460     event.mode = NotifyNormal;
461     event.detail = NotifyDetailNone;
462
463     dispatchNPEvent(npEvent);
464 }
465
466 void PluginView::handleFocusOutEvent()
467 {
468     XEvent npEvent;
469     initXEvent(&npEvent);
470
471     XFocusChangeEvent& event = npEvent.xfocus;
472     event.type = 10; // FocusOut gets unset somewhere
473     event.mode = NotifyNormal;
474     event.detail = NotifyDetailNone;
475
476     dispatchNPEvent(npEvent);
477 }
478 #endif
479
480 void PluginView::setParent(ScrollView* parent)
481 {
482     Widget::setParent(parent);
483
484     if (parent)
485         init();
486 }
487
488 void PluginView::setNPWindowRect(const IntRect&)
489 {
490     if (!m_isWindowed)
491         setNPWindowIfNeeded();
492 }
493
494 void PluginView::setNPWindowIfNeeded()
495 {
496     if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow)
497         return;
498
499     // If the plugin didn't load sucessfully, no point in calling setwindow
500     if (m_status != PluginStatusLoadedSuccessfully)
501         return;
502
503     // On Unix, only call plugin's setwindow if it's full-page or windowed
504     if (m_mode != NP_FULL && m_mode != NP_EMBED)
505         return;
506
507     // Check if the platformPluginWidget still exists
508     if (m_isWindowed && !platformPluginWidget())
509         return;
510
511     if (m_isWindowed) {
512         m_npWindow.x = m_windowRect.x();
513         m_npWindow.y = m_windowRect.y();
514         m_npWindow.width = m_windowRect.width();
515         m_npWindow.height = m_windowRect.height();
516
517         m_npWindow.clipRect.left = max(0, m_clipRect.x());
518         m_npWindow.clipRect.top = max(0, m_clipRect.y());
519         m_npWindow.clipRect.right = m_clipRect.x() + m_clipRect.width();
520         m_npWindow.clipRect.bottom = m_clipRect.y() + m_clipRect.height();
521
522         GtkAllocation allocation = { m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height() };
523         gtk_widget_size_allocate(platformPluginWidget(), &allocation);
524 #if defined(XP_UNIX)
525         if (!m_needsXEmbed) {
526             gtk_xtbin_set_position(GTK_XTBIN(platformPluginWidget()), m_windowRect.x(), m_windowRect.y());
527             gtk_xtbin_resize(platformPluginWidget(), m_windowRect.width(), m_windowRect.height());
528         }
529 #endif
530     } else {
531         m_npWindow.x = 0;
532         m_npWindow.y = 0;
533         m_npWindow.width = m_windowRect.width();
534         m_npWindow.height = m_windowRect.height();
535
536         m_npWindow.clipRect.left = 0;
537         m_npWindow.clipRect.top = 0;
538         m_npWindow.clipRect.right = 0;
539         m_npWindow.clipRect.bottom = 0;
540     }
541
542     PluginView::setCurrentPluginView(this);
543     JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
544     setCallingPlugin(true);
545     m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
546     setCallingPlugin(false);
547     PluginView::setCurrentPluginView(0);
548 }
549
550 void PluginView::setParentVisible(bool visible)
551 {
552     if (isParentVisible() == visible)
553         return;
554
555     Widget::setParentVisible(visible);
556
557     if (isSelfVisible() && platformPluginWidget()) {
558         if (visible)
559             gtk_widget_show(platformPluginWidget());
560         else
561             gtk_widget_hide(platformPluginWidget());
562     }
563 }
564
565 NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf)
566 {
567     WTF::String filename(buf, len);
568
569     if (filename.startsWith("file:///"))
570         filename = filename.substring(8);
571
572     // Get file info
573     if (!g_file_test ((filename.utf8()).data(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)))
574         return NPERR_FILE_NOT_FOUND;
575
576     //FIXME - read the file data into buffer
577     FILE* fileHandle = fopen((filename.utf8()).data(), "r");
578     
579     if (fileHandle == 0)
580         return NPERR_FILE_NOT_FOUND;
581
582     //buffer.resize();
583
584     int bytesRead = fread(buffer.data(), 1, 0, fileHandle);
585
586     fclose(fileHandle);
587
588     if (bytesRead <= 0)
589         return NPERR_FILE_NOT_FOUND;
590
591     return NPERR_NO_ERROR;
592 }
593
594 bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result)
595 {
596     switch (variable) {
597     case NPNVToolkit:
598 #if defined(XP_UNIX)
599         *static_cast<uint32_t*>(value) = 2;
600 #else
601         *static_cast<uint32_t*>(value) = 0;
602 #endif
603         *result = NPERR_NO_ERROR;
604         return true;
605
606     case NPNVSupportsXEmbedBool:
607 #if defined(XP_UNIX)
608         *static_cast<NPBool*>(value) = true;
609 #else
610         *static_cast<NPBool*>(value) = false;
611 #endif
612         *result = NPERR_NO_ERROR;
613         return true;
614
615     case NPNVjavascriptEnabledBool:
616         *static_cast<NPBool*>(value) = true;
617         *result = NPERR_NO_ERROR;
618         return true;
619
620     case NPNVSupportsWindowless:
621 #if defined(XP_UNIX)
622         *static_cast<NPBool*>(value) = true;
623 #else
624         *static_cast<NPBool*>(value) = false;
625 #endif
626         *result = NPERR_NO_ERROR;
627         return true;
628
629     default:
630         return false;
631     }
632 }
633
634 bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result)
635 {
636     switch (variable) {
637     case NPNVxDisplay:
638 #if defined(XP_UNIX)
639         if (m_needsXEmbed)
640             *(void **)value = (void *)GDK_DISPLAY();
641         else
642             *(void **)value = (void *)GTK_XTBIN(platformPluginWidget())->xtclient.xtdisplay;
643         *result = NPERR_NO_ERROR;
644 #else
645         *result = NPERR_GENERIC_ERROR;
646 #endif
647         return true;
648
649 #if defined(XP_UNIX)
650     case NPNVxtAppContext:
651         if (!m_needsXEmbed) {
652             *(void **)value = XtDisplayToApplicationContext (GTK_XTBIN(platformPluginWidget())->xtclient.xtdisplay);
653
654             *result = NPERR_NO_ERROR;
655         } else
656             *result = NPERR_GENERIC_ERROR;
657         return true;
658 #endif
659
660         case NPNVnetscapeWindow: {
661 #if defined(XP_UNIX)
662             void* w = reinterpret_cast<void*>(value);
663             GtkWidget* widget = m_parentFrame->view()->hostWindow()->platformPageClient();
664             *((XID *)w) = GDK_WINDOW_XWINDOW(gtk_widget_get_window(widget));
665 #endif
666 #ifdef GDK_WINDOWING_WIN32
667             HGDIOBJ* w = reinterpret_cast<HGDIOBJ*>(value);
668             GtkWidget* widget = m_parentFrame->view()->hostWindow()->platformPageClient();
669             *w = GDK_WINDOW_HWND(gtk_widget_get_window(widget));
670 #endif
671             *result = NPERR_NO_ERROR;
672             return true;
673         }
674
675     default:
676         return false;
677     }
678 }
679
680 void PluginView::invalidateRect(const IntRect& rect)
681 {
682     if (m_isWindowed) {
683         gtk_widget_queue_draw_area(GTK_WIDGET(platformPluginWidget()), rect.x(), rect.y(), rect.width(), rect.height());
684         return;
685     }
686
687     invalidateWindowlessPluginRect(rect);
688 }
689
690 void PluginView::invalidateRect(NPRect* rect)
691 {
692     if (!rect) {
693         invalidate();
694         return;
695     }
696
697     IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top);
698     invalidateWindowlessPluginRect(r);
699 }
700
701 void PluginView::invalidateRegion(NPRegion)
702 {
703     // TODO: optimize
704     invalidate();
705 }
706
707 void PluginView::forceRedraw()
708 {
709     if (m_isWindowed)
710         gtk_widget_queue_draw(platformPluginWidget());
711     else
712         gtk_widget_queue_draw(m_parentFrame->view()->hostWindow()->platformPageClient());
713 }
714
715 static Display* getPluginDisplay()
716 {
717     // The plugin toolkit might have a different X connection open.  Since we're
718     // a gdk/gtk app, we'll (probably?) have the same X connection as any gdk-based
719     // plugins, so we can return that.  We might want to add other implementations here
720     // later.
721
722 #if defined(XP_UNIX)
723     return GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
724 #else
725     return 0;
726 #endif
727 }
728
729 #if defined(XP_UNIX)
730 static void getVisualAndColormap(int depth, Visual** visual, Colormap* colormap)
731 {
732     *visual = 0;
733     *colormap = 0;
734
735     int rmaj, rmin;
736     if (depth == 32 && (!XRenderQueryVersion(GDK_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(GDK_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(GDK_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(GDK_DISPLAY(), GDK_ROOT_WINDOW(), *visual, AllocNone);
764 }
765 #endif
766
767 static gboolean plugRemovedCallback(GtkSocket* socket, gpointer)
768 {
769     return TRUE;
770 }
771
772 static void plugAddedCallback(GtkSocket* socket, PluginView* view)
773 {
774     if (!socket || !view)
775         return;
776
777     // FIXME: Java Plugins do not seem to draw themselves properly the
778     // first time unless we do a size-allocate after they have done
779     // the plug operation on their side, which in general does not
780     // happen since we do size-allocates before setting the
781     // NPWindow. Apply this workaround until we figure out a better
782     // solution, if any.
783     IntRect rect = view->frameRect();
784     GtkAllocation allocation = { rect.x(), rect.y(), rect.width(), rect.height() };
785     gtk_widget_size_allocate(GTK_WIDGET(socket), &allocation);
786 }
787
788 bool PluginView::platformStart()
789 {
790     ASSERT(m_isStarted);
791     ASSERT(m_status == PluginStatusLoadedSuccessfully);
792
793     if (m_plugin->pluginFuncs()->getvalue) {
794         PluginView::setCurrentPluginView(this);
795         JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
796         setCallingPlugin(true);
797         m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed);
798         setCallingPlugin(false);
799         PluginView::setCurrentPluginView(0);
800     }
801
802     if (m_isWindowed) {
803 #if defined(XP_UNIX)
804         GtkWidget* pageClient = m_parentFrame->view()->hostWindow()->platformPageClient();
805
806         if (m_needsXEmbed) {
807             // If our parent is not anchored the startup process will
808             // fail miserably for XEmbed plugins a bit later on when
809             // we try to get the ID of our window (since realize will
810             // fail), so let's just abort here.
811             if (!gtk_widget_get_parent(pageClient))
812                 return false;
813
814             setPlatformWidget(gtk_socket_new());
815             gtk_container_add(GTK_CONTAINER(pageClient), platformPluginWidget());
816             g_signal_connect(platformPluginWidget(), "plug-added", G_CALLBACK(plugAddedCallback), this);
817             g_signal_connect(platformPluginWidget(), "plug-removed", G_CALLBACK(plugRemovedCallback), NULL);
818         } else
819             setPlatformWidget(gtk_xtbin_new(gtk_widget_get_window(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         m_pluginDisplay = getPluginDisplay();
827     }
828
829     show();
830
831 #if defined(XP_UNIX)
832         NPSetWindowCallbackStruct* ws = new NPSetWindowCallbackStruct();
833         ws->type = 0;
834 #endif
835
836     if (m_isWindowed) {
837         m_npWindow.type = NPWindowTypeWindow;
838 #if defined(XP_UNIX)
839         if (m_needsXEmbed) {
840             GtkWidget* widget = platformPluginWidget();
841             gtk_widget_realize(widget);
842             m_npWindow.window = (void*)gtk_socket_get_id(GTK_SOCKET(platformPluginWidget()));
843             GdkWindow* window = gtk_widget_get_window(widget);
844             ws->display = GDK_WINDOW_XDISPLAY(window);
845             ws->visual = GDK_VISUAL_XVISUAL(gdk_drawable_get_visual(GDK_DRAWABLE(window)));
846             ws->depth = gdk_visual_get_depth(gdk_drawable_get_visual(GDK_DRAWABLE(window)));
847             ws->colormap = GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap(GDK_DRAWABLE(window)));
848         } else {
849             m_npWindow.window = (void*)GTK_XTBIN(platformPluginWidget())->xtwindow;
850             ws->display = GTK_XTBIN(platformPluginWidget())->xtdisplay;
851             ws->visual = GTK_XTBIN(platformPluginWidget())->xtclient.xtvisual;
852             ws->depth = GTK_XTBIN(platformPluginWidget())->xtclient.xtdepth;
853             ws->colormap = GTK_XTBIN(platformPluginWidget())->xtclient.xtcolormap;
854         }
855         XFlush (ws->display);
856 #elif defined(GDK_WINDOWING_WIN32)
857         m_npWindow.window = (void*)GDK_WINDOW_HWND(gtk_widget_get_window(platformPluginWidget()));
858 #endif
859     } else {
860         m_npWindow.type = NPWindowTypeDrawable;
861         m_npWindow.window = 0; // Not used?
862
863 #if defined(XP_UNIX)
864         GdkScreen* gscreen = gdk_screen_get_default();
865         GdkVisual* gvisual = gdk_screen_get_system_visual(gscreen);
866
867         if (gdk_visual_get_depth(gvisual) == 32 || !m_plugin->quirks().contains(PluginQuirkRequiresDefaultScreenDepth)) {
868             getVisualAndColormap(32, &m_visual, &m_colormap);
869             ws->depth = 32;
870         }
871
872         if (!m_visual) {
873             getVisualAndColormap(gdk_visual_get_depth(gvisual), &m_visual, &m_colormap);
874             ws->depth = gdk_visual_get_depth(gvisual);
875         }
876
877         ws->display = GDK_DISPLAY();
878         ws->visual = m_visual;
879         ws->colormap = m_colormap;
880
881         m_npWindow.x = 0;
882         m_npWindow.y = 0;
883         m_npWindow.width = -1;
884         m_npWindow.height = -1;
885 #else
886         notImplemented();
887         m_status = PluginStatusCanNotLoadPlugin;
888         return false;
889 #endif
890     }
891
892 #if defined(XP_UNIX)
893     m_npWindow.ws_info = ws;
894 #endif
895
896     // TODO remove in favor of null events, like mac port?
897     if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall)))
898         updatePluginWidget(); // was: setNPWindowIfNeeded(), but this doesn't produce 0x0 rects at first go
899
900     return true;
901 }
902
903 void PluginView::platformDestroy()
904 {
905 #if defined(XP_UNIX)
906     if (m_drawable) {
907         XFreePixmap(GDK_DISPLAY(), m_drawable);
908         m_drawable = 0;
909     }
910 #endif
911 }
912
913 void PluginView::halt()
914 {
915 }
916
917 void PluginView::restart()
918 {
919 }
920
921 } // namespace WebCore
922