1/*
2 * Copyright (C) 2011, 2012 Nokia Corporation and/or its subsidiary(-ies)
3 * Copyright (C) 2011 Benjamin Poulain <[email protected]>
4 * Copyright (C) 2014 Igalia S.L.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this program; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23#include "SimpleViewportController.h"
24
25#if USE(COORDINATED_GRAPHICS)
26
27namespace WebKit {
28using namespace WebCore;
29
30SimpleViewportController::SimpleViewportController(const IntSize& size)
31 : m_viewportSize(size)
32{
33 resetViewportToDefaultState();
34}
35
36void SimpleViewportController::didChangeViewportSize(const IntSize& newSize)
37{
38 if (newSize.isEmpty())
39 return;
40
41 m_viewportSize = newSize;
42 updateMinimumScaleToFit();
43}
44
45void SimpleViewportController::didChangeContentsSize(const IntSize& newSize)
46{
47 m_contentsSize = newSize;
48
49 updateMinimumScaleToFit();
50
51 if (m_initiallyFitToViewport) {
52 // Restrict scale factors to m_minimumScaleToFit.
53 ASSERT(m_minimumScaleToFit > 0);
54 m_rawAttributes.initialScale = m_minimumScaleToFit;
55 restrictScaleFactorToInitialScaleIfNotUserScalable(m_rawAttributes);
56 }
57}
58
59void SimpleViewportController::didChangeViewportAttributes(ViewportAttributes&& newAttributes)
60{
61 if (newAttributes.layoutSize.isEmpty()) {
62 resetViewportToDefaultState();
63 return;
64 }
65
66 m_hasViewportAttribute = true;
67
68 m_rawAttributes = WTFMove(newAttributes);
69 m_allowsUserScaling = m_rawAttributes.userScalable;
70 m_initiallyFitToViewport = m_rawAttributes.initialScale < 0;
71
72 if (!m_initiallyFitToViewport)
73 restrictScaleFactorToInitialScaleIfNotUserScalable(m_rawAttributes);
74
75 updateMinimumScaleToFit();
76}
77
78void SimpleViewportController::didScroll(const IntPoint& position)
79{
80 m_contentsPosition = position;
81}
82
83FloatRect SimpleViewportController::visibleContentsRect() const
84{
85 if (m_viewportSize.isEmpty() || m_contentsSize.isEmpty())
86 return { };
87
88 FloatRect visibleContentsRect(boundContentsPosition(m_contentsPosition), visibleContentsSize());
89 visibleContentsRect.intersect(FloatRect(FloatPoint::zero(), m_contentsSize));
90 return visibleContentsRect;
91}
92
93FloatSize SimpleViewportController::visibleContentsSize() const
94{
95 return FloatSize(m_viewportSize.width() / m_pageScaleFactor, m_viewportSize.height() / m_pageScaleFactor);
96}
97
98FloatPoint SimpleViewportController::boundContentsPositionAtScale(const FloatPoint& framePosition, float scale) const
99{
100 // We need to floor the viewport here as to allow aligning the content in device units. If not,
101 // it might not be possible to scroll the last pixel and that affects fixed position elements.
102 return FloatPoint(
103 clampTo(framePosition.x(), .0f, std::max(.0f, m_contentsSize.width() - floorf(m_viewportSize.width() / scale))),
104 clampTo(framePosition.y(), .0f, std::max(.0f, m_contentsSize.height() - floorf(m_viewportSize.height() / scale))));
105}
106
107FloatPoint SimpleViewportController::boundContentsPosition(const FloatPoint& framePosition) const
108{
109 return boundContentsPositionAtScale(framePosition, m_pageScaleFactor);
110}
111
112bool fuzzyCompare(float a, float b, float epsilon)
113{
114 return std::abs(a - b) < epsilon;
115}
116
117bool SimpleViewportController::updateMinimumScaleToFit()
118{
119 if (m_viewportSize.isEmpty() || m_contentsSize.isEmpty() || !m_hasViewportAttribute)
120 return false;
121
122 bool currentlyScaledToFit = fuzzyCompare(m_pageScaleFactor, m_minimumScaleToFit, 0.0001);
123
124 float minimumScale = computeMinimumScaleFactorForContentContained(m_rawAttributes, roundedIntSize(m_viewportSize), roundedIntSize(m_contentsSize));
125
126 if (minimumScale <= 0)
127 return false;
128
129 if (!fuzzyCompare(minimumScale, m_minimumScaleToFit, 0.0001)) {
130 m_minimumScaleToFit = minimumScale;
131
132 if (currentlyScaledToFit)
133 m_pageScaleFactor = m_minimumScaleToFit;
134 else {
135 // Ensure the effective scale stays within bounds.
136 float boundedScale = innerBoundedViewportScale(m_pageScaleFactor);
137 if (!fuzzyCompare(boundedScale, m_pageScaleFactor, 0.0001))
138 m_pageScaleFactor = boundedScale;
139 }
140
141 return true;
142 }
143 return false;
144}
145
146float SimpleViewportController::innerBoundedViewportScale(float viewportScale) const
147{
148 return clampTo(viewportScale, m_minimumScaleToFit, m_rawAttributes.maximumScale);
149}
150
151void SimpleViewportController::resetViewportToDefaultState()
152{
153 m_hasViewportAttribute = false;
154 m_pageScaleFactor = 1;
155 m_minimumScaleToFit = 1;
156
157 // Initializing Viewport Raw Attributes to avoid random negative or infinity scale factors
158 // if there is a race condition between the first layout and setting the viewport attributes for the first time.
159 m_rawAttributes.minimumScale = 1;
160 m_rawAttributes.maximumScale = 1;
161 m_rawAttributes.userScalable = m_allowsUserScaling;
162
163 // The initial scale might be implicit and set to -1, in this case we have to infer it
164 // using the viewport size and the final layout size.
165 // To be able to assert for valid scale we initialize it to -1.
166 m_rawAttributes.initialScale = -1;
167}
168
169} // namespace WebCore
170
171#endif // USE(COORDINATED_GRAPHICS)
172