[Qt][WK2] Move non-api classes to WebKit namespace at WebKit2/UiProcess/qt
[WebKit-https.git] / Source / WebKit2 / UIProcess / qt / QtViewportInteractionEngine.cpp
1 /*
2  * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
3  * Copyright (C) 2011 Benjamin Poulain <benjamin@webkit.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this program; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  */
21
22 #include "config.h"
23 #include "QtViewportInteractionEngine.h"
24
25 #include "qquickwebpage_p.h"
26 #include "qquickwebview_p.h"
27 #include <QPointF>
28 #include <QTransform>
29 #include <QWheelEvent>
30 #include <QtQuick/qquickitem.h>
31 #include <wtf/PassOwnPtr.h>
32
33 namespace WebKit {
34
35 static const int kScaleAnimationDurationMillis = 250;
36
37 // UPDATE DEFERRING (SUSPEND/RESUME)
38 // =================================
39 //
40 // When interaction with the content, either by animating or by the hand of the user,
41 // it is important to ensure smooth animations of at least 60fps in order to give a
42 // good user experience.
43 //
44 // In order to do this we need to get rid of unknown factors. These include device
45 // sensors (geolocation, orientation updates etc), CSS3 animations, JavaScript
46 // exectution, sub resource loads etc. We do this by emitting suspend and resume
47 // signals, which are then handled by the viewport and propagates to the right place.
48 //
49 // For this reason the ViewportUpdateDeferrer guard must be used when we interact
50 // or animate the content.
51 //
52 // It should be noted that when we update content properties, we might receive notify
53 // signals send my the content item itself, and care should be taken to not act on
54 // these unconditionally. An example of this is the pinch zoom, which changes the
55 // position and will thus result in a QQuickWebPage::geometryChanged() signal getting
56 // emitted.
57 //
58 // If something should only be executed during update deferring, it is possible to
59 // check for that using ASSERT(m_suspendCount).
60
61 class ViewportUpdateDeferrer {
62 public:
63     enum SuspendContentFlag { DeferUpdate, DeferUpdateAndSuspendContent };
64     ViewportUpdateDeferrer(QtViewportInteractionEngine* engine, SuspendContentFlag suspendContentFlag = DeferUpdate)
65         : engine(engine)
66     {
67         engine->m_suspendCount++;
68
69         // There is no need to suspend content for immediate updates
70         // only during animations or longer gestures.
71         if (suspendContentFlag == DeferUpdateAndSuspendContent && !engine->m_hasSuspendedContent) {
72             engine->m_hasSuspendedContent = true;
73             emit engine->contentSuspendRequested();
74         }
75     }
76
77     ~ViewportUpdateDeferrer()
78     {
79         if (--(engine->m_suspendCount))
80             return;
81
82         if (engine->m_hasSuspendedContent) {
83             engine->m_hasSuspendedContent = false;
84             emit engine->contentResumeRequested();
85         }
86
87         // Make sure that tiles all around the viewport will be requested.
88         emit engine->contentViewportChanged(QPointF());
89     }
90
91 private:
92     QtViewportInteractionEngine* const engine;
93 };
94
95 inline qreal QtViewportInteractionEngine::cssScaleFromItem(qreal itemScale)
96 {
97     return itemScale / m_devicePixelRatio;
98 }
99
100 inline qreal QtViewportInteractionEngine::itemScaleFromCSS(qreal cssScale)
101 {
102     return cssScale * m_devicePixelRatio;
103 }
104
105 inline qreal QtViewportInteractionEngine::itemCoordFromCSS(qreal value)
106 {
107     return value * m_devicePixelRatio;
108 }
109
110 inline QRectF QtViewportInteractionEngine::itemRectFromCSS(const QRectF& cssRect)
111 {
112     QRectF itemRect;
113
114     itemRect.setX(itemCoordFromCSS(cssRect.x()));
115     itemRect.setY(itemCoordFromCSS(cssRect.y()));
116     itemRect.setWidth(itemCoordFromCSS(cssRect.width()));
117     itemRect.setHeight(itemCoordFromCSS(cssRect.height()));
118
119     return itemRect;
120 }
121
122 QtViewportInteractionEngine::QtViewportInteractionEngine(QQuickWebView* viewport, QQuickWebPage* content)
123     : m_viewport(viewport)
124     , m_content(content)
125     , m_suspendCount(0)
126     , m_hasSuspendedContent(false)
127     , m_hadUserInteraction(false)
128     , m_scaleAnimation(new ScaleAnimation(this))
129     , m_pinchStartScale(-1)
130     , m_zoomOutScale(0.0)
131 {
132     reset();
133
134     connect(m_content, SIGNAL(widthChanged()), SLOT(itemSizeChanged()), Qt::DirectConnection);
135     connect(m_content, SIGNAL(heightChanged()), SLOT(itemSizeChanged()), Qt::DirectConnection);
136     connect(m_viewport, SIGNAL(movementStarted()), SLOT(flickableMoveStarted()), Qt::DirectConnection);
137     connect(m_viewport, SIGNAL(movementEnded()), SLOT(flickableMoveEnded()), Qt::DirectConnection);
138
139     connect(m_scaleAnimation, SIGNAL(valueChanged(QVariant)),
140             SLOT(scaleAnimationValueChanged(QVariant)), Qt::DirectConnection);
141     connect(m_scaleAnimation, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)),
142             SLOT(scaleAnimationStateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), Qt::DirectConnection);
143 }
144
145 QtViewportInteractionEngine::~QtViewportInteractionEngine()
146 {
147 }
148
149 qreal QtViewportInteractionEngine::innerBoundedCSSScale(qreal cssScale)
150 {
151     return qBound(m_minimumScale, cssScale, m_maximumScale);
152 }
153
154 qreal QtViewportInteractionEngine::outerBoundedCSSScale(qreal cssScale)
155 {
156     if (m_allowsUserScaling) {
157         // Bounded by [0.1, 10.0] like the viewport meta code in WebCore.
158         qreal hardMin = qMax<qreal>(0.1, qreal(0.5) * m_minimumScale);
159         qreal hardMax = qMin<qreal>(10, qreal(2.0) * m_maximumScale);
160         return qBound(hardMin, cssScale, hardMax);
161     }
162     return innerBoundedCSSScale(cssScale);
163 }
164
165 void QtViewportInteractionEngine::setItemRectVisible(const QRectF& itemRect)
166 {
167     if (itemRect.isEmpty())
168         return;
169
170     ViewportUpdateDeferrer guard(this);
171
172     qreal itemScale = m_viewport->width() / itemRect.width();
173
174     m_content->setContentsScale(itemScale);
175
176     // To animate the position together with the scale we multiply the position with the current scale
177     // and add it to the page position (displacement on the flickable contentItem because of additional items).
178     QPointF newPosition(m_content->pos() + (itemRect.topLeft() * itemScale));
179
180     m_viewport->setContentPos(newPosition);
181 }
182
183 bool QtViewportInteractionEngine::animateItemRectVisible(const QRectF& itemRect)
184 {
185     QRectF currentItemRectVisible = m_viewport->mapRectToWebContent(m_viewport->boundingRect());
186     if (itemRect == currentItemRectVisible)
187         return false;
188
189     m_scaleAnimation->setDuration(kScaleAnimationDurationMillis);
190     m_scaleAnimation->setEasingCurve(QEasingCurve::OutCubic);
191
192     m_scaleAnimation->setStartValue(currentItemRectVisible);
193     m_scaleAnimation->setEndValue(itemRect);
194
195     m_scaleAnimation->start();
196     return true;
197 }
198
199 void QtViewportInteractionEngine::flickableMoveStarted()
200 {
201     Q_ASSERT(m_viewport->isMoving());
202     m_scrollUpdateDeferrer = adoptPtr(new ViewportUpdateDeferrer(this, ViewportUpdateDeferrer::DeferUpdateAndSuspendContent));
203
204     m_lastScrollPosition = m_viewport->contentPos();
205     connect(m_viewport, SIGNAL(contentXChanged()), SLOT(flickableMovingPositionUpdate()));
206     connect(m_viewport, SIGNAL(contentYChanged()), SLOT(flickableMovingPositionUpdate()));
207 }
208
209 void QtViewportInteractionEngine::flickableMoveEnded()
210 {
211     Q_ASSERT(!m_viewport->isMoving());
212     // This method is called on the end of the pan or pan kinetic animation.
213     m_scrollUpdateDeferrer.clear();
214
215     m_lastScrollPosition = QPointF();
216     disconnect(m_viewport, SIGNAL(contentXChanged()), this, SLOT(flickableMovingPositionUpdate()));
217     disconnect(m_viewport, SIGNAL(contentYChanged()), this, SLOT(flickableMovingPositionUpdate()));
218 }
219
220 void QtViewportInteractionEngine::flickableMovingPositionUpdate()
221 {
222     QPointF newPosition = m_viewport->contentPos();
223
224     emit contentViewportChanged(m_lastScrollPosition - newPosition);
225
226     m_lastScrollPosition = newPosition;
227 }
228
229 void QtViewportInteractionEngine::scaleAnimationStateChanged(QAbstractAnimation::State newState, QAbstractAnimation::State /*oldState*/)
230 {
231     switch (newState) {
232     case QAbstractAnimation::Running:
233         m_viewport->cancelFlick();
234         if (!m_scaleUpdateDeferrer)
235             m_scaleUpdateDeferrer = adoptPtr(new ViewportUpdateDeferrer(this, ViewportUpdateDeferrer::DeferUpdateAndSuspendContent));
236         break;
237     case QAbstractAnimation::Stopped:
238         m_scaleUpdateDeferrer.clear();
239         break;
240     default:
241         break;
242     }
243 }
244
245 static inline QPointF boundPosition(const QPointF minPosition, const QPointF& position, const QPointF& maxPosition)
246 {
247     return QPointF(qBound(minPosition.x(), position.x(), maxPosition.x()),
248                    qBound(minPosition.y(), position.y(), maxPosition.y()));
249 }
250
251 void QtViewportInteractionEngine::wheelEvent(QWheelEvent* ev)
252 {
253     if (scrollAnimationActive() || scaleAnimationActive() || pinchGestureActive())
254         return; // Ignore.
255
256
257     // A normal scroll-tick should have a delta of 120 (1/8) degrees. Convert this to
258     // local standard scroll step of 3 lines of 20 pixels.
259     static const int cDefaultQtScrollStep = 20;
260     static const int wheelScrollLines = 3;
261     const int wheelTick = wheelScrollLines * cDefaultQtScrollStep;
262
263     int pixelDelta = ev->delta() * (wheelTick / 120.f);
264
265     QPointF newPosition = m_viewport->contentPos();
266
267     if (ev->orientation() == Qt::Horizontal)
268         newPosition.rx() -= pixelDelta;
269     else
270         newPosition.ry() -= pixelDelta;
271
272     QRectF endPosRange = computePosRangeForItemAtScale(m_content->contentsScale());
273
274     QPointF currentPosition = m_viewport->contentPos();
275     newPosition = boundPosition(endPosRange.topLeft(), newPosition, endPosRange.bottomRight());
276     m_viewport->setContentPos(newPosition);
277
278     emit contentViewportChanged(currentPosition - newPosition);
279 }
280
281 void QtViewportInteractionEngine::pagePositionRequest(const QPoint& pagePosition)
282 {
283     // Ignore the request if suspended. Can only happen due to delay in event delivery.
284     if (m_suspendCount)
285         return;
286
287     qreal endItemScale = m_content->contentsScale(); // Stay at same scale.
288
289     QRectF endPosRange = computePosRangeForItemAtScale(endItemScale);
290     QPointF endPosition = boundPosition(endPosRange.topLeft(), pagePosition * endItemScale, endPosRange.bottomRight());
291
292     QRectF endVisibleContentRect(endPosition / endItemScale, m_viewport->boundingRect().size() / endItemScale);
293
294     setItemRectVisible(endVisibleContentRect);
295 }
296
297 void QtViewportInteractionEngine::touchBegin()
298 {
299     // Prevents resuming the page between the user's flicks of the page while the animation is running.
300     if (scrollAnimationActive())
301         m_touchUpdateDeferrer = adoptPtr(new ViewportUpdateDeferrer(this, ViewportUpdateDeferrer::DeferUpdateAndSuspendContent));
302 }
303
304 void QtViewportInteractionEngine::touchEnd()
305 {
306     m_touchUpdateDeferrer.clear();
307 }
308
309 QRectF QtViewportInteractionEngine::computePosRangeForItemAtScale(qreal itemScale) const
310 {
311     const QSizeF contentItemSize = m_content->contentsSize() * itemScale;
312     const QSizeF viewportItemSize = m_viewport->boundingRect().size();
313
314     const qreal horizontalRange = contentItemSize.width() - viewportItemSize.width();
315     const qreal verticalRange = contentItemSize.height() - viewportItemSize.height();
316
317     return QRectF(QPointF(0, 0), QSizeF(horizontalRange, verticalRange));
318 }
319
320 void QtViewportInteractionEngine::focusEditableArea(const QRectF& caretArea, const QRectF& targetArea)
321 {
322     QRectF endArea = itemRectFromCSS(targetArea);
323
324     qreal endItemScale = itemScaleFromCSS(innerBoundedCSSScale(2.0));
325     const QRectF viewportRect = m_viewport->boundingRect();
326
327     qreal x;
328     const qreal borderOffset = 10;
329     if ((endArea.width() + borderOffset) * endItemScale <= viewportRect.width()) {
330         // Center the input field in the middle of the view, if it is smaller than
331         // the view at the scale target.
332         x = viewportRect.center().x() - endArea.width() * endItemScale / 2.0;
333     } else {
334         // Ensure that the caret always has borderOffset contents pixels to the right
335         // of it, and secondarily (if possible), that the area has borderOffset
336         // contents pixels to the left of it.
337         qreal caretOffset = itemCoordFromCSS(caretArea.x()) - endArea.x();
338         x = qMin(viewportRect.width() - (caretOffset + borderOffset) * endItemScale, borderOffset * endItemScale);
339     }
340
341     const QPointF hotspot = QPointF(endArea.x(), endArea.center().y());
342     const QPointF viewportHotspot = QPointF(x, /* FIXME: visibleCenter */ viewportRect.center().y());
343
344     QPointF endPosition = hotspot * endItemScale - viewportHotspot;
345     QRectF endPosRange = computePosRangeForItemAtScale(endItemScale);
346
347     endPosition = boundPosition(endPosRange.topLeft(), endPosition, endPosRange.bottomRight());
348
349     QRectF endVisibleContentRect(endPosition / endItemScale, viewportRect.size() / endItemScale);
350
351     animateItemRectVisible(endVisibleContentRect);
352 }
353
354 void QtViewportInteractionEngine::zoomToAreaGestureEnded(const QPointF& touchPoint, const QRectF& targetArea)
355 {
356     if (!targetArea.isValid())
357         return;
358
359     if (scrollAnimationActive() || scaleAnimationActive())
360         return;
361
362     const int margin = 10; // We want at least a little bit of margin.
363     QRectF endArea = itemRectFromCSS(targetArea.adjusted(-margin, -margin, margin, margin));
364
365     const QRectF viewportRect = m_viewport->boundingRect();
366
367     qreal targetCSSScale = cssScaleFromItem(viewportRect.size().width() / endArea.size().width());
368     qreal endItemScale = itemScaleFromCSS(innerBoundedCSSScale(qMin(targetCSSScale, qreal(2.5))));
369
370     qreal currentScale = m_content->contentsScale();
371     if (!m_scaleStack.isEmpty()) {
372         // Zoom back out if attempting to scale to the same current scale, or
373         // attempting to continue scaling out from the inner most level.
374         if (endItemScale == m_zoomOutScale || endItemScale == currentScale)
375             endItemScale = m_scaleStack.takeLast();
376         else if (endItemScale > currentScale) {
377             m_scaleStack.append(currentScale);
378             m_zoomOutScale = endItemScale;
379         } else { // endItemScale < currentScale
380             // Unstack all scale-levels deeper than the new level, so a zoom-out won't zoom in to a previous level.
381             while (!m_scaleStack.isEmpty() && m_scaleStack.last() >= endItemScale)
382                 m_scaleStack.removeLast();
383             m_zoomOutScale = endItemScale;
384         }
385     } else {
386         m_scaleStack.append(currentScale);
387         m_zoomOutScale = endItemScale;
388     }
389
390     // We want to end up with the target area filling the whole width of the viewport (if possible),
391     // and centralized vertically where the user requested zoom. Thus our hotspot is the center of
392     // the targetArea x-wise and the requested zoom position, y-wise.
393     const QPointF hotspot = QPointF(endArea.center().x(), touchPoint.y() * m_devicePixelRatio);
394     const QPointF viewportHotspot = viewportRect.center();
395
396     QPointF endPosition = hotspot * endItemScale - viewportHotspot;
397
398     QRectF endPosRange = computePosRangeForItemAtScale(endItemScale);
399     endPosition = boundPosition(endPosRange.topLeft(), endPosition, endPosRange.bottomRight());
400
401     QRectF endVisibleContentRect(endPosition / endItemScale, viewportRect.size() / endItemScale);
402
403     animateItemRectVisible(endVisibleContentRect);
404 }
405
406 bool QtViewportInteractionEngine::ensureContentWithinViewportBoundary(bool immediate)
407 {
408     if (scrollAnimationActive() || scaleAnimationActive())
409         return false;
410
411     qreal endItemScale = itemScaleFromCSS(innerBoundedCSSScale(currentCSSScale()));
412
413     const QRectF viewportRect = m_viewport->boundingRect();
414     QPointF viewportHotspot = viewportRect.center();
415
416     QPointF endPosition = m_viewport->mapToWebContent(viewportHotspot) * endItemScale - viewportHotspot;
417
418     QRectF endPosRange = computePosRangeForItemAtScale(endItemScale);
419     endPosition = boundPosition(endPosRange.topLeft(), endPosition, endPosRange.bottomRight());
420
421     QRectF endVisibleContentRect(endPosition / endItemScale, viewportRect.size() / endItemScale);
422
423     if (immediate) {
424         setItemRectVisible(endVisibleContentRect);
425         return true;
426     }
427     return !animateItemRectVisible(endVisibleContentRect);
428 }
429
430 void QtViewportInteractionEngine::reset()
431 {
432     ASSERT(!m_suspendCount);
433
434     m_hadUserInteraction = false;
435
436     m_allowsUserScaling = false;
437     m_minimumScale = 1;
438     m_maximumScale = 1;
439     m_devicePixelRatio = 1;
440
441     m_viewport->cancelFlick();
442     m_scaleAnimation->stop();
443     m_scaleUpdateDeferrer.clear();
444     m_scrollUpdateDeferrer.clear();
445 }
446
447 void QtViewportInteractionEngine::setCSSScaleBounds(qreal minimum, qreal maximum)
448 {
449     m_minimumScale = minimum;
450     m_maximumScale = maximum;
451 }
452
453 void QtViewportInteractionEngine::setCSSScale(qreal scale)
454 {
455     ViewportUpdateDeferrer guard(this);
456
457     qreal newScale = innerBoundedCSSScale(scale);
458     m_content->setContentsScale(itemScaleFromCSS(newScale));
459 }
460
461 qreal QtViewportInteractionEngine::currentCSSScale()
462 {
463     return cssScaleFromItem(m_content->contentsScale());
464 }
465
466 bool QtViewportInteractionEngine::scrollAnimationActive() const
467 {
468     return m_viewport->isFlicking();
469 }
470
471 bool QtViewportInteractionEngine::panGestureActive() const
472 {
473     return m_viewport->isDragging();
474 }
475
476 void QtViewportInteractionEngine::panGestureStarted(const QPointF& position, qint64 eventTimestampMillis)
477 {
478     m_hadUserInteraction = true;
479     m_viewport->handleFlickableMousePress(position, eventTimestampMillis);
480     m_lastPinchCenterInViewportCoordinates = position;
481 }
482
483 void QtViewportInteractionEngine::panGestureRequestUpdate(const QPointF& position, qint64 eventTimestampMillis)
484 {
485     m_viewport->handleFlickableMouseMove(position, eventTimestampMillis);
486     m_lastPinchCenterInViewportCoordinates = position;
487 }
488
489 void QtViewportInteractionEngine::panGestureEnded(const QPointF& position, qint64 eventTimestampMillis)
490 {
491     m_viewport->handleFlickableMouseRelease(position, eventTimestampMillis);
492     m_lastPinchCenterInViewportCoordinates = position;
493 }
494
495 void QtViewportInteractionEngine::panGestureCancelled()
496 {
497     // Reset the velocity samples of the flickable.
498     // This should only be called by the recognizer if we have a recognized
499     // pan gesture and receive a touch event with multiple touch points
500     // (ie. transition to a pinch gesture) as it does not move the content
501     // back inside valid bounds.
502     // When the pinch gesture ends, the content is positioned and scaled
503     // back to valid boundaries.
504     m_viewport->cancelFlick();
505 }
506
507 bool QtViewportInteractionEngine::scaleAnimationActive() const
508 {
509     return m_scaleAnimation->state() == QAbstractAnimation::Running;
510 }
511
512 void QtViewportInteractionEngine::cancelScrollAnimation()
513 {
514     ViewportUpdateDeferrer guard(this);
515
516     // If the pan gesture recognizer receives a touch begin event
517     // during an ongoing kinetic scroll animation of a previous
518     // pan gesture, the animation is stopped and the content is
519     // immediately positioned back to valid boundaries.
520
521     m_viewport->cancelFlick();
522     ensureContentWithinViewportBoundary(/*immediate*/ true);
523 }
524
525 void QtViewportInteractionEngine::interruptScaleAnimation()
526 {
527     // This interrupts the scale animation exactly where it is, even if it is out of bounds.
528     m_scaleAnimation->stop();
529 }
530
531 bool QtViewportInteractionEngine::pinchGestureActive() const
532 {
533     return m_pinchStartScale > 0;
534 }
535
536 void QtViewportInteractionEngine::pinchGestureStarted(const QPointF& pinchCenterInViewportCoordinates)
537 {
538     if (!m_allowsUserScaling)
539         return;
540
541     m_hadUserInteraction = true;
542     m_scaleStack.clear();
543     m_zoomOutScale = 0.0;
544
545     m_scaleUpdateDeferrer = adoptPtr(new ViewportUpdateDeferrer(this, ViewportUpdateDeferrer::DeferUpdateAndSuspendContent));
546
547     m_lastPinchCenterInViewportCoordinates = pinchCenterInViewportCoordinates;
548     m_pinchStartScale = m_content->contentsScale();
549
550     // Reset the tiling look-ahead vector so that tiles all around the viewport will be requested on pinch-end.
551     emit contentViewportChanged(QPointF());
552 }
553
554 void QtViewportInteractionEngine::pinchGestureRequestUpdate(const QPointF& pinchCenterInViewportCoordinates, qreal totalScaleFactor)
555 {
556     ASSERT(m_suspendCount);
557
558     if (!m_allowsUserScaling)
559         return;
560
561     //  Changes of the center position should move the page even if the zoom factor
562     //  does not change.
563     const qreal cssScale = cssScaleFromItem(m_pinchStartScale * totalScaleFactor);
564
565     // Allow zooming out beyond mimimum scale on pages that do not explicitly disallow it.
566     const qreal targetCSSScale = outerBoundedCSSScale(cssScale);
567
568     scaleContent(m_viewport->mapToWebContent(pinchCenterInViewportCoordinates), targetCSSScale);
569
570     const QPointF positionDiff = pinchCenterInViewportCoordinates - m_lastPinchCenterInViewportCoordinates;
571     m_lastPinchCenterInViewportCoordinates = pinchCenterInViewportCoordinates;
572
573     m_viewport->setContentPos(m_viewport->contentPos() - positionDiff);
574 }
575
576 void QtViewportInteractionEngine::pinchGestureEnded()
577 {
578     ASSERT(m_suspendCount);
579
580     if (!m_allowsUserScaling)
581         return;
582
583     m_pinchStartScale = -1;
584     // Clear the update deferrer now if we're in our final position and there won't be any animation to clear it later.
585     if (ensureContentWithinViewportBoundary())
586         m_scaleUpdateDeferrer.clear();
587 }
588
589 void QtViewportInteractionEngine::pinchGestureCancelled()
590 {
591     m_pinchStartScale = -1;
592     m_scaleUpdateDeferrer.clear();
593 }
594
595 void QtViewportInteractionEngine::itemSizeChanged()
596 {
597     // FIXME: This needs to be done smarter. What happens if it resizes when we were interacting?
598     if (m_suspendCount)
599         return;
600
601     ViewportUpdateDeferrer guard(this);
602     ensureContentWithinViewportBoundary(true);
603 }
604
605 void QtViewportInteractionEngine::scaleContent(const QPointF& centerInCSSCoordinates, qreal cssScale)
606 {
607     QPointF oldPinchCenterOnViewport = m_viewport->mapFromWebContent(centerInCSSCoordinates);
608     m_content->setContentsScale(itemScaleFromCSS(cssScale));
609     QPointF newPinchCenterOnViewport = m_viewport->mapFromWebContent(centerInCSSCoordinates);
610
611     m_viewport->setContentPos(m_viewport->contentPos() + (newPinchCenterOnViewport - oldPinchCenterOnViewport));
612 }
613
614 } // namespace WebKit
615
616 #include "moc_QtViewportInteractionEngine.cpp"
617
618