src/3rdparty/webkit/WebKit/qt/Api/qgraphicswebview.cpp
changeset 30 5dc02b23752f
parent 19 fcece45ef507
child 33 3e2da88830cd
--- a/src/3rdparty/webkit/WebKit/qt/Api/qgraphicswebview.cpp	Wed Jun 23 19:07:03 2010 +0300
+++ b/src/3rdparty/webkit/WebKit/qt/Api/qgraphicswebview.cpp	Tue Jul 06 15:10:48 2010 +0300
@@ -22,27 +22,79 @@
 #include "qgraphicswebview.h"
 
 #include "qwebframe.h"
+#include "qwebframe_p.h"
 #include "qwebpage.h"
 #include "qwebpage_p.h"
 #include "QWebPageClient.h"
-#include <QtGui/QGraphicsScene>
-#include <QtGui/QGraphicsView>
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "IntRect.h"
+#include "TiledBackingStore.h"
+#include <QtCore/qmetaobject.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qtimer.h>
 #include <QtGui/qapplication.h>
+#include <QtGui/qgraphicsscene.h>
 #include <QtGui/qgraphicssceneevent.h>
+#include <QtGui/qgraphicsview.h>
+#include <QtGui/qpixmapcache.h>
+#include <QtGui/qscrollbar.h>
 #include <QtGui/qstyleoption.h>
 #include <QtGui/qinputcontext.h>
 #if defined(Q_WS_X11)
 #include <QX11Info>
 #endif
+#include <Settings.h>
+
+// the overlay is here for one reason only: to have the scroll-bars and other
+// extra UI elements appear on top of any QGraphicsItems created by CSS compositing layers
+class QGraphicsWebViewOverlay : public QGraphicsItem {
+    public:
+    QGraphicsWebViewOverlay(QGraphicsWebView* view)
+            :QGraphicsItem(view)
+            , q(view)
+    {
+        setPos(0, 0);
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+        setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true);
+#endif
+        setCacheMode(QGraphicsItem::DeviceCoordinateCache);
+    }
+
+    QRectF boundingRect() const
+    {
+        return q->boundingRect();
+    }
+
+    void paint(QPainter* painter, const QStyleOptionGraphicsItem* options, QWidget*)
+    {
+        q->page()->mainFrame()->render(painter, static_cast<QWebFrame::RenderLayer>(QWebFrame::AllLayers&(~QWebFrame::ContentsLayer)), options->exposedRect.toRect());
+    }
+
+    friend class QGraphicsWebView;
+    QGraphicsWebView* q;
+};
 
 class QGraphicsWebViewPrivate : public QWebPageClient {
 public:
     QGraphicsWebViewPrivate(QGraphicsWebView* parent)
         : q(parent)
         , page(0)
-    {}
+        , resizesToContents(false)
+#if USE(ACCELERATED_COMPOSITING)
+        , shouldSync(false)
+#endif
+    {
+#if USE(ACCELERATED_COMPOSITING)
+        // the overlay and stays alive for the lifetime of
+        // this QGraphicsWebView as the scrollbars are needed when there's no compositing
+        q->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption);
+        syncMetaMethod = q->metaObject()->method(q->metaObject()->indexOfMethod("syncLayers()"));
+#endif
+    }
 
     virtual ~QGraphicsWebViewPrivate();
+
     virtual void scroll(int dx, int dy, const QRect&);
     virtual void update(const QRect& dirtyRect);
     virtual void setInputMethodEnabled(bool enable);
@@ -59,20 +111,137 @@
     virtual QPalette palette() const;
     virtual int screenNumber() const;
     virtual QWidget* ownerWidget() const;
+    virtual QRect geometryRelativeToOwnerWidget() const;
 
     virtual QObject* pluginParent() const;
 
+    virtual QStyle* style() const;
+
+#if USE(ACCELERATED_COMPOSITING)
+    virtual void setRootGraphicsLayer(QGraphicsItem* layer);
+    virtual void markForSync(bool scheduleSync);
+    void updateCompositingScrollPosition();
+
+    // QGraphicsWebView can render composited layers
+    virtual bool allowsAcceleratedCompositing() const { return true; }
+#endif
+
+    void updateResizesToContentsForPage();
+    QRectF graphicsItemVisibleRect() const;
+#if ENABLE(TILED_BACKING_STORE)
+    void updateTiledBackingStoreScale();
+#endif
+
+    void createOrDeleteOverlay();
+
+    void syncLayers();
+
+    void unsetPageIfExists();
+
     void _q_doLoadFinished(bool success);
+    void _q_contentsSizeChanged(const QSize&);
+    void _q_scaleChanged();
 
     void _q_updateMicroFocus();
     void _q_pageDestroyed();
 
     QGraphicsWebView* q;
     QWebPage* page;
+
+    bool resizesToContents;
+
+    // the overlay gets instantiated when the root layer is attached, and get deleted when it's detached
+    QSharedPointer<QGraphicsWebViewOverlay> overlay;
+
+    // we need to put the root graphics layer behind the overlay (which contains the scrollbar)
+    enum { RootGraphicsLayerZValue, OverlayZValue };
+
+#if USE(ACCELERATED_COMPOSITING)
+    QWeakPointer<QGraphicsObject> rootGraphicsLayer;
+    // we need to sync the layers if we get a special call from the WebCore
+    // compositor telling us to do so. We'll get that call from ChromeClientQt
+    bool shouldSync;
+
+    // we have to flush quite often, so we use a meta-method instead of QTimer::singleShot for putting the event in the queue
+    QMetaMethod syncMetaMethod;
+#endif
 };
 
 QGraphicsWebViewPrivate::~QGraphicsWebViewPrivate()
 {
+#if USE(ACCELERATED_COMPOSITING)
+    if (!rootGraphicsLayer)
+        return;
+    // we don't need to delete the root graphics layer. The lifecycle is managed in GraphicsLayerQt.cpp.
+    rootGraphicsLayer.data()->setParentItem(0);
+    q->scene()->removeItem(rootGraphicsLayer.data());
+#endif
+}
+
+void QGraphicsWebViewPrivate::createOrDeleteOverlay()
+{
+    bool useOverlay = false;
+    if (!resizesToContents) {
+#if USE(ACCELERATED_COMPOSITING)
+        useOverlay = useOverlay || rootGraphicsLayer;
+#endif
+#if ENABLE(TILED_BACKING_STORE)
+        useOverlay = useOverlay || QWebFramePrivate::core(q->page()->mainFrame())->tiledBackingStore();
+#endif
+    }
+    if (useOverlay == !!overlay)
+        return;
+    if (useOverlay) {
+        overlay = QSharedPointer<QGraphicsWebViewOverlay>(new QGraphicsWebViewOverlay(q));
+        overlay->setZValue(OverlayZValue);
+    } else
+        overlay.clear();
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+void QGraphicsWebViewPrivate::setRootGraphicsLayer(QGraphicsItem* layer)
+{
+    if (rootGraphicsLayer) {
+        rootGraphicsLayer.data()->setParentItem(0);
+        q->scene()->removeItem(rootGraphicsLayer.data());
+        QWebFramePrivate::core(q->page()->mainFrame())->view()->syncCompositingStateRecursive();
+    }
+
+    rootGraphicsLayer = layer ? layer->toGraphicsObject() : 0;
+
+    if (layer) {
+        layer->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
+        layer->setParentItem(q);
+        layer->setZValue(RootGraphicsLayerZValue);
+        updateCompositingScrollPosition();
+    }
+    createOrDeleteOverlay();
+}
+
+void QGraphicsWebViewPrivate::markForSync(bool scheduleSync)
+{
+    shouldSync = true;
+    if (scheduleSync)
+        syncMetaMethod.invoke(q, Qt::QueuedConnection);
+}
+
+void QGraphicsWebViewPrivate::updateCompositingScrollPosition()
+{
+    if (rootGraphicsLayer && q->page() && q->page()->mainFrame()) {
+        const QPoint scrollPosition = q->page()->mainFrame()->scrollPosition();
+        rootGraphicsLayer.data()->setPos(-scrollPosition);
+    }
+}
+#endif
+
+void QGraphicsWebViewPrivate::syncLayers()
+{
+#if USE(ACCELERATED_COMPOSITING)
+    if (shouldSync) {
+        QWebFramePrivate::core(q->page()->mainFrame())->view()->syncCompositingStateRecursive();
+        shouldSync = false;
+    }
+#endif
 }
 
 void QGraphicsWebViewPrivate::_q_doLoadFinished(bool success)
@@ -107,11 +276,23 @@
 void QGraphicsWebViewPrivate::scroll(int dx, int dy, const QRect& rectToScroll)
 {
     q->scroll(qreal(dx), qreal(dy), QRectF(rectToScroll));
+
+#if USE(ACCELERATED_COMPOSITING)
+    updateCompositingScrollPosition();
+#endif
 }
 
 void QGraphicsWebViewPrivate::update(const QRect & dirtyRect)
 {
     q->update(QRectF(dirtyRect));
+
+    createOrDeleteOverlay();
+    if (overlay)
+        overlay->update(QRectF(dirtyRect));
+#if USE(ACCELERATED_COMPOSITING)
+    updateCompositingScrollPosition();
+    syncLayers();
+#endif
 }
 
 
@@ -160,10 +341,12 @@
 int QGraphicsWebViewPrivate::screenNumber() const
 {
 #if defined(Q_WS_X11)
-    const QList<QGraphicsView*> views = q->scene()->views();
+    if (QGraphicsScene* scene = q->scene()) {
+        const QList<QGraphicsView*> views = scene->views();
 
-    if (!views.isEmpty())
-        return views.at(0)->x11Info().screen();
+        if (!views.isEmpty())
+            return views.at(0)->x11Info().screen();
+    }
 #endif
 
     return 0;
@@ -171,8 +354,24 @@
 
 QWidget* QGraphicsWebViewPrivate::ownerWidget() const
 {
-    const QList<QGraphicsView*> views = q->scene()->views();
-    return views.value(0);
+    if (QGraphicsScene* scene = q->scene()) {
+        const QList<QGraphicsView*> views = scene->views();
+        return views.value(0);
+    }
+    return 0;
+}
+
+QRect QGraphicsWebViewPrivate::geometryRelativeToOwnerWidget() const
+{
+    if (!q->scene())
+        return QRect();
+
+    QList<QGraphicsView*> views = q->scene()->views();
+    if (views.isEmpty())
+        return QRect();
+
+    QGraphicsView* view = views.at(0);
+    return view->mapFromScene(q->boundingRect()).boundingRect();
 }
 
 QObject* QGraphicsWebViewPrivate::pluginParent() const
@@ -180,6 +379,76 @@
     return q;
 }
 
+QStyle* QGraphicsWebViewPrivate::style() const
+{
+    return q->style();
+}
+
+void QGraphicsWebViewPrivate::updateResizesToContentsForPage()
+{
+    ASSERT(page);
+
+    if (resizesToContents) {
+        // resizes to contents mode requires preferred contents size to be set
+        if (!page->preferredContentsSize().isValid())
+            page->setPreferredContentsSize(QSize(960, 800));
+
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+        QObject::connect(page->mainFrame(), SIGNAL(contentsSizeChanged(QSize)),
+            q, SLOT(_q_contentsSizeChanged(const QSize&)), Qt::UniqueConnection);
+#else
+        QObject::connect(page->mainFrame(), SIGNAL(contentsSizeChanged(QSize)),
+            q, SLOT(_q_contentsSizeChanged(const QSize&)));
+#endif
+    } else {
+        QObject::disconnect(page->mainFrame(), SIGNAL(contentsSizeChanged(QSize)),
+                         q, SLOT(_q_contentsSizeChanged(const QSize&)));
+    }
+}
+
+void QGraphicsWebViewPrivate::_q_contentsSizeChanged(const QSize& size)
+{
+    if (!resizesToContents)
+        return;
+    q->setGeometry(QRectF(q->geometry().topLeft(), size));
+}
+
+void QGraphicsWebViewPrivate::_q_scaleChanged()
+{
+#if ENABLE(TILED_BACKING_STORE)
+    updateTiledBackingStoreScale();
+#endif
+}
+
+QRectF QGraphicsWebViewPrivate::graphicsItemVisibleRect() const
+{
+    if (!q->scene())
+        return QRectF();
+    QList<QGraphicsView*> views = q->scene()->views();
+    if (views.size() > 1) {
+#ifndef QT_NO_DEBUG_STREAM
+        qDebug() << "QGraphicsWebView is in more than one graphics views, unable to compute the visible rect";
+#endif
+        return QRectF();
+    }
+    if (views.size() < 1)
+        return QRectF();
+
+    int xPosition = views[0]->horizontalScrollBar()->value();
+    int yPosition = views[0]->verticalScrollBar()->value();
+    return q->mapRectFromScene(QRectF(QPoint(xPosition, yPosition), views[0]->viewport()->size()));
+}
+
+#if ENABLE(TILED_BACKING_STORE)
+void QGraphicsWebViewPrivate::updateTiledBackingStoreScale()
+{
+    WebCore::TiledBackingStore* backingStore = QWebFramePrivate::core(page->mainFrame())->tiledBackingStore();
+    if (!backingStore)
+        return;
+    backingStore->setContentsScale(q->scale());
+}
+#endif
+
 /*!
     \class QGraphicsWebView
     \brief The QGraphicsWebView class allows Web content to be added to a GraphicsView.
@@ -266,8 +535,16 @@
 #if QT_VERSION >= 0x040600
     setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true);
 #endif
+    setAcceptDrops(true);
     setAcceptHoverEvents(true);
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+    setAcceptTouchEvents(true);
+#endif
     setFocusPolicy(Qt::StrongFocus);
+    setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
+#if ENABLE(TILED_BACKING_STORE)
+    QObject::connect(this, SIGNAL(scaleChanged()), this, SLOT(_q_scaleChanged()));
+#endif
 }
 
 /*!
@@ -317,7 +594,26 @@
 */
 void QGraphicsWebView::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget*)
 {
-    page()->mainFrame()->render(painter, option->exposedRect.toRect());
+#if ENABLE(TILED_BACKING_STORE)
+    if (WebCore::TiledBackingStore* backingStore = QWebFramePrivate::core(page()->mainFrame())->tiledBackingStore()) {
+        // FIXME: We should set the backing store viewport earlier than in paint
+        if (d->resizesToContents)
+            backingStore->viewportChanged(WebCore::IntRect(d->graphicsItemVisibleRect()));
+        else {
+            QRectF visibleRect(d->page->mainFrame()->scrollPosition(), d->page->mainFrame()->geometry().size());
+            backingStore->viewportChanged(WebCore::IntRect(visibleRect));
+        }
+        // QWebFrame::render is a public API, bypass it for tiled rendering so behavior does not need to change.
+        WebCore::GraphicsContext context(painter); 
+        page()->mainFrame()->d->renderFromTiledBackingStore(&context, option->exposedRect.toAlignedRect());
+        return;
+    } 
+#endif
+#if USE(ACCELERATED_COMPOSITING)
+    page()->mainFrame()->render(painter, d->overlay ? QWebFrame::ContentsLayer : QWebFrame::AllLayers, option->exposedRect.toAlignedRect());
+#else
+    page()->mainFrame()->render(painter, QWebFrame::AllLayers, option->exposedRect.toRect());
+#endif
 }
 
 /*! \reimp
@@ -325,6 +621,17 @@
 bool QGraphicsWebView::sceneEvent(QEvent* event)
 {
     // Re-implemented in order to allows fixing event-related bugs in patch releases.
+
+#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
+    if (d->page && (event->type() == QEvent::TouchBegin
+                || event->type() == QEvent::TouchEnd
+                || event->type() == QEvent::TouchUpdate)) {
+        d->page->event(event);
+        if (event->isAccepted())
+            return true;
+    }
+#endif
+
     return QGraphicsWidget::sceneEvent(event);
 }
 
@@ -391,7 +698,6 @@
 #endif // QT_NO_CONTEXTMENU
         {
 #ifndef QT_NO_CURSOR
-#if QT_VERSION >= 0x040400
             if (event->type() == QEvent::CursorChange) {
                 // An unsetCursor will set the cursor to Qt::ArrowCursor.
                 // Thus this cursor change might be a QWidget::unsetCursor()
@@ -405,12 +711,34 @@
                     d->resetCursor();
             }
 #endif
-#endif
         }
     }
     return QGraphicsWidget::event(event);
 }
 
+void QGraphicsWebViewPrivate::unsetPageIfExists()
+{
+    if (!page)
+        return;
+
+    // if the page client is the special client constructed for
+    // delegating the responsibilities to a QWidget, we need
+    // to destroy it.
+
+    if (page->d->client && page->d->client->isQWidgetClient())
+        delete page->d->client;
+
+    page->d->client = 0;
+
+    // if the page was created by us, we own it and need to
+    // destroy it as well.
+
+    if (page->parent() == q)
+        delete page;
+    else
+        page->disconnect(q);
+}
+
 /*!
     Makes \a page the new web page of the web graphicsitem.
 
@@ -425,22 +753,23 @@
     if (d->page == page)
         return;
 
-    if (d->page) {
-        d->page->d->client = 0; // unset the page client
-        if (d->page->parent() == this)
-            delete d->page;
-        else
-            d->page->disconnect(this);
-    }
+    d->unsetPageIfExists();
+    d->page = page;
 
-    d->page = page;
     if (!d->page)
         return;
+
     d->page->d->client = d; // set the page client
 
+    if (d->overlay)
+        d->overlay->prepareGeometryChange();
+
     QSize size = geometry().size().toSize();
     page->setViewportSize(size);
 
+    if (d->resizesToContents)
+        d->updateResizesToContentsForPage();
+
     QWebFrame* mainFrame = d->page->mainFrame();
 
     connect(mainFrame, SIGNAL(titleChanged(QString)),
@@ -523,7 +852,6 @@
 
 /*!
     \property QGraphicsWebView::zoomFactor
-    \since 4.5
     \brief the zoom factor for the view
 */
 
@@ -544,6 +872,9 @@
 */
 void QGraphicsWebView::updateGeometry()
 {
+    if (d->overlay)
+        d->overlay->prepareGeometryChange();
+
     QGraphicsWidget::updateGeometry();
 
     if (!d->page)
@@ -559,6 +890,9 @@
 {
     QGraphicsWidget::setGeometry(rect);
 
+    if (d->overlay)
+        d->overlay->prepareGeometryChange();
+
     if (!d->page)
         return;
 
@@ -729,7 +1063,12 @@
 */
 QAction *QGraphicsWebView::pageAction(QWebPage::WebAction action) const
 {
+#ifdef QT_NO_ACTION
+    Q_UNUSED(action)
+    return 0;
+#else
     return page()->action(action);
+#endif
 }
 
 /*!
@@ -766,6 +1105,77 @@
     return false;
 }
 
+/*!
+    \property QGraphicsWebView::resizesToContents
+    \brief whether the size of the QGraphicsWebView and its viewport changes to match the contents size
+    \since 4.7 
+
+    If this property is set, the QGraphicsWebView will automatically change its
+    size to match the size of the main frame contents. As a result the top level frame
+    will never have scrollbars. It will also make CSS fixed positioning to behave like absolute positioning
+    with elements positioned relative to the document instead of the viewport.
+
+    This property should be used in conjunction with the QWebPage::preferredContentsSize property.
+    If not explicitly set, the preferredContentsSize is automatically set to a reasonable value.
+
+    \sa QWebPage::setPreferredContentsSize()
+*/
+void QGraphicsWebView::setResizesToContents(bool enabled)
+{
+    if (d->resizesToContents == enabled)
+        return;
+    d->resizesToContents = enabled;
+    if (d->page)
+        d->updateResizesToContentsForPage();
+}
+
+bool QGraphicsWebView::resizesToContents() const
+{
+    return d->resizesToContents;
+}
+
+/*!
+    \property QGraphicsWebView::tiledBackingStoreFrozen
+    \brief whether the tiled backing store updates its contents
+    \since 4.7 
+
+    If the tiled backing store is enabled using QWebSettings::TiledBackingStoreEnabled attribute, this property
+    can be used to disable backing store updates temporarily. This can be useful for example for running
+    a smooth animation that changes the scale of the QGraphicsWebView.
+ 
+    When the backing store is unfrozen, its contents will be automatically updated to match the current
+    state of the document. If the QGraphicsWebView scale was changed, the backing store is also
+    re-rendered using the new scale.
+ 
+    If the tiled backing store is not enabled, this property does nothing.
+
+    \sa QWebSettings::TiledBackingStoreEnabled
+    \sa QGraphicsObject::scale
+*/
+bool QGraphicsWebView::isTiledBackingStoreFrozen() const
+{
+#if ENABLE(TILED_BACKING_STORE)
+    WebCore::TiledBackingStore* backingStore = QWebFramePrivate::core(page()->mainFrame())->tiledBackingStore();
+    if (!backingStore)
+        return false;
+    return backingStore->contentsFrozen();
+#else
+    return false;
+#endif
+}
+
+void QGraphicsWebView::setTiledBackingStoreFrozen(bool frozen)
+{
+#if ENABLE(TILED_BACKING_STORE)
+    WebCore::TiledBackingStore* backingStore = QWebFramePrivate::core(page()->mainFrame())->tiledBackingStore();
+    if (!backingStore)
+        return;
+    backingStore->setContentsFrozen(frozen);
+#else
+    UNUSED_PARAM(frozen);
+#endif
+}
+
 /*! \reimp
 */
 void QGraphicsWebView::hoverMoveEvent(QGraphicsSceneHoverEvent* ev)
@@ -903,10 +1313,8 @@
 void QGraphicsWebView::dragEnterEvent(QGraphicsSceneDragDropEvent* ev)
 {
 #ifndef QT_NO_DRAGANDDROP
-    //if (d->page)
-    //    d->page->event(ev);
-    //Just remove this line below when the code above is working
-    Q_UNUSED(ev);
+    if (d->page)
+        d->page->event(ev);
 #else
     Q_UNUSED(ev);
 #endif