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