Allow WebKit clients to specify a minimum effective width for layout.
[WebKit-https.git] / Source / WebCore / page / ViewportConfiguration.cpp
1 /*
2  * Copyright (C) 2005-2014 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "ViewportConfiguration.h"
28
29 #include "Logging.h"
30 #include <wtf/Assertions.h>
31 #include <wtf/MathExtras.h>
32 #include <wtf/text/CString.h>
33 #include <wtf/text/TextStream.h>
34
35 #if PLATFORM(IOS_FAMILY)
36 #include "PlatformScreen.h"
37 #endif
38
39 namespace WebCore {
40
41 #if !ASSERT_DISABLED
42 static bool constraintsAreAllRelative(const ViewportConfiguration::Parameters& configuration)
43 {
44     return !configuration.widthIsSet && !configuration.heightIsSet && !configuration.initialScaleIsSet;
45 }
46 #endif
47
48 static float platformDeviceWidthOverride()
49 {
50 #if PLATFORM(WATCHOS)
51     return 320;
52 #else
53     return 0;
54 #endif
55 }
56
57 static bool shouldOverrideShrinkToFitArgument()
58 {
59 #if PLATFORM(WATCHOS)
60     return true;
61 #else
62     return false;
63 #endif
64 }
65
66 static bool needsUpdateAfterChangingDisabledAdaptations(const OptionSet<DisabledAdaptations>& oldDisabledAdaptations, const OptionSet<DisabledAdaptations>& newDisabledAdaptations)
67 {
68     if (oldDisabledAdaptations == newDisabledAdaptations)
69         return false;
70
71 #if PLATFORM(WATCHOS)
72     if (oldDisabledAdaptations.contains(DisabledAdaptations::Watch) != newDisabledAdaptations.contains(DisabledAdaptations::Watch))
73         return true;
74 #endif
75
76     return false;
77 }
78
79 ViewportConfiguration::ViewportConfiguration()
80     : m_minimumLayoutSize(1024, 768)
81     , m_viewLayoutSize(1024, 768)
82     , m_canIgnoreScalingConstraints(false)
83     , m_forceAlwaysUserScalable(false)
84 {
85     // Setup a reasonable default configuration to avoid computing infinite scale/sizes.
86     // Those are the original iPhone configuration.
87     m_defaultConfiguration = ViewportConfiguration::webpageParameters();
88     updateConfiguration();
89 }
90
91 void ViewportConfiguration::setDefaultConfiguration(const ViewportConfiguration::Parameters& defaultConfiguration)
92 {
93     ASSERT(!constraintsAreAllRelative(m_configuration));
94     ASSERT(!defaultConfiguration.initialScaleIsSet || defaultConfiguration.initialScale > 0);
95     ASSERT(defaultConfiguration.minimumScale > 0);
96     ASSERT(defaultConfiguration.maximumScale >= defaultConfiguration.minimumScale);
97
98     if (m_defaultConfiguration == defaultConfiguration)
99         return;
100
101     m_defaultConfiguration = defaultConfiguration;
102     updateConfiguration();
103 }
104
105 bool ViewportConfiguration::setContentsSize(const IntSize& contentSize)
106 {
107     if (m_contentSize == contentSize)
108         return false;
109
110     LOG_WITH_STREAM(Viewports, stream << "ViewportConfiguration::setContentsSize " << contentSize << " (was " << m_contentSize << ")");
111
112     m_contentSize = contentSize;
113     updateConfiguration();
114     return true;
115 }
116
117 bool ViewportConfiguration::setViewLayoutSize(const FloatSize& viewLayoutSize, std::optional<double>&& scaleFactor, std::optional<double>&& minimumEffectiveDeviceWidth)
118 {
119     double newScaleFactor = scaleFactor.value_or(m_layoutSizeScaleFactor);
120     double newEffectiveWidth = minimumEffectiveDeviceWidth.value_or(m_minimumEffectiveDeviceWidth);
121     if (m_viewLayoutSize == viewLayoutSize && m_layoutSizeScaleFactor == newScaleFactor && newEffectiveWidth == m_minimumEffectiveDeviceWidth)
122         return false;
123
124     m_layoutSizeScaleFactor = newScaleFactor;
125     m_viewLayoutSize = viewLayoutSize;
126     m_minimumEffectiveDeviceWidth = newEffectiveWidth;
127
128     updateMinimumLayoutSize();
129     updateConfiguration();
130     return true;
131 }
132
133 bool ViewportConfiguration::setDisabledAdaptations(const OptionSet<DisabledAdaptations>& disabledAdaptations)
134 {
135     auto previousDisabledAdaptations = m_disabledAdaptations;
136     m_disabledAdaptations = disabledAdaptations;
137
138     if (!needsUpdateAfterChangingDisabledAdaptations(previousDisabledAdaptations, disabledAdaptations))
139         return false;
140
141     updateMinimumLayoutSize();
142     updateConfiguration();
143     return true;
144 }
145
146 bool ViewportConfiguration::setViewportArguments(const ViewportArguments& viewportArguments)
147 {
148     if (m_viewportArguments == viewportArguments)
149         return false;
150
151     LOG_WITH_STREAM(Viewports, stream << "ViewportConfiguration::setViewportArguments " << viewportArguments);
152     m_viewportArguments = viewportArguments;
153
154     updateMinimumLayoutSize();
155     updateConfiguration();
156     return true;
157 }
158
159 bool ViewportConfiguration::setCanIgnoreScalingConstraints(bool canIgnoreScalingConstraints)
160 {
161     if (canIgnoreScalingConstraints == m_canIgnoreScalingConstraints)
162         return false;
163     
164     m_canIgnoreScalingConstraints = canIgnoreScalingConstraints;
165     updateConfiguration();
166     return true;
167 }
168
169 IntSize ViewportConfiguration::layoutSize() const
170 {
171     return IntSize(layoutWidth(), layoutHeight());
172 }
173
174 bool ViewportConfiguration::shouldOverrideDeviceWidthAndShrinkToFit() const
175 {
176     if (m_disabledAdaptations.contains(DisabledAdaptations::Watch))
177         return false;
178
179     auto viewWidth = m_viewLayoutSize.width();
180     return 0 < viewWidth && viewWidth < platformDeviceWidthOverride();
181 }
182
183 bool ViewportConfiguration::shouldIgnoreHorizontalScalingConstraints() const
184 {
185     if (!m_canIgnoreScalingConstraints)
186         return false;
187
188     if (shouldOverrideDeviceWidthAndShrinkToFit())
189         return true;
190
191     if (!m_configuration.allowsShrinkToFit)
192         return false;
193
194     bool laidOutWiderThanViewport = m_contentSize.width() > layoutWidth();
195     if (m_viewportArguments.width == ViewportArguments::ValueDeviceWidth)
196         return laidOutWiderThanViewport;
197
198     if (m_configuration.initialScaleIsSet && m_configuration.initialScaleIgnoringLayoutScaleFactor == 1)
199         return laidOutWiderThanViewport;
200
201     return false;
202 }
203
204 bool ViewportConfiguration::shouldIgnoreVerticalScalingConstraints() const
205 {
206     if (!m_canIgnoreScalingConstraints)
207         return false;
208
209     if (!m_configuration.allowsShrinkToFit)
210         return false;
211
212     bool laidOutTallerThanViewport = m_contentSize.height() > layoutHeight();
213     if (m_viewportArguments.height == ViewportArguments::ValueDeviceHeight && m_viewportArguments.width == ViewportArguments::ValueAuto)
214         return laidOutTallerThanViewport;
215
216     return false;
217 }
218
219 bool ViewportConfiguration::shouldIgnoreScalingConstraints() const
220 {
221     return shouldIgnoreHorizontalScalingConstraints() || shouldIgnoreVerticalScalingConstraints();
222 }
223
224 bool ViewportConfiguration::shouldIgnoreScalingConstraintsRegardlessOfContentSize() const
225 {
226     return m_canIgnoreScalingConstraints && shouldOverrideDeviceWidthAndShrinkToFit();
227 }
228
229 double ViewportConfiguration::initialScaleFromSize(double width, double height, bool shouldIgnoreScalingConstraints) const
230 {
231     ASSERT(!constraintsAreAllRelative(m_configuration));
232
233     // If the document has specified its own initial scale, use it regardless.
234     // This is guaranteed to be sanity checked already, so no need for MIN/MAX.
235     if (m_configuration.initialScaleIsSet && !shouldIgnoreScalingConstraints)
236         return m_configuration.initialScale;
237
238     // If not, it is up to us to determine the initial scale.
239     // We want a scale small enough to fit the document width-wise.
240     double initialScale = 0;
241     if (width > 0 && !shouldIgnoreVerticalScalingConstraints())
242         initialScale = m_viewLayoutSize.width() / width;
243
244     // Prevent the initial scale from shrinking to a height smaller than our view's minimum height.
245     if (height > 0 && height * initialScale < m_viewLayoutSize.height() && !shouldIgnoreHorizontalScalingConstraints())
246         initialScale = m_viewLayoutSize.height() / height;
247
248     return std::min(std::max(initialScale, shouldIgnoreScalingConstraints ? m_defaultConfiguration.minimumScale : m_configuration.minimumScale), m_configuration.maximumScale);
249 }
250
251 double ViewportConfiguration::initialScale() const
252 {
253     return initialScaleFromSize(m_contentSize.width() > 0 ? m_contentSize.width() : layoutWidth(), m_contentSize.height() > 0 ? m_contentSize.height() : layoutHeight(), shouldIgnoreScalingConstraints());
254 }
255
256 double ViewportConfiguration::initialScaleIgnoringContentSize() const
257 {
258     return initialScaleFromSize(layoutWidth(), layoutHeight(), shouldIgnoreScalingConstraintsRegardlessOfContentSize());
259 }
260
261 double ViewportConfiguration::minimumScale() const
262 {
263     // If we scale to fit, then this is our minimum scale as well.
264     if (!m_configuration.initialScaleIsSet || shouldIgnoreScalingConstraints())
265         return initialScale();
266
267     // If not, we still need to sanity check our value.
268     double minimumScale = m_configuration.minimumScale;
269     
270     if (m_forceAlwaysUserScalable)
271         minimumScale = std::min(minimumScale, forceAlwaysUserScalableMinimumScale());
272
273     auto scaleForFittingContentIsApproximatelyEqualToMinimumScale = [] (double viewLength, double contentLength, double minimumScale) {
274         if (contentLength <= 1 || viewLength <= 1)
275             return false;
276
277         if (minimumScale < (viewLength - 0.5) / (contentLength + 0.5))
278             return false;
279
280         if (minimumScale > (viewLength + 0.5) / (contentLength - 0.5))
281             return false;
282
283         return true;
284     };
285
286     double contentWidth = m_contentSize.width();
287     if (contentWidth > 0 && contentWidth * minimumScale < m_viewLayoutSize.width() && !shouldIgnoreVerticalScalingConstraints()) {
288         if (!scaleForFittingContentIsApproximatelyEqualToMinimumScale(m_viewLayoutSize.width(), contentWidth, minimumScale))
289             minimumScale = m_viewLayoutSize.width() / contentWidth;
290     }
291
292     double contentHeight = m_contentSize.height();
293     if (contentHeight > 0 && contentHeight * minimumScale < m_viewLayoutSize.height() && !shouldIgnoreHorizontalScalingConstraints()) {
294         if (!scaleForFittingContentIsApproximatelyEqualToMinimumScale(m_viewLayoutSize.height(), contentHeight, minimumScale))
295             minimumScale = m_viewLayoutSize.height() / contentHeight;
296     }
297
298     minimumScale = std::min(std::max(minimumScale, m_configuration.minimumScale), m_configuration.maximumScale);
299
300     return minimumScale;
301 }
302
303 bool ViewportConfiguration::allowsUserScaling() const
304 {
305     return m_forceAlwaysUserScalable || allowsUserScalingIgnoringAlwaysScalable();
306 }
307     
308 bool ViewportConfiguration::allowsUserScalingIgnoringAlwaysScalable() const
309 {
310     return shouldIgnoreScalingConstraints() || m_configuration.allowsUserScaling;
311 }
312
313 ViewportConfiguration::Parameters ViewportConfiguration::nativeWebpageParameters()
314 {
315     Parameters parameters;
316     parameters.width = ViewportArguments::ValueDeviceWidth;
317     parameters.widthIsSet = true;
318     parameters.allowsUserScaling = true;
319     parameters.allowsShrinkToFit = false;
320     parameters.minimumScale = 1;
321     parameters.maximumScale = 5;
322     parameters.initialScale = 1;
323     parameters.initialScaleIgnoringLayoutScaleFactor = 1;
324     parameters.initialScaleIsSet = true;
325     return parameters;
326 }
327
328 ViewportConfiguration::Parameters ViewportConfiguration::webpageParameters()
329 {
330     Parameters parameters;
331     parameters.width = 980;
332     parameters.widthIsSet = true;
333     parameters.allowsUserScaling = true;
334     parameters.allowsShrinkToFit = true;
335     parameters.minimumScale = 0.25;
336     parameters.maximumScale = 5;
337     return parameters;
338 }
339
340 ViewportConfiguration::Parameters ViewportConfiguration::textDocumentParameters()
341 {
342     Parameters parameters;
343
344 #if PLATFORM(IOS_FAMILY)
345     parameters.width = static_cast<int>(screenSize().width());
346 #else
347     // FIXME: this needs to be unified with ViewportArguments on all ports.
348     parameters.width = 320;
349 #endif
350
351     parameters.widthIsSet = true;
352     parameters.allowsUserScaling = true;
353     parameters.allowsShrinkToFit = false;
354     parameters.minimumScale = 0.25;
355     parameters.maximumScale = 5;
356     return parameters;
357 }
358
359 ViewportConfiguration::Parameters ViewportConfiguration::imageDocumentParameters()
360 {
361     Parameters parameters;
362     parameters.width = 980;
363     parameters.widthIsSet = true;
364     parameters.allowsUserScaling = true;
365     parameters.allowsShrinkToFit = false;
366     parameters.minimumScale = 0.01;
367     parameters.maximumScale = 5;
368     return parameters;
369 }
370
371 ViewportConfiguration::Parameters ViewportConfiguration::xhtmlMobileParameters()
372 {
373     Parameters parameters = webpageParameters();
374     parameters.width = 320;
375     return parameters;
376 }
377
378 ViewportConfiguration::Parameters ViewportConfiguration::testingParameters()
379 {
380     Parameters parameters;
381     parameters.initialScale = 1;
382     parameters.initialScaleIgnoringLayoutScaleFactor = 1;
383     parameters.initialScaleIsSet = true;
384     parameters.allowsShrinkToFit = true;
385     parameters.minimumScale = 1;
386     parameters.maximumScale = 5;
387     return parameters;
388 }
389
390 static inline bool viewportArgumentValueIsValid(float value)
391 {
392     return value > 0;
393 }
394
395 template<typename ValueType, typename ViewportArgumentsType>
396 static inline void applyViewportArgument(ValueType& value, ViewportArgumentsType viewportArgumentValue, ValueType minimum, ValueType maximum)
397 {
398     if (viewportArgumentValueIsValid(viewportArgumentValue))
399         value = std::min(maximum, std::max(minimum, static_cast<ValueType>(viewportArgumentValue)));
400 }
401
402 template<typename ValueType, typename ViewportArgumentsType>
403 static inline void applyViewportArgument(ValueType& value, bool& valueIsSet, ViewportArgumentsType viewportArgumentValue, ValueType minimum, ValueType maximum)
404 {
405     if (viewportArgumentValueIsValid(viewportArgumentValue)) {
406         value = std::min(maximum, std::max(minimum, static_cast<ValueType>(viewportArgumentValue)));
407         valueIsSet = true;
408     } else
409         valueIsSet = false;
410 }
411
412 static inline bool booleanViewportArgumentIsSet(float value)
413 {
414     return !value || value == 1;
415 }
416
417 void ViewportConfiguration::updateConfiguration()
418 {
419     m_configuration = m_defaultConfiguration;
420
421     const double minimumViewportArgumentsScaleFactor = 0.1;
422     const double maximumViewportArgumentsScaleFactor = 10.0;
423
424     bool viewportArgumentsOverridesInitialScale;
425     bool viewportArgumentsOverridesWidth;
426     bool viewportArgumentsOverridesHeight;
427
428     applyViewportArgument(m_configuration.minimumScale, m_viewportArguments.minZoom, minimumViewportArgumentsScaleFactor, maximumViewportArgumentsScaleFactor);
429     applyViewportArgument(m_configuration.maximumScale, m_viewportArguments.maxZoom, m_configuration.minimumScale, maximumViewportArgumentsScaleFactor);
430     applyViewportArgument(m_configuration.initialScale, viewportArgumentsOverridesInitialScale, m_viewportArguments.zoom, m_configuration.minimumScale, m_configuration.maximumScale);
431
432     double minimumViewportArgumentsDimension = 10;
433     double maximumViewportArgumentsDimension = 10000;
434     applyViewportArgument(m_configuration.width, viewportArgumentsOverridesWidth, viewportArgumentsLength(m_viewportArguments.width), minimumViewportArgumentsDimension, maximumViewportArgumentsDimension);
435     applyViewportArgument(m_configuration.height, viewportArgumentsOverridesHeight, viewportArgumentsLength(m_viewportArguments.height), minimumViewportArgumentsDimension, maximumViewportArgumentsDimension);
436
437     if (viewportArgumentsOverridesInitialScale || viewportArgumentsOverridesWidth || viewportArgumentsOverridesHeight) {
438         m_configuration.initialScaleIsSet = viewportArgumentsOverridesInitialScale;
439         m_configuration.widthIsSet = viewportArgumentsOverridesWidth;
440         m_configuration.heightIsSet = viewportArgumentsOverridesHeight;
441     }
442
443     if (booleanViewportArgumentIsSet(m_viewportArguments.userZoom))
444         m_configuration.allowsUserScaling = m_viewportArguments.userZoom != 0.;
445
446     if (shouldOverrideShrinkToFitArgument())
447         m_configuration.allowsShrinkToFit = shouldOverrideDeviceWidthAndShrinkToFit();
448     else if (booleanViewportArgumentIsSet(m_viewportArguments.shrinkToFit))
449         m_configuration.allowsShrinkToFit = m_viewportArguments.shrinkToFit != 0.;
450
451     m_configuration.avoidsUnsafeArea = m_viewportArguments.viewportFit != ViewportFit::Cover;
452     m_configuration.initialScaleIgnoringLayoutScaleFactor = m_configuration.initialScale;
453     float effectiveLayoutScale = effectiveLayoutSizeScaleFactor();
454     m_configuration.initialScale *= effectiveLayoutScale;
455     m_configuration.minimumScale *= effectiveLayoutScale;
456     m_configuration.maximumScale *= effectiveLayoutScale;
457
458     LOG_WITH_STREAM(Viewports, stream << "ViewportConfiguration " << this << " updateConfiguration " << *this << " gives initial scale " << initialScale() << " based on contentSize " << m_contentSize << " and layout size " << layoutWidth() << "x" << layoutHeight());
459 }
460
461 void ViewportConfiguration::updateMinimumLayoutSize()
462 {
463     m_minimumLayoutSize = m_viewLayoutSize / effectiveLayoutSizeScaleFactor();
464
465     if (!shouldOverrideDeviceWidthAndShrinkToFit())
466         return;
467
468     float minDeviceWidth = platformDeviceWidthOverride();
469     m_minimumLayoutSize = FloatSize(minDeviceWidth, std::roundf(m_minimumLayoutSize.height() * (minDeviceWidth / m_minimumLayoutSize.width())));
470 }
471
472 double ViewportConfiguration::viewportArgumentsLength(double length) const
473 {
474     if (length == ViewportArguments::ValueDeviceWidth)
475         return m_minimumLayoutSize.width();
476     if (length == ViewportArguments::ValueDeviceHeight)
477         return m_minimumLayoutSize.height();
478     return length;
479 }
480
481 int ViewportConfiguration::layoutWidth() const
482 {
483     ASSERT(!constraintsAreAllRelative(m_configuration));
484
485     const FloatSize& minimumLayoutSize = m_minimumLayoutSize;
486     if (m_configuration.widthIsSet) {
487         // If we scale to fit, then accept the viewport width with sanity checking.
488         if (!m_configuration.initialScaleIsSet) {
489             double maximumScale = this->maximumScale();
490             double maximumContentWidthInViewportCoordinate = maximumScale * m_configuration.width;
491             if (maximumContentWidthInViewportCoordinate < minimumLayoutSize.width()) {
492                 // The content zoomed to maxScale does not fit the view. Return the minimum width
493                 // satisfying the constraint maximumScale.
494                 return std::round(minimumLayoutSize.width() / maximumScale);
495             }
496             return std::round(m_configuration.width);
497         }
498
499         // If not, make sure the viewport width and initial scale can co-exist.
500         double initialContentWidthInViewportCoordinate = m_configuration.width * m_configuration.initialScaleIgnoringLayoutScaleFactor;
501         if (initialContentWidthInViewportCoordinate < minimumLayoutSize.width()) {
502             // The specified width does not fit in viewport. Return the minimum width that satisfy the initialScale constraint.
503             return std::round(minimumLayoutSize.width() / m_configuration.initialScaleIgnoringLayoutScaleFactor);
504         }
505         return std::round(m_configuration.width);
506     }
507
508     // If the page has a real scale, then just return the minimum size over the initial scale.
509     if (m_configuration.initialScaleIsSet && !m_configuration.heightIsSet)
510         return std::round(minimumLayoutSize.width() / m_configuration.initialScaleIgnoringLayoutScaleFactor);
511
512     if (minimumLayoutSize.height() > 0)
513         return std::round(minimumLayoutSize.width() * layoutHeight() / minimumLayoutSize.height());
514     return minimumLayoutSize.width();
515 }
516
517 int ViewportConfiguration::layoutHeight() const
518 {
519     ASSERT(!constraintsAreAllRelative(m_configuration));
520
521     const FloatSize& minimumLayoutSize = m_minimumLayoutSize;
522     if (m_configuration.heightIsSet) {
523         // If we scale to fit, then accept the viewport height with sanity checking.
524         if (!m_configuration.initialScaleIsSet) {
525             double maximumScale = this->maximumScale();
526             double maximumContentHeightInViewportCoordinate = maximumScale * m_configuration.height;
527             if (maximumContentHeightInViewportCoordinate < minimumLayoutSize.height()) {
528                 // The content zoomed to maxScale does not fit the view. Return the minimum height that
529                 // satisfy the constraint maximumScale.
530                 return std::round(minimumLayoutSize.height() / maximumScale);
531             }
532             return std::round(m_configuration.height);
533         }
534
535         // If not, make sure the viewport width and initial scale can co-exist.
536         double initialContentHeightInViewportCoordinate = m_configuration.height * m_configuration.initialScaleIgnoringLayoutScaleFactor;
537         if (initialContentHeightInViewportCoordinate < minimumLayoutSize.height()) {
538             // The specified width does not fit in viewport. Return the minimum height that satisfy the initialScale constraint.
539             return std::round(minimumLayoutSize.height() / m_configuration.initialScaleIgnoringLayoutScaleFactor);
540         }
541         return std::round(m_configuration.height);
542     }
543
544     // If the page has a real scale, then just return the minimum size over the initial scale.
545     if (m_configuration.initialScaleIsSet && !m_configuration.widthIsSet)
546         return std::round(minimumLayoutSize.height() / m_configuration.initialScaleIgnoringLayoutScaleFactor);
547
548     if (minimumLayoutSize.width() > 0)
549         return std::round(minimumLayoutSize.height() * layoutWidth() / minimumLayoutSize.width());
550     return minimumLayoutSize.height();
551 }
552
553 #ifndef NDEBUG
554
555 TextStream& operator<<(TextStream& ts, const ViewportConfiguration::Parameters& parameters)
556 {
557     ts.startGroup();
558     ts << "width " << parameters.width << ", set: " << (parameters.widthIsSet ? "true" : "false");
559     ts.endGroup();
560
561     ts.startGroup();
562     ts << "height " << parameters.height << ", set: " << (parameters.heightIsSet ? "true" : "false");
563     ts.endGroup();
564
565     ts.startGroup();
566     ts << "initialScale " << parameters.initialScale << ", set: " << (parameters.initialScaleIsSet ? "true" : "false");
567     ts.endGroup();
568
569     ts.dumpProperty("initialScaleIgnoringLayoutScaleFactor", parameters.initialScaleIgnoringLayoutScaleFactor);
570     ts.dumpProperty("minimumScale", parameters.minimumScale);
571     ts.dumpProperty("maximumScale", parameters.maximumScale);
572     ts.dumpProperty("allowsUserScaling", parameters.allowsUserScaling);
573     ts.dumpProperty("allowsShrinkToFit", parameters.allowsShrinkToFit);
574     ts.dumpProperty("avoidsUnsafeArea", parameters.avoidsUnsafeArea);
575
576     return ts;
577 }
578
579 TextStream& operator<<(TextStream& ts, const ViewportConfiguration& config)
580 {
581     return ts << config.description();
582 }
583
584 String ViewportConfiguration::description() const
585 {
586     TextStream ts;
587
588     ts.startGroup();
589     ts << "viewport-configuration " << (void*)this;
590     {
591         TextStream::GroupScope scope(ts);
592         ts << "viewport arguments";
593         ts << m_viewportArguments;
594     }
595     {
596         TextStream::GroupScope scope(ts);
597         ts << "configuration";
598         ts << m_configuration;
599     }
600     {
601         TextStream::GroupScope scope(ts);
602         ts << "default configuration";
603         ts << m_defaultConfiguration;
604     }
605
606     ts.dumpProperty("contentSize", m_contentSize);
607     ts.dumpProperty("minimumLayoutSize", m_minimumLayoutSize);
608     ts.dumpProperty("layoutSizeScaleFactor", m_layoutSizeScaleFactor);
609     ts.dumpProperty("computed initial scale", initialScale());
610     ts.dumpProperty("computed minimum scale", minimumScale());
611     ts.dumpProperty("computed layout size", layoutSize());
612     ts.dumpProperty("ignoring horizontal scaling constraints", shouldIgnoreHorizontalScalingConstraints() ? "true" : "false");
613     ts.dumpProperty("ignoring vertical scaling constraints", shouldIgnoreVerticalScalingConstraints() ? "true" : "false");
614     ts.dumpProperty("avoids unsafe area", avoidsUnsafeArea() ? "true" : "false");
615     ts.dumpProperty("minimum effective device width", m_minimumEffectiveDeviceWidth);
616     
617     ts.endGroup();
618
619     return ts.release();
620 }
621
622 void ViewportConfiguration::dump() const
623 {
624     WTFLogAlways("%s", description().utf8().data());
625 }
626
627 #endif
628
629 } // namespace WebCore