| /* |
| * 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 float platformDeviceWidthOverride() |
| { |
| #if PLATFORM(WATCHOS) |
| return 320; |
| #else |
| return 0; |
| #endif |
| } |
| |
| static 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, Optional<double>&& scaleFactor, Optional<double>&& minimumEffectiveDeviceWidth) |
| { |
| double newScaleFactor = scaleFactor.valueOr(m_layoutSizeScaleFactor); |
| double newEffectiveWidth = minimumEffectiveDeviceWidth.valueOr(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 (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 = 0.25; |
| 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 = 0.25; |
| 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::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 |