2009-03-03 Onne Gorter <onne.gorter@avinity.net>
[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  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "PluginView.h"
29
30 #include "Document.h"
31 #include "DocumentLoader.h"
32 #include "Element.h"
33 #include "FrameLoader.h"
34 #include "FrameLoadRequest.h"
35 #include "FrameTree.h"
36 #include "Frame.h"
37 #include "FrameView.h"
38 #include "GraphicsContext.h"
39 #include "Image.h"
40 #include "HTMLNames.h"
41 #include "HTMLPlugInElement.h"
42 #include "KeyboardEvent.h"
43 #include "MouseEvent.h"
44 #include "NotImplemented.h"
45 #include "Page.h"
46 #include "PlatformMouseEvent.h"
47 #include "PluginDebug.h"
48 #include "PluginPackage.h"
49 #include "RenderLayer.h"
50 #include "Settings.h"
51 #include "JSDOMBinding.h"
52 #include "ScriptController.h"
53 #include "npruntime_impl.h"
54 #include "runtime.h"
55 #include "runtime_root.h"
56 #include <runtime/JSLock.h>
57 #include <runtime/JSValue.h>
58
59 #include <gdkconfig.h>
60 #include <gtk/gtk.h>
61
62 #if PLATFORM(X11)
63 #include "gtk2xtbin.h"
64 #include <gdk/gdkx.h>
65 #endif
66 #ifdef GDK_WINDOWING_WIN32
67 #include "PluginMessageThrottlerWin.h"
68 #include <gdk/gdkwin32.h>
69 #endif
70
71 using JSC::ExecState;
72 using JSC::Interpreter;
73 using JSC::JSLock;
74 using JSC::JSObject;
75 using JSC::UString;
76
77 using std::min;
78
79 using namespace WTF;
80
81 namespace WebCore {
82
83 using namespace HTMLNames;
84
85 bool PluginView::dispatchNPEvent(NPEvent& event)
86 {
87     // sanity check
88     if (!m_plugin->pluginFuncs()->event)
89         return false;
90
91     PluginView::setCurrentPluginView(this);
92     JSC::JSLock::DropAllLocks dropAllLocks(false);
93     setCallingPlugin(true);
94
95     bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event);
96
97     setCallingPlugin(false);
98     PluginView::setCurrentPluginView(0);
99     return accepted;
100 }
101
102 void PluginView::updatePluginWidget()
103 {
104     if (!parent() || !m_isWindowed)
105         return;
106
107     ASSERT(parent()->isFrameView());
108     FrameView* frameView = static_cast<FrameView*>(parent());
109
110     IntRect oldWindowRect = m_windowRect;
111     IntRect oldClipRect = m_clipRect;
112
113     m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size());
114     m_clipRect = windowClipRect();
115     m_clipRect.move(-m_windowRect.x(), -m_windowRect.y());
116
117     if (platformPluginWidget() && (m_windowRect != oldWindowRect || m_clipRect != oldClipRect))
118         setNPWindowIfNeeded();
119 }
120
121 void PluginView::setFocus()
122 {
123     if (platformPluginWidget())
124         gtk_widget_grab_focus(platformPluginWidget());
125
126     Widget::setFocus();
127 }
128
129 void PluginView::show()
130 {
131     setSelfVisible(true);
132
133     if (isParentVisible() && platformPluginWidget())
134         gtk_widget_show(platformPluginWidget());
135
136     Widget::show();
137 }
138
139 void PluginView::hide()
140 {
141     setSelfVisible(false);
142
143     if (isParentVisible() && platformPluginWidget())
144         gtk_widget_hide(platformPluginWidget());
145
146     Widget::hide();
147 }
148
149 void PluginView::paint(GraphicsContext* context, const IntRect& rect)
150 {
151     if (!m_isStarted) {
152         paintMissingPluginIcon(context, rect);
153         return;
154     }
155
156     if (context->paintingDisabled())
157         return;
158
159     setNPWindowIfNeeded();
160
161     if (m_isWindowed)
162         return;
163
164     NPEvent npEvent;
165     /* Need to synthesize Xevents here */
166
167     m_npWindow.type = NPWindowTypeDrawable;
168
169     ASSERT(parent()->isFrameView());
170
171     if (!dispatchNPEvent(npEvent))
172         LOG(Events, "PluginView::paint(): Paint event not accepted");
173 }
174
175 void PluginView::handleKeyboardEvent(KeyboardEvent* event)
176 {
177     NPEvent npEvent;
178     
179     /* FIXME: Synthesize an XEvent to pass through */
180
181     JSC::JSLock::DropAllLocks dropAllLocks(false);
182     if (!dispatchNPEvent(npEvent))
183         event->setDefaultHandled();
184 }
185
186 void PluginView::handleMouseEvent(MouseEvent* event)
187 {
188     NPEvent npEvent;
189
190     if (!m_isWindowed)
191       return;
192
193     /* FIXME: Synthesize an XEvent to pass through */
194     IntPoint p = static_cast<FrameView*>(parent())->contentsToWindow(IntPoint(event->pageX(), event->pageY()));
195
196     JSC::JSLock::DropAllLocks dropAllLocks(false);
197     if (!dispatchNPEvent(npEvent))
198         event->setDefaultHandled();
199 }
200
201 void PluginView::setParent(ScrollView* parent)
202 {
203     Widget::setParent(parent);
204
205     if (parent)
206         init();
207     else {
208         if (!platformPluginWidget())
209             return;
210     }
211 }
212
213 void PluginView::setNPWindowRect(const IntRect&)
214 {
215     setNPWindowIfNeeded();
216 }
217
218 void PluginView::setNPWindowIfNeeded()
219 {
220     if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow)
221         return;
222
223     m_npWindow.x = m_windowRect.x();
224     m_npWindow.y = m_windowRect.y();
225     m_npWindow.width = m_windowRect.width();
226     m_npWindow.height = m_windowRect.height();
227
228     m_npWindow.clipRect.left = m_clipRect.x();
229     m_npWindow.clipRect.top = m_clipRect.y();
230     m_npWindow.clipRect.right = m_clipRect.width();
231     m_npWindow.clipRect.bottom = m_clipRect.height();
232
233     GtkAllocation allocation = { m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height() };
234     gtk_widget_size_allocate(platformPluginWidget(), &allocation);
235 #if PLATFORM(X11)
236     if (!m_needsXEmbed) {
237         gtk_xtbin_set_position(GTK_XTBIN(platformPluginWidget()), m_windowRect.x(), m_windowRect.y());
238         gtk_xtbin_resize(platformPluginWidget(), m_windowRect.width(), m_windowRect.height());
239     }
240 #endif
241
242     PluginView::setCurrentPluginView(this);
243     JSC::JSLock::DropAllLocks dropAllLocks(false);
244     setCallingPlugin(true);
245     m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
246     setCallingPlugin(false);
247     PluginView::setCurrentPluginView(0);
248 }
249
250 void PluginView::setParentVisible(bool visible)
251 {
252     if (isParentVisible() == visible)
253         return;
254
255     Widget::setParentVisible(visible);
256
257     if (isSelfVisible() && platformPluginWidget()) {
258         if (visible)
259             gtk_widget_show(platformPluginWidget());
260         else
261             gtk_widget_hide(platformPluginWidget());
262     }
263 }
264
265 void PluginView::stop()
266 {
267     if (!m_isStarted)
268         return;
269
270     HashSet<RefPtr<PluginStream> > streams = m_streams;
271     HashSet<RefPtr<PluginStream> >::iterator end = streams.end();
272     for (HashSet<RefPtr<PluginStream> >::iterator it = streams.begin(); it != end; ++it) {
273         (*it)->stop();
274         disconnectStream((*it).get());
275     }
276
277     ASSERT(m_streams.isEmpty());
278
279     m_isStarted = false;
280     JSC::JSLock::DropAllLocks dropAllLocks(false);
281
282     // Clear the window
283     m_npWindow.window = 0;
284     if (m_plugin->pluginFuncs()->setwindow && !m_plugin->quirks().contains(PluginQuirkDontSetNullWindowHandleOnDestroy)) {
285         PluginView::setCurrentPluginView(this);
286         setCallingPlugin(true);
287         m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
288         setCallingPlugin(false);
289         PluginView::setCurrentPluginView(0);
290     }
291
292 #ifdef XP_UNIX
293     if (m_isWindowed && m_npWindow.ws_info)
294            delete (NPSetWindowCallbackStruct *)m_npWindow.ws_info;
295     m_npWindow.ws_info = 0;
296 #endif
297
298     // Destroy the plugin
299     {
300         PluginView::setCurrentPluginView(this);
301         setCallingPlugin(true);
302         m_plugin->pluginFuncs()->destroy(m_instance, 0);
303         setCallingPlugin(false);
304         PluginView::setCurrentPluginView(0);
305     }
306
307     m_instance->pdata = 0;
308 }
309
310 static const char* MozillaUserAgent = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0";
311
312 const char* PluginView::userAgent()
313 {
314     if (m_plugin->quirks().contains(PluginQuirkWantsMozillaUserAgent))
315         return MozillaUserAgent;
316
317     if (m_userAgent.isNull())
318         m_userAgent = m_parentFrame->loader()->userAgent(m_url).utf8();
319
320     return m_userAgent.data();
321 }
322
323 const char* PluginView::userAgentStatic()
324 {
325     //FIXME - Lie and say we are Mozilla
326     return MozillaUserAgent;
327 }
328
329 NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32 len, const char* buf)
330 {
331     String filename(buf, len);
332
333     if (filename.startsWith("file:///"))
334         filename = filename.substring(8);
335
336     // Get file info
337     if (!g_file_test ((filename.utf8()).data(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)))
338         return NPERR_FILE_NOT_FOUND;
339
340     //FIXME - read the file data into buffer
341     FILE* fileHandle = fopen((filename.utf8()).data(), "r");
342     
343     if (fileHandle == 0)
344         return NPERR_FILE_NOT_FOUND;
345
346     //buffer.resize();
347
348     int bytesRead = fread(buffer.data(), 1, 0, fileHandle);
349
350     fclose(fileHandle);
351
352     if (bytesRead <= 0)
353         return NPERR_FILE_NOT_FOUND;
354
355     return NPERR_NO_ERROR;
356 }
357
358 NPError PluginView::getValueStatic(NPNVariable variable, void* value)
359 {
360     switch (variable) {
361     case NPNVToolkit:
362 #if PLATFORM(GTK)
363         *static_cast<uint32*>(value) = 2;
364 #else
365         *static_cast<uint32*>(value) = 0;
366 #endif
367         return NPERR_NO_ERROR;
368
369     case NPNVSupportsXEmbedBool:
370 #if PLATFORM(X11)
371         *static_cast<NPBool*>(value) = true;
372 #else
373         *static_cast<NPBool*>(value) = false;
374 #endif
375         return NPERR_NO_ERROR;
376
377     case NPNVjavascriptEnabledBool:
378         *static_cast<NPBool*>(value) = true;
379         return NPERR_NO_ERROR;
380
381     default:
382         return NPERR_GENERIC_ERROR;
383     }
384 }
385
386 NPError PluginView::getValue(NPNVariable variable, void* value)
387 {
388     switch (variable) {
389     case NPNVxDisplay:
390 #if PLATFORM(X11)
391         if (m_needsXEmbed)
392             *(void **)value = (void *)GDK_DISPLAY();
393         else
394             *(void **)value = (void *)GTK_XTBIN(platformPluginWidget())->xtclient.xtdisplay;
395         return NPERR_NO_ERROR;
396 #else
397         return NPERR_GENERIC_ERROR;
398 #endif
399
400 #if PLATFORM(X11)
401     case NPNVxtAppContext:
402         if (!m_needsXEmbed) {
403             *(void **)value = XtDisplayToApplicationContext (GTK_XTBIN(platformPluginWidget())->xtclient.xtdisplay);
404
405             return NPERR_NO_ERROR;
406         } else
407             return NPERR_GENERIC_ERROR;
408 #endif
409
410 #if ENABLE(NETSCAPE_PLUGIN_API)
411         case NPNVWindowNPObject: {
412             if (m_isJavaScriptPaused)
413                 return NPERR_GENERIC_ERROR;
414
415             NPObject* windowScriptObject = m_parentFrame->script()->windowScriptNPObject();
416
417             // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugin/npruntime.html>
418             if (windowScriptObject)
419                 _NPN_RetainObject(windowScriptObject);
420
421             void** v = (void**)value;
422             *v = windowScriptObject;
423             
424             return NPERR_NO_ERROR;
425         }
426
427         case NPNVPluginElementNPObject: {
428             if (m_isJavaScriptPaused)
429                 return NPERR_GENERIC_ERROR;
430
431             NPObject* pluginScriptObject = 0;
432
433             if (m_element->hasTagName(appletTag) || m_element->hasTagName(embedTag) || m_element->hasTagName(objectTag))
434                 pluginScriptObject = static_cast<HTMLPlugInElement*>(m_element)->getNPObject();
435
436             // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugin/npruntime.html>
437             if (pluginScriptObject)
438                 _NPN_RetainObject(pluginScriptObject);
439
440             void** v = (void**)value;
441             *v = pluginScriptObject;
442
443             return NPERR_NO_ERROR;
444         }
445 #endif
446
447         case NPNVnetscapeWindow: {
448 #if PLATFORM(X11)
449             void* w = reinterpret_cast<void*>(value);
450             *((XID *)w) = GDK_WINDOW_XWINDOW(m_parentFrame->view()->hostWindow()->platformWindow()->window);
451 #endif
452 #ifdef GDK_WINDOWING_WIN32
453             HGDIOBJ* w = reinterpret_cast<HGDIOBJ*>(value);
454             *w = GDK_WINDOW_HWND(m_parentFrame->view()->hostWindow()->platformWindow()->window);
455 #endif
456             return NPERR_NO_ERROR;
457         }
458
459         default:
460             return getValueStatic(variable, value);
461     }
462 }
463
464 void PluginView::invalidateRect(const IntRect& rect)
465 {
466     if (m_isWindowed) {
467         gtk_widget_queue_draw_area(GTK_WIDGET(platformPluginWidget()), rect.x(), rect.y(), rect.width(), rect.height());
468         return;
469     }
470
471     invalidateWindowlessPluginRect(rect);
472 }
473
474 void PluginView::invalidateRect(NPRect* rect)
475 {
476     if (!rect) {
477         invalidate();
478         return;
479     }
480
481     IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top);
482     invalidateRect(r);
483 }
484
485 void PluginView::forceRedraw()
486 {
487     if (m_isWindowed)
488         gtk_widget_queue_draw(platformPluginWidget());
489     else
490         gtk_widget_queue_draw(m_parentFrame->view()->hostWindow()->platformWindow());
491 }
492
493 PluginView::~PluginView()
494 {
495     stop();
496
497     deleteAllValues(m_requests);
498
499     freeStringArray(m_paramNames, m_paramCount);
500     freeStringArray(m_paramValues, m_paramCount);
501
502     m_parentFrame->script()->cleanupScriptObjectsForPlugin(this);
503
504     if (m_plugin && !(m_plugin->quirks().contains(PluginQuirkDontUnloadPlugin)))
505         m_plugin->unload();
506 }
507
508 static gboolean
509 plug_removed_cb(GtkSocket *socket, gpointer)
510 {
511     return TRUE;
512 }
513
514 void PluginView::init()
515 {
516     if (m_haveInitialized)
517         return;
518     m_haveInitialized = true;
519
520     if (!m_plugin) {
521         ASSERT(m_status == PluginStatusCanNotFindPlugin);
522         return;
523     }
524
525     if (!m_plugin->load()) {
526         m_plugin = 0;
527         m_status = PluginStatusCanNotLoadPlugin;
528         return;
529     }
530
531     if (!start()) {
532         m_status = PluginStatusCanNotLoadPlugin;
533         return;
534     }
535
536     if (m_plugin->pluginFuncs()->getvalue) {
537         PluginView::setCurrentPluginView(this);
538         JSC::JSLock::DropAllLocks dropAllLocks(false);
539         setCallingPlugin(true);
540         m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed);
541         setCallingPlugin(false);
542         PluginView::setCurrentPluginView(0);
543     }
544
545 #if PLATFORM(X11)
546     if (m_needsXEmbed) {
547         setPlatformWidget(gtk_socket_new());
548         gtk_container_add(GTK_CONTAINER(m_parentFrame->view()->hostWindow()->platformWindow()), platformPluginWidget());
549         g_signal_connect(platformPluginWidget(), "plug_removed", G_CALLBACK(plug_removed_cb), NULL);
550     } else if (m_isWindowed)
551         setPlatformWidget(gtk_xtbin_new(m_parentFrame->view()->hostWindow()->platformWindow()->window, 0));
552 #else
553     setPlatformWidget(gtk_socket_new());
554     gtk_container_add(GTK_CONTAINER(m_parentFrame->view()->hostWindow()->platformWindow()), platformPluginWidget());
555 #endif
556     show();
557
558     if (m_isWindowed) {
559         m_npWindow.type = NPWindowTypeWindow;
560 #if PLATFORM(X11)
561         NPSetWindowCallbackStruct *ws = new NPSetWindowCallbackStruct();
562
563         ws->type = 0;
564
565         if (m_needsXEmbed) {
566             gtk_widget_realize(platformPluginWidget());
567             m_npWindow.window = (void*)gtk_socket_get_id(GTK_SOCKET(platformPluginWidget()));
568             ws->display = GDK_WINDOW_XDISPLAY(platformPluginWidget()->window);
569             ws->visual = GDK_VISUAL_XVISUAL(gdk_drawable_get_visual(GDK_DRAWABLE(platformPluginWidget()->window)));
570             ws->depth = gdk_drawable_get_visual(GDK_DRAWABLE(platformPluginWidget()->window))->depth;
571             ws->colormap = GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap(GDK_DRAWABLE(platformPluginWidget()->window)));
572         } else {
573             m_npWindow.window = (void*)GTK_XTBIN(platformPluginWidget())->xtwindow;
574             ws->display = GTK_XTBIN(platformPluginWidget())->xtdisplay;
575             ws->visual = GTK_XTBIN(platformPluginWidget())->xtclient.xtvisual;
576             ws->depth = GTK_XTBIN(platformPluginWidget())->xtclient.xtdepth;
577             ws->colormap = GTK_XTBIN(platformPluginWidget())->xtclient.xtcolormap;
578         }
579         XFlush (ws->display);
580
581         m_npWindow.ws_info = ws;
582 #elif defined(GDK_WINDOWING_WIN32)
583         m_npWindow.window = (void*)GDK_WINDOW_HWND(platformPluginWidget()->window);
584 #endif
585     } else {
586         m_npWindow.type = NPWindowTypeDrawable;
587         m_npWindow.window = 0;
588     }
589
590     // TODO remove in favor of null events, like mac port?
591     if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall)))
592         updatePluginWidget(); // was: setNPWindowIfNeeded(), but this doesn't produce 0x0 rects at first go
593
594     m_status = PluginStatusLoadedSuccessfully;
595 }
596
597 } // namespace WebCore
598