Unreviewed. Add Silvia Pfeiffer to contributor list.
[WebKit-https.git] / Tools / QtTestBrowser / webview.cpp
1 /*
2  * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
3  * Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in>
4  * Copyright (C) 2006 George Staikos <staikos@kde.org>
5  * Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
6  * Copyright (C) 2006 Zack Rusin <zack@kde.org>
7  * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org>
8  *
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
28  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "webview.h"
34
35 #include <QAction>
36 #include <QGraphicsScene>
37 #include <QGraphicsSceneContextMenuEvent>
38 #include <QGraphicsSceneMouseEvent>
39 #include <QMenu>
40 #include <QScrollBar>
41 #include <QTimer>
42
43 #ifndef QT_NO_ANIMATION
44 #include <QAbstractAnimation>
45 #include <QAbstractTransition>
46 #include <QFinalState>
47 #include <QPropertyAnimation>
48 #include <QState>
49 #include <QStateMachine>
50 #endif
51
52 WebViewGraphicsBased::WebViewGraphicsBased(QWidget* parent)
53     : QGraphicsView(parent)
54     , m_item(new GraphicsWebView)
55     , m_numPaintsTotal(0)
56     , m_numPaintsSinceLastMeasure(0)
57     , m_measureFps(false)
58     , m_resizesToContents(false)
59     , m_machine(0)
60 {
61     setScene(new QGraphicsScene(this));
62     scene()->addItem(m_item);
63     scene()->setFocusItem(m_item);
64
65     setFrameShape(QFrame::NoFrame);
66     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
67     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
68
69     m_updateTimer = new QTimer(this);
70     m_updateTimer->setInterval(1000);
71     connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(updateFrameRate()));
72 }
73
74 void WebViewGraphicsBased::setPage(QWebPage* page)
75 {
76     connect(page->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)), SLOT(contentsSizeChanged(const QSize&)));
77     connect(page, SIGNAL(scrollRequested(int, int, const QRect&)), SLOT(scrollRequested(int, int)));
78     graphicsWebView()->setPage(page);
79 }
80
81 void WebViewGraphicsBased::scrollRequested(int x, int y)
82 {
83     if (!m_resizesToContents)
84         return;
85
86     // Turn off interactive mode while scrolling, or QGraphicsView will replay the
87     // last mouse event which may cause WebKit to initiate a drag operation.
88     bool interactive = isInteractive();
89     setInteractive(false);
90
91     verticalScrollBar()->setValue(-y);
92     horizontalScrollBar()->setValue(-x);
93
94     setInteractive(interactive);
95 }
96
97 void WebViewGraphicsBased::contentsSizeChanged(const QSize& size)
98 {
99     if (m_resizesToContents)
100         scene()->setSceneRect(0, 0, size.width(), size.height());
101 }
102
103 void WebViewGraphicsBased::setResizesToContents(bool b)
104 {
105     if (b == m_resizesToContents)
106         return;
107
108     m_resizesToContents = b;
109     graphicsWebView()->setResizesToContents(m_resizesToContents);
110
111     // When setting resizesToContents ON, our web view widget will always size as big as the
112     // web content being displayed, and so will the QWebPage's viewport. It implies that internally
113     // WebCore will work as if there was no content rendered offscreen, and then no scrollbars need
114     // drawing. In order to keep scrolling working, we:
115     //
116     // 1) Set QGraphicsView's scrollbars policy back to 'auto'.
117     // 2) Set scene's boundaries rect to an invalid size, which automatically makes it to be as big
118     //    as it needs to enclose all items onto it. We do that because QGraphicsView also calculates
119     //    the size of its scrollable area according to the amount of content in scene that is rendered
120     //    offscreen.
121     // 3) Set QWebPage's preferredContentsSize according to the size of QGraphicsView's viewport,
122     //    so WebCore properly lays pages out.
123     //
124     // On the other hand, when toggling resizesToContents OFF, we set back the default values, as
125     // opposite as described above.
126     if (m_resizesToContents) {
127         setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
128         setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
129         graphicsWebView()->page()->setPreferredContentsSize(size());
130         QRectF itemRect(graphicsWebView()->geometry().topLeft(), graphicsWebView()->page()->mainFrame()->contentsSize());
131         graphicsWebView()->setGeometry(itemRect);
132         scene()->setSceneRect(itemRect);
133     } else {
134         setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
135         setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
136         graphicsWebView()->page()->setPreferredContentsSize(QSize());
137         QRect viewportRect(QPoint(0, 0), size());
138         graphicsWebView()->setGeometry(viewportRect);
139         scene()->setSceneRect(viewportRect);
140     }
141 }
142
143 void WebViewGraphicsBased::resizeEvent(QResizeEvent* event)
144 {
145     QGraphicsView::resizeEvent(event);
146
147     QSize size(event->size());
148
149     if (m_resizesToContents) {
150         graphicsWebView()->page()->setPreferredContentsSize(size);
151         return;
152     }
153
154     QRectF rect(QPoint(0, 0), size);
155     graphicsWebView()->setGeometry(rect);
156     scene()->setSceneRect(rect);
157 }
158
159 void WebViewGraphicsBased::setFrameRateMeasurementEnabled(bool enabled)
160 {
161     m_measureFps = enabled;
162     if (m_measureFps) {
163         m_lastConsultTime = m_startTime = QTime::currentTime();
164         m_fpsTimer.start();
165         m_updateTimer->start();
166     } else {
167         m_fpsTimer.stop();
168         m_updateTimer->stop();
169     }
170 }
171
172 void WebViewGraphicsBased::updateFrameRate()
173 {
174     const QTime now = QTime::currentTime();
175     int interval = m_lastConsultTime.msecsTo(now);
176     int frames = m_fpsTimer.numFrames(interval);
177     int current = interval ? frames * 1000 / interval : 0;
178
179     emit currentFPSUpdated(current);
180
181     m_lastConsultTime = now;
182 }
183
184 void WebViewGraphicsBased::animatedFlip()
185 {
186 #ifndef QT_NO_ANIMATION
187     QSizeF center = graphicsWebView()->boundingRect().size() / 2;
188     QPointF centerPoint = QPointF(center.width(), center.height());
189     graphicsWebView()->setTransformOriginPoint(centerPoint);
190
191     QPropertyAnimation* animation = new QPropertyAnimation(graphicsWebView(), "rotation", this);
192     animation->setDuration(1000);
193
194     int rotation = int(graphicsWebView()->rotation());
195
196     animation->setStartValue(rotation);
197     animation->setEndValue(rotation + 180 - (rotation % 180));
198
199     animation->start(QAbstractAnimation::DeleteWhenStopped);
200 #endif
201 }
202
203 void WebViewGraphicsBased::animatedYFlip()
204 {
205 #ifndef QT_NO_ANIMATION
206     if (!m_machine) {
207         m_machine = new QStateMachine(this);
208
209         QState* s0 = new QState(m_machine);
210         s0->assignProperty(this, "yRotation", 0);
211
212         QState* s1 = new QState(m_machine);
213         s1->assignProperty(this, "yRotation", 90);
214
215         QAbstractTransition* t1 = s0->addTransition(s1);
216         QPropertyAnimation* yRotationAnim = new QPropertyAnimation(this, "yRotation", this);
217         t1->addAnimation(yRotationAnim);
218
219         QState* s2 = new QState(m_machine);
220         s2->assignProperty(this, "yRotation", -90);
221         s1->addTransition(s1, SIGNAL(propertiesAssigned()), s2);
222
223         QState* s3 = new QState(m_machine);
224         s3->assignProperty(this, "yRotation", 0);
225
226         QAbstractTransition* t2 = s2->addTransition(s3);
227         t2->addAnimation(yRotationAnim);
228
229         QFinalState* final = new QFinalState(m_machine);
230         s3->addTransition(s3, SIGNAL(propertiesAssigned()), final);
231
232         m_machine->setInitialState(s0);
233         yRotationAnim->setDuration(1000);
234     }
235
236     m_machine->start();
237 #endif
238 }
239
240 void WebViewGraphicsBased::paintEvent(QPaintEvent* event)
241 {
242     QGraphicsView::paintEvent(event);
243     if (!m_measureFps)
244         return;
245 }
246
247 static QMenu* createContextMenu(QWebPage* page, QPoint position)
248 {
249     QMenu* menu = page->createStandardContextMenu();
250
251     QWebHitTestResult r = page->mainFrame()->hitTestContent(position);
252
253     if (!r.linkUrl().isEmpty()) {
254 #ifndef QT_NO_DESKTOPSERVICES
255         WebPage* webPage = qobject_cast<WebPage*>(page);
256         QAction* newTabAction = menu->addAction("Open in Default &Browser", webPage, SLOT(openUrlInDefaultBrowser()));
257         newTabAction->setData(r.linkUrl());
258         menu->insertAction(menu->actions().at(2), newTabAction);
259 #endif
260         if (r.linkTargetFrame() != r.frame())
261             menu->insertAction(menu->actions().at(0), page->action(QWebPage::OpenLinkInThisWindow));
262     }
263     return menu;
264 }
265
266 void GraphicsWebView::mousePressEvent(QGraphicsSceneMouseEvent* event)
267 {
268     setProperty("mouseButtons", QVariant::fromValue(int(event->buttons())));
269     setProperty("keyboardModifiers", QVariant::fromValue(int(event->modifiers())));
270
271     QGraphicsWebView::mousePressEvent(event);
272 }
273
274 void WebViewTraditional::mousePressEvent(QMouseEvent* event)
275 {
276     setProperty("mouseButtons", QVariant::fromValue(int(event->buttons())));
277     setProperty("keyboardModifiers", QVariant::fromValue(int(event->modifiers())));
278
279     QWebView::mousePressEvent(event);
280 }
281
282 void GraphicsWebView::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
283 {
284     QMenu* menu = createContextMenu(page(), event->pos().toPoint());
285     menu->exec(event->screenPos());
286     delete menu;
287 }
288
289 void WebViewTraditional::contextMenuEvent(QContextMenuEvent* event)
290 {
291     QMenu* menu = createContextMenu(page(), event->pos());
292     menu->exec(event->globalPos());
293     delete menu;
294 }
295