1/*
2 * Copyright (C) 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 */
20
21#include "config.h"
22
23#include "PageClientQt.h"
24#include "TextureMapperQt.h"
25#include "texmap/TextureMapperPlatformLayer.h"
26#include <QGraphicsScene>
27#include <QGraphicsView>
28#if defined(Q_WS_X11)
29#include <QX11Info>
30#endif
31
32#ifdef QT_OPENGL_LIB
33#include "opengl/TextureMapperGL.h"
34#include <QGLWidget>
35#endif
36
37namespace WebCore {
38
39#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
40class PlatformLayerProxyQt : public QObject, public virtual TextureMapperLayerClient {
41public:
42    PlatformLayerProxyQt(QWebFrame* frame, TextureMapperContentLayer* layer, QObject* object)
43        : QObject(object)
44        , m_frame(frame)
45        , m_layer(layer)
46    {
47        if (m_layer)
48            m_layer->setPlatformLayerClient(this);
49        m_frame->d->rootGraphicsLayer = m_layer;
50    }
51
52    void setTextureMapper(PassOwnPtr<TextureMapper> textureMapper)
53    {
54        m_frame->d->textureMapper = textureMapper;
55    }
56
57    virtual ~PlatformLayerProxyQt()
58    {
59        if (m_layer)
60            m_layer->setPlatformLayerClient(0);
61        if (m_frame->d)
62            m_frame->d->rootGraphicsLayer = 0;
63    }
64
65    virtual TextureMapper* textureMapper()
66    {
67        return m_frame->d->textureMapper.get();
68    }
69
70    // Since we just paint the composited tree and never create a special item for it, we don't have to handle its size changes.
71    void setSizeChanged(const IntSize&) { }
72
73private:
74    QWebFrame* m_frame;
75    TextureMapperContentLayer* m_layer;
76};
77
78class PlatformLayerProxyQWidget : public PlatformLayerProxyQt {
79public:
80    PlatformLayerProxyQWidget(QWebFrame* frame, TextureMapperContentLayer* layer, QWidget* widget)
81        : PlatformLayerProxyQt(frame, layer, widget)
82        , m_widget(widget)
83    {
84        if (m_widget)
85            m_widget->installEventFilter(this);
86
87        if (textureMapper())
88            return;
89
90        setTextureMapper(TextureMapperQt::create());
91    }
92
93    // We don't want a huge region-clip on the compositing layers; instead we unite the rectangles together
94    // and clear them when the paint actually occurs.
95    bool eventFilter(QObject* object, QEvent* event)
96    {
97        if (object == m_widget && event->type() == QEvent::Paint)
98            m_dirtyRect = QRect();
99        return QObject::eventFilter(object, event);
100    }
101
102    void setNeedsDisplay()
103    {
104        if (m_widget)
105            m_widget->update();
106    }
107
108    void setNeedsDisplayInRect(const IntRect& rect)
109    {
110        m_dirtyRect |= rect;
111        m_widget->update(m_dirtyRect);
112    }
113
114private:
115    QRect m_dirtyRect;
116    QWidget* m_widget;
117};
118
119#if !defined(QT_NO_GRAPHICSVIEW)
120class PlatformLayerProxyQGraphicsObject : public PlatformLayerProxyQt {
121public:
122    PlatformLayerProxyQGraphicsObject(QWebFrame* frame, TextureMapperContentLayer* layer, QGraphicsObject* object)
123        : PlatformLayerProxyQt(frame, layer, object)
124        , m_graphicsItem(object)
125    {
126        if (textureMapper())
127            return;
128
129#ifdef QT_OPENGL_LIB
130        QGraphicsView* view = object->scene()->views()[0];
131        if (view && view->viewport() && view->viewport()->inherits("QGLWidget")) {
132            setTextureMapper(TextureMapperGL::create());
133            return;
134        }
135#endif
136        setTextureMapper(TextureMapperQt::create());
137    }
138
139    void setNeedsDisplay()
140    {
141        if (m_graphicsItem)
142            m_graphicsItem->update();
143    }
144
145    void setNeedsDisplayInRect(const IntRect& rect)
146    {
147        if (m_graphicsItem)
148            m_graphicsItem->update(QRectF(rect));
149    }
150
151private:
152    QGraphicsItem* m_graphicsItem;
153};
154#endif // QT_NO_GRAPHICSVIEW
155
156void PageClientQWidget::setRootGraphicsLayer(TextureMapperPlatformLayer* layer)
157{
158    if (layer) {
159        platformLayerProxy = new PlatformLayerProxyQWidget(page->mainFrame(), static_cast<TextureMapperContentLayer*>(layer), view);
160        return;
161    }
162    delete platformLayerProxy;
163    platformLayerProxy = 0;
164}
165
166void PageClientQWidget::markForSync(bool scheduleSync)
167{
168    syncTimer.startOneShot(0);
169}
170
171void PageClientQWidget::syncLayers(Timer<PageClientQWidget>*)
172{
173    QWebFramePrivate::core(page->mainFrame())->view()->syncCompositingStateIncludingSubframes();
174}
175#endif
176
177void PageClientQWidget::scroll(int dx, int dy, const QRect& rectToScroll)
178{
179    view->scroll(qreal(dx), qreal(dy), rectToScroll);
180}
181
182void PageClientQWidget::update(const QRect & dirtyRect)
183{
184    view->update(dirtyRect);
185}
186
187void PageClientQWidget::setInputMethodEnabled(bool enable)
188{
189    view->setAttribute(Qt::WA_InputMethodEnabled, enable);
190}
191
192bool PageClientQWidget::inputMethodEnabled() const
193{
194    return view->testAttribute(Qt::WA_InputMethodEnabled);
195}
196
197void PageClientQWidget::setInputMethodHints(Qt::InputMethodHints hints)
198{
199    view->setInputMethodHints(hints);
200}
201
202PageClientQWidget::~PageClientQWidget()
203{
204#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER)
205    delete platformLayerProxy;
206#endif
207}
208
209#ifndef QT_NO_CURSOR
210QCursor PageClientQWidget::cursor() const
211{
212    return view->cursor();
213}
214
215void PageClientQWidget::updateCursor(const QCursor& cursor)
216{
217    view->setCursor(cursor);
218}
219#endif
220
221QPalette PageClientQWidget::palette() const
222{
223    return view->palette();
224}
225
226int PageClientQWidget::screenNumber() const
227{
228#if defined(Q_WS_X11)
229    return view->x11Info().screen();
230#endif
231    return 0;
232}
233
234QWidget* PageClientQWidget::ownerWidget() const
235{
236    return view;
237}
238
239QRect PageClientQWidget::geometryRelativeToOwnerWidget() const
240{
241    return view->geometry();
242}
243
244QObject* PageClientQWidget::pluginParent() const
245{
246    return view;
247}
248
249QStyle* PageClientQWidget::style() const
250{
251    return view->style();
252}
253
254QRectF PageClientQWidget::windowRect() const
255{
256    return QRectF(view->window()->geometry());
257}
258
259#if !defined(QT_NO_GRAPHICSVIEW)
260PageClientQGraphicsWidget::~PageClientQGraphicsWidget()
261{
262    delete overlay;
263#if USE(ACCELERATED_COMPOSITING)
264#if USE(TEXTURE_MAPPER)
265    delete platformLayerProxy;
266#else
267    if (!rootGraphicsLayer)
268        return;
269    // we don't need to delete the root graphics layer. The lifecycle is managed in GraphicsLayerQt.cpp.
270    rootGraphicsLayer.data()->setParentItem(0);
271    view->scene()->removeItem(rootGraphicsLayer.data());
272#endif
273#endif
274}
275
276void PageClientQGraphicsWidget::scroll(int dx, int dy, const QRect& rectToScroll)
277{
278    view->scroll(qreal(dx), qreal(dy), rectToScroll);
279}
280
281void PageClientQGraphicsWidget::update(const QRect& dirtyRect)
282{
283    view->update(dirtyRect);
284
285    createOrDeleteOverlay();
286    if (overlay)
287        overlay->update(QRectF(dirtyRect));
288#if USE(ACCELERATED_COMPOSITING)
289    syncLayers();
290#endif
291}
292
293void PageClientQGraphicsWidget::createOrDeleteOverlay()
294{
295    // We don't use an overlay with TextureMapper. Instead, the overlay is drawn inside QWebFrame.
296#if !USE(TEXTURE_MAPPER)
297    bool useOverlay = false;
298    if (!viewResizesToContents) {
299#if USE(ACCELERATED_COMPOSITING)
300        useOverlay = useOverlay || rootGraphicsLayer;
301#endif
302#if ENABLE(TILED_BACKING_STORE)
303        useOverlay = useOverlay || QWebFramePrivate::core(page->mainFrame())->tiledBackingStore();
304#endif
305    }
306    if (useOverlay == !!overlay)
307        return;
308
309    if (useOverlay) {
310        overlay = new QGraphicsItemOverlay(view, page);
311        overlay->setZValue(OverlayZValue);
312    } else {
313        // Changing the overlay might be done inside paint events.
314        overlay->deleteLater();
315        overlay = 0;
316    }
317#endif // !USE(TEXTURE_MAPPER)
318}
319
320#if USE(ACCELERATED_COMPOSITING)
321void PageClientQGraphicsWidget::syncLayers()
322{
323    if (shouldSync) {
324        QWebFramePrivate::core(page->mainFrame())->view()->syncCompositingStateIncludingSubframes();
325        shouldSync = false;
326    }
327}
328
329#if USE(TEXTURE_MAPPER)
330void PageClientQGraphicsWidget::setRootGraphicsLayer(TextureMapperPlatformLayer* layer)
331{
332    if (layer) {
333        platformLayerProxy = new PlatformLayerProxyQGraphicsObject(page->mainFrame(), static_cast<TextureMapperContentLayer*>(layer), view);
334        return;
335    }
336    delete platformLayerProxy;
337    platformLayerProxy = 0;
338}
339#else
340void PageClientQGraphicsWidget::setRootGraphicsLayer(QGraphicsObject* layer)
341{
342    if (rootGraphicsLayer) {
343        rootGraphicsLayer.data()->setParentItem(0);
344        view->scene()->removeItem(rootGraphicsLayer.data());
345        QWebFramePrivate::core(page->mainFrame())->view()->syncCompositingStateIncludingSubframes();
346    }
347
348    rootGraphicsLayer = layer;
349
350    if (layer) {
351        layer->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
352        layer->setParentItem(view);
353        layer->setZValue(RootGraphicsLayerZValue);
354    }
355    createOrDeleteOverlay();
356}
357#endif
358
359void PageClientQGraphicsWidget::markForSync(bool scheduleSync)
360{
361    shouldSync = true;
362    if (scheduleSync)
363        syncMetaMethod.invoke(view, Qt::QueuedConnection);
364}
365
366#endif
367
368#if ENABLE(TILED_BACKING_STORE)
369void PageClientQGraphicsWidget::updateTiledBackingStoreScale()
370{
371    WebCore::TiledBackingStore* backingStore = QWebFramePrivate::core(page->mainFrame())->tiledBackingStore();
372    if (!backingStore)
373        return;
374    backingStore->setContentsScale(view->scale());
375}
376#endif
377
378void PageClientQGraphicsWidget::setInputMethodEnabled(bool enable)
379{
380    view->setFlag(QGraphicsItem::ItemAcceptsInputMethod, enable);
381}
382
383bool PageClientQGraphicsWidget::inputMethodEnabled() const
384{
385    return view->flags() & QGraphicsItem::ItemAcceptsInputMethod;
386}
387
388void PageClientQGraphicsWidget::setInputMethodHints(Qt::InputMethodHints hints)
389{
390    view->setInputMethodHints(hints);
391}
392
393#ifndef QT_NO_CURSOR
394QCursor PageClientQGraphicsWidget::cursor() const
395{
396    return view->cursor();
397}
398
399void PageClientQGraphicsWidget::updateCursor(const QCursor& cursor)
400{
401    view->setCursor(cursor);
402}
403#endif
404
405QPalette PageClientQGraphicsWidget::palette() const
406{
407    return view->palette();
408}
409
410int PageClientQGraphicsWidget::screenNumber() const
411{
412#if defined(Q_WS_X11)
413    if (QGraphicsScene* scene = view->scene()) {
414        const QList<QGraphicsView*> views = scene->views();
415
416        if (!views.isEmpty())
417            return views.at(0)->x11Info().screen();
418    }
419#endif
420
421    return 0;
422}
423
424QWidget* PageClientQGraphicsWidget::ownerWidget() const
425{
426    if (QGraphicsScene* scene = view->scene()) {
427        const QList<QGraphicsView*> views = scene->views();
428        return views.value(0);
429    }
430    return 0;
431}
432
433QRect PageClientQGraphicsWidget::geometryRelativeToOwnerWidget() const
434{
435    if (!view->scene())
436        return QRect();
437
438    QList<QGraphicsView*> views = view->scene()->views();
439    if (views.isEmpty())
440        return QRect();
441
442    QGraphicsView* graphicsView = views.at(0);
443    return graphicsView->mapFromScene(view->boundingRect()).boundingRect();
444}
445
446#if ENABLE(TILED_BACKING_STORE)
447QRectF PageClientQGraphicsWidget::graphicsItemVisibleRect() const
448{
449    if (!view->scene())
450        return QRectF();
451
452    QList<QGraphicsView*> views = view->scene()->views();
453    if (views.isEmpty())
454        return QRectF();
455
456    QGraphicsView* graphicsView = views.at(0);
457    int xOffset = graphicsView->horizontalScrollBar()->value();
458    int yOffset = graphicsView->verticalScrollBar()->value();
459    return view->mapRectFromScene(QRectF(QPointF(xOffset, yOffset), graphicsView->viewport()->size()));
460}
461#endif
462
463QObject* PageClientQGraphicsWidget::pluginParent() const
464{
465    return view;
466}
467
468QStyle* PageClientQGraphicsWidget::style() const
469{
470    return view->style();
471}
472
473QRectF PageClientQGraphicsWidget::windowRect() const
474{
475    if (!view->scene())
476        return QRectF();
477
478    // The sceneRect is a good approximation of the size of the application, independent of the view.
479    return view->scene()->sceneRect();
480}
481#endif // QT_NO_GRAPHICSVIEW
482
483} // namespace WebCore
484