blob: 97537262bd12e478ab00a0af20f15964059c7ce7 [file] [log] [blame]
/*
* Copyright (C) 2005-2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "ViewportConfiguration.h"
#include "Logging.h"
#include <wtf/Assertions.h>
#include <wtf/MathExtras.h>
#include <wtf/text/CString.h>
#include <wtf/text/TextStream.h>
#if PLATFORM(IOS_FAMILY)
#include "PlatformScreen.h"
#endif
namespace WebCore {
constexpr double defaultDesktopViewportWidth = 980;
#if ASSERT_ENABLED
static bool constraintsAreAllRelative(const ViewportConfiguration::Parameters& configuration)
{
return !configuration.widthIsSet && !configuration.heightIsSet && !configuration.initialScaleIsSet;
}
#endif // ASSERT_ENABLED
static constexpr float platformDeviceWidthOverride()
{
#if PLATFORM(WATCHOS)
return 320;
#else
return 0;
#endif
}
static constexpr double platformMinimumScaleForWebpage()
{
#if PLATFORM(WATCHOS)
return 0.1;
#else
return 0.25;
#endif
}
static constexpr bool shouldOverrideShrinkToFitArgument()
{
#if PLATFORM(WATCHOS)
return true;
#else
return false;
#endif
}
static bool needsUpdateAfterChangingDisabledAdaptations(const OptionSet<DisabledAdaptations>& oldDisabledAdaptations, const OptionSet<DisabledAdaptations>& newDisabledAdaptations)
{
if (oldDisabledAdaptations == newDisabledAdaptations)
return false;
#if PLATFORM(WATCHOS)
if (oldDisabledAdaptations.contains(DisabledAdaptations::Watch) != newDisabledAdaptations.contains(DisabledAdaptations::Watch))
return true;
#endif
return false;
}
ViewportConfiguration::ViewportConfiguration()
: m_minimumLayoutSize(1024, 768)
, m_viewLayoutSize(1024, 768)
, m_canIgnoreScalingConstraints(false)
, m_forceAlwaysUserScalable(false)
{
// Setup a reasonable default configuration to avoid computing infinite scale/sizes.
// Those are the original iPhone configuration.
m_defaultConfiguration = ViewportConfiguration::webpageParameters();
updateConfiguration();
}
void ViewportConfiguration::setDefaultConfiguration(const ViewportConfiguration::Parameters& defaultConfiguration)
{
ASSERT(!constraintsAreAllRelative(m_configuration));
ASSERT(!defaultConfiguration.initialScaleIsSet || defaultConfiguration.initialScale > 0);
ASSERT(defaultConfiguration.minimumScale > 0);
ASSERT(defaultConfiguration.maximumScale >= defaultConfiguration.minimumScale);
if (m_defaultConfiguration == defaultConfiguration)
return;
m_defaultConfiguration = defaultConfiguration;
updateConfiguration();
}
bool ViewportConfiguration::setContentsSize(const IntSize& contentSize)
{
if (m_contentSize == contentSize)
return false;
LOG_WITH_STREAM(Viewports, stream << "ViewportConfiguration::setContentsSize " << contentSize << " (was " << m_contentSize << ")");
m_contentSize = contentSize;
updateConfiguration();
return true;
}
bool ViewportConfiguration::setViewLayoutSize(const FloatSize& viewLayoutSize, std::optional<double>&& scaleFactor, std::optional<double>&& minimumEffectiveDeviceWidthFromClient)
{
double newScaleFactor = scaleFactor.value_or(m_layoutSizeScaleFactor);
double newEffectiveWidth = minimumEffectiveDeviceWidthFromClient.value_or(m_minimumEffectiveDeviceWidthForView);
if (m_viewLayoutSize == viewLayoutSize && m_layoutSizeScaleFactor == newScaleFactor && newEffectiveWidth == m_minimumEffectiveDeviceWidthForView)
return false;
m_layoutSizeScaleFactor = newScaleFactor;
m_viewLayoutSize = viewLayoutSize;
m_minimumEffectiveDeviceWidthForView = newEffectiveWidth;
updateMinimumLayoutSize();
updateConfiguration();
return true;
}
bool ViewportConfiguration::setDisabledAdaptations(const OptionSet<DisabledAdaptations>& disabledAdaptations)
{
auto previousDisabledAdaptations = m_disabledAdaptations;
m_disabledAdaptations = disabledAdaptations;
if (!needsUpdateAfterChangingDisabledAdaptations(previousDisabledAdaptations, disabledAdaptations))
return false;
updateMinimumLayoutSize();
updateConfiguration();
return true;
}
bool ViewportConfiguration::canOverrideConfigurationParameters() const
{
return m_defaultConfiguration == ViewportConfiguration::nativeWebpageParametersWithoutShrinkToFit() || m_defaultConfiguration == ViewportConfiguration::nativeWebpageParametersWithShrinkToFit();
}
void ViewportConfiguration::updateDefaultConfiguration()
{
if (!canOverrideConfigurationParameters())
return;
m_defaultConfiguration = nativeWebpageParameters();
}
bool ViewportConfiguration::setViewportArguments(const ViewportArguments& viewportArguments)
{
if (m_viewportArguments == viewportArguments)
return false;
LOG_WITH_STREAM(Viewports, stream << "ViewportConfiguration::setViewportArguments " << viewportArguments);
m_viewportArguments = viewportArguments;
updateDefaultConfiguration();
updateMinimumLayoutSize();
updateConfiguration();
return true;
}
bool ViewportConfiguration::setCanIgnoreScalingConstraints(bool canIgnoreScalingConstraints)
{
if (canIgnoreScalingConstraints == m_canIgnoreScalingConstraints)
return false;
m_canIgnoreScalingConstraints = canIgnoreScalingConstraints;
updateDefaultConfiguration();
updateMinimumLayoutSize();
updateConfiguration();
return true;
}
IntSize ViewportConfiguration::layoutSize() const
{
return IntSize(layoutWidth(), layoutHeight());
}
bool ViewportConfiguration::shouldOverrideDeviceWidthAndShrinkToFit() const
{
if (m_disabledAdaptations.contains(DisabledAdaptations::Watch))
return false;
auto viewWidth = m_viewLayoutSize.width();
return 0 < viewWidth && viewWidth < platformDeviceWidthOverride();
}
bool ViewportConfiguration::shouldIgnoreHorizontalScalingConstraints() const
{
if (!m_canIgnoreScalingConstraints)
return false;
if (shouldOverrideDeviceWidthAndShrinkToFit())
return true;
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.initialScaleIgnoringLayoutScaleFactor == 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();
}
bool ViewportConfiguration::shouldIgnoreScalingConstraintsRegardlessOfContentSize() const
{
return m_canIgnoreScalingConstraints && shouldOverrideDeviceWidthAndShrinkToFit();
}
double ViewportConfiguration::initialScaleFromSize(double width, double height, bool shouldIgnoreScalingConstraints) const
{
ASSERT(!constraintsAreAllRelative(m_configuration));
auto clampToMinimumAndMaximumScales = [&] (double initialScale) {
return clampTo<double>(initialScale, shouldIgnoreScalingConstraints ? m_defaultConfiguration.minimumScale : m_configuration.minimumScale, m_configuration.maximumScale);
};
if (layoutSizeIsExplicitlyScaled()) {
if (m_configuration.initialScaleIsSet)
return clampToMinimumAndMaximumScales(m_configuration.initialScale);
if (m_configuration.width > 0)
return clampToMinimumAndMaximumScales(m_viewLayoutSize.width() / m_configuration.width);
}
// If the document has specified its own initial scale, use it regardless.
// This is guaranteed to be sanity checked already, so no need for MIN/MAX.
if (m_configuration.initialScaleIsSet && !shouldIgnoreScalingConstraints)
return m_configuration.initialScale;
// If not, it is up to us to determine the initial scale.
// We want a scale small enough to fit the document width-wise.
double initialScale = 0;
if (!shouldIgnoreVerticalScalingConstraints()) {
static const double maximumContentWidthBeforePreferringExplicitWidthToAvoidExcessiveScaling = 1920;
if (width > maximumContentWidthBeforePreferringExplicitWidthToAvoidExcessiveScaling && m_configuration.widthIsSet && 0 < m_configuration.width && m_configuration.width < width)
initialScale = m_viewLayoutSize.width() / m_configuration.width;
else if (shouldShrinkToFitMinimumEffectiveDeviceWidthWhenIgnoringScalingConstraints())
initialScale = effectiveLayoutSizeScaleFactor();
else if (width > 0) {
auto shrinkToFitWidth = m_viewLayoutSize.width();
if (m_prefersHorizontalScrollingBelowDesktopViewportWidths)
shrinkToFitWidth = std::max<float>(shrinkToFitWidth, std::min(width, defaultDesktopViewportWidth));
initialScale = shrinkToFitWidth / width;
}
}
// Prevent the initial scale from shrinking to a height smaller than our view's minimum height.
if (height > 0 && height * initialScale < m_viewLayoutSize.height() && !shouldIgnoreHorizontalScalingConstraints())
initialScale = m_viewLayoutSize.height() / height;
return clampToMinimumAndMaximumScales(initialScale);
}
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(), shouldIgnoreScalingConstraintsRegardlessOfContentSize());
}
double ViewportConfiguration::minimumScale() const
{
// If we scale to fit, then this is our minimum scale as well.
if (!m_configuration.initialScaleIsSet || shouldIgnoreScalingConstraints())
return initialScale();
// If not, we still need to sanity check our value.
double minimumScale = m_configuration.minimumScale;
if (m_forceAlwaysUserScalable)
minimumScale = std::min(minimumScale, forceAlwaysUserScalableMinimumScale());
auto scaleForFittingContentIsApproximatelyEqualToMinimumScale = [] (double viewLength, double contentLength, double minimumScale) {
if (contentLength <= 1 || viewLength <= 1)
return false;
if (minimumScale < (viewLength - 0.5) / (contentLength + 0.5))
return false;
if (minimumScale > (viewLength + 0.5) / (contentLength - 0.5))
return false;
return true;
};
double contentWidth = m_contentSize.width();
if (contentWidth > 0 && contentWidth * minimumScale < m_viewLayoutSize.width() && !shouldIgnoreVerticalScalingConstraints()) {
if (!scaleForFittingContentIsApproximatelyEqualToMinimumScale(m_viewLayoutSize.width(), contentWidth, minimumScale))
minimumScale = m_viewLayoutSize.width() / contentWidth;
}
double contentHeight = m_contentSize.height();
if (contentHeight > 0 && contentHeight * minimumScale < m_viewLayoutSize.height() && !shouldIgnoreHorizontalScalingConstraints()) {
if (!scaleForFittingContentIsApproximatelyEqualToMinimumScale(m_viewLayoutSize.height(), contentHeight, minimumScale))
minimumScale = m_viewLayoutSize.height() / contentHeight;
}
minimumScale = std::min(std::max(minimumScale, m_configuration.minimumScale), m_configuration.maximumScale);
return minimumScale;
}
bool ViewportConfiguration::allowsUserScaling() const
{
return m_forceAlwaysUserScalable || allowsUserScalingIgnoringAlwaysScalable();
}
bool ViewportConfiguration::allowsUserScalingIgnoringAlwaysScalable() const
{
return shouldIgnoreScalingConstraints() || m_configuration.allowsUserScaling;
}
ViewportConfiguration::Parameters ViewportConfiguration::nativeWebpageParameters()
{
if (m_canIgnoreScalingConstraints || !shouldIgnoreMinimumEffectiveDeviceWidthForShrinkToFit())
return ViewportConfiguration::nativeWebpageParametersWithShrinkToFit();
return ViewportConfiguration::nativeWebpageParametersWithoutShrinkToFit();
}
ViewportConfiguration::Parameters ViewportConfiguration::nativeWebpageParametersWithoutShrinkToFit()
{
Parameters parameters;
parameters.width = ViewportArguments::ValueDeviceWidth;
parameters.widthIsSet = true;
parameters.allowsUserScaling = true;
parameters.allowsShrinkToFit = false;
parameters.minimumScale = 1;
parameters.maximumScale = 5;
parameters.initialScale = 1;
parameters.initialScaleIgnoringLayoutScaleFactor = 1;
parameters.initialScaleIsSet = true;
return parameters;
}
ViewportConfiguration::Parameters ViewportConfiguration::nativeWebpageParametersWithShrinkToFit()
{
Parameters parameters = ViewportConfiguration::nativeWebpageParametersWithoutShrinkToFit();
parameters.allowsShrinkToFit = true;
parameters.minimumScale = platformMinimumScaleForWebpage();
parameters.initialScaleIsSet = false;
return parameters;
}
ViewportConfiguration::Parameters ViewportConfiguration::webpageParameters()
{
Parameters parameters;
parameters.width = defaultDesktopViewportWidth;
parameters.widthIsSet = true;
parameters.allowsUserScaling = true;
parameters.allowsShrinkToFit = true;
parameters.minimumScale = platformMinimumScaleForWebpage();
parameters.maximumScale = 5;
return parameters;
}
ViewportConfiguration::Parameters ViewportConfiguration::textDocumentParameters()
{
Parameters parameters;
#if PLATFORM(IOS_FAMILY)
parameters.width = static_cast<int>(screenSize().width());
#else
// FIXME: this needs to be unified with ViewportArguments on all ports.
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 = defaultDesktopViewportWidth;
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.initialScaleIgnoringLayoutScaleFactor = 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;
auto effectiveLayoutScale = effectiveLayoutSizeScaleFactor();
if (layoutSizeIsExplicitlyScaled())
m_configuration.width /= effectiveLayoutScale;
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 (shouldOverrideShrinkToFitArgument())
m_configuration.allowsShrinkToFit = shouldOverrideDeviceWidthAndShrinkToFit();
else if (booleanViewportArgumentIsSet(m_viewportArguments.shrinkToFit))
m_configuration.allowsShrinkToFit = m_viewportArguments.shrinkToFit != 0.;
if (canOverrideConfigurationParameters() && !viewportArgumentsOverridesWidth)
m_configuration.width = m_minimumLayoutSize.width();
m_configuration.avoidsUnsafeArea = m_viewportArguments.viewportFit != ViewportFit::Cover;
m_configuration.initialScaleIgnoringLayoutScaleFactor = m_configuration.initialScale;
m_configuration.initialScale *= effectiveLayoutScale;
m_configuration.minimumScale *= effectiveLayoutScale;
m_configuration.maximumScale *= effectiveLayoutScale;
LOG_WITH_STREAM(Viewports, stream << "ViewportConfiguration " << this << " updateConfiguration " << *this << " gives initial scale " << initialScale() << " based on contentSize " << m_contentSize << " and layout size " << layoutWidth() << "x" << layoutHeight());
}
void ViewportConfiguration::updateMinimumLayoutSize()
{
m_minimumLayoutSize = m_viewLayoutSize / effectiveLayoutSizeScaleFactor();
if (!shouldOverrideDeviceWidthAndShrinkToFit())
return;
float minDeviceWidth = platformDeviceWidthOverride();
m_minimumLayoutSize = FloatSize(minDeviceWidth, std::roundf(m_minimumLayoutSize.height() * (minDeviceWidth / m_minimumLayoutSize.width())));
}
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 we scale to fit, then accept the viewport width with sanity checking.
if (!m_configuration.initialScaleIsSet) {
double maximumScale = this->maximumScale();
double maximumContentWidthInViewportCoordinate = maximumScale * m_configuration.width;
if (maximumContentWidthInViewportCoordinate < minimumLayoutSize.width()) {
// The content zoomed to maxScale does not fit the view. Return the minimum width
// satisfying the constraint maximumScale.
return std::round(minimumLayoutSize.width() / maximumScale);
}
return std::round(m_configuration.width);
}
// If not, make sure the viewport width and initial scale can co-exist.
double initialContentWidthInViewportCoordinate = m_configuration.width * m_configuration.initialScaleIgnoringLayoutScaleFactor;
if (initialContentWidthInViewportCoordinate < minimumLayoutSize.width()) {
// The specified width does not fit in viewport. Return the minimum width that satisfy the initialScale constraint.
return std::round(minimumLayoutSize.width() / m_configuration.initialScaleIgnoringLayoutScaleFactor);
}
return std::round(m_configuration.width);
}
// If the page has a real scale, then just return the minimum size over the initial scale.
if (m_configuration.initialScaleIsSet && !m_configuration.heightIsSet)
return std::round(minimumLayoutSize.width() / m_configuration.initialScaleIgnoringLayoutScaleFactor);
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 we scale to fit, then accept the viewport height with sanity checking.
if (!m_configuration.initialScaleIsSet) {
double maximumScale = this->maximumScale();
double maximumContentHeightInViewportCoordinate = maximumScale * m_configuration.height;
if (maximumContentHeightInViewportCoordinate < minimumLayoutSize.height()) {
// The content zoomed to maxScale does not fit the view. Return the minimum height that
// satisfy the constraint maximumScale.
return std::round(minimumLayoutSize.height() / maximumScale);
}
return std::round(m_configuration.height);
}
// If not, make sure the viewport width and initial scale can co-exist.
double initialContentHeightInViewportCoordinate = m_configuration.height * m_configuration.initialScaleIgnoringLayoutScaleFactor;
if (initialContentHeightInViewportCoordinate < minimumLayoutSize.height()) {
// The specified width does not fit in viewport. Return the minimum height that satisfy the initialScale constraint.
return std::round(minimumLayoutSize.height() / m_configuration.initialScaleIgnoringLayoutScaleFactor);
}
return std::round(m_configuration.height);
}
// If the page has a real scale, then just return the minimum size over the initial scale.
if (m_configuration.initialScaleIsSet && !m_configuration.widthIsSet)
return std::round(minimumLayoutSize.height() / m_configuration.initialScaleIgnoringLayoutScaleFactor);
if (minimumLayoutSize.width() > 0)
return std::round(minimumLayoutSize.height() * layoutWidth() / minimumLayoutSize.width());
return minimumLayoutSize.height();
}
bool ViewportConfiguration::setMinimumEffectiveDeviceWidthForShrinkToFit(double width)
{
if (WTF::areEssentiallyEqual(m_minimumEffectiveDeviceWidthForShrinkToFit, width))
return false;
m_minimumEffectiveDeviceWidthForShrinkToFit = width;
if (shouldIgnoreMinimumEffectiveDeviceWidthForShrinkToFit())
return false;
updateMinimumLayoutSize();
updateConfiguration();
return true;
}
bool ViewportConfiguration::setMinimumEffectiveDeviceWidthWhenIgnoringScalingConstraints(double width)
{
if (WTF::areEssentiallyEqual(m_minimumEffectiveDeviceWidthWhenIgnoringScalingConstraints, width))
return false;
bool wasShrinkingToFitMinimumEffectiveDeviceWidth = shouldShrinkToFitMinimumEffectiveDeviceWidthWhenIgnoringScalingConstraints();
m_minimumEffectiveDeviceWidthWhenIgnoringScalingConstraints = width;
if (wasShrinkingToFitMinimumEffectiveDeviceWidth == shouldShrinkToFitMinimumEffectiveDeviceWidthWhenIgnoringScalingConstraints())
return false;
updateMinimumLayoutSize();
updateConfiguration();
return true;
}
bool ViewportConfiguration::setIsKnownToLayOutWiderThanViewport(bool value)
{
if (m_isKnownToLayOutWiderThanViewport == value)
return false;
m_isKnownToLayOutWiderThanViewport = value;
updateMinimumLayoutSize();
updateConfiguration();
return true;
}
#if !LOG_DISABLED
TextStream& operator<<(TextStream& ts, const ViewportConfiguration::Parameters& parameters)
{
ts.startGroup();
ts << "width " << parameters.width << ", set: " << (parameters.widthIsSet ? "true" : "false");
ts.endGroup();
ts.startGroup();
ts << "height " << parameters.height << ", set: " << (parameters.heightIsSet ? "true" : "false");
ts.endGroup();
ts.startGroup();
ts << "initialScale " << parameters.initialScale << ", set: " << (parameters.initialScaleIsSet ? "true" : "false");
ts.endGroup();
ts.dumpProperty("initialScaleIgnoringLayoutScaleFactor", parameters.initialScaleIgnoringLayoutScaleFactor);
ts.dumpProperty("minimumScale", parameters.minimumScale);
ts.dumpProperty("maximumScale", parameters.maximumScale);
ts.dumpProperty("allowsUserScaling", parameters.allowsUserScaling);
ts.dumpProperty("allowsShrinkToFit", parameters.allowsShrinkToFit);
ts.dumpProperty("avoidsUnsafeArea", parameters.avoidsUnsafeArea);
return ts;
}
TextStream& operator<<(TextStream& ts, const ViewportConfiguration& config)
{
return ts << config.description();
}
String ViewportConfiguration::description() const
{
TextStream ts;
ts.startGroup();
ts << "viewport-configuration " << (void*)this;
{
TextStream::GroupScope scope(ts);
ts << "viewport arguments";
ts << m_viewportArguments;
}
{
TextStream::GroupScope scope(ts);
ts << "configuration";
ts << m_configuration;
}
{
TextStream::GroupScope scope(ts);
ts << "default configuration";
ts << m_defaultConfiguration;
}
ts.dumpProperty("contentSize", m_contentSize);
ts.dumpProperty("minimumLayoutSize", m_minimumLayoutSize);
ts.dumpProperty("layoutSizeScaleFactor", m_layoutSizeScaleFactor);
ts.dumpProperty("computed initial scale", initialScale());
ts.dumpProperty("computed minimum scale", minimumScale());
ts.dumpProperty("computed layout size", layoutSize());
ts.dumpProperty("ignoring horizontal scaling constraints", shouldIgnoreHorizontalScalingConstraints() ? "true" : "false");
ts.dumpProperty("ignoring vertical scaling constraints", shouldIgnoreVerticalScalingConstraints() ? "true" : "false");
ts.dumpProperty("avoids unsafe area", avoidsUnsafeArea() ? "true" : "false");
ts.dumpProperty("minimum effective device width (for view)", m_minimumEffectiveDeviceWidthForView);
ts.dumpProperty("minimum effective device width (for shrink-to-fit)", m_minimumEffectiveDeviceWidthForShrinkToFit);
ts.dumpProperty("known to lay out wider than viewport", m_isKnownToLayOutWiderThanViewport ? "true" : "false");
ts.dumpProperty("prefers horizontal scrolling", m_prefersHorizontalScrollingBelowDesktopViewportWidths ? "true" : "false");
ts.endGroup();
return ts.release();
}
void ViewportConfiguration::dump() const
{
WTFLogAlways("%s", description().utf8().data());
}
#endif
} // namespace WebCore