ViewportConfiguration.cpp [plain text]
#include "config.h"
#include "ViewportConfiguration.h"
#include <WebCore/TextStream.h>
#include <wtf/Assertions.h>
#include <wtf/MathExtras.h>
#include <wtf/text/CString.h>
#if PLATFORM(IOS)
#include "WebCoreSystemInterface.h"
#endif
namespace WebCore {
#if !ASSERT_DISABLED
static bool constraintsAreAllRelative(const ViewportConfiguration::Parameters& configuration)
{
return !configuration.widthIsSet && !configuration.heightIsSet && !configuration.initialScaleIsSet;
}
#endif
ViewportConfiguration::ViewportConfiguration()
: m_minimumLayoutSize(1024, 768)
, m_canIgnoreScalingConstraints(false)
, m_forceAlwaysUserScalable(false)
{
m_defaultConfiguration = ViewportConfiguration::webpageParameters();
updateConfiguration();
}
void ViewportConfiguration::setDefaultConfiguration(const ViewportConfiguration::Parameters& defaultConfiguration)
{
ASSERT(!constraintsAreAllRelative(m_configuration));
ASSERT(!m_defaultConfiguration.initialScaleIsSet || defaultConfiguration.initialScale > 0);
ASSERT(defaultConfiguration.minimumScale > 0);
ASSERT(defaultConfiguration.maximumScale >= defaultConfiguration.minimumScale);
m_defaultConfiguration = defaultConfiguration;
updateConfiguration();
}
void ViewportConfiguration::setContentsSize(const IntSize& contentSize)
{
if (m_contentSize == contentSize)
return;
m_contentSize = contentSize;
updateConfiguration();
}
void ViewportConfiguration::setMinimumLayoutSize(const FloatSize& minimumLayoutSize)
{
if (m_minimumLayoutSize == minimumLayoutSize)
return;
m_minimumLayoutSize = minimumLayoutSize;
updateConfiguration();
}
void ViewportConfiguration::setViewportArguments(const ViewportArguments& viewportArguments)
{
if (m_viewportArguments == viewportArguments)
return;
m_viewportArguments = viewportArguments;
updateConfiguration();
}
IntSize ViewportConfiguration::layoutSize() const
{
return IntSize(layoutWidth(), layoutHeight());
}
bool ViewportConfiguration::shouldIgnoreHorizontalScalingConstraints() const
{
if (!m_canIgnoreScalingConstraints)
return false;
if (!m_configuration.allowsShrinkToFit)
return false;
bool laidOutWiderThanViewport = m_contentSize.width() > layoutWidth();
if (m_viewportArguments.width == ViewportArguments::ValueDeviceWidth)
return laidOutWiderThanViewport;
if (m_configuration.initialScaleIsSet && m_configuration.initialScale == 1)
return laidOutWiderThanViewport;
return false;
}
bool ViewportConfiguration::shouldIgnoreVerticalScalingConstraints() const
{
if (!m_canIgnoreScalingConstraints)
return false;
if (!m_configuration.allowsShrinkToFit)
return false;
bool laidOutTallerThanViewport = m_contentSize.height() > layoutHeight();
if (m_viewportArguments.height == ViewportArguments::ValueDeviceHeight && m_viewportArguments.width == ViewportArguments::ValueAuto)
return laidOutTallerThanViewport;
return false;
}
bool ViewportConfiguration::shouldIgnoreScalingConstraints() const
{
return shouldIgnoreHorizontalScalingConstraints() || shouldIgnoreVerticalScalingConstraints();
}
double ViewportConfiguration::initialScaleFromSize(double width, double height, bool shouldIgnoreScalingConstraints) const
{
ASSERT(!constraintsAreAllRelative(m_configuration));
if (m_configuration.initialScaleIsSet && !shouldIgnoreScalingConstraints)
return m_configuration.initialScale;
const FloatSize& minimumLayoutSize = m_minimumLayoutSize;
double initialScale = 0;
if (width > 0 && !shouldIgnoreVerticalScalingConstraints())
initialScale = minimumLayoutSize.width() / width;
if (height > 0 && height * initialScale < minimumLayoutSize.height() && !shouldIgnoreHorizontalScalingConstraints())
initialScale = minimumLayoutSize.height() / height;
return std::min(std::max(initialScale, shouldIgnoreScalingConstraints ? m_defaultConfiguration.minimumScale : m_configuration.minimumScale), m_configuration.maximumScale);
}
double ViewportConfiguration::initialScale() const
{
return initialScaleFromSize(m_contentSize.width() > 0 ? m_contentSize.width() : layoutWidth(), m_contentSize.height() > 0 ? m_contentSize.height() : layoutHeight(), shouldIgnoreScalingConstraints());
}
double ViewportConfiguration::initialScaleIgnoringContentSize() const
{
return initialScaleFromSize(layoutWidth(), layoutHeight(), false);
}
double ViewportConfiguration::minimumScale() const
{
if (!m_configuration.initialScaleIsSet || shouldIgnoreScalingConstraints())
return initialScale();
double minimumScale = m_configuration.minimumScale;
const FloatSize& minimumLayoutSize = m_minimumLayoutSize;
double contentWidth = m_contentSize.width();
if (contentWidth > 0 && contentWidth * minimumScale < minimumLayoutSize.width() && !shouldIgnoreVerticalScalingConstraints())
minimumScale = minimumLayoutSize.width() / contentWidth;
double contentHeight = m_contentSize.height();
if (contentHeight > 0 && contentHeight * minimumScale < minimumLayoutSize.height() && !shouldIgnoreHorizontalScalingConstraints())
minimumScale = minimumLayoutSize.height() / contentHeight;
minimumScale = std::min(std::max(minimumScale, m_configuration.minimumScale), m_configuration.maximumScale);
return minimumScale;
}
bool ViewportConfiguration::allowsUserScaling() const
{
return m_forceAlwaysUserScalable || shouldIgnoreScalingConstraints() || m_configuration.allowsUserScaling;
}
ViewportConfiguration::Parameters ViewportConfiguration::webpageParameters()
{
Parameters parameters;
parameters.width = 980;
parameters.widthIsSet = true;
parameters.allowsUserScaling = true;
parameters.allowsShrinkToFit = true;
parameters.minimumScale = 0.25;
parameters.maximumScale = 5;
return parameters;
}
ViewportConfiguration::Parameters ViewportConfiguration::textDocumentParameters()
{
Parameters parameters;
#if PLATFORM(IOS)
parameters.width = static_cast<int>(wkGetScreenSize().width);
#else
parameters.width = 320;
#endif
parameters.widthIsSet = true;
parameters.allowsUserScaling = true;
parameters.allowsShrinkToFit = false;
parameters.minimumScale = 0.25;
parameters.maximumScale = 5;
return parameters;
}
ViewportConfiguration::Parameters ViewportConfiguration::imageDocumentParameters()
{
Parameters parameters;
parameters.width = 980;
parameters.widthIsSet = true;
parameters.allowsUserScaling = true;
parameters.allowsShrinkToFit = false;
parameters.minimumScale = 0.01;
parameters.maximumScale = 5;
return parameters;
}
ViewportConfiguration::Parameters ViewportConfiguration::xhtmlMobileParameters()
{
Parameters parameters = webpageParameters();
parameters.width = 320;
return parameters;
}
ViewportConfiguration::Parameters ViewportConfiguration::testingParameters()
{
Parameters parameters;
parameters.initialScale = 1;
parameters.initialScaleIsSet = true;
parameters.allowsShrinkToFit = true;
parameters.minimumScale = 1;
parameters.maximumScale = 5;
return parameters;
}
static inline bool viewportArgumentValueIsValid(float value)
{
return value > 0;
}
template<typename ValueType, typename ViewportArgumentsType>
static inline void applyViewportArgument(ValueType& value, ViewportArgumentsType viewportArgumentValue, ValueType minimum, ValueType maximum)
{
if (viewportArgumentValueIsValid(viewportArgumentValue))
value = std::min(maximum, std::max(minimum, static_cast<ValueType>(viewportArgumentValue)));
}
template<typename ValueType, typename ViewportArgumentsType>
static inline void applyViewportArgument(ValueType& value, bool& valueIsSet, ViewportArgumentsType viewportArgumentValue, ValueType minimum, ValueType maximum)
{
if (viewportArgumentValueIsValid(viewportArgumentValue)) {
value = std::min(maximum, std::max(minimum, static_cast<ValueType>(viewportArgumentValue)));
valueIsSet = true;
} else
valueIsSet = false;
}
static inline bool booleanViewportArgumentIsSet(float value)
{
return !value || value == 1;
}
void ViewportConfiguration::updateConfiguration()
{
m_configuration = m_defaultConfiguration;
const double minimumViewportArgumentsScaleFactor = 0.1;
const double maximumViewportArgumentsScaleFactor = 10.0;
bool viewportArgumentsOverridesInitialScale;
bool viewportArgumentsOverridesWidth;
bool viewportArgumentsOverridesHeight;
applyViewportArgument(m_configuration.minimumScale, m_viewportArguments.minZoom, minimumViewportArgumentsScaleFactor, maximumViewportArgumentsScaleFactor);
applyViewportArgument(m_configuration.maximumScale, m_viewportArguments.maxZoom, m_configuration.minimumScale, maximumViewportArgumentsScaleFactor);
applyViewportArgument(m_configuration.initialScale, viewportArgumentsOverridesInitialScale, m_viewportArguments.zoom, m_configuration.minimumScale, m_configuration.maximumScale);
double minimumViewportArgumentsDimension = 10;
double maximumViewportArgumentsDimension = 10000;
applyViewportArgument(m_configuration.width, viewportArgumentsOverridesWidth, viewportArgumentsLength(m_viewportArguments.width), minimumViewportArgumentsDimension, maximumViewportArgumentsDimension);
applyViewportArgument(m_configuration.height, viewportArgumentsOverridesHeight, viewportArgumentsLength(m_viewportArguments.height), minimumViewportArgumentsDimension, maximumViewportArgumentsDimension);
if (viewportArgumentsOverridesInitialScale || viewportArgumentsOverridesWidth || viewportArgumentsOverridesHeight) {
m_configuration.initialScaleIsSet = viewportArgumentsOverridesInitialScale;
m_configuration.widthIsSet = viewportArgumentsOverridesWidth;
m_configuration.heightIsSet = viewportArgumentsOverridesHeight;
}
if (booleanViewportArgumentIsSet(m_viewportArguments.userZoom))
m_configuration.allowsUserScaling = m_viewportArguments.userZoom != 0.;
if (booleanViewportArgumentIsSet(m_viewportArguments.shrinkToFit))
m_configuration.allowsShrinkToFit = m_viewportArguments.shrinkToFit != 0.;
}
double ViewportConfiguration::viewportArgumentsLength(double length) const
{
if (length == ViewportArguments::ValueDeviceWidth)
return m_minimumLayoutSize.width();
if (length == ViewportArguments::ValueDeviceHeight)
return m_minimumLayoutSize.height();
return length;
}
int ViewportConfiguration::layoutWidth() const
{
ASSERT(!constraintsAreAllRelative(m_configuration));
const FloatSize& minimumLayoutSize = m_minimumLayoutSize;
if (m_configuration.widthIsSet) {
if (!m_configuration.initialScaleIsSet) {
double maximumScale = this->maximumScale();
double maximumContentWidthInViewportCoordinate = maximumScale * m_configuration.width;
if (maximumContentWidthInViewportCoordinate < minimumLayoutSize.width()) {
return std::round(minimumLayoutSize.width() / maximumScale);
}
return std::round(m_configuration.width);
}
double initialContentWidthInViewportCoordinate = m_configuration.width * m_configuration.initialScale;
if (initialContentWidthInViewportCoordinate < minimumLayoutSize.width()) {
return std::round(minimumLayoutSize.width() / m_configuration.initialScale);
}
return std::round(m_configuration.width);
}
if (m_configuration.initialScaleIsSet && !m_configuration.heightIsSet)
return std::round(minimumLayoutSize.width() / m_configuration.initialScale);
if (minimumLayoutSize.height() > 0)
return std::round(minimumLayoutSize.width() * layoutHeight() / minimumLayoutSize.height());
return minimumLayoutSize.width();
}
int ViewportConfiguration::layoutHeight() const
{
ASSERT(!constraintsAreAllRelative(m_configuration));
const FloatSize& minimumLayoutSize = m_minimumLayoutSize;
if (m_configuration.heightIsSet) {
if (!m_configuration.initialScaleIsSet) {
double maximumScale = this->maximumScale();
double maximumContentHeightInViewportCoordinate = maximumScale * m_configuration.height;
if (maximumContentHeightInViewportCoordinate < minimumLayoutSize.height()) {
return std::round(minimumLayoutSize.height() / maximumScale);
}
return std::round(m_configuration.height);
}
double initialContentHeightInViewportCoordinate = m_configuration.height * m_configuration.initialScale;
if (initialContentHeightInViewportCoordinate < minimumLayoutSize.height()) {
return std::round(minimumLayoutSize.height() / m_configuration.initialScale);
}
return std::round(m_configuration.height);
}
if (m_configuration.initialScaleIsSet && !m_configuration.widthIsSet)
return std::round(minimumLayoutSize.height() / m_configuration.initialScale);
if (minimumLayoutSize.width() > 0)
return std::round(minimumLayoutSize.height() * layoutWidth() / minimumLayoutSize.width());
return minimumLayoutSize.height();
}
#ifndef NDEBUG
class ViewportConfigurationTextStream : public TextStream {
public:
ViewportConfigurationTextStream()
: m_indent(0)
{
}
using TextStream::operator<<;
ViewportConfigurationTextStream& operator<<(const ViewportConfiguration::Parameters&);
ViewportConfigurationTextStream& operator<<(const ViewportArguments&);
void increaseIndent() { ++m_indent; }
void decreaseIndent() { --m_indent; ASSERT(m_indent >= 0); }
void writeIndent();
private:
int m_indent;
};
template <typename T>
static void dumpProperty(ViewportConfigurationTextStream& ts, String name, T value)
{
ts << "\n";
ts.increaseIndent();
ts.writeIndent();
ts << "(" << name << " ";
ts << value << ")";
ts.decreaseIndent();
}
void ViewportConfigurationTextStream::writeIndent()
{
for (int i = 0; i < m_indent; ++i)
*this << " ";
}
ViewportConfigurationTextStream& ViewportConfigurationTextStream::operator<<(const ViewportConfiguration::Parameters& parameters)
{
ViewportConfigurationTextStream& ts = *this;
ts.increaseIndent();
ts << "\n";
ts.writeIndent();
ts << "(width " << parameters.width << ", set: " << (parameters.widthIsSet ? "true" : "false") << ")";
ts << "\n";
ts.writeIndent();
ts << "(height " << parameters.height << ", set: " << (parameters.heightIsSet ? "true" : "false") << ")";
ts << "\n";
ts.writeIndent();
ts << "(initialScale " << parameters.width << ", set: " << (parameters.initialScaleIsSet ? "true" : "false") << ")";
ts.decreaseIndent();
dumpProperty(ts, "minimumScale", parameters.minimumScale);
dumpProperty(ts, "maximumScale", parameters.maximumScale);
dumpProperty(ts, "allowsUserScaling", parameters.allowsUserScaling);
dumpProperty(ts, "allowsShrinkToFit", parameters.allowsShrinkToFit);
return ts;
}
ViewportConfigurationTextStream& ViewportConfigurationTextStream::operator<<(const ViewportArguments& viewportArguments)
{
ViewportConfigurationTextStream& ts = *this;
ts.increaseIndent();
ts << "\n";
ts.writeIndent();
ts << "(width " << viewportArguments.width << ", minWidth " << viewportArguments.minWidth << ", maxWidth " << viewportArguments.maxWidth << ")";
ts << "\n";
ts.writeIndent();
ts << "(height " << viewportArguments.height << ", minHeight " << viewportArguments.minHeight << ", maxHeight " << viewportArguments.maxHeight << ")";
ts << "\n";
ts.writeIndent();
ts << "(zoom " << viewportArguments.zoom << ", minZoom " << viewportArguments.minZoom << ", maxZoom " << viewportArguments.maxZoom << ")";
ts.decreaseIndent();
return ts;
}
CString ViewportConfiguration::description() const
{
ViewportConfigurationTextStream ts;
ts << "(viewport-configuration " << (void*)this;
ts << "\n";
ts.increaseIndent();
ts.writeIndent();
ts << "(viewport arguments";
ts << m_viewportArguments;
ts << ")";
ts.decreaseIndent();
ts << "\n";
ts.increaseIndent();
ts.writeIndent();
ts << "(configuration";
ts << m_configuration;
ts << ")";
ts.decreaseIndent();
ts << "\n";
ts.increaseIndent();
ts.writeIndent();
ts << "(default configuration";
ts << m_defaultConfiguration;
ts << ")";
ts.decreaseIndent();
dumpProperty(ts, "contentSize", m_contentSize);
dumpProperty(ts, "minimumLayoutSize", m_minimumLayoutSize);
ts << "\n";
ts.increaseIndent();
ts.writeIndent();
ts << "(computed initial scale " << initialScale() << ")\n";
ts.writeIndent();
ts << "(computed minimum scale " << minimumScale() << ")\n";
ts.writeIndent();
ts << "(computed layout size " << layoutSize() << ")\n";
ts.writeIndent();
ts << "(ignoring horizontal scaling constraints " << (shouldIgnoreHorizontalScalingConstraints() ? "true" : "false") << ")\n";
ts.writeIndent();
ts << "(ignoring vertical scaling constraints " << (shouldIgnoreVerticalScalingConstraints() ? "true" : "false") << ")";
ts.decreaseIndent();
ts << ")\n";
return ts.release().utf8();
}
void ViewportConfiguration::dump() const
{
WTFLogAlways("%s", description().data());
}
#endif
}