1 /* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "web/PageScaleConstraintsSet.h" 33 34 #include "platform/Length.h" 35 #include "wtf/Assertions.h" 36 37 namespace blink { 38 39 static const float defaultMinimumScale = 0.25f; 40 static const float defaultMaximumScale = 5.0f; 41 42 PageScaleConstraintsSet::PageScaleConstraintsSet() 43 : m_finalConstraints(1, 1, 1) 44 , m_lastContentsWidth(0) 45 , m_needsReset(false) 46 , m_constraintsDirty(false) 47 { 48 } 49 50 PageScaleConstraints PageScaleConstraintsSet::defaultConstraints() const 51 { 52 return PageScaleConstraints(-1, defaultMinimumScale, defaultMaximumScale); 53 } 54 55 void PageScaleConstraintsSet::updatePageDefinedConstraints(const ViewportDescription& description, Length legacyFallbackWidth) 56 { 57 m_pageDefinedConstraints = description.resolve(m_viewSize, legacyFallbackWidth); 58 59 m_constraintsDirty = true; 60 } 61 62 void PageScaleConstraintsSet::setUserAgentConstraints(const PageScaleConstraints& userAgentConstraints) 63 { 64 m_userAgentConstraints = userAgentConstraints; 65 m_constraintsDirty = true; 66 } 67 68 PageScaleConstraints PageScaleConstraintsSet::computeConstraintsStack() const 69 { 70 PageScaleConstraints constraints = defaultConstraints(); 71 constraints.overrideWith(m_pageDefinedConstraints); 72 constraints.overrideWith(m_userAgentConstraints); 73 return constraints; 74 } 75 76 void PageScaleConstraintsSet::computeFinalConstraints() 77 { 78 m_finalConstraints = computeConstraintsStack(); 79 80 m_constraintsDirty = false; 81 } 82 83 void PageScaleConstraintsSet::adjustFinalConstraintsToContentsSize(IntSize contentsSize, int nonOverlayScrollbarWidth) 84 { 85 m_finalConstraints.fitToContentsWidth(contentsSize.width(), m_viewSize.width() - nonOverlayScrollbarWidth); 86 } 87 88 void PageScaleConstraintsSet::setNeedsReset(bool needsReset) 89 { 90 m_needsReset = needsReset; 91 if (needsReset) 92 m_constraintsDirty = true; 93 } 94 95 void PageScaleConstraintsSet::didChangeContentsSize(IntSize contentsSize, float pageScaleFactor) 96 { 97 // If a large fixed-width element expanded the size of the document late in 98 // loading and our initial scale is not set (or set to be less than the last 99 // minimum scale), reset the page scale factor to the new initial scale. 100 if (contentsSize.width() > m_lastContentsWidth 101 && pageScaleFactor == finalConstraints().minimumScale 102 && computeConstraintsStack().initialScale < finalConstraints().minimumScale) 103 setNeedsReset(true); 104 105 m_constraintsDirty = true; 106 m_lastContentsWidth = contentsSize.width(); 107 } 108 109 static float computeDeprecatedTargetDensityDPIFactor(const ViewportDescription& description, float deviceScaleFactor) 110 { 111 if (description.deprecatedTargetDensityDPI == ViewportDescription::ValueDeviceDPI) 112 return 1.0f / deviceScaleFactor; 113 114 float targetDPI = -1.0f; 115 if (description.deprecatedTargetDensityDPI == ViewportDescription::ValueLowDPI) 116 targetDPI = 120.0f; 117 else if (description.deprecatedTargetDensityDPI == ViewportDescription::ValueMediumDPI) 118 targetDPI = 160.0f; 119 else if (description.deprecatedTargetDensityDPI == ViewportDescription::ValueHighDPI) 120 targetDPI = 240.0f; 121 else if (description.deprecatedTargetDensityDPI != ViewportDescription::ValueAuto) 122 targetDPI = description.deprecatedTargetDensityDPI; 123 return targetDPI > 0 ? 160.0f / targetDPI : 1.0f; 124 } 125 126 static float getLayoutWidthForNonWideViewport(const FloatSize& deviceSize, float initialScale) 127 { 128 return initialScale == -1 ? deviceSize.width() : deviceSize.width() / initialScale; 129 } 130 131 static float computeHeightByAspectRatio(float width, const FloatSize& deviceSize) 132 { 133 return width * (deviceSize.height() / deviceSize.width()); 134 } 135 136 void PageScaleConstraintsSet::didChangeViewSize(const IntSize& size) 137 { 138 if (m_viewSize == size) 139 return; 140 141 m_viewSize = size; 142 m_constraintsDirty = true; 143 } 144 145 IntSize PageScaleConstraintsSet::mainFrameSize(const IntSize& contentsSize) const 146 { 147 // If there's no explicit minimum scale factor set, size the frame so that its width == content width 148 // so there's no horizontal scrolling at the minimum scale. 149 if (m_pageDefinedConstraints.minimumScale < finalConstraints().minimumScale 150 && m_userAgentConstraints.minimumScale < finalConstraints().minimumScale 151 && contentsSize.width() 152 && m_viewSize.width()) 153 return IntSize(contentsSize.width(), computeHeightByAspectRatio(contentsSize.width(), m_viewSize)); 154 155 // If there is a minimum scale (or there's no content size yet), the frame size should match the viewport 156 // size at minimum scale, since the viewport must always be contained by the frame. 157 IntSize frameSize(m_viewSize); 158 frameSize.scale(1 / finalConstraints().minimumScale); 159 return frameSize; 160 } 161 162 void PageScaleConstraintsSet::adjustForAndroidWebViewQuirks(const ViewportDescription& description, int layoutFallbackWidth, float deviceScaleFactor, bool supportTargetDensityDPI, bool wideViewportQuirkEnabled, bool useWideViewport, bool loadWithOverviewMode, bool nonUserScalableQuirkEnabled) 163 { 164 if (!supportTargetDensityDPI && !wideViewportQuirkEnabled && loadWithOverviewMode && !nonUserScalableQuirkEnabled) 165 return; 166 167 const float oldInitialScale = m_pageDefinedConstraints.initialScale; 168 if (!loadWithOverviewMode) { 169 bool resetInitialScale = false; 170 if (description.zoom == -1) { 171 if (description.maxWidth.isAuto() || description.maxWidth.type() == ExtendToZoom) 172 resetInitialScale = true; 173 if (useWideViewport || description.maxWidth.type() == DeviceWidth) 174 resetInitialScale = true; 175 } 176 if (resetInitialScale) 177 m_pageDefinedConstraints.initialScale = 1.0f; 178 } 179 180 float adjustedLayoutSizeWidth = m_pageDefinedConstraints.layoutSize.width(); 181 float adjustedLayoutSizeHeight = m_pageDefinedConstraints.layoutSize.height(); 182 float targetDensityDPIFactor = 1.0f; 183 184 if (supportTargetDensityDPI) { 185 targetDensityDPIFactor = computeDeprecatedTargetDensityDPIFactor(description, deviceScaleFactor); 186 if (m_pageDefinedConstraints.initialScale != -1) 187 m_pageDefinedConstraints.initialScale *= targetDensityDPIFactor; 188 if (m_pageDefinedConstraints.minimumScale != -1) 189 m_pageDefinedConstraints.minimumScale *= targetDensityDPIFactor; 190 if (m_pageDefinedConstraints.maximumScale != -1) 191 m_pageDefinedConstraints.maximumScale *= targetDensityDPIFactor; 192 if (wideViewportQuirkEnabled && (!useWideViewport || description.maxWidth.type() == DeviceWidth)) { 193 adjustedLayoutSizeWidth /= targetDensityDPIFactor; 194 adjustedLayoutSizeHeight /= targetDensityDPIFactor; 195 } 196 } 197 198 if (wideViewportQuirkEnabled) { 199 if (useWideViewport && (description.maxWidth.isAuto() || description.maxWidth.type() == ExtendToZoom) && description.zoom != 1.0f) { 200 adjustedLayoutSizeWidth = layoutFallbackWidth; 201 adjustedLayoutSizeHeight = computeHeightByAspectRatio(adjustedLayoutSizeWidth, m_viewSize); 202 } else if (!useWideViewport) { 203 const float nonWideScale = description.zoom < 1 && description.maxWidth.type() != DeviceWidth && description.maxWidth.type() != DeviceHeight ? -1 : oldInitialScale; 204 adjustedLayoutSizeWidth = getLayoutWidthForNonWideViewport(m_viewSize, nonWideScale) / targetDensityDPIFactor; 205 float newInitialScale = targetDensityDPIFactor; 206 if (m_userAgentConstraints.initialScale != -1 && (description.maxWidth.type() == DeviceWidth || ((description.maxWidth.isAuto() || description.maxWidth.type() == ExtendToZoom) && description.zoom == -1))) { 207 adjustedLayoutSizeWidth /= m_userAgentConstraints.initialScale; 208 newInitialScale = m_userAgentConstraints.initialScale; 209 } 210 adjustedLayoutSizeHeight = computeHeightByAspectRatio(adjustedLayoutSizeWidth, m_viewSize); 211 if (description.zoom < 1) { 212 m_pageDefinedConstraints.initialScale = newInitialScale; 213 if (m_pageDefinedConstraints.minimumScale != -1) 214 m_pageDefinedConstraints.minimumScale = std::min<float>(m_pageDefinedConstraints.minimumScale, m_pageDefinedConstraints.initialScale); 215 if (m_pageDefinedConstraints.maximumScale != -1) 216 m_pageDefinedConstraints.maximumScale = std::max<float>(m_pageDefinedConstraints.maximumScale, m_pageDefinedConstraints.initialScale); 217 } 218 } 219 } 220 221 if (nonUserScalableQuirkEnabled && !description.userZoom) { 222 m_pageDefinedConstraints.initialScale = targetDensityDPIFactor; 223 m_pageDefinedConstraints.minimumScale = m_pageDefinedConstraints.initialScale; 224 m_pageDefinedConstraints.maximumScale = m_pageDefinedConstraints.initialScale; 225 if (description.maxWidth.isAuto() || description.maxWidth.type() == ExtendToZoom || description.maxWidth.type() == DeviceWidth) { 226 adjustedLayoutSizeWidth = m_viewSize.width() / targetDensityDPIFactor; 227 adjustedLayoutSizeHeight = computeHeightByAspectRatio(adjustedLayoutSizeWidth, m_viewSize); 228 } 229 } 230 231 m_pageDefinedConstraints.layoutSize.setWidth(adjustedLayoutSizeWidth); 232 m_pageDefinedConstraints.layoutSize.setHeight(adjustedLayoutSizeHeight); 233 } 234 235 } // namespace blink 236