PluginViewSymbian.cpp revision 1325a8463a7f9bc2ec969c7728aba33e3de91a17
1/*
2    Copyright (C) 2009 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 "Bridge.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 "Image.h"
36#include "JSDOMBinding.h"
37#include "KeyboardEvent.h"
38#include "MouseEvent.h"
39#include "NotImplemented.h"
40#include "Page.h"
41#include "PlatformKeyboardEvent.h"
42#include "PlatformMouseEvent.h"
43#include "PluginContainerSymbian.h"
44#include "PluginDebug.h"
45#include "PluginMainThreadScheduler.h"
46#include "PluginPackage.h"
47#include "QWebPageClient.h"
48#include "RenderLayer.h"
49#include "ScriptController.h"
50#include "Settings.h"
51#include "npfunctions.h"
52#include "npinterface.h"
53#include "npruntime_impl.h"
54#include "runtime_root.h"
55#include <QKeyEvent>
56#include <QPixmap.h>
57#include <QRegion>
58#include <QVector>
59#include <QWidget>
60#include <runtime/JSLock.h>
61#include <runtime/JSValue.h>
62
63using JSC::ExecState;
64using JSC::Interpreter;
65using JSC::JSLock;
66using JSC::JSObject;
67using JSC::UString;
68
69using namespace std;
70
71using namespace WTF;
72
73namespace WebCore {
74
75using namespace HTMLNames;
76
77void PluginView::updatePluginWidget()
78{
79    if (!parent())
80        return;
81    ASSERT(parent()->isFrameView());
82    FrameView* frameView = static_cast<FrameView*>(parent());
83    IntRect oldWindowRect = m_windowRect;
84    IntRect oldClipRect = m_clipRect;
85
86    m_windowRect = IntRect(frameView->contentsToWindow(frameRect().location()), frameRect().size());
87    m_clipRect = windowClipRect();
88    m_clipRect.move(-m_windowRect.x(), -m_windowRect.y());
89    if (m_windowRect == oldWindowRect && m_clipRect == oldClipRect)
90        return;
91
92    // in order to move/resize the plugin window at the same time as the rest of frame
93    // during e.g. scrolling, we set the mask and geometry in the paint() function, but
94    // as paint() isn't called when the plugin window is outside the frame which can
95    // be caused by a scroll, we need to move/resize immediately.
96    if (!m_windowRect.intersects(frameView->frameRect()))
97        setNPWindowIfNeeded();
98}
99
100void PluginView::setFocus()
101{
102    if (platformPluginWidget())
103        platformPluginWidget()->setFocus(Qt::OtherFocusReason);
104    else
105        Widget::setFocus();
106}
107
108void PluginView::show()
109{
110    setSelfVisible(true);
111
112    if (isParentVisible() && platformPluginWidget())
113        platformPluginWidget()->setVisible(true);
114}
115
116void PluginView::hide()
117{
118    setSelfVisible(false);
119
120    if (isParentVisible() && platformPluginWidget())
121        platformPluginWidget()->setVisible(false);
122}
123
124void PluginView::paint(GraphicsContext* context, const IntRect& rect)
125{
126    if (!m_isStarted) {
127        paintMissingPluginIcon(context, rect);
128        return;
129    }
130
131    if (context->paintingDisabled())
132        return;
133    m_npWindow.ws_info = (void*)(context->platformContext());
134    setNPWindowIfNeeded();
135
136    if (m_isWindowed && platformPluginWidget())
137        static_cast<PluginContainerSymbian*>(platformPluginWidget())->adjustGeometry();
138
139    if (m_isWindowed)
140        return;
141
142    context->save();
143    IntRect clipRect(rect);
144    clipRect.intersect(frameRect());
145    context->clip(clipRect);
146    context->translate(frameRect().location().x(), frameRect().location().y());
147
148    QPaintEvent ev(rect);
149    QEvent& npEvent = ev;
150    dispatchNPEvent(npEvent);
151
152    context->restore();
153}
154
155// TODO: Unify across ports.
156bool PluginView::dispatchNPEvent(NPEvent& event)
157{
158    if (!m_plugin->pluginFuncs()->event)
159        return false;
160
161    PluginView::setCurrentPluginView(this);
162    JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
163
164    setCallingPlugin(true);
165    bool accepted = m_plugin->pluginFuncs()->event(m_instance, &event);
166    setCallingPlugin(false);
167    PluginView::setCurrentPluginView(0);
168
169    return accepted;
170}
171
172void PluginView::handleKeyboardEvent(KeyboardEvent* event)
173{
174    if (m_isWindowed)
175        return;
176
177    QEvent& npEvent = *(event->keyEvent()->qtEvent());
178    if (!dispatchNPEvent(npEvent))
179        event->setDefaultHandled();
180}
181
182void PluginView::handleMouseEvent(MouseEvent* event)
183{
184    if (m_isWindowed)
185        return;
186
187    if (event->type() == eventNames().mousedownEvent) {
188        // Give focus to the plugin on click
189        if (Page* page = m_parentFrame->page())
190            page->focusController()->setActive(true);
191
192        focusPluginElement();
193    }
194
195    QEvent::Type type;
196    if (event->type() == eventNames().mousedownEvent)
197        type = QEvent::MouseButtonPress;
198    else if (event->type() == eventNames().mousemoveEvent)
199        type = QEvent::MouseMove;
200    else if (event->type() == eventNames().mouseupEvent)
201        type = QEvent::MouseButtonRelease;
202    else
203        return;
204
205    QPoint position(event->offsetX(), event->offsetY());
206    Qt::MouseButton button;
207    switch (event->which()) {
208    case 1:
209        button = Qt::LeftButton;
210        break;
211    case 2:
212        button = Qt::MidButton;
213        break;
214    case 3:
215        button = Qt::RightButton;
216        break;
217    default:
218        button = Qt::NoButton;
219    }
220    Qt::KeyboardModifiers modifiers = 0;
221    if (event->ctrlKey())
222        modifiers |= Qt::ControlModifier;
223    if (event->altKey())
224        modifiers |= Qt::AltModifier;
225    if (event->shiftKey())
226        modifiers |= Qt::ShiftModifier;
227    if (event->metaKey())
228        modifiers |= Qt::MetaModifier;
229    QMouseEvent mouseEvent(type, position, button, button, modifiers);
230    QEvent& npEvent = mouseEvent;
231    if (!dispatchNPEvent(npEvent))
232        event->setDefaultHandled();
233}
234
235void PluginView::setParent(ScrollView* parent)
236{
237    Widget::setParent(parent);
238
239    if (parent)
240        init();
241}
242
243void PluginView::setNPWindowRect(const IntRect&)
244{
245    if (!m_isWindowed)
246        setNPWindowIfNeeded();
247}
248
249void PluginView::setNPWindowIfNeeded()
250{
251    if (!m_isStarted || !parent() || !m_plugin->pluginFuncs()->setwindow)
252        return;
253    if (m_isWindowed) {
254        ASSERT(platformPluginWidget());
255        platformPluginWidget()->setGeometry(m_windowRect);
256        // if setMask is set with an empty QRegion, no clipping will
257        // be performed, so in that case we hide the plugin view
258        platformPluginWidget()->setVisible(!m_clipRect.isEmpty());
259        platformPluginWidget()->setMask(QRegion(m_clipRect));
260
261        m_npWindow.x = m_windowRect.x();
262        m_npWindow.y = m_windowRect.y();
263
264        m_npWindow.clipRect.left = m_clipRect.x();
265        m_npWindow.clipRect.top = m_clipRect.y();
266        m_npWindow.clipRect.right = m_clipRect.width();
267        m_npWindow.clipRect.bottom = m_clipRect.height();
268
269    } else {
270        // always call this method before painting.
271        m_npWindow.x = 0;
272        m_npWindow.y = 0;
273
274        m_npWindow.clipRect.left = 0;
275        m_npWindow.clipRect.top = 0;
276        m_npWindow.clipRect.right = m_windowRect.width();
277        m_npWindow.clipRect.bottom = m_windowRect.height();
278        m_npWindow.window = 0;
279    }
280
281    m_npWindow.width = m_windowRect.width();
282    m_npWindow.height = m_windowRect.height();
283    if (m_npWindow.x < 0 || m_npWindow.y < 0 || m_npWindow.width <= 0 || m_npWindow.height <= 0)
284        return;
285
286    PluginView::setCurrentPluginView(this);
287    JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
288    setCallingPlugin(true);
289    m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow);
290    setCallingPlugin(false);
291    PluginView::setCurrentPluginView(0);
292}
293
294void PluginView::setParentVisible(bool visible)
295{
296    if (isParentVisible() == visible)
297        return;
298
299    Widget::setParentVisible(visible);
300
301    if (isSelfVisible() && platformPluginWidget())
302        platformPluginWidget()->setVisible(visible);
303}
304
305NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32 len, const char* buf)
306{
307    notImplemented();
308    return NPERR_NO_ERROR;
309}
310
311NPError PluginView::getValueStatic(NPNVariable variable, void* value)
312{
313    LOG(Plugins, "PluginView::getValueStatic(%s)", prettyNameForNPNVariable(variable).data());
314
315    switch (variable) {
316    case NPNVjavascriptEnabledBool:
317        *static_cast<NPBool*>(value) = true;
318        return NPERR_NO_ERROR;
319
320    case NPNVSupportsWindowless:
321        *static_cast<NPBool*>(value) = true;
322        return NPERR_NO_ERROR;
323
324    default:
325        return NPERR_GENERIC_ERROR;
326    }
327}
328
329NPError PluginView::getValue(NPNVariable variable, void* value)
330{
331    LOG(Plugins, "PluginView::getValue(%s)", prettyNameForNPNVariable(variable).data());
332
333    switch (variable) {
334    case NPNVWindowNPObject: {
335        if (m_isJavaScriptPaused)
336            return NPERR_GENERIC_ERROR;
337
338        NPObject* windowScriptObject = m_parentFrame->script()->windowScriptNPObject();
339
340        // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugin/npruntime.html>
341        if (windowScriptObject)
342            _NPN_RetainObject(windowScriptObject);
343
344        void** v = (void**)value;
345        *v = windowScriptObject;
346
347        return NPERR_NO_ERROR;
348    }
349
350    case NPNVPluginElementNPObject: {
351        if (m_isJavaScriptPaused)
352            return NPERR_GENERIC_ERROR;
353
354        NPObject* pluginScriptObject = 0;
355
356        if (m_element->hasTagName(appletTag) || m_element->hasTagName(embedTag) || m_element->hasTagName(objectTag))
357            pluginScriptObject = static_cast<HTMLPlugInElement*>(m_element)->getNPObject();
358
359        // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugin/npruntime.html>
360        if (pluginScriptObject)
361            _NPN_RetainObject(pluginScriptObject);
362
363        void** v = (void**)value;
364        *v = pluginScriptObject;
365
366        return NPERR_NO_ERROR;
367    }
368    default:
369        return getValueStatic(variable, value);
370    }
371}
372
373void PluginView::invalidateRect(const IntRect& rect)
374{
375    if (m_isWindowed) {
376        platformWidget()->update(rect);
377        return;
378    }
379
380    invalidateWindowlessPluginRect(rect);
381}
382
383void PluginView::invalidateRect(NPRect* rect)
384{
385    if (m_isWindowed)
386        return;
387    if (!rect) {
388        invalidate();
389        return;
390    }
391    IntRect r(rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top);
392    m_invalidRects.append(r);
393    if (!m_invalidateTimer.isActive())
394        m_invalidateTimer.startOneShot(0.001);
395}
396
397void PluginView::invalidateRegion(NPRegion region)
398{
399    if (m_isWindowed)
400        return;
401
402    if (!region)
403        return;
404
405    QVector<QRect> rects = region->rects();
406    for (int i = 0; i < rects.size(); ++i) {
407        const QRect& qRect = rects.at(i);
408        m_invalidRects.append(qRect);
409        if (!m_invalidateTimer.isActive())
410            m_invalidateTimer.startOneShot(0.001);
411    }
412}
413
414void PluginView::forceRedraw()
415{
416    if (m_isWindowed)
417        return;
418    invalidate();
419}
420
421bool PluginView::platformStart()
422{
423    ASSERT(m_isStarted);
424    ASSERT(m_status == PluginStatusLoadedSuccessfully);
425
426    show();
427
428    if (m_isWindowed) {
429        QWebPageClient* client = m_parentFrame->view()->hostWindow()->platformPageClient();
430        // FIXME this will not work for QGraphicsView.
431        // But we cannot use winId because it will create a window and on S60,
432        // QWidgets should not create a window.
433        Q_ASSERT(qobject_cast<QWidget*>(client->pluginParent()));
434        setPlatformWidget(new PluginContainerSymbian(this,
435            qobject_cast<QWidget*>(client->pluginParent())));
436        m_npWindow.type = NPWindowTypeWindow;
437        m_npWindow.window = (void*)platformPluginWidget();
438
439    } else {
440        setPlatformWidget(0);
441        m_npWindow.type = NPWindowTypeDrawable;
442        m_npWindow.window = 0; // Not used?
443    }
444    setNPWindowIfNeeded();
445
446    return true;
447}
448
449void PluginView::platformDestroy()
450{
451    delete platformPluginWidget();
452}
453
454void PluginView::halt()
455{
456}
457
458void PluginView::restart()
459{
460}
461
462} // namespace WebCore
463