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 using namespace WebCore; 38 39 namespace blink { 40 41 static const float defaultMinimumScale = 0.25f; 42 static const float defaultMaximumScale = 5.0f; 43 44 PageScaleConstraintsSet::PageScaleConstraintsSet() 45 : m_lastContentsWidth(0) 46 , m_needsReset(false) 47 , m_constraintsDirty(false) 48 { 49 m_finalConstraints = defaultConstraints(); 50 } 51 52 PageScaleConstraints PageScaleConstraintsSet::defaultConstraints() const 53 { 54 return PageScaleConstraints(-1, defaultMinimumScale, defaultMaximumScale); 55 } 56 57 void PageScaleConstraintsSet::updatePageDefinedConstraints(const ViewportDescription& description, Length legacyFallbackWidth) 58 { 59 m_pageDefinedConstraints = description.resolve(m_viewSize, legacyFallbackWidth); 60 61 m_constraintsDirty = true; 62 } 63 64 void PageScaleConstraintsSet::setUserAgentConstraints(const PageScaleConstraints& userAgentConstraints) 65 { 66 m_userAgentConstraints = userAgentConstraints; 67 m_constraintsDirty = true; 68 } 69 70 PageScaleConstraints PageScaleConstraintsSet::computeConstraintsStack() const 71 { 72 PageScaleConstraints constraints = defaultConstraints(); 73 constraints.overrideWith(m_pageDefinedConstraints); 74 constraints.overrideWith(m_userAgentConstraints); 75 return constraints; 76 } 77 78 void PageScaleConstraintsSet::computeFinalConstraints() 79 { 80 m_finalConstraints = computeConstraintsStack(); 81 82 m_constraintsDirty = false; 83 } 84 85 void PageScaleConstraintsSet::adjustFinalConstraintsToContentsSize(IntSize contentsSize, int nonOverlayScrollbarWidth) 86 { 87 m_finalConstraints.fitToContentsWidth(contentsSize.width(), m_viewSize.width() - nonOverlayScrollbarWidth); 88 } 89 90 void PageScaleConstraintsSet::setNeedsReset(bool needsReset) 91 { 92 m_needsReset = needsReset; 93 if (needsReset) 94 m_constraintsDirty = true; 95 } 96 97 void PageScaleConstraintsSet::didChangeContentsSize(IntSize contentsSize, float pageScaleFactor) 98 { 99 // If a large fixed-width element expanded the size of the document late in 100 // loading and our initial scale is not set (or set to be less than the last 101 // minimum scale), reset the page scale factor to the new initial scale. 102 if (contentsSize.width() > m_lastContentsWidth 103 && pageScaleFactor == finalConstraints().minimumScale 104 && computeConstraintsStack().initialScale < finalConstraints().minimumScale) 105 setNeedsReset(true); 106 107 m_constraintsDirty = true; 108 m_lastContentsWidth = contentsSize.width(); 109 } 110 111 static float computeDeprecatedTargetDensityDPIFactor(const ViewportDescription& description, float deviceScaleFactor) 112 { 113 if (description.deprecatedTargetDensityDPI == ViewportDescription::ValueDeviceDPI) 114 return 1.0f / deviceScaleFactor; 115 116 float targetDPI = -1.0f; 117 if (description.deprecatedTargetDensityDPI == ViewportDescription::ValueLowDPI) 118 targetDPI = 120.0f; 119 else if (description.deprecatedTargetDensityDPI == ViewportDescription::ValueMediumDPI) 120 targetDPI = 160.0f; 121 else if (description.deprecatedTargetDensityDPI == ViewportDescription::ValueHighDPI) 122 targetDPI = 240.0f; 123 else if (description.deprecatedTargetDensityDPI != ViewportDescription::ValueAuto) 124 targetDPI = description.deprecatedTargetDensityDPI; 125 return targetDPI > 0 ? 160.0f / targetDPI : 1.0f; 126 } 127 128 static float getLayoutWidthForNonWideViewport(const FloatSize& deviceSize, float initialScale) 129 { 130 return initialScale == -1 ? deviceSize.width() : deviceSize.width() / initialScale; 131 } 132 133 static float computeHeightByAspectRatio(float width, const FloatSize& deviceSize) 134 { 135 return width * (deviceSize.height() / deviceSize.width()); 136 } 137 138 void PageScaleConstraintsSet::didChangeViewSize(const IntSize& size) 139 { 140 if (m_viewSize == size) 141 return; 142 143 m_viewSize = size; 144 m_constraintsDirty = true; 145 } 146 147 IntSize PageScaleConstraintsSet::mainFrameSize(const IntSize& contentsSize) const 148 { 149 // If there's no explicit minimum scale factor set, size the frame so that its width == content width 150 // so there's no horizontal scrolling at the minimum scale. 151 if (m_pageDefinedConstraints.minimumScale < finalConstraints().minimumScale 152 && m_userAgentConstraints.minimumScale < finalConstraints().minimumScale 153 && contentsSize.width() 154 && m_viewSize.width()) 155 return IntSize(contentsSize.width(), computeHeightByAspectRatio(contentsSize.width(), m_viewSize)); 156 157 // If there is a minimum scale (or there's no content size yet), the frame size should match the viewport 158 // size at minimum scale, since the viewport must always be contained by the frame. 159 IntSize frameSize(m_viewSize); 160 frameSize.scale(1 / finalConstraints().minimumScale); 161 return frameSize; 162 } 163 164 void PageScaleConstraintsSet::adjustForAndroidWebViewQuirks(const ViewportDescription& description, int layoutFallbackWidth, float deviceScaleFactor, bool supportTargetDensityDPI, bool wideViewportQuirkEnabled, bool useWideViewport, bool loadWithOverviewMode, bool nonUserScalableQuirkEnabled) 165 { 166 if (!supportTargetDensityDPI && !wideViewportQuirkEnabled && loadWithOverviewMode && !nonUserScalableQuirkEnabled) 167 return; 168 169 const float oldInitialScale = m_pageDefinedConstraints.initialScale; 170 if (!loadWithOverviewMode) { 171 bool resetInitialScale = false; 172 if (description.zoom == -1) { 173 if (description.maxWidth.isAuto() || description.maxWidth.type() == ExtendToZoom) 174 resetInitialScale = true; 175 if (useWideViewport || description.maxWidth.type() == DeviceWidth) 176 resetInitialScale = true; 177 } 178 if (resetInitialScale) 179 m_pageDefinedConstraints.initialScale = 1.0f; 180 } 181 182 float adjustedLayoutSizeWidth = m_pageDefinedConstraints.layoutSize.width(); 183 float adjustedLayoutSizeHeight = m_pageDefinedConstraints.layoutSize.height(); 184 float targetDensityDPIFactor = 1.0f; 185 186 if (supportTargetDensityDPI) { 187 targetDensityDPIFactor = computeDeprecatedTargetDensityDPIFactor(description, deviceScaleFactor); 188 if (m_pageDefinedConstraints.initialScale != -1) 189 m_pageDefinedConstraints.initialScale *= targetDensityDPIFactor; 190 if (m_pageDefinedConstraints.minimumScale != -1) 191 m_pageDefinedConstraints.minimumScale *= targetDensityDPIFactor; 192 if (m_pageDefinedConstraints.maximumScale != -1) 193 m_pageDefinedConstraints.maximumScale *= targetDensityDPIFactor; 194 if (wideViewportQuirkEnabled && (!useWideViewport || description.maxWidth.type() == DeviceWidth)) { 195 adjustedLayoutSizeWidth /= targetDensityDPIFactor; 196 adjustedLayoutSizeHeight /= targetDensityDPIFactor; 197 } 198 } 199 200 if (wideViewportQuirkEnabled) { 201 if (useWideViewport && (description.maxWidth.isAuto() || description.maxWidth.type() == ExtendToZoom) && description.zoom != 1.0f) { 202 adjustedLayoutSizeWidth = layoutFallbackWidth; 203 adjustedLayoutSizeHeight = computeHeightByAspectRatio(adjustedLayoutSizeWidth, m_viewSize); 204 } else if (!useWideViewport) { 205 const float nonWideScale = description.zoom < 1 && description.maxWidth.type() != DeviceWidth && description.maxWidth.type() != DeviceHeight ? -1 : oldInitialScale; 206 adjustedLayoutSizeWidth = getLayoutWidthForNonWideViewport(m_viewSize, nonWideScale) / targetDensityDPIFactor; 207 float newInitialScale = targetDensityDPIFactor; 208 if (m_userAgentConstraints.initialScale != -1 && (description.maxWidth.type() == DeviceWidth || ((description.maxWidth.isAuto() || description.maxWidth.type() == ExtendToZoom) && description.zoom == -1))) { 209 adjustedLayoutSizeWidth /= m_userAgentConstraints.initialScale; 210 newInitialScale = m_userAgentConstraints.initialScale; 211 } 212 adjustedLayoutSizeHeight = computeHeightByAspectRatio(adjustedLayoutSizeWidth, m_viewSize); 213 if (description.zoom < 1) { 214 m_pageDefinedConstraints.initialScale = newInitialScale; 215 if (m_pageDefinedConstraints.minimumScale != -1) 216 m_pageDefinedConstraints.minimumScale = std::min<float>(m_pageDefinedConstraints.minimumScale, m_pageDefinedConstraints.initialScale); 217 if (m_pageDefinedConstraints.maximumScale != -1) 218 m_pageDefinedConstraints.maximumScale = std::max<float>(m_pageDefinedConstraints.maximumScale, m_pageDefinedConstraints.initialScale); 219 } 220 } 221 } 222 223 if (nonUserScalableQuirkEnabled && !description.userZoom) { 224 m_pageDefinedConstraints.initialScale = targetDensityDPIFactor; 225 m_pageDefinedConstraints.minimumScale = m_pageDefinedConstraints.initialScale; 226 m_pageDefinedConstraints.maximumScale = m_pageDefinedConstraints.initialScale; 227 if (description.maxWidth.isAuto() || description.maxWidth.type() == ExtendToZoom || description.maxWidth.type() == DeviceWidth) { 228 adjustedLayoutSizeWidth = m_viewSize.width() / targetDensityDPIFactor; 229 adjustedLayoutSizeHeight = computeHeightByAspectRatio(adjustedLayoutSizeWidth, m_viewSize); 230 } 231 } 232 233 m_pageDefinedConstraints.layoutSize.setWidth(adjustedLayoutSizeWidth); 234 m_pageDefinedConstraints.layoutSize.setHeight(adjustedLayoutSizeHeight); 235 } 236 237 } // namespace WebCore 238