2 * Copyright (C) 2005-2014 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "ViewportConfiguration.h"
29 #include <WebCore/TextStream.h>
30 #include <wtf/Assertions.h>
31 #include <wtf/MathExtras.h>
32 #include <wtf/text/CString.h>
35 #include "WebCoreSystemInterface.h"
41 static bool constraintsAreAllRelative(const ViewportConfiguration::Parameters& configuration)
43 return !configuration.widthIsSet && !configuration.heightIsSet && !configuration.initialScaleIsSet;
47 ViewportConfiguration::ViewportConfiguration()
48 : m_minimumLayoutSize(1024, 768)
49 , m_canIgnoreScalingConstraints(false)
50 , m_forceAlwaysUserScalable(false)
52 // Setup a reasonable default configuration to avoid computing infinite scale/sizes.
53 // Those are the original iPhone configuration.
54 m_defaultConfiguration = ViewportConfiguration::webpageParameters();
55 updateConfiguration();
58 void ViewportConfiguration::setDefaultConfiguration(const ViewportConfiguration::Parameters& defaultConfiguration)
60 ASSERT(!constraintsAreAllRelative(m_configuration));
61 ASSERT(!defaultConfiguration.initialScaleIsSet || defaultConfiguration.initialScale > 0);
62 ASSERT(defaultConfiguration.minimumScale > 0);
63 ASSERT(defaultConfiguration.maximumScale >= defaultConfiguration.minimumScale);
65 m_defaultConfiguration = defaultConfiguration;
66 updateConfiguration();
69 void ViewportConfiguration::setContentsSize(const IntSize& contentSize)
71 if (m_contentSize == contentSize)
74 m_contentSize = contentSize;
75 updateConfiguration();
78 void ViewportConfiguration::setMinimumLayoutSize(const FloatSize& minimumLayoutSize)
80 if (m_minimumLayoutSize == minimumLayoutSize)
83 m_minimumLayoutSize = minimumLayoutSize;
84 updateConfiguration();
87 void ViewportConfiguration::setViewportArguments(const ViewportArguments& viewportArguments)
89 if (m_viewportArguments == viewportArguments)
92 m_viewportArguments = viewportArguments;
93 updateConfiguration();
96 IntSize ViewportConfiguration::layoutSize() const
98 return IntSize(layoutWidth(), layoutHeight());
101 bool ViewportConfiguration::shouldIgnoreHorizontalScalingConstraints() const
103 if (!m_canIgnoreScalingConstraints)
106 if (!m_configuration.allowsShrinkToFit)
109 bool laidOutWiderThanViewport = m_contentSize.width() > layoutWidth();
110 if (m_viewportArguments.width == ViewportArguments::ValueDeviceWidth)
111 return laidOutWiderThanViewport;
113 if (m_configuration.initialScaleIsSet && m_configuration.initialScale == 1)
114 return laidOutWiderThanViewport;
119 bool ViewportConfiguration::shouldIgnoreVerticalScalingConstraints() const
121 if (!m_canIgnoreScalingConstraints)
124 if (!m_configuration.allowsShrinkToFit)
127 bool laidOutTallerThanViewport = m_contentSize.height() > layoutHeight();
128 if (m_viewportArguments.height == ViewportArguments::ValueDeviceHeight && m_viewportArguments.width == ViewportArguments::ValueAuto)
129 return laidOutTallerThanViewport;
134 bool ViewportConfiguration::shouldIgnoreScalingConstraints() const
136 return shouldIgnoreHorizontalScalingConstraints() || shouldIgnoreVerticalScalingConstraints();
139 double ViewportConfiguration::initialScaleFromSize(double width, double height, bool shouldIgnoreScalingConstraints) const
141 ASSERT(!constraintsAreAllRelative(m_configuration));
143 // If the document has specified its own initial scale, use it regardless.
144 // This is guaranteed to be sanity checked already, so no need for MIN/MAX.
145 if (m_configuration.initialScaleIsSet && !shouldIgnoreScalingConstraints)
146 return m_configuration.initialScale;
148 // If not, it is up to us to determine the initial scale.
149 // We want a scale small enough to fit the document width-wise.
150 const FloatSize& minimumLayoutSize = m_minimumLayoutSize;
151 double initialScale = 0;
152 if (width > 0 && !shouldIgnoreVerticalScalingConstraints())
153 initialScale = minimumLayoutSize.width() / width;
155 // Prevent the initial scale from shrinking to a height smaller than our view's minimum height.
156 if (height > 0 && height * initialScale < minimumLayoutSize.height() && !shouldIgnoreHorizontalScalingConstraints())
157 initialScale = minimumLayoutSize.height() / height;
158 return std::min(std::max(initialScale, shouldIgnoreScalingConstraints ? m_defaultConfiguration.minimumScale : m_configuration.minimumScale), m_configuration.maximumScale);
161 double ViewportConfiguration::initialScale() const
163 return initialScaleFromSize(m_contentSize.width() > 0 ? m_contentSize.width() : layoutWidth(), m_contentSize.height() > 0 ? m_contentSize.height() : layoutHeight(), shouldIgnoreScalingConstraints());
166 double ViewportConfiguration::initialScaleIgnoringContentSize() const
168 return initialScaleFromSize(layoutWidth(), layoutHeight(), false);
171 double ViewportConfiguration::minimumScale() const
173 // If we scale to fit, then this is our minimum scale as well.
174 if (!m_configuration.initialScaleIsSet || shouldIgnoreScalingConstraints())
175 return initialScale();
177 // If not, we still need to sanity check our value.
178 double minimumScale = m_configuration.minimumScale;
180 const FloatSize& minimumLayoutSize = m_minimumLayoutSize;
181 double contentWidth = m_contentSize.width();
182 if (contentWidth > 0 && contentWidth * minimumScale < minimumLayoutSize.width() && !shouldIgnoreVerticalScalingConstraints())
183 minimumScale = minimumLayoutSize.width() / contentWidth;
185 double contentHeight = m_contentSize.height();
186 if (contentHeight > 0 && contentHeight * minimumScale < minimumLayoutSize.height() && !shouldIgnoreHorizontalScalingConstraints())
187 minimumScale = minimumLayoutSize.height() / contentHeight;
189 minimumScale = std::min(std::max(minimumScale, m_configuration.minimumScale), m_configuration.maximumScale);
194 bool ViewportConfiguration::allowsUserScaling() const
196 return m_forceAlwaysUserScalable || shouldIgnoreScalingConstraints() || m_configuration.allowsUserScaling;
199 ViewportConfiguration::Parameters ViewportConfiguration::webpageParameters()
201 Parameters parameters;
202 parameters.width = 980;
203 parameters.widthIsSet = true;
204 parameters.allowsUserScaling = true;
205 parameters.allowsShrinkToFit = true;
206 parameters.minimumScale = 0.25;
207 parameters.maximumScale = 5;
211 ViewportConfiguration::Parameters ViewportConfiguration::textDocumentParameters()
213 Parameters parameters;
216 parameters.width = static_cast<int>(wkGetScreenSize().width);
218 // FIXME: this needs to be unified with ViewportArguments on all ports.
219 parameters.width = 320;
222 parameters.widthIsSet = true;
223 parameters.allowsUserScaling = true;
224 parameters.allowsShrinkToFit = false;
225 parameters.minimumScale = 0.25;
226 parameters.maximumScale = 5;
230 ViewportConfiguration::Parameters ViewportConfiguration::imageDocumentParameters()
232 Parameters parameters;
233 parameters.width = 980;
234 parameters.widthIsSet = true;
235 parameters.allowsUserScaling = true;
236 parameters.allowsShrinkToFit = false;
237 parameters.minimumScale = 0.01;
238 parameters.maximumScale = 5;
242 ViewportConfiguration::Parameters ViewportConfiguration::xhtmlMobileParameters()
244 Parameters parameters = webpageParameters();
245 parameters.width = 320;
249 ViewportConfiguration::Parameters ViewportConfiguration::testingParameters()
251 Parameters parameters;
252 parameters.initialScale = 1;
253 parameters.initialScaleIsSet = true;
254 parameters.allowsShrinkToFit = true;
255 parameters.minimumScale = 1;
256 parameters.maximumScale = 5;
260 static inline bool viewportArgumentValueIsValid(float value)
265 template<typename ValueType, typename ViewportArgumentsType>
266 static inline void applyViewportArgument(ValueType& value, ViewportArgumentsType viewportArgumentValue, ValueType minimum, ValueType maximum)
268 if (viewportArgumentValueIsValid(viewportArgumentValue))
269 value = std::min(maximum, std::max(minimum, static_cast<ValueType>(viewportArgumentValue)));
272 template<typename ValueType, typename ViewportArgumentsType>
273 static inline void applyViewportArgument(ValueType& value, bool& valueIsSet, ViewportArgumentsType viewportArgumentValue, ValueType minimum, ValueType maximum)
275 if (viewportArgumentValueIsValid(viewportArgumentValue)) {
276 value = std::min(maximum, std::max(minimum, static_cast<ValueType>(viewportArgumentValue)));
282 static inline bool booleanViewportArgumentIsSet(float value)
284 return !value || value == 1;
287 void ViewportConfiguration::updateConfiguration()
289 m_configuration = m_defaultConfiguration;
291 const double minimumViewportArgumentsScaleFactor = 0.1;
292 const double maximumViewportArgumentsScaleFactor = 10.0;
294 bool viewportArgumentsOverridesInitialScale;
295 bool viewportArgumentsOverridesWidth;
296 bool viewportArgumentsOverridesHeight;
298 applyViewportArgument(m_configuration.minimumScale, m_viewportArguments.minZoom, minimumViewportArgumentsScaleFactor, maximumViewportArgumentsScaleFactor);
299 applyViewportArgument(m_configuration.maximumScale, m_viewportArguments.maxZoom, m_configuration.minimumScale, maximumViewportArgumentsScaleFactor);
300 applyViewportArgument(m_configuration.initialScale, viewportArgumentsOverridesInitialScale, m_viewportArguments.zoom, m_configuration.minimumScale, m_configuration.maximumScale);
302 double minimumViewportArgumentsDimension = 10;
303 double maximumViewportArgumentsDimension = 10000;
304 applyViewportArgument(m_configuration.width, viewportArgumentsOverridesWidth, viewportArgumentsLength(m_viewportArguments.width), minimumViewportArgumentsDimension, maximumViewportArgumentsDimension);
305 applyViewportArgument(m_configuration.height, viewportArgumentsOverridesHeight, viewportArgumentsLength(m_viewportArguments.height), minimumViewportArgumentsDimension, maximumViewportArgumentsDimension);
307 if (viewportArgumentsOverridesInitialScale || viewportArgumentsOverridesWidth || viewportArgumentsOverridesHeight) {
308 m_configuration.initialScaleIsSet = viewportArgumentsOverridesInitialScale;
309 m_configuration.widthIsSet = viewportArgumentsOverridesWidth;
310 m_configuration.heightIsSet = viewportArgumentsOverridesHeight;
313 if (booleanViewportArgumentIsSet(m_viewportArguments.userZoom))
314 m_configuration.allowsUserScaling = m_viewportArguments.userZoom != 0.;
316 if (booleanViewportArgumentIsSet(m_viewportArguments.shrinkToFit))
317 m_configuration.allowsShrinkToFit = m_viewportArguments.shrinkToFit != 0.;
320 double ViewportConfiguration::viewportArgumentsLength(double length) const
322 if (length == ViewportArguments::ValueDeviceWidth)
323 return m_minimumLayoutSize.width();
324 if (length == ViewportArguments::ValueDeviceHeight)
325 return m_minimumLayoutSize.height();
329 int ViewportConfiguration::layoutWidth() const
331 ASSERT(!constraintsAreAllRelative(m_configuration));
333 const FloatSize& minimumLayoutSize = m_minimumLayoutSize;
334 if (m_configuration.widthIsSet) {
335 // If we scale to fit, then accept the viewport width with sanity checking.
336 if (!m_configuration.initialScaleIsSet) {
337 double maximumScale = this->maximumScale();
338 double maximumContentWidthInViewportCoordinate = maximumScale * m_configuration.width;
339 if (maximumContentWidthInViewportCoordinate < minimumLayoutSize.width()) {
340 // The content zoomed to maxScale does not fit the the view. Return the minimum width
341 // satisfying the constraint maximumScale.
342 return std::round(minimumLayoutSize.width() / maximumScale);
344 return std::round(m_configuration.width);
347 // If not, make sure the viewport width and initial scale can co-exist.
348 double initialContentWidthInViewportCoordinate = m_configuration.width * m_configuration.initialScale;
349 if (initialContentWidthInViewportCoordinate < minimumLayoutSize.width()) {
350 // The specified width does not fit in viewport. Return the minimum width that satisfy the initialScale constraint.
351 return std::round(minimumLayoutSize.width() / m_configuration.initialScale);
353 return std::round(m_configuration.width);
356 // If the page has a real scale, then just return the minimum size over the initial scale.
357 if (m_configuration.initialScaleIsSet && !m_configuration.heightIsSet)
358 return std::round(minimumLayoutSize.width() / m_configuration.initialScale);
360 if (minimumLayoutSize.height() > 0)
361 return std::round(minimumLayoutSize.width() * layoutHeight() / minimumLayoutSize.height());
362 return minimumLayoutSize.width();
365 int ViewportConfiguration::layoutHeight() const
367 ASSERT(!constraintsAreAllRelative(m_configuration));
369 const FloatSize& minimumLayoutSize = m_minimumLayoutSize;
370 if (m_configuration.heightIsSet) {
371 // If we scale to fit, then accept the viewport height with sanity checking.
372 if (!m_configuration.initialScaleIsSet) {
373 double maximumScale = this->maximumScale();
374 double maximumContentHeightInViewportCoordinate = maximumScale * m_configuration.height;
375 if (maximumContentHeightInViewportCoordinate < minimumLayoutSize.height()) {
376 // The content zoomed to maxScale does not fit the the view. Return the minimum height that
377 // satisfy the constraint maximumScale.
378 return std::round(minimumLayoutSize.height() / maximumScale);
380 return std::round(m_configuration.height);
383 // If not, make sure the viewport width and initial scale can co-exist.
384 double initialContentHeightInViewportCoordinate = m_configuration.height * m_configuration.initialScale;
385 if (initialContentHeightInViewportCoordinate < minimumLayoutSize.height()) {
386 // The specified width does not fit in viewport. Return the minimum height that satisfy the initialScale constraint.
387 return std::round(minimumLayoutSize.height() / m_configuration.initialScale);
389 return std::round(m_configuration.height);
392 // If the page has a real scale, then just return the minimum size over the initial scale.
393 if (m_configuration.initialScaleIsSet && !m_configuration.widthIsSet)
394 return std::round(minimumLayoutSize.height() / m_configuration.initialScale);
396 if (minimumLayoutSize.width() > 0)
397 return std::round(minimumLayoutSize.height() * layoutWidth() / minimumLayoutSize.width());
398 return minimumLayoutSize.height();
402 class ViewportConfigurationTextStream : public TextStream {
404 ViewportConfigurationTextStream()
409 using TextStream::operator<<;
411 ViewportConfigurationTextStream& operator<<(const ViewportConfiguration::Parameters&);
412 ViewportConfigurationTextStream& operator<<(const ViewportArguments&);
414 void increaseIndent() { ++m_indent; }
415 void decreaseIndent() { --m_indent; ASSERT(m_indent >= 0); }
423 template <typename T>
424 static void dumpProperty(ViewportConfigurationTextStream& ts, String name, T value)
429 ts << "(" << name << " ";
434 void ViewportConfigurationTextStream::writeIndent()
436 for (int i = 0; i < m_indent; ++i)
440 ViewportConfigurationTextStream& ViewportConfigurationTextStream::operator<<(const ViewportConfiguration::Parameters& parameters)
442 ViewportConfigurationTextStream& ts = *this;
447 ts << "(width " << parameters.width << ", set: " << (parameters.widthIsSet ? "true" : "false") << ")";
451 ts << "(height " << parameters.height << ", set: " << (parameters.heightIsSet ? "true" : "false") << ")";
455 ts << "(initialScale " << parameters.initialScale << ", set: " << (parameters.initialScaleIsSet ? "true" : "false") << ")";
458 dumpProperty(ts, "minimumScale", parameters.minimumScale);
459 dumpProperty(ts, "maximumScale", parameters.maximumScale);
460 dumpProperty(ts, "allowsUserScaling", parameters.allowsUserScaling);
461 dumpProperty(ts, "allowsShrinkToFit", parameters.allowsShrinkToFit);
466 ViewportConfigurationTextStream& ViewportConfigurationTextStream::operator<<(const ViewportArguments& viewportArguments)
468 ViewportConfigurationTextStream& ts = *this;
474 ts << "(width " << viewportArguments.width << ", minWidth " << viewportArguments.minWidth << ", maxWidth " << viewportArguments.maxWidth << ")";
478 ts << "(height " << viewportArguments.height << ", minHeight " << viewportArguments.minHeight << ", maxHeight " << viewportArguments.maxHeight << ")";
482 ts << "(zoom " << viewportArguments.zoom << ", minZoom " << viewportArguments.minZoom << ", maxZoom " << viewportArguments.maxZoom << ")";
488 CString ViewportConfiguration::description() const
490 ViewportConfigurationTextStream ts;
492 ts << "(viewport-configuration " << (void*)this;
496 ts << "(viewport arguments";
497 ts << m_viewportArguments;
504 ts << "(configuration";
505 ts << m_configuration;
512 ts << "(default configuration";
513 ts << m_defaultConfiguration;
517 dumpProperty(ts, "contentSize", m_contentSize);
518 dumpProperty(ts, "minimumLayoutSize", m_minimumLayoutSize);
523 ts << "(computed initial scale " << initialScale() << ")\n";
525 ts << "(computed minimum scale " << minimumScale() << ")\n";
527 ts << "(computed layout size " << layoutSize() << ")\n";
529 ts << "(ignoring horizontal scaling constraints " << (shouldIgnoreHorizontalScalingConstraints() ? "true" : "false") << ")\n";
531 ts << "(ignoring vertical scaling constraints " << (shouldIgnoreVerticalScalingConstraints() ? "true" : "false") << ")";
536 return ts.release().utf8();
539 void ViewportConfiguration::dump() const
541 WTFLogAlways("%s", description().data());
546 } // namespace WebCore