Use Optional::valueOr() instead of Optional::value_or()
[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, Optional<double>&& scaleFactor, Optional<double>&& minimumEffectiveDeviceWidth)
118 {
119     double newScaleFactor = scaleFactor.valueOr(m_layoutSizeScaleFactor);
120     double newEffectiveWidth = minimumEffectiveDeviceWidth.valueOr(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::canOverrideConfigurationParameters() const
147 {
148     return m_defaultConfiguration == ViewportConfiguration::nativeWebpageParameters() || m_defaultConfiguration == ViewportConfiguration::scalableNativeWebpageParameters();
149 }
150
151 void ViewportConfiguration::updateDefaultConfiguration()
152 {
153     if (!canOverrideConfigurationParameters())
154         return;
155
156     if (m_canIgnoreScalingConstraints) {
157         m_defaultConfiguration = ViewportConfiguration::scalableNativeWebpageParameters();
158         return;
159     }
160
161     if (shouldIgnoreMinimumEffectiveDeviceWidth())
162         m_defaultConfiguration = ViewportConfiguration::nativeWebpageParameters();
163     else
164         m_defaultConfiguration = ViewportConfiguration::scalableNativeWebpageParameters();
165 }
166
167 bool ViewportConfiguration::setViewportArguments(const ViewportArguments& viewportArguments)
168 {
169     if (m_viewportArguments == viewportArguments)
170         return false;
171
172     LOG_WITH_STREAM(Viewports, stream << "ViewportConfiguration::setViewportArguments " << viewportArguments);
173     m_viewportArguments = viewportArguments;
174
175     updateDefaultConfiguration();
176     updateMinimumLayoutSize();
177     updateConfiguration();
178     return true;
179 }
180
181 bool ViewportConfiguration::setCanIgnoreScalingConstraints(bool canIgnoreScalingConstraints)
182 {
183     if (canIgnoreScalingConstraints == m_canIgnoreScalingConstraints)
184         return false;
185     
186     m_canIgnoreScalingConstraints = canIgnoreScalingConstraints;
187     updateDefaultConfiguration();
188     updateConfiguration();
189     return true;
190 }
191
192 IntSize ViewportConfiguration::layoutSize() const
193 {
194     return IntSize(layoutWidth(), layoutHeight());
195 }
196
197 bool ViewportConfiguration::shouldOverrideDeviceWidthAndShrinkToFit() const
198 {
199     if (m_disabledAdaptations.contains(DisabledAdaptations::Watch))
200         return false;
201
202     auto viewWidth = m_viewLayoutSize.width();
203     return 0 < viewWidth && viewWidth < platformDeviceWidthOverride();
204 }
205
206 bool ViewportConfiguration::shouldIgnoreHorizontalScalingConstraints() const
207 {
208     if (!m_canIgnoreScalingConstraints)
209         return false;
210
211     if (shouldOverrideDeviceWidthAndShrinkToFit())
212         return true;
213
214     if (!m_configuration.allowsShrinkToFit)
215         return false;
216
217     bool laidOutWiderThanViewport = m_contentSize.width() > layoutWidth();
218     if (m_viewportArguments.width == ViewportArguments::ValueDeviceWidth)
219         return laidOutWiderThanViewport;
220
221     if (m_configuration.initialScaleIsSet && m_configuration.initialScaleIgnoringLayoutScaleFactor == 1)
222         return laidOutWiderThanViewport;
223
224     return false;
225 }
226
227 bool ViewportConfiguration::shouldIgnoreVerticalScalingConstraints() const
228 {
229     if (!m_canIgnoreScalingConstraints)
230         return false;
231
232     if (!m_configuration.allowsShrinkToFit)
233         return false;
234
235     bool laidOutTallerThanViewport = m_contentSize.height() > layoutHeight();
236     if (m_viewportArguments.height == ViewportArguments::ValueDeviceHeight && m_viewportArguments.width == ViewportArguments::ValueAuto)
237         return laidOutTallerThanViewport;
238
239     return false;
240 }
241
242 bool ViewportConfiguration::shouldIgnoreScalingConstraints() const
243 {
244     return shouldIgnoreHorizontalScalingConstraints() || shouldIgnoreVerticalScalingConstraints();
245 }
246
247 bool ViewportConfiguration::shouldIgnoreScalingConstraintsRegardlessOfContentSize() const
248 {
249     return m_canIgnoreScalingConstraints && shouldOverrideDeviceWidthAndShrinkToFit();
250 }
251
252 double ViewportConfiguration::initialScaleFromSize(double width, double height, bool shouldIgnoreScalingConstraints) const
253 {
254     ASSERT(!constraintsAreAllRelative(m_configuration));
255
256     // If the document has specified its own initial scale, use it regardless.
257     // This is guaranteed to be sanity checked already, so no need for MIN/MAX.
258     if (m_configuration.initialScaleIsSet && !shouldIgnoreScalingConstraints)
259         return m_configuration.initialScale;
260
261     // If not, it is up to us to determine the initial scale.
262     // We want a scale small enough to fit the document width-wise.
263     double initialScale = 0;
264     if (width > 0 && !shouldIgnoreVerticalScalingConstraints())
265         initialScale = m_viewLayoutSize.width() / width;
266
267     // Prevent the initial scale from shrinking to a height smaller than our view's minimum height.
268     if (height > 0 && height * initialScale < m_viewLayoutSize.height() && !shouldIgnoreHorizontalScalingConstraints())
269         initialScale = m_viewLayoutSize.height() / height;
270
271     return std::min(std::max(initialScale, shouldIgnoreScalingConstraints ? m_defaultConfiguration.minimumScale : m_configuration.minimumScale), m_configuration.maximumScale);
272 }
273
274 double ViewportConfiguration::initialScale() const
275 {
276     return initialScaleFromSize(m_contentSize.width() > 0 ? m_contentSize.width() : layoutWidth(), m_contentSize.height() > 0 ? m_contentSize.height() : layoutHeight(), shouldIgnoreScalingConstraints());
277 }
278
279 double ViewportConfiguration::initialScaleIgnoringContentSize() const
280 {
281     return initialScaleFromSize(layoutWidth(), layoutHeight(), shouldIgnoreScalingConstraintsRegardlessOfContentSize());
282 }
283
284 double ViewportConfiguration::minimumScale() const
285 {
286     // If we scale to fit, then this is our minimum scale as well.
287     if (!m_configuration.initialScaleIsSet || shouldIgnoreScalingConstraints())
288         return initialScale();
289
290     // If not, we still need to sanity check our value.
291     double minimumScale = m_configuration.minimumScale;
292     
293     if (m_forceAlwaysUserScalable)
294         minimumScale = std::min(minimumScale, forceAlwaysUserScalableMinimumScale());
295
296     auto scaleForFittingContentIsApproximatelyEqualToMinimumScale = [] (double viewLength, double contentLength, double minimumScale) {
297         if (contentLength <= 1 || viewLength <= 1)
298             return false;
299
300         if (minimumScale < (viewLength - 0.5) / (contentLength + 0.5))
301             return false;
302
303         if (minimumScale > (viewLength + 0.5) / (contentLength - 0.5))
304             return false;
305
306         return true;
307     };
308
309     double contentWidth = m_contentSize.width();
310     if (contentWidth > 0 && contentWidth * minimumScale < m_viewLayoutSize.width() && !shouldIgnoreVerticalScalingConstraints()) {
311         if (!scaleForFittingContentIsApproximatelyEqualToMinimumScale(m_viewLayoutSize.width(), contentWidth, minimumScale))
312             minimumScale = m_viewLayoutSize.width() / contentWidth;
313     }
314
315     double contentHeight = m_contentSize.height();
316     if (contentHeight > 0 && contentHeight * minimumScale < m_viewLayoutSize.height() && !shouldIgnoreHorizontalScalingConstraints()) {
317         if (!scaleForFittingContentIsApproximatelyEqualToMinimumScale(m_viewLayoutSize.height(), contentHeight, minimumScale))
318             minimumScale = m_viewLayoutSize.height() / contentHeight;
319     }
320
321     minimumScale = std::min(std::max(minimumScale, m_configuration.minimumScale), m_configuration.maximumScale);
322
323     return minimumScale;
324 }
325
326 bool ViewportConfiguration::allowsUserScaling() const
327 {
328     return m_forceAlwaysUserScalable || allowsUserScalingIgnoringAlwaysScalable();
329 }
330     
331 bool ViewportConfiguration::allowsUserScalingIgnoringAlwaysScalable() const
332 {
333     return shouldIgnoreScalingConstraints() || m_configuration.allowsUserScaling;
334 }
335
336 ViewportConfiguration::Parameters ViewportConfiguration::nativeWebpageParameters()
337 {
338     Parameters parameters;
339     parameters.width = ViewportArguments::ValueDeviceWidth;
340     parameters.widthIsSet = true;
341     parameters.allowsUserScaling = true;
342     parameters.allowsShrinkToFit = false;
343     parameters.minimumScale = 1;
344     parameters.maximumScale = 5;
345     parameters.initialScale = 1;
346     parameters.initialScaleIgnoringLayoutScaleFactor = 1;
347     parameters.initialScaleIsSet = true;
348     return parameters;
349 }
350
351 ViewportConfiguration::Parameters ViewportConfiguration::scalableNativeWebpageParameters()
352 {
353     Parameters parameters = ViewportConfiguration::nativeWebpageParameters();
354     parameters.allowsShrinkToFit = true;
355     parameters.minimumScale = 0.25;
356     return parameters;
357 }
358
359 ViewportConfiguration::Parameters ViewportConfiguration::webpageParameters()
360 {
361     Parameters parameters;
362     parameters.width = 980;
363     parameters.widthIsSet = true;
364     parameters.allowsUserScaling = true;
365     parameters.allowsShrinkToFit = true;
366     parameters.minimumScale = 0.25;
367     parameters.maximumScale = 5;
368     return parameters;
369 }
370
371 ViewportConfiguration::Parameters ViewportConfiguration::textDocumentParameters()
372 {
373     Parameters parameters;
374
375 #if PLATFORM(IOS_FAMILY)
376     parameters.width = static_cast<int>(screenSize().width());
377 #else
378     // FIXME: this needs to be unified with ViewportArguments on all ports.
379     parameters.width = 320;
380 #endif
381
382     parameters.widthIsSet = true;
383     parameters.allowsUserScaling = true;
384     parameters.allowsShrinkToFit = false;
385     parameters.minimumScale = 0.25;
386     parameters.maximumScale = 5;
387     return parameters;
388 }
389
390 ViewportConfiguration::Parameters ViewportConfiguration::imageDocumentParameters()
391 {
392     Parameters parameters;
393     parameters.width = 980;
394     parameters.widthIsSet = true;
395     parameters.allowsUserScaling = true;
396     parameters.allowsShrinkToFit = false;
397     parameters.minimumScale = 0.01;
398     parameters.maximumScale = 5;
399     return parameters;
400 }
401
402 ViewportConfiguration::Parameters ViewportConfiguration::xhtmlMobileParameters()
403 {
404     Parameters parameters = webpageParameters();
405     parameters.width = 320;
406     return parameters;
407 }
408
409 ViewportConfiguration::Parameters ViewportConfiguration::testingParameters()
410 {
411     Parameters parameters;
412     parameters.initialScale = 1;
413     parameters.initialScaleIgnoringLayoutScaleFactor = 1;
414     parameters.initialScaleIsSet = true;
415     parameters.allowsShrinkToFit = true;
416     parameters.minimumScale = 1;
417     parameters.maximumScale = 5;
418     return parameters;
419 }
420
421 static inline bool viewportArgumentValueIsValid(float value)
422 {
423     return value > 0;
424 }
425
426 template<typename ValueType, typename ViewportArgumentsType>
427 static inline void applyViewportArgument(ValueType& value, ViewportArgumentsType viewportArgumentValue, ValueType minimum, ValueType maximum)
428 {
429     if (viewportArgumentValueIsValid(viewportArgumentValue))
430         value = std::min(maximum, std::max(minimum, static_cast<ValueType>(viewportArgumentValue)));
431 }
432
433 template<typename ValueType, typename ViewportArgumentsType>
434 static inline void applyViewportArgument(ValueType& value, bool& valueIsSet, ViewportArgumentsType viewportArgumentValue, ValueType minimum, ValueType maximum)
435 {
436     if (viewportArgumentValueIsValid(viewportArgumentValue)) {
437         value = std::min(maximum, std::max(minimum, static_cast<ValueType>(viewportArgumentValue)));
438         valueIsSet = true;
439     } else
440         valueIsSet = false;
441 }
442
443 static inline bool booleanViewportArgumentIsSet(float value)
444 {
445     return !value || value == 1;
446 }
447
448 void ViewportConfiguration::updateConfiguration()
449 {
450     m_configuration = m_defaultConfiguration;
451
452     const double minimumViewportArgumentsScaleFactor = 0.1;
453     const double maximumViewportArgumentsScaleFactor = 10.0;
454
455     bool viewportArgumentsOverridesInitialScale;
456     bool viewportArgumentsOverridesWidth;
457     bool viewportArgumentsOverridesHeight;
458
459     applyViewportArgument(m_configuration.minimumScale, m_viewportArguments.minZoom, minimumViewportArgumentsScaleFactor, maximumViewportArgumentsScaleFactor);
460     applyViewportArgument(m_configuration.maximumScale, m_viewportArguments.maxZoom, m_configuration.minimumScale, maximumViewportArgumentsScaleFactor);
461     applyViewportArgument(m_configuration.initialScale, viewportArgumentsOverridesInitialScale, m_viewportArguments.zoom, m_configuration.minimumScale, m_configuration.maximumScale);
462
463     double minimumViewportArgumentsDimension = 10;
464     double maximumViewportArgumentsDimension = 10000;
465     applyViewportArgument(m_configuration.width, viewportArgumentsOverridesWidth, viewportArgumentsLength(m_viewportArguments.width), minimumViewportArgumentsDimension, maximumViewportArgumentsDimension);
466     applyViewportArgument(m_configuration.height, viewportArgumentsOverridesHeight, viewportArgumentsLength(m_viewportArguments.height), minimumViewportArgumentsDimension, maximumViewportArgumentsDimension);
467
468     if (viewportArgumentsOverridesInitialScale || viewportArgumentsOverridesWidth || viewportArgumentsOverridesHeight) {
469         m_configuration.initialScaleIsSet = viewportArgumentsOverridesInitialScale;
470         m_configuration.widthIsSet = viewportArgumentsOverridesWidth;
471         m_configuration.heightIsSet = viewportArgumentsOverridesHeight;
472     }
473
474     if (booleanViewportArgumentIsSet(m_viewportArguments.userZoom))
475         m_configuration.allowsUserScaling = m_viewportArguments.userZoom != 0.;
476
477     if (shouldOverrideShrinkToFitArgument())
478         m_configuration.allowsShrinkToFit = shouldOverrideDeviceWidthAndShrinkToFit();
479     else if (booleanViewportArgumentIsSet(m_viewportArguments.shrinkToFit))
480         m_configuration.allowsShrinkToFit = m_viewportArguments.shrinkToFit != 0.;
481
482     if (canOverrideConfigurationParameters() && !viewportArgumentsOverridesWidth)
483         m_configuration.width = m_minimumLayoutSize.width();
484
485     m_configuration.avoidsUnsafeArea = m_viewportArguments.viewportFit != ViewportFit::Cover;
486     m_configuration.initialScaleIgnoringLayoutScaleFactor = m_configuration.initialScale;
487     float effectiveLayoutScale = effectiveLayoutSizeScaleFactor();
488     m_configuration.initialScale *= effectiveLayoutScale;
489     m_configuration.minimumScale *= effectiveLayoutScale;
490     m_configuration.maximumScale *= effectiveLayoutScale;
491
492     LOG_WITH_STREAM(Viewports, stream << "ViewportConfiguration " << this << " updateConfiguration " << *this << " gives initial scale " << initialScale() << " based on contentSize " << m_contentSize << " and layout size " << layoutWidth() << "x" << layoutHeight());
493 }
494
495 void ViewportConfiguration::updateMinimumLayoutSize()
496 {
497     m_minimumLayoutSize = m_viewLayoutSize / effectiveLayoutSizeScaleFactor();
498
499     if (!shouldOverrideDeviceWidthAndShrinkToFit())
500         return;
501
502     float minDeviceWidth = platformDeviceWidthOverride();
503     m_minimumLayoutSize = FloatSize(minDeviceWidth, std::roundf(m_minimumLayoutSize.height() * (minDeviceWidth / m_minimumLayoutSize.width())));
504 }
505
506 double ViewportConfiguration::viewportArgumentsLength(double length) const
507 {
508     if (length == ViewportArguments::ValueDeviceWidth)
509         return m_minimumLayoutSize.width();
510     if (length == ViewportArguments::ValueDeviceHeight)
511         return m_minimumLayoutSize.height();
512     return length;
513 }
514
515 int ViewportConfiguration::layoutWidth() const
516 {
517     ASSERT(!constraintsAreAllRelative(m_configuration));
518
519     const FloatSize& minimumLayoutSize = m_minimumLayoutSize;
520     if (m_configuration.widthIsSet) {
521         // If we scale to fit, then accept the viewport width with sanity checking.
522         if (!m_configuration.initialScaleIsSet) {
523             double maximumScale = this->maximumScale();
524             double maximumContentWidthInViewportCoordinate = maximumScale * m_configuration.width;
525             if (maximumContentWidthInViewportCoordinate < minimumLayoutSize.width()) {
526                 // The content zoomed to maxScale does not fit the view. Return the minimum width
527                 // satisfying the constraint maximumScale.
528                 return std::round(minimumLayoutSize.width() / maximumScale);
529             }
530             return std::round(m_configuration.width);
531         }
532
533         // If not, make sure the viewport width and initial scale can co-exist.
534         double initialContentWidthInViewportCoordinate = m_configuration.width * m_configuration.initialScaleIgnoringLayoutScaleFactor;
535         if (initialContentWidthInViewportCoordinate < minimumLayoutSize.width()) {
536             // The specified width does not fit in viewport. Return the minimum width that satisfy the initialScale constraint.
537             return std::round(minimumLayoutSize.width() / m_configuration.initialScaleIgnoringLayoutScaleFactor);
538         }
539         return std::round(m_configuration.width);
540     }
541
542     // If the page has a real scale, then just return the minimum size over the initial scale.
543     if (m_configuration.initialScaleIsSet && !m_configuration.heightIsSet)
544         return std::round(minimumLayoutSize.width() / m_configuration.initialScaleIgnoringLayoutScaleFactor);
545
546     if (minimumLayoutSize.height() > 0)
547         return std::round(minimumLayoutSize.width() * layoutHeight() / minimumLayoutSize.height());
548     return minimumLayoutSize.width();
549 }
550
551 int ViewportConfiguration::layoutHeight() const
552 {
553     ASSERT(!constraintsAreAllRelative(m_configuration));
554
555     const FloatSize& minimumLayoutSize = m_minimumLayoutSize;
556     if (m_configuration.heightIsSet) {
557         // If we scale to fit, then accept the viewport height with sanity checking.
558         if (!m_configuration.initialScaleIsSet) {
559             double maximumScale = this->maximumScale();
560             double maximumContentHeightInViewportCoordinate = maximumScale * m_configuration.height;
561             if (maximumContentHeightInViewportCoordinate < minimumLayoutSize.height()) {
562                 // The content zoomed to maxScale does not fit the view. Return the minimum height that
563                 // satisfy the constraint maximumScale.
564                 return std::round(minimumLayoutSize.height() / maximumScale);
565             }
566             return std::round(m_configuration.height);
567         }
568
569         // If not, make sure the viewport width and initial scale can co-exist.
570         double initialContentHeightInViewportCoordinate = m_configuration.height * m_configuration.initialScaleIgnoringLayoutScaleFactor;
571         if (initialContentHeightInViewportCoordinate < minimumLayoutSize.height()) {
572             // The specified width does not fit in viewport. Return the minimum height that satisfy the initialScale constraint.
573             return std::round(minimumLayoutSize.height() / m_configuration.initialScaleIgnoringLayoutScaleFactor);
574         }
575         return std::round(m_configuration.height);
576     }
577
578     // If the page has a real scale, then just return the minimum size over the initial scale.
579     if (m_configuration.initialScaleIsSet && !m_configuration.widthIsSet)
580         return std::round(minimumLayoutSize.height() / m_configuration.initialScaleIgnoringLayoutScaleFactor);
581
582     if (minimumLayoutSize.width() > 0)
583         return std::round(minimumLayoutSize.height() * layoutWidth() / minimumLayoutSize.width());
584     return minimumLayoutSize.height();
585 }
586
587 #ifndef NDEBUG
588
589 TextStream& operator<<(TextStream& ts, const ViewportConfiguration::Parameters& parameters)
590 {
591     ts.startGroup();
592     ts << "width " << parameters.width << ", set: " << (parameters.widthIsSet ? "true" : "false");
593     ts.endGroup();
594
595     ts.startGroup();
596     ts << "height " << parameters.height << ", set: " << (parameters.heightIsSet ? "true" : "false");
597     ts.endGroup();
598
599     ts.startGroup();
600     ts << "initialScale " << parameters.initialScale << ", set: " << (parameters.initialScaleIsSet ? "true" : "false");
601     ts.endGroup();
602
603     ts.dumpProperty("initialScaleIgnoringLayoutScaleFactor", parameters.initialScaleIgnoringLayoutScaleFactor);
604     ts.dumpProperty("minimumScale", parameters.minimumScale);
605     ts.dumpProperty("maximumScale", parameters.maximumScale);
606     ts.dumpProperty("allowsUserScaling", parameters.allowsUserScaling);
607     ts.dumpProperty("allowsShrinkToFit", parameters.allowsShrinkToFit);
608     ts.dumpProperty("avoidsUnsafeArea", parameters.avoidsUnsafeArea);
609
610     return ts;
611 }
612
613 TextStream& operator<<(TextStream& ts, const ViewportConfiguration& config)
614 {
615     return ts << config.description();
616 }
617
618 String ViewportConfiguration::description() const
619 {
620     TextStream ts;
621
622     ts.startGroup();
623     ts << "viewport-configuration " << (void*)this;
624     {
625         TextStream::GroupScope scope(ts);
626         ts << "viewport arguments";
627         ts << m_viewportArguments;
628     }
629     {
630         TextStream::GroupScope scope(ts);
631         ts << "configuration";
632         ts << m_configuration;
633     }
634     {
635         TextStream::GroupScope scope(ts);
636         ts << "default configuration";
637         ts << m_defaultConfiguration;
638     }
639
640     ts.dumpProperty("contentSize", m_contentSize);
641     ts.dumpProperty("minimumLayoutSize", m_minimumLayoutSize);
642     ts.dumpProperty("layoutSizeScaleFactor", m_layoutSizeScaleFactor);
643     ts.dumpProperty("computed initial scale", initialScale());
644     ts.dumpProperty("computed minimum scale", minimumScale());
645     ts.dumpProperty("computed layout size", layoutSize());
646     ts.dumpProperty("ignoring horizontal scaling constraints", shouldIgnoreHorizontalScalingConstraints() ? "true" : "false");
647     ts.dumpProperty("ignoring vertical scaling constraints", shouldIgnoreVerticalScalingConstraints() ? "true" : "false");
648     ts.dumpProperty("avoids unsafe area", avoidsUnsafeArea() ? "true" : "false");
649     ts.dumpProperty("minimum effective device width", m_minimumEffectiveDeviceWidth);
650     
651     ts.endGroup();
652
653     return ts.release();
654 }
655
656 void ViewportConfiguration::dump() const
657 {
658     WTFLogAlways("%s", description().utf8().data());
659 }
660
661 #endif
662
663 } // namespace WebCore