1/*
2    Copyright (C) 2009, 2010 Nokia Corporation and/or its subsidiary(-ies)
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17    Boston, MA 02110-1301, USA.
18*/
19#include "config.h"
20#include "PluginView.h"
21
22#include "BridgeJSC.h"
23#include "Document.h"
24#include "DocumentLoader.h"
25#include "Element.h"
26#include "FocusController.h"
27#include "Frame.h"
28#include "FrameLoadRequest.h"
29#include "FrameLoader.h"
30#include "FrameTree.h"
31#include "FrameView.h"
32#include "GraphicsContext.h"
33#include "HTMLNames.h"
34#include "HTMLPlugInElement.h"
35#include "HostWindow.h"
36#include "Image.h"
37#include "JSDOMBinding.h"
38#include "KeyboardEvent.h"
39#include "MouseEvent.h"
40#include "NotImplemented.h"
41#include "Page.h"
42#include "PlatformKeyboardEvent.h"
43#include "PlatformMouseEvent.h"
44#include "PluginContainerSymbian.h"
45#include "PluginDebug.h"
46#include "PluginMainThreadScheduler.h"
47#include "PluginPackage.h"
48#include "QWebPageClient.h"
49#include "RenderLayer.h"
50#include "ScriptController.h"
51#include "Settings.h"
52#include "npfunctions.h"
53#include "npinterface.h"
54#include "npruntime_impl.h"
55#include "qgraphicswebview.h"
56#include "qwebframe.h"
57#include "qwebframe_p.h"
58#include "runtime_root.h"
59#include <QGraphicsProxyWidget>
60#include <QKeyEvent>
61#include <QPixmap>
62#include <QRegion>
63#include <QVector>
64#include <QWidget>
65#include <runtime/JSLock.h>
66#include <runtime/JSValue.h>
67
68typedef void (*_qtwebkit_page_plugin_created)(QWebFrame*, void*, void*); // frame, plugin instance, plugin functions
69static _qtwebkit_page_plugin_created qtwebkit_page_plugin_created = 0;
70QWEBKIT_EXPORT void qtwebkit_setPluginCreatedCallback(_qtwebkit_page_plugin_created cb)
71{
72    qtwebkit_page_plugin_created = cb;
73}
74
75using JSC::ExecState;
76using JSC::Interpreter;
77using JSC::JSLock;
78using JSC::JSObject;
79using JSC::UString;
80
81using namespace std;
82
83using namespace WTF;
84
85namespace WebCore {
86
87using namespace HTMLNames;
88
89void PluginView::updatePluginWidget()
90{
91    if (!parent())
92        return;
93    ASSERT(parent()->isFrameView());
94    FrameView* frameView = static_cast<FrameView*>(parent());
95    IntRect oldWindowRect = m_windowRect;
96    IntRect oldClipRect = m_clipRect;
97
98    m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size());
99
100    m_clipRect = windowClipRect();
101    m_clipRect.move(-m_windowRect.x(), -m_windowRect.y());
102    if (m_windowRect == oldWindowRect && m_clipRect == oldClipRect)
103        return;
104
105    setNPWindowIfNeeded();
106}
107
108void PluginView::setFocus(bool focused)
109{
110    if (platformPluginWidget()) {
111        if (focused)
112            platformPluginWidget()->setFocus(Qt::OtherFocusReason);
113    } else {
114        Widget::setFocus(focused);
115    }
116}
117
118void PluginView::show()
119{
120    setSelfVisible(true);
121
122    if (isParentVisible() && platformPluginWidget())
123        platformPluginWidget()->setVisible(true);
124}
125
126void PluginView::hide()
127{
128    setSelfVisible(false);
129
130    if (isParentVisible() && platformPluginWidget())
131        platformPluginWidget()->setVisible(false);
132}
133
134void PluginView::paint(GraphicsContext* context, const IntRect& rect)
135{
136    if (!m_isStarted) {
137        paintMissingPluginIcon(context, rect);
138        return;
139    }
140
141    if (context->paintingDisabled())
142        return;
143    m_npWindow.ws_info = (void*)(context->platformContext());
144    setNPWindowIfNeeded();
145
146    if (m_isWindowed && platformPluginWidget())
147        static_cast<PluginContainerSymbian*>(platformPluginWidget())->adjustGeometry();
148
149    if (m_isWindowed)
150        return;
151
152    context->save();
153    IntRect clipRect(rect);
154    clipRect.intersect(frameRect());
155    context->clip(clipRect);
156    context->translate(frameRect().location().x(), frameRect().location().y());
157
158    QPaintEvent ev(rect);
159    QEvent& npEvent = ev;
160    dispatchNPEvent(npEvent);
161
162    context->restore();
163}
164
165// TODO: Unify across ports.
166bool PluginView::dispatchNPEvent(NPEvent& event)
167{
168    if (!m_plugin->pluginFuncs()->event)
169        return false;
170
171    PluginView::setCurrentPluginView(this);
172    JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
173
174    setCallingPlugin(true);
175    bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event);
176    setCallingPlugin(false);
177    PluginView::setCurrentPluginView(0);
178
179    return accepted;
180}
181
182void PluginView::handleKeyboardEvent(KeyboardEvent* event)
183{
184    if (m_isWindowed)
185        return;
186
187    ASSERT(event->keyEvent()->qtEvent());
188    QEvent& npEvent = *(event->keyEvent()->qtEvent());
189    if (!dispatchNPEvent(npEvent))
190        event->setDefaultHandled();
191}
192
193void PluginView::handleMouseEvent(MouseEvent* event)
194{
195    if (m_isWindowed)
196        return;
197
198    if (event->type() == eventNames().mousedownEvent) {
199        // Give focus to the plugin on click
200        if (Page* page = m_parentFrame->page())
201            page->focusController()->setActive(true);
202
203        focusPluginElement();
204    }
205
206    QEvent::Type type;
207    if (event->type() == eventNames().mousedownEvent)
208        type = QEvent::MouseButtonPress;
209    else if (event->type() == eventNames().mousemoveEvent)
210        type = QEvent::MouseMove;
211    else if (event->type() == eventNames().mouseupEvent)
212        type = QEvent::MouseButtonRelease;
213    else
214        return;
215
216    QPoint position(event->offsetX(), event->offsetY());
217    Qt::MouseButton button;
218    switch (event->which()) {
219    case 1:
220        button = Qt::LeftButton;
221        break;
222    case 2:
223        button = Qt::MidButton;
224        break;
225    case 3:
226        button = Qt::RightButton;
227        break;
228    default:
229        button = Qt::NoButton;
230    }
231    Qt::KeyboardModifiers modifiers = 0;
232    if (event->ctrlKey())
233        modifiers |= Qt::ControlModifier;
234    if (event->altKey())
235        modifiers |= Qt::AltModifier;
236    if (event->shiftKey())
237        modifiers |= Qt::ShiftModifier;
238    if (event->metaKey())
239        modifiers |= Qt::MetaModifier;
240    QMouseEvent mouseEvent(type, position, button, button, modifiers);
241    QEvent& npEvent = mouseEvent;
242    if (!dispatchNPEvent(npEvent))
243        event->setDefaultHandled();
244}
245
246void PluginView::setParent(ScrollView* parent)
247{
248    Widget::setParent(parent);
249
250    if (parent) {
251        init();
252        if (m_status == PluginStatusLoadedSuccessfully)
253            updatePluginWidget();
254    }
255}
256
257void PluginView::setNPWindowRect(const IntRect&)
258{
259    if (!m_isWindowed)
260        setNPWindowIfNeeded();
261}
262
263void PluginView::setNPWindowIfNeeded()
264{
265    if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow)
266        return;
267    if (m_isWindowed) {
268        ASSERT(platformPluginWidget());
269        platformPluginWidget()->setGeometry(m_windowRect);
270        // if setMask is set with an empty QRegion, no clipping will
271        // be performed, so in that case we hide the plugin view
272        platformPluginWidget()->setVisible(!m_clipRect.isEmpty());
273        platformPluginWidget()->setMask(QRegion(m_clipRect));
274
275        m_npWindow.x = m_windowRect.x();
276        m_npWindow.y = m_windowRect.y();
277
278        m_npWindow.clipRect.left = max(0, m_clipRect.x());
279        m_npWindow.clipRect.top = max(0, m_clipRect.y());
280        m_npWindow.clipRect.right = m_clipRect.x() + m_clipRect.width();
281        m_npWindow.clipRect.bottom = m_clipRect.y() + m_clipRect.height();
282
283    } else {
284        // always call this method before painting.
285        m_npWindow.x = m_windowRect.x();
286        m_npWindow.y = m_windowRect.y();
287
288        m_npWindow.clipRect.left = 0;
289        m_npWindow.clipRect.top = 0;
290        m_npWindow.clipRect.right = m_windowRect.width();
291        m_npWindow.clipRect.bottom = m_windowRect.height();
292        m_npWindow.window = 0;
293    }
294
295    m_npWindow.width = m_windowRect.width();
296    m_npWindow.height = m_windowRect.height();
297
298    PluginView::setCurrentPluginView(this);
299    JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
300    setCallingPlugin(true);
301    m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
302    setCallingPlugin(false);
303    PluginView::setCurrentPluginView(0);
304}
305
306void PluginView::setParentVisible(bool visible)
307{
308    if (isParentVisible() == visible)
309        return;
310
311    Widget::setParentVisible(visible);
312
313    if (isSelfVisible() && platformPluginWidget())
314        platformPluginWidget()->setVisible(visible);
315}
316
317NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf)
318{
319    notImplemented();
320    return NPERR_NO_ERROR;
321}
322
323bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result)
324{
325    switch (variable) {
326    case NPNVjavascriptEnabledBool:
327        *static_cast<NPBool*>(value) = true;
328        *result = NPERR_NO_ERROR;
329        return true;
330
331    case NPNVSupportsWindowless:
332        *static_cast<NPBool*>(value) = true;
333        *result = NPERR_NO_ERROR;
334        return true;
335
336    default:
337        return false;
338    }
339}
340
341bool PluginView::platformGetValue(NPNVariable, void*, NPError*)
342{
343    return false;
344}
345
346void PluginView::invalidateRect(const IntRect& rect)
347{
348    if (m_isWindowed) {
349        platformWidget()->update(rect);
350        return;
351    }
352
353    invalidateWindowlessPluginRect(rect);
354}
355
356void PluginView::invalidateRect(NPRect* rect)
357{
358    if (m_isWindowed)
359        return;
360    if (!rect) {
361        invalidate();
362        return;
363    }
364    IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top);
365    m_invalidRects.append(r);
366    if (!m_invalidateTimer.isActive())
367        m_invalidateTimer.startOneShot(0.001);
368}
369
370void PluginView::invalidateRegion(NPRegion region)
371{
372    if (m_isWindowed)
373        return;
374
375    if (!region)
376        return;
377
378    QVector<QRect> rects = region->rects();
379    for (int i = 0; i < rects.size(); ++i) {
380        const QRect& qRect = rects.at(i);
381        m_invalidRects.append(qRect);
382        if (!m_invalidateTimer.isActive())
383            m_invalidateTimer.startOneShot(0.001);
384    }
385}
386
387void PluginView::forceRedraw()
388{
389    if (m_isWindowed)
390        return;
391    invalidate();
392}
393
394bool PluginView::platformStart()
395{
396    ASSERT(m_isStarted);
397    ASSERT(m_status == PluginStatusLoadedSuccessfully);
398
399    show();
400
401    if (m_isWindowed) {
402        QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient();
403        QGraphicsProxyWidget* proxy = 0;
404        if (QGraphicsWebView *webView = qobject_cast<QGraphicsWebView*>(client->pluginParent()))
405            proxy = new QGraphicsProxyWidget(webView);
406
407        PluginContainerSymbian* container = new PluginContainerSymbian(this, proxy ? 0 : client->ownerWidget(), proxy);
408        setPlatformWidget(container);
409        if (proxy)
410            proxy->setWidget(container);
411
412        m_npWindow.type = NPWindowTypeWindow;
413        m_npWindow.window = (void*)platformPluginWidget();
414
415    } else {
416        setPlatformWidget(0);
417        m_npWindow.type = NPWindowTypeDrawable;
418        m_npWindow.window = 0; // Not used?
419    }
420    updatePluginWidget();
421    setNPWindowIfNeeded();
422
423    if (qtwebkit_page_plugin_created)
424        qtwebkit_page_plugin_created(QWebFramePrivate::kit(m_parentFrame.get()), m_instance, (void*)(m_plugin->pluginFuncs()));
425
426    return true;
427}
428
429void PluginView::platformDestroy()
430{
431    if (platformPluginWidget()) {
432        PluginContainerSymbian* container = static_cast<PluginContainerSymbian*>(platformPluginWidget());
433        if (container && container->proxy())
434            delete container->proxy();
435        else
436            delete container;
437    }
438}
439
440void PluginView::halt()
441{
442}
443
444void PluginView::restart()
445{
446}
447
448} // namespace WebCore
449