| 1 | /* |
| 2 | * Copyright (C) 2011, 2012 Nokia Corporation and/or its subsidiary(-ies) |
| 3 | * Copyright (C) 2011 Benjamin Poulain <benjamin@webkit.org> |
| 4 | * Copyright (C) 2014 Igalia S.L. |
| 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Library General Public |
| 8 | * License as published by the Free Software Foundation; either |
| 9 | * version 2 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Library General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Library General Public License |
| 17 | * along with this program; see the file COPYING.LIB. If not, write to |
| 18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 19 | * Boston, MA 02110-1301, USA. |
| 20 | */ |
| 21 | |
| 22 | #include "config.h" |
| 23 | #include "SimpleViewportController.h" |
| 24 | |
| 25 | #if USE(COORDINATED_GRAPHICS) |
| 26 | |
| 27 | namespace WebKit { |
| 28 | using namespace WebCore; |
| 29 | |
| 30 | SimpleViewportController::SimpleViewportController(const IntSize& size) |
| 31 | : m_viewportSize(size) |
| 32 | { |
| 33 | resetViewportToDefaultState(); |
| 34 | } |
| 35 | |
| 36 | void SimpleViewportController::didChangeViewportSize(const IntSize& newSize) |
| 37 | { |
| 38 | if (newSize.isEmpty()) |
| 39 | return; |
| 40 | |
| 41 | m_viewportSize = newSize; |
| 42 | updateMinimumScaleToFit(); |
| 43 | } |
| 44 | |
| 45 | void SimpleViewportController::didChangeContentsSize(const IntSize& newSize) |
| 46 | { |
| 47 | m_contentsSize = newSize; |
| 48 | |
| 49 | updateMinimumScaleToFit(); |
| 50 | |
| 51 | if (m_initiallyFitToViewport) { |
| 52 | // Restrict scale factors to m_minimumScaleToFit. |
| 53 | ASSERT(m_minimumScaleToFit > 0); |
| 54 | m_rawAttributes.initialScale = m_minimumScaleToFit; |
| 55 | restrictScaleFactorToInitialScaleIfNotUserScalable(m_rawAttributes); |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | void SimpleViewportController::didChangeViewportAttributes(ViewportAttributes&& newAttributes) |
| 60 | { |
| 61 | if (newAttributes.layoutSize.isEmpty()) { |
| 62 | resetViewportToDefaultState(); |
| 63 | return; |
| 64 | } |
| 65 | |
| 66 | m_hasViewportAttribute = true; |
| 67 | |
| 68 | m_rawAttributes = WTFMove(newAttributes); |
| 69 | m_allowsUserScaling = m_rawAttributes.userScalable; |
| 70 | m_initiallyFitToViewport = m_rawAttributes.initialScale < 0; |
| 71 | |
| 72 | if (!m_initiallyFitToViewport) |
| 73 | restrictScaleFactorToInitialScaleIfNotUserScalable(m_rawAttributes); |
| 74 | |
| 75 | updateMinimumScaleToFit(); |
| 76 | } |
| 77 | |
| 78 | void SimpleViewportController::didScroll(const IntPoint& position) |
| 79 | { |
| 80 | m_contentsPosition = position; |
| 81 | } |
| 82 | |
| 83 | FloatRect SimpleViewportController::visibleContentsRect() const |
| 84 | { |
| 85 | if (m_viewportSize.isEmpty() || m_contentsSize.isEmpty()) |
| 86 | return { }; |
| 87 | |
| 88 | FloatRect visibleContentsRect(boundContentsPosition(m_contentsPosition), visibleContentsSize()); |
| 89 | visibleContentsRect.intersect(FloatRect(FloatPoint::zero(), m_contentsSize)); |
| 90 | return visibleContentsRect; |
| 91 | } |
| 92 | |
| 93 | FloatSize SimpleViewportController::visibleContentsSize() const |
| 94 | { |
| 95 | return FloatSize(m_viewportSize.width() / m_pageScaleFactor, m_viewportSize.height() / m_pageScaleFactor); |
| 96 | } |
| 97 | |
| 98 | FloatPoint SimpleViewportController::boundContentsPositionAtScale(const FloatPoint& framePosition, float scale) const |
| 99 | { |
| 100 | // We need to floor the viewport here as to allow aligning the content in device units. If not, |
| 101 | // it might not be possible to scroll the last pixel and that affects fixed position elements. |
| 102 | return FloatPoint( |
| 103 | clampTo(framePosition.x(), .0f, std::max(.0f, m_contentsSize.width() - floorf(m_viewportSize.width() / scale))), |
| 104 | clampTo(framePosition.y(), .0f, std::max(.0f, m_contentsSize.height() - floorf(m_viewportSize.height() / scale)))); |
| 105 | } |
| 106 | |
| 107 | FloatPoint SimpleViewportController::boundContentsPosition(const FloatPoint& framePosition) const |
| 108 | { |
| 109 | return boundContentsPositionAtScale(framePosition, m_pageScaleFactor); |
| 110 | } |
| 111 | |
| 112 | bool fuzzyCompare(float a, float b, float epsilon) |
| 113 | { |
| 114 | return std::abs(a - b) < epsilon; |
| 115 | } |
| 116 | |
| 117 | bool SimpleViewportController::updateMinimumScaleToFit() |
| 118 | { |
| 119 | if (m_viewportSize.isEmpty() || m_contentsSize.isEmpty() || !m_hasViewportAttribute) |
| 120 | return false; |
| 121 | |
| 122 | bool currentlyScaledToFit = fuzzyCompare(m_pageScaleFactor, m_minimumScaleToFit, 0.0001); |
| 123 | |
| 124 | float minimumScale = computeMinimumScaleFactorForContentContained(m_rawAttributes, roundedIntSize(m_viewportSize), roundedIntSize(m_contentsSize)); |
| 125 | |
| 126 | if (minimumScale <= 0) |
| 127 | return false; |
| 128 | |
| 129 | if (!fuzzyCompare(minimumScale, m_minimumScaleToFit, 0.0001)) { |
| 130 | m_minimumScaleToFit = minimumScale; |
| 131 | |
| 132 | if (currentlyScaledToFit) |
| 133 | m_pageScaleFactor = m_minimumScaleToFit; |
| 134 | else { |
| 135 | // Ensure the effective scale stays within bounds. |
| 136 | float boundedScale = innerBoundedViewportScale(m_pageScaleFactor); |
| 137 | if (!fuzzyCompare(boundedScale, m_pageScaleFactor, 0.0001)) |
| 138 | m_pageScaleFactor = boundedScale; |
| 139 | } |
| 140 | |
| 141 | return true; |
| 142 | } |
| 143 | return false; |
| 144 | } |
| 145 | |
| 146 | float SimpleViewportController::innerBoundedViewportScale(float viewportScale) const |
| 147 | { |
| 148 | return clampTo(viewportScale, m_minimumScaleToFit, m_rawAttributes.maximumScale); |
| 149 | } |
| 150 | |
| 151 | void SimpleViewportController::resetViewportToDefaultState() |
| 152 | { |
| 153 | m_hasViewportAttribute = false; |
| 154 | m_pageScaleFactor = 1; |
| 155 | m_minimumScaleToFit = 1; |
| 156 | |
| 157 | // Initializing Viewport Raw Attributes to avoid random negative or infinity scale factors |
| 158 | // if there is a race condition between the first layout and setting the viewport attributes for the first time. |
| 159 | m_rawAttributes.minimumScale = 1; |
| 160 | m_rawAttributes.maximumScale = 1; |
| 161 | m_rawAttributes.userScalable = m_allowsUserScaling; |
| 162 | |
| 163 | // The initial scale might be implicit and set to -1, in this case we have to infer it |
| 164 | // using the viewport size and the final layout size. |
| 165 | // To be able to assert for valid scale we initialize it to -1. |
| 166 | m_rawAttributes.initialScale = -1; |
| 167 | } |
| 168 | |
| 169 | } // namespace WebCore |
| 170 | |
| 171 | #endif // USE(COORDINATED_GRAPHICS) |
| 172 | |