740 lines
28 KiB
C++
740 lines
28 KiB
C++
/*
|
|
* 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 {
|
|
|
|
#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>&& minimumEffectiveDeviceWidth)
|
|
{
|
|
double newScaleFactor = scaleFactor.value_or(m_layoutSizeScaleFactor);
|
|
double newEffectiveWidth = minimumEffectiveDeviceWidth.value_or(m_minimumEffectiveDeviceWidth);
|
|
if (m_viewLayoutSize == viewLayoutSize && m_layoutSizeScaleFactor == newScaleFactor && newEffectiveWidth == m_minimumEffectiveDeviceWidth)
|
|
return false;
|
|
|
|
m_layoutSizeScaleFactor = newScaleFactor;
|
|
m_viewLayoutSize = viewLayoutSize;
|
|
m_minimumEffectiveDeviceWidth = 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)
|
|
initialScale = m_viewLayoutSize.width() / 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 || !shouldIgnoreMinimumEffectiveDeviceWidth())
|
|
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 = 980;
|
|
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 = 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.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::setMinimumEffectiveDeviceWidth(double width)
|
|
{
|
|
if (WTF::areEssentiallyEqual(m_minimumEffectiveDeviceWidth, width))
|
|
return false;
|
|
|
|
m_minimumEffectiveDeviceWidth = width;
|
|
|
|
if (shouldIgnoreMinimumEffectiveDeviceWidth())
|
|
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", m_minimumEffectiveDeviceWidth);
|
|
ts.dumpProperty("known to lay out wider than viewport", m_isKnownToLayOutWiderThanViewport ? "true" : "false");
|
|
|
|
ts.endGroup();
|
|
|
|
return ts.release();
|
|
}
|
|
|
|
void ViewportConfiguration::dump() const
|
|
{
|
|
WTFLogAlways("%s", description().utf8().data());
|
|
}
|
|
|
|
#endif
|
|
|
|
} // namespace WebCore
|