[Qt] Unify the PageViewportController<->Client interface regarding positions
[WebKit-https.git] / Source / WebKit2 / UIProcess / PageViewportController.cpp
1 /*
2  * Copyright (C) 2011, 2012 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 "PageViewportController.h"
24
25 #include "PageViewportControllerClient.h"
26 #include "WebPageProxy.h"
27 #include <WebCore/FloatRect.h>
28 #include <WebCore/FloatSize.h>
29 #include <wtf/MathExtras.h>
30
31 using namespace WebCore;
32
33 namespace WebKit {
34
35 static inline float bound(float min, float value, float max)
36 {
37     return clampTo<float>(value, min, max);
38 }
39
40 bool fuzzyCompare(float a, float b, float epsilon)
41 {
42     return std::abs(a - b) < epsilon;
43 }
44
45 FloatPoint boundPosition(const FloatPoint minPosition, const FloatPoint& position, const FloatPoint& maxPosition)
46 {
47     return FloatPoint(bound(minPosition.x(), position.x(), maxPosition.x()), bound(minPosition.y(), position.y(), maxPosition.y()));
48 }
49
50 ViewportUpdateDeferrer::ViewportUpdateDeferrer(PageViewportController* PageViewportController, SuspendContentFlag suspendContentFlag)
51     : m_controller(PageViewportController)
52 {
53     m_controller->m_activeDeferrerCount++;
54
55     // There is no need to suspend content for immediate updates
56     // only during animations or longer gestures.
57     if (suspendContentFlag == DeferUpdateAndSuspendContent)
58         m_controller->suspendContent();
59 }
60
61 ViewportUpdateDeferrer::~ViewportUpdateDeferrer()
62 {
63     if (--(m_controller->m_activeDeferrerCount))
64         return;
65
66     m_controller->resumeContent();
67
68     // Make sure that tiles all around the viewport will be requested.
69     m_controller->syncVisibleContents();
70 }
71
72 PageViewportController::PageViewportController(WebKit::WebPageProxy* proxy, PageViewportControllerClient* client)
73     : m_webPageProxy(proxy)
74     , m_client(client)
75     , m_allowsUserScaling(false)
76     , m_minimumScaleToFit(1)
77     , m_activeDeferrerCount(0)
78     , m_hasSuspendedContent(false)
79     , m_hadUserInteraction(false)
80     , m_effectiveScale(1)
81 {
82     // Initializing Viewport Raw Attributes to avoid random negative scale factors
83     // if there is a race condition between the first layout and setting the viewport attributes for the first time.
84     m_rawAttributes.initialScale = 1;
85     m_rawAttributes.minimumScale = 1;
86     m_rawAttributes.maximumScale = 1;
87     m_rawAttributes.userScalable = m_allowsUserScaling;
88
89     ASSERT(m_client);
90     m_client->setController(this);
91 }
92
93 float PageViewportController::innerBoundedViewportScale(float viewportScale) const
94 {
95     return bound(toViewportScale(m_minimumScaleToFit), viewportScale, toViewportScale(m_rawAttributes.maximumScale));
96 }
97
98 float PageViewportController::outerBoundedViewportScale(float viewportScale) const
99 {
100     if (m_allowsUserScaling) {
101         // Bounded by [0.1, 10.0] like the viewport meta code in WebCore.
102         float hardMin = toViewportScale(std::max<float>(0.1, 0.5 * m_minimumScaleToFit));
103         float hardMax = toViewportScale(std::min<float>(10, 2 * m_rawAttributes.maximumScale));
104         return bound(hardMin, viewportScale, hardMax);
105     }
106     return innerBoundedViewportScale(viewportScale);
107 }
108
109 float PageViewportController::devicePixelRatio() const
110 {
111     return m_webPageProxy->deviceScaleFactor();
112 }
113
114 void PageViewportController::didChangeContentsSize(const IntSize& newSize)
115 {
116     if (m_viewportSize.isEmpty() || newSize.isEmpty())
117         return;
118
119     m_contentsSize = newSize;
120     updateMinimumScaleToFit();
121
122     m_client->didChangeContentsSize();
123 }
124
125 void PageViewportController::pageDidRequestScroll(const IntPoint& cssPosition)
126 {
127     // Ignore the request if suspended. Can only happen due to delay in event delivery.
128     if (m_activeDeferrerCount)
129         return;
130
131     FloatRect endPosRange = positionRangeForViewportAtScale(m_effectiveScale);
132     FloatPoint endPosition = boundPosition(endPosRange.minXMinYCorner(), cssPosition, endPosRange.maxXMaxYCorner());
133
134     m_client->setViewportPosition(endPosition);
135 }
136
137 void PageViewportController::didChangeViewportSize(const FloatSize& newSize)
138 {
139     if (newSize.isEmpty())
140         return;
141
142     m_viewportSize = newSize;
143
144     // Let the WebProcess know about the new viewport size, so that
145     // it can resize the content accordingly.
146     m_webPageProxy->setViewportSize(roundedIntSize(newSize));
147
148     syncVisibleContents();
149 }
150
151 void PageViewportController::didChangeContentsVisibility(const FloatPoint& viewportPos, float viewportScale, const FloatPoint& trajectoryVector)
152 {
153     m_viewportPos = viewportPos;
154     m_effectiveScale = viewportScale;
155     syncVisibleContents(trajectoryVector);
156 }
157
158 void PageViewportController::syncVisibleContents(const FloatPoint& trajectoryVector)
159 {
160     DrawingAreaProxy* const drawingArea = m_webPageProxy->drawingArea();
161     if (!drawingArea || m_viewportSize.isEmpty() || m_contentsSize.isEmpty())
162         return;
163
164     FloatRect endPosRange = positionRangeForViewportAtScale(m_effectiveScale);
165     FloatPoint endPosition = boundPosition(endPosRange.minXMinYCorner(), m_viewportPos, endPosRange.maxXMaxYCorner());
166
167     FloatRect visibleContentsRect(endPosition, m_viewportSize / m_effectiveScale);
168     visibleContentsRect.intersect(FloatRect(FloatPoint::zero(), m_contentsSize));
169     drawingArea->setVisibleContentsRect(visibleContentsRect, m_effectiveScale, trajectoryVector);
170
171     m_client->didChangeVisibleContents();
172 }
173
174 void PageViewportController::didChangeViewportAttributes(const WebCore::ViewportAttributes& newAttributes)
175 {
176     if (newAttributes.layoutSize.isEmpty())
177         return;
178
179     m_rawAttributes = newAttributes;
180     WebCore::restrictScaleFactorToInitialScaleIfNotUserScalable(m_rawAttributes);
181
182     m_allowsUserScaling = !!m_rawAttributes.userScalable;
183     updateMinimumScaleToFit();
184
185     m_client->didChangeViewportAttributes();
186 }
187
188 void PageViewportController::suspendContent()
189 {
190     if (m_hasSuspendedContent)
191         return;
192
193     m_hasSuspendedContent = true;
194     m_webPageProxy->suspendActiveDOMObjectsAndAnimations();
195 }
196
197 void PageViewportController::resumeContent()
198 {
199     if (!m_rawAttributes.layoutSize.isEmpty() && m_rawAttributes.initialScale > 0) {
200         m_hadUserInteraction = false;
201         m_client->setContentsScale(innerBoundedViewportScale(toViewportScale(m_rawAttributes.initialScale)), /* isInitialScale */ true);
202         m_rawAttributes.initialScale = -1; // Mark used.
203     }
204
205     m_client->didResumeContent();
206
207     if (!m_hasSuspendedContent)
208         return;
209
210     m_hasSuspendedContent = false;
211     m_webPageProxy->resumeActiveDOMObjectsAndAnimations();
212 }
213
214 void PageViewportController::updateMinimumScaleToFit()
215 {
216     float minimumScale = WebCore::computeMinimumScaleFactorForContentContained(m_rawAttributes, WebCore::roundedIntSize(m_viewportSize), WebCore::roundedIntSize(m_contentsSize));
217
218     if (!fuzzyCompare(minimumScale, m_minimumScaleToFit, 0.001)) {
219         m_minimumScaleToFit = minimumScale;
220
221         if (!m_hadUserInteraction && !hasSuspendedContent())
222             m_client->setContentsScale(toViewportScale(minimumScale), true /* isInitialScale */);
223
224         m_client->didChangeViewportAttributes();
225     }
226 }
227
228 FloatRect PageViewportController::positionRangeForViewportAtScale(float viewportScale) const
229 {
230     const float horizontalRange = m_contentsSize.width() - m_viewportSize.width() / viewportScale;
231     const float verticalRange = m_contentsSize.height() - m_viewportSize.height() / viewportScale;
232
233     return FloatRect(0, 0, horizontalRange, verticalRange);
234 }
235
236 } // namespace WebKit