5ec515b8d1d307c75cc10effcc57d0d41ab99e22
[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 void PluginView::updatePluginWidget()
86 {
87     if (!parent() || !m_isWindowed)
88         return;
89
90     ASSERT(parent()->isFrameView());
91     FrameView* frameView = static_cast<FrameView*>(parent());
92
93     IntRect oldWindowRect = m_windowRect;
94     IntRect oldClipRect = m_clipRect;
95
96     m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size());
97     m_clipRect = windowClipRect();
98     m_clipRect.move(-m_windowRect.x(), -m_windowRect.y());
99
100     GtkAllocation allocation = { m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height() };
101     if (platformPluginWidget()) {
102         gtk_widget_size_allocate(platformPluginWidget(), &allocation);
103 #if PLATFORM(X11)
104         if (!m_needsXEmbed) {
105             gtk_xtbin_set_position(GTK_XTBIN(platformPluginWidget()), m_windowRect.x(), m_windowRect.y());
106             gtk_xtbin_resize(platformPluginWidget(), m_windowRect.width(), m_windowRect.height());
107         }
108 #endif
109     }
110 }
111
112 void PluginView::setFocus()
113 {
114     if (platformPluginWidget())
115         gtk_widget_grab_focus(platformPluginWidget());
116
117     Widget::setFocus();
118 }
119
120 void PluginView::show()
121 {
122     setSelfVisible(true);
123
124     if (isParentVisible() && platformPluginWidget())
125         gtk_widget_show(platformPluginWidget());
126
127     Widget::show();
128 }
129
130 void PluginView::hide()
131 {
132     setSelfVisible(false);
133
134     if (isParentVisible() && platformPluginWidget())
135         gtk_widget_hide(platformPluginWidget());
136
137     Widget::hide();
138 }
139
140 void PluginView::paint(GraphicsContext* context, const IntRect& rect)
141 {
142     if (!m_isStarted) {
143         paintMissingPluginIcon(context, rect);
144         return;
145     }
146
147     if (m_isWindowed || context->paintingDisabled())
148         return;
149
150     NPEvent npEvent;
151     /* Need to synthesize Xevents here */
152
153     m_npWindow.type = NPWindowTypeDrawable;
154
155     ASSERT(parent()->isFrameView());
156
157     if (m_plugin->pluginFuncs()->event) {
158         JSC::JSLock::DropAllLocks dropAllLocks(false);
159         m_plugin->pluginFuncs()->event(m_instance, &npEvent);
160     }
161
162     setNPWindowRect(frameRect());
163 }
164
165 void PluginView::handleKeyboardEvent(KeyboardEvent* event)
166 {
167     NPEvent npEvent;
168     
169     /* FIXME: Synthesize an XEvent to pass through */
170
171     JSC::JSLock::DropAllLocks dropAllLocks(false);
172     if (!m_plugin->pluginFuncs()->event(m_instance, &npEvent))
173         event->setDefaultHandled();
174 }
175
176 void PluginView::handleMouseEvent(MouseEvent* event)
177 {
178     NPEvent npEvent;
179
180     if (!m_isWindowed)
181       return;
182
183     /* FIXME: Synthesize an XEvent to pass through */
184     IntPoint p = static_cast<FrameView*>(parent())->contentsToWindow(IntPoint(event->pageX(), event->pageY()));
185
186     JSC::JSLock::DropAllLocks dropAllLocks(false);
187     if (!m_plugin->pluginFuncs()->event(m_instance, &npEvent))
188         event->setDefaultHandled();
189 }
190
191 void PluginView::setParent(ScrollView* parent)
192 {
193     Widget::setParent(parent);
194
195     if (parent)
196         init();
197     else {
198         if (!platformPluginWidget())
199             return;
200     }
201 }
202
203 void PluginView::setNPWindowRect(const IntRect& rect)
204 {
205     if (!m_isStarted || !parent())
206         return;
207
208     IntPoint p = static_cast<FrameView*>(parent())->contentsToWindow(rect.location());
209     m_npWindow.x = p.x();
210     m_npWindow.y = p.y();
211
212     m_npWindow.width = rect.width();
213     m_npWindow.height = rect.height();
214
215     m_npWindow.clipRect.left = 0;
216     m_npWindow.clipRect.top = 0;
217     m_npWindow.clipRect.right = rect.width();
218     m_npWindow.clipRect.bottom = rect.height();
219
220     if (m_npWindow.x < 0 || m_npWindow.y < 0 ||
221         m_npWindow.width <= 0 || m_npWindow.height <= 0)
222         return;
223
224     if (m_plugin->pluginFuncs()->setwindow) {
225         PluginView::setCurrentPluginView(this);
226         JSC::JSLock::DropAllLocks dropAllLocks(false);
227         setCallingPlugin(true);
228         m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
229         setCallingPlugin(false);
230         PluginView::setCurrentPluginView(0);
231
232         if (!m_isWindowed)
233             return;
234
235         ASSERT(platformPluginWidget());
236     }
237 }
238
239 void PluginView::setParentVisible(bool visible)
240 {
241     if (isParentVisible() == visible)
242         return;
243
244     Widget::setParentVisible(visible);
245
246     if (isSelfVisible() && platformPluginWidget()) {
247         if (visible)
248             gtk_widget_show(platformPluginWidget());
249         else
250             gtk_widget_hide(platformPluginWidget());
251     }
252 }
253
254 void PluginView::stop()
255 {
256     if (!m_isStarted)
257         return;
258
259     HashSet<RefPtr<PluginStream> > streams = m_streams;
260     HashSet<RefPtr<PluginStream> >::iterator end = streams.end();
261     for (HashSet<RefPtr<PluginStream> >::iterator it = streams.begin(); it != end; ++it) {
262         (*it)->stop();
263         disconnectStream((*it).get());
264     }
265
266     ASSERT(m_streams.isEmpty());
267
268     m_isStarted = false;
269     JSC::JSLock::DropAllLocks dropAllLocks(false);
270
271     // Clear the window
272     m_npWindow.window = 0;
273     if (m_plugin->pluginFuncs()->setwindow && !m_plugin->quirks().contains(PluginQuirkDontSetNullWindowHandleOnDestroy)) {
274         PluginView::setCurrentPluginView(this);
275         setCallingPlugin(true);
276         m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
277         setCallingPlugin(false);
278         PluginView::setCurrentPluginView(0);
279     }
280
281 #ifdef XP_UNIX
282     if (m_isWindowed && m_npWindow.ws_info)
283            delete (NPSetWindowCallbackStruct *)m_npWindow.ws_info;
284     m_npWindow.ws_info = 0;
285 #endif
286
287     // Destroy the plugin
288     {
289         PluginView::setCurrentPluginView(this);
290         setCallingPlugin(true);
291         m_plugin->pluginFuncs()->destroy(m_instance, 0);
292         setCallingPlugin(false);
293         PluginView::setCurrentPluginView(0);
294     }
295
296     m_instance->pdata = 0;
297 }
298
299 static const char* MozillaUserAgent = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0";
300
301 const char* PluginView::userAgent()
302 {
303     if (m_plugin->quirks().contains(PluginQuirkWantsMozillaUserAgent))
304         return MozillaUserAgent;
305
306     if (m_userAgent.isNull())
307         m_userAgent = m_parentFrame->loader()->userAgent(m_url).utf8();
308
309     return m_userAgent.data();
310 }
311
312 const char* PluginView::userAgentStatic()
313 {
314     //FIXME - Lie and say we are Mozilla
315     return MozillaUserAgent;
316 }
317
318 NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32 len, const char* buf)
319 {
320     String filename(buf, len);
321
322     if (filename.startsWith("file:///"))
323         filename = filename.substring(8);
324
325     // Get file info
326     if (!g_file_test ((filename.utf8()).data(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)))
327         return NPERR_FILE_NOT_FOUND;
328
329     //FIXME - read the file data into buffer
330     FILE* fileHandle = fopen((filename.utf8()).data(), "r");
331     
332     if (fileHandle == 0)
333         return NPERR_FILE_NOT_FOUND;
334
335     //buffer.resize();
336
337     int bytesRead = fread(buffer.data(), 1, 0, fileHandle);
338
339     fclose(fileHandle);
340
341     if (bytesRead <= 0)
342         return NPERR_FILE_NOT_FOUND;
343
344     return NPERR_NO_ERROR;
345 }
346
347 NPError PluginView::getValueStatic(NPNVariable variable, void* value)
348 {
349     switch (variable) {
350     case NPNVToolkit:
351 #if PLATFORM(GTK)
352         *static_cast<uint32*>(value) = 2;
353 #else
354         *static_cast<uint32*>(value) = 0;
355 #endif
356         return NPERR_NO_ERROR;
357
358     case NPNVSupportsXEmbedBool:
359 #if PLATFORM(X11)
360         *static_cast<NPBool*>(value) = true;
361 #else
362         *static_cast<NPBool*>(value) = false;
363 #endif
364         return NPERR_NO_ERROR;
365
366     case NPNVjavascriptEnabledBool:
367         *static_cast<NPBool*>(value) = true;
368         return NPERR_NO_ERROR;
369
370     default:
371         return NPERR_GENERIC_ERROR;
372     }
373 }
374
375 NPError PluginView::getValue(NPNVariable variable, void* value)
376 {
377     switch (variable) {
378     case NPNVxDisplay:
379 #if PLATFORM(X11)
380         if (m_needsXEmbed)
381             *(void **)value = (void *)GDK_DISPLAY();
382         else
383             *(void **)value = (void *)GTK_XTBIN(platformPluginWidget())->xtclient.xtdisplay;
384         return NPERR_NO_ERROR;
385 #else
386         return NPERR_GENERIC_ERROR;
387 #endif
388
389 #if PLATFORM(X11)
390     case NPNVxtAppContext:
391         if (!m_needsXEmbed) {
392             *(void **)value = XtDisplayToApplicationContext (GTK_XTBIN(platformPluginWidget())->xtclient.xtdisplay);
393
394             return NPERR_NO_ERROR;
395         } else
396             return NPERR_GENERIC_ERROR;
397 #endif
398
399 #if ENABLE(NETSCAPE_PLUGIN_API)
400         case NPNVWindowNPObject: {
401             if (m_isJavaScriptPaused)
402                 return NPERR_GENERIC_ERROR;
403
404             NPObject* windowScriptObject = m_parentFrame->script()->windowScriptNPObject();
405
406             // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugin/npruntime.html>
407             if (windowScriptObject)
408                 _NPN_RetainObject(windowScriptObject);
409
410             void** v = (void**)value;
411             *v = windowScriptObject;
412             
413             return NPERR_NO_ERROR;
414         }
415
416         case NPNVPluginElementNPObject: {
417             if (m_isJavaScriptPaused)
418                 return NPERR_GENERIC_ERROR;
419
420             NPObject* pluginScriptObject = 0;
421
422             if (m_element->hasTagName(appletTag) || m_element->hasTagName(embedTag) || m_element->hasTagName(objectTag))
423                 pluginScriptObject = static_cast<HTMLPlugInElement*>(m_element)->getNPObject();
424
425             // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugin/npruntime.html>
426             if (pluginScriptObject)
427                 _NPN_RetainObject(pluginScriptObject);
428
429             void** v = (void**)value;
430             *v = pluginScriptObject;
431
432             return NPERR_NO_ERROR;
433         }
434 #endif
435
436         case NPNVnetscapeWindow: {
437 #if PLATFORM(X11)
438             void* w = reinterpret_cast<void*>(value);
439             *((XID *)w) = GDK_WINDOW_XWINDOW(m_parentFrame->view()->hostWindow()->platformWindow()->window);
440 #endif
441 #ifdef GDK_WINDOWING_WIN32
442             HGDIOBJ* w = reinterpret_cast<HGDIOBJ*>(value);
443             *w = GDK_WINDOW_HWND(m_parentFrame->view()->hostWindow()->platformWindow()->window);
444 #endif
445             return NPERR_NO_ERROR;
446         }
447
448         default:
449             return getValueStatic(variable, value);
450     }
451 }
452
453 void PluginView::invalidateRect(const IntRect& rect)
454 {
455     if (m_isWindowed) {
456         gtk_widget_queue_draw_area(GTK_WIDGET(platformPluginWidget()), rect.x(), rect.y(), rect.width(), rect.height());
457         return;
458     }
459
460     invalidateWindowlessPluginRect(rect);
461 }
462
463 void PluginView::invalidateRect(NPRect* rect)
464 {
465     if (!rect) {
466         invalidate();
467         return;
468     }
469
470     IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top);
471     invalidateRect(r);
472 }
473
474 void PluginView::forceRedraw()
475 {
476     if (m_isWindowed)
477         gtk_widget_queue_draw(platformPluginWidget());
478     else
479         gtk_widget_queue_draw(m_parentFrame->view()->hostWindow()->platformWindow());
480 }
481
482 PluginView::~PluginView()
483 {
484     stop();
485
486     deleteAllValues(m_requests);
487
488     freeStringArray(m_paramNames, m_paramCount);
489     freeStringArray(m_paramValues, m_paramCount);
490
491     m_parentFrame->script()->cleanupScriptObjectsForPlugin(this);
492
493     if (m_plugin && !(m_plugin->quirks().contains(PluginQuirkDontUnloadPlugin)))
494         m_plugin->unload();
495 }
496
497 static gboolean
498 plug_removed_cb(GtkSocket *socket, gpointer)
499 {
500     return TRUE;
501 }
502
503 void PluginView::init()
504 {
505     if (m_haveInitialized)
506         return;
507     m_haveInitialized = true;
508
509     if (!m_plugin) {
510         ASSERT(m_status == PluginStatusCanNotFindPlugin);
511         return;
512     }
513
514     if (!m_plugin->load()) {
515         m_plugin = 0;
516         m_status = PluginStatusCanNotLoadPlugin;
517         return;
518     }
519
520     if (!start()) {
521         m_status = PluginStatusCanNotLoadPlugin;
522         return;
523     }
524
525     if (m_plugin->pluginFuncs()->getvalue) {
526         PluginView::setCurrentPluginView(this);
527         JSC::JSLock::DropAllLocks dropAllLocks(false);
528         setCallingPlugin(true);
529         m_plugin->pluginFuncs()->getvalue(m_instance, NPPVpluginNeedsXEmbed, &m_needsXEmbed);
530         setCallingPlugin(false);
531         PluginView::setCurrentPluginView(0);
532     }
533
534 #if PLATFORM(X11)
535     if (m_needsXEmbed) {
536         setPlatformWidget(gtk_socket_new());
537         gtk_container_add(GTK_CONTAINER(m_parentFrame->view()->hostWindow()->platformWindow()), platformPluginWidget());
538         g_signal_connect(platformPluginWidget(), "plug_removed", G_CALLBACK(plug_removed_cb), NULL);
539     } else if (m_isWindowed)
540         setPlatformWidget(gtk_xtbin_new(m_parentFrame->view()->hostWindow()->platformWindow()->window, 0));
541 #else
542     setPlatformWidget(gtk_socket_new());
543     gtk_container_add(GTK_CONTAINER(m_parentFrame->view()->hostWindow()->platformWindow()), platformPluginWidget());
544 #endif
545     show();
546
547     if (m_isWindowed) {
548         m_npWindow.type = NPWindowTypeWindow;
549 #if PLATFORM(X11)
550         NPSetWindowCallbackStruct *ws = new NPSetWindowCallbackStruct();
551
552         ws->type = 0;
553
554         if (m_needsXEmbed) {
555             gtk_widget_realize(platformPluginWidget());
556             m_npWindow.window = (void*)gtk_socket_get_id(GTK_SOCKET(platformPluginWidget()));
557             ws->display = GDK_WINDOW_XDISPLAY(platformPluginWidget()->window);
558             ws->visual = GDK_VISUAL_XVISUAL(gdk_drawable_get_visual(GDK_DRAWABLE(platformPluginWidget()->window)));
559             ws->depth = gdk_drawable_get_visual(GDK_DRAWABLE(platformPluginWidget()->window))->depth;
560             ws->colormap = GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap(GDK_DRAWABLE(platformPluginWidget()->window)));
561         } else {
562             m_npWindow.window = (void*)GTK_XTBIN(platformPluginWidget())->xtwindow;
563             ws->display = GTK_XTBIN(platformPluginWidget())->xtdisplay;
564             ws->visual = GTK_XTBIN(platformPluginWidget())->xtclient.xtvisual;
565             ws->depth = GTK_XTBIN(platformPluginWidget())->xtclient.xtdepth;
566             ws->colormap = GTK_XTBIN(platformPluginWidget())->xtclient.xtcolormap;
567         }
568         XFlush (ws->display);
569
570         m_npWindow.ws_info = ws;
571 #elif defined(GDK_WINDOWING_WIN32)
572         m_npWindow.window = (void*)GDK_WINDOW_HWND(platformPluginWidget()->window);
573 #endif
574     } else {
575         m_npWindow.type = NPWindowTypeDrawable;
576         m_npWindow.window = 0;
577     }
578
579     if (!(m_plugin->quirks().contains(PluginQuirkDeferFirstSetWindowCall)))
580         setNPWindowRect(frameRect());
581
582     m_status = PluginStatusLoadedSuccessfully;
583 }
584
585 } // namespace WebCore