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