[iOS] Implement idempotent mode for text autosizing
https://bugs.webkit.org/show_bug.cgi?id=197250
<rdar://problem/50211034>

Reviewed by Jon Lee.

Source/WebCore:

Our text autosizing code has this interesting behavior where it is sensitive to the width of the text's container
and the number of lines of text inside the element. Not only is it sensitive to those things, but as those things
change, their values are stored inside the RenderObject itself and then never recomputed. This means that the text
autosizing parameters are sensitive to the entire history of an element. So, a newly created element with the same
style as an existing element can have dramatically different results.

This patch adds a new mode for text autosizing, which isn't sensitive to either of those things, and therefore
maintains the invariant that a newly created element will behave the same as an existing element with the same style.
Instead of using container size, it instead uses the viewport's initial scale. As the viewport's initial scale
changes, new layouts will be triggered, which will cause the autosizing code to use the new value.

Tests: fast/text-autosizing/ios/idempotentmode/idempotent-autosizing-identity.html
       fast/text-autosizing/ios/idempotentmode/idempotent-autosizing.html

* page/FrameViewLayoutContext.cpp:
(WebCore::FrameViewLayoutContext::applyTextSizingIfNeeded):
* page/Page.cpp:
(WebCore::Page::setInitialScale): WebKit will push the initial scale down into the page.
* page/Page.h:
(WebCore::Page::initialScale const):
* page/SettingsBase.h:
* page/cocoa/SettingsBaseCocoa.mm:
(WebCore::SettingsBase::textAutosizingUsesIdempotentMode):
(WebCore::SettingsBase::defaultTextAutosizingEnabled):
* rendering/RenderBlockFlow.cpp:
(WebCore::idempotentTextSize): Describe a piecewise-linear curve for the text size to follow. The curve scales
depending on the viewport's initial scale.
(WebCore::RenderBlockFlow::adjustComputedFontSizes):
* rendering/RenderBlockFlow.h:
* rendering/RenderElement.cpp:
(WebCore::includeNonFixedHeight): This new mode should consider max-height as well as height when determining if
content overflows.
(WebCore::RenderElement::adjustComputedFontSizesOnBlocks):
(WebCore::RenderElement::resetTextAutosizing):
* rendering/RenderElement.h:
* rendering/RenderObject.h:

Source/WebKit:

Push the initial scale down into the page.

* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::dynamicViewportSizeUpdate):
(WebKit::WebPage::viewportConfigurationChanged):

LayoutTests:

Add two simple tests that make sure that fonts get autosized > 1x when the layout viewport is wide,
and that fonts don't get autosized when the layout viewport isn't wide.

We don't want to add tons of tests to test exact values because the curve will likely be tweaked
in the future.

* fast/text-autosizing/ios/idempotentmode/idempotent-autosizing-expected.txt: Added.
* fast/text-autosizing/ios/idempotentmode/idempotent-autosizing-identity-expected.txt: Added.
* fast/text-autosizing/ios/idempotentmode/idempotent-autosizing-identity.html: Added.
* fast/text-autosizing/ios/idempotentmode/idempotent-autosizing.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@244682 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/rendering/RenderBlockFlow.cpp b/Source/WebCore/rendering/RenderBlockFlow.cpp
index 0ecf11f..766591c 100644
--- a/Source/WebCore/rendering/RenderBlockFlow.cpp
+++ b/Source/WebCore/rendering/RenderBlockFlow.cpp
@@ -3723,12 +3723,44 @@
     return std::max((1.0f / log10f(specifiedSize) * coefficient), 1.0f);
 }
 
-void RenderBlockFlow::adjustComputedFontSizes(float size, float visibleWidth)
+static inline float idempotentTextSize(float specifiedSize, float pageScale)
+{
+    // This describes a piecewise curve when the page scale is 2/3.
+    FloatPoint points[] = { {0.0f, 0.0f}, {6.0f, 12.0f}, {12.0f, 18.0f} };
+
+    // When the page scale is 1, the curve should be the identity.
+    // Linearly interpolate between the curve above and identity based on the page scale.
+    // Beware that depending on the specific values picked in the curve, this interpolation might change the shape of the curve for very small pageScales.
+    pageScale = std::min(std::max(pageScale, 0.5f), 1.0f);
+    auto scalePoint = [&](FloatPoint point) {
+        float fraction = 3.0f - 3.0f * pageScale;
+        point.setY(point.x() + (point.y() - point.x()) * fraction);
+        return point;
+    };
+
+    if (specifiedSize <= 0)
+        return 0;
+
+    float result = scalePoint(points[WTF_ARRAY_LENGTH(points) - 1]).y();
+    for (size_t i = 1; i < WTF_ARRAY_LENGTH(points); ++i) {
+        if (points[i].x() < specifiedSize)
+            continue;
+        auto leftPoint = scalePoint(points[i - 1]);
+        auto rightPoint = scalePoint(points[i]);
+        float fraction = (specifiedSize - leftPoint.x()) / (rightPoint.x() - leftPoint.x());
+        result = leftPoint.y() + fraction * (rightPoint.y() - leftPoint.y());
+        break;
+    }
+
+    return std::max(result, specifiedSize);
+}
+
+void RenderBlockFlow::adjustComputedFontSizes(float size, float visibleWidth, float pageScale, bool idempotentMode)
 {
     LOG(TextAutosizing, "RenderBlockFlow %p adjustComputedFontSizes, size=%f visibleWidth=%f, width()=%f. Bailing: %d", this, size, visibleWidth, width().toFloat(), visibleWidth >= width());
 
     // Don't do any work if the block is smaller than the visible area.
-    if (visibleWidth >= width())
+    if (!idempotentMode && visibleWidth >= width())
         return;
     
     unsigned lineCount;
@@ -3766,7 +3798,7 @@
         auto& fontDescription = oldStyle.fontDescription();
         float specifiedSize = fontDescription.specifiedSize();
         float scaledSize = roundf(specifiedSize * scale);
-        if (scaledSize > 0 && scaledSize < minFontSize) {
+        if (idempotentMode || (scaledSize > 0 && scaledSize < minFontSize)) {
             // Record the width of the block and the line count the first time we resize text and use it from then on for text resizing.
             // This makes text resizing consistent even if the block's width or line count changes (which can be caused by text resizing itself 5159915).
             if (m_lineCountForTextAutosizing == NOT_SET)
@@ -3774,8 +3806,15 @@
             if (m_widthForTextAutosizing == -1)
                 m_widthForTextAutosizing = actualWidth;
 
-            float lineTextMultiplier = lineCount == ONE_LINE ? oneLineTextMultiplier(text, specifiedSize) : textMultiplier(text, specifiedSize);
-            float candidateNewSize = roundf(std::min(minFontSize, specifiedSize * lineTextMultiplier));
+            float candidateNewSize;
+            if (idempotentMode) {
+                float lineTextSize = idempotentTextSize(specifiedSize, pageScale);
+                candidateNewSize = roundf(lineTextSize);
+            } else {
+                float lineTextMultiplier = lineCount == ONE_LINE ? oneLineTextMultiplier(text, specifiedSize) : textMultiplier(text, specifiedSize);
+                candidateNewSize = roundf(std::min(minFontSize, specifiedSize * lineTextMultiplier));
+            }
+
             if (candidateNewSize > specifiedSize && candidateNewSize != fontDescription.computedSize() && text.textNode() && oldStyle.textSizeAdjust().isAuto())
                 document().textAutoSizing().addTextNode(*text.textNode(), candidateNewSize);
         }