diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/page/ViewportConfiguration.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/page/ViewportConfiguration.cpp')
-rw-r--r-- | Source/WebCore/page/ViewportConfiguration.cpp | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/Source/WebCore/page/ViewportConfiguration.cpp b/Source/WebCore/page/ViewportConfiguration.cpp new file mode 100644 index 000000000..805c6e0cc --- /dev/null +++ b/Source/WebCore/page/ViewportConfiguration.cpp @@ -0,0 +1,488 @@ +/* + * 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 "TextStream.h" +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> +#include <wtf/text/CString.h> + +#if PLATFORM(IOS) +#include "PlatformScreen.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) +{ + // 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); + + m_defaultConfiguration = defaultConfiguration; + updateConfiguration(); +} + +bool ViewportConfiguration::setContentsSize(const IntSize& contentSize) +{ + if (m_contentSize == contentSize) + return false; + + m_contentSize = contentSize; + updateConfiguration(); + return true; +} + +bool ViewportConfiguration::setMinimumLayoutSize(const FloatSize& minimumLayoutSize) +{ + if (m_minimumLayoutSize == minimumLayoutSize) + return false; + + m_minimumLayoutSize = minimumLayoutSize; + updateConfiguration(); + return true; +} + +bool ViewportConfiguration::setViewportArguments(const ViewportArguments& viewportArguments) +{ + if (m_viewportArguments == viewportArguments) + return false; + + m_viewportArguments = viewportArguments; + updateConfiguration(); + return true; +} + +bool ViewportConfiguration::setCanIgnoreScalingConstraints(bool canIgnoreScalingConstraints) +{ + if (canIgnoreScalingConstraints == m_canIgnoreScalingConstraints) + return false; + + m_canIgnoreScalingConstraints = canIgnoreScalingConstraints; + updateConfiguration(); + return true; +} + +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 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. + const FloatSize& minimumLayoutSize = m_minimumLayoutSize; + double initialScale = 0; + if (width > 0 && !shouldIgnoreVerticalScalingConstraints()) + initialScale = minimumLayoutSize.width() / width; + + // Prevent the initial scale from shrinking to a height smaller than our view's minimum height. + 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 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); + + 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 || allowsUserScalingIgnoringAlwaysScalable(); +} + +bool ViewportConfiguration::allowsUserScalingIgnoringAlwaysScalable() const +{ + return 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>(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.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 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 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.initialScale; + 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.initialScale); + } + 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.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 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 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.initialScale; + 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.initialScale); + } + 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.initialScale); + + if (minimumLayoutSize.width() > 0) + return std::round(minimumLayoutSize.height() * layoutWidth() / minimumLayoutSize.width()); + return minimumLayoutSize.height(); +} + +#ifndef NDEBUG + +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("minimumScale", parameters.minimumScale); + ts.dumpProperty("maximumScale", parameters.maximumScale); + ts.dumpProperty("allowsUserScaling", parameters.allowsUserScaling); + ts.dumpProperty("allowsShrinkToFit", parameters.allowsShrinkToFit); + + return ts; +} + +CString 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("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.endGroup(); + + return ts.release().utf8(); +} + +void ViewportConfiguration::dump() const +{ + WTFLogAlways("%s", description().data()); +} + +#endif + +} // namespace WebCore |