Text Autosizing: Multiply large fonts less, as they are already more legible.
[WebKit-https.git] / Source / WebCore / rendering / TextAutosizer.cpp
index 0824aa3..4cd735a 100644 (file)
 #include "Document.h"
 #include "InspectorInstrumentation.h"
 #include "RenderObject.h"
 #include "Document.h"
 #include "InspectorInstrumentation.h"
 #include "RenderObject.h"
+#include "RenderStyle.h"
 #include "RenderText.h"
 #include "RenderView.h"
 #include "Settings.h"
 #include "RenderText.h"
 #include "RenderView.h"
 #include "Settings.h"
+#include "StyleInheritedData.h"
 
 namespace WebCore {
 
 
 namespace WebCore {
 
@@ -49,73 +51,112 @@ bool TextAutosizer::processSubtree(RenderObject* layoutRoot)
     if (!m_document->settings() || !m_document->settings()->textAutosizingEnabled() || layoutRoot->view()->printing() || !m_document->page())
         return false;
 
     if (!m_document->settings() || !m_document->settings()->textAutosizingEnabled() || layoutRoot->view()->printing() || !m_document->page())
         return false;
 
+    Frame* mainFrame = m_document->page()->mainFrame();
+
+    // Window area, in logical (density-independent) pixels.
     IntSize windowSize = m_document->settings()->textAutosizingWindowSizeOverride();
     if (windowSize.isEmpty()) {
     IntSize windowSize = m_document->settings()->textAutosizingWindowSizeOverride();
     if (windowSize.isEmpty()) {
-        Frame* mainFrame = m_document->page()->mainFrame();
         bool includeScrollbars = !InspectorInstrumentation::shouldApplyScreenWidthOverride(mainFrame);
         windowSize = mainFrame->view()->visibleContentRect(includeScrollbars).size(); // FIXME: Check that this is always in logical (density-independent) pixels (see wkbug.com/87440).
     }
 
         bool includeScrollbars = !InspectorInstrumentation::shouldApplyScreenWidthOverride(mainFrame);
         windowSize = mainFrame->view()->visibleContentRect(includeScrollbars).size(); // FIXME: Check that this is always in logical (density-independent) pixels (see wkbug.com/87440).
     }
 
-    for (RenderObject* descendant = traverseNext(layoutRoot, layoutRoot); descendant; descendant = traverseNext(descendant, layoutRoot)) {
-        if (!treatAsInline(descendant))
-            processBlock(toRenderBlock(descendant), windowSize);
+    // Largest area of block that can be visible at once (assuming the main
+    // frame doesn't get scaled to less than overview scale), in CSS pixels.
+    IntSize minLayoutSize = mainFrame->view()->layoutSize();
+    for (Frame* frame = m_document->frame(); frame; frame = frame->tree()->parent()) {
+        if (!frame->view()->isInChildFrameWithFrameFlattening())
+            minLayoutSize = minLayoutSize.shrunkTo(frame->view()->layoutSize());
+    }
+
+    for (RenderObject* descendant = layoutRoot->nextInPreOrder(layoutRoot); descendant; descendant = descendant->nextInPreOrder(layoutRoot)) {
+        if (isNotAnAutosizingContainer(descendant))
+            continue;
+        processBox(toRenderBox(descendant), windowSize, minLayoutSize);
     }
 
     return true;
 }
 
     }
 
     return true;
 }
 
-void TextAutosizer::processBlock(RenderBlock* block, const IntSize& windowSize)
+void TextAutosizer::processBox(RenderBox* box, const IntSize& windowSize, const IntSize& layoutSize)
 {
 {
-    int windowLogicalWidth = block->isHorizontalWritingMode() ? windowSize.width() : windowSize.height();
-    float multiplier = static_cast<float>(block->logicalWidth()) / windowLogicalWidth; // FIXME: This is overly simplistic.
+    int logicalWindowWidth = box->isHorizontalWritingMode() ? windowSize.width() : windowSize.height();
+    int logicalLayoutWidth = box->isHorizontalWritingMode() ? layoutSize.width() : layoutSize.height();
+    // Ignore box width in excess of the layout width, to avoid extreme multipliers.
+    float logicalBoxWidth = std::min<float>(box->logicalWidth(), logicalLayoutWidth);
+
+    float multiplier = logicalBoxWidth / logicalWindowWidth;
+    multiplier *= m_document->settings()->textAutosizingFontScaleFactor();
+
     if (multiplier < 1)
         return;
     if (multiplier < 1)
         return;
-    for (RenderObject* descendant = traverseNext(block, block, treatAsInline); descendant; descendant = traverseNext(descendant, block, treatAsInline)) {
-        if (descendant->isText())
-            processText(toRenderText(descendant), multiplier);
+    RenderObject* descendant = nextInPreOrderMatchingFilter(box, box, isNotAnAutosizingContainer);
+    while (descendant) {
+        if (descendant->isText()) {
+            setMultiplier(descendant, multiplier);
+            setMultiplier(descendant->parent(), multiplier); // Parent does line spacing.
+            // FIXME: Increase list marker size proportionately.
+        }
+        descendant = nextInPreOrderMatchingFilter(descendant, box, isNotAnAutosizingContainer);
     }
 }
 
     }
 }
 
-void TextAutosizer::processText(RenderText* text, float multiplier)
+void TextAutosizer::setMultiplier(RenderObject* renderer, float multiplier)
 {
 {
-    float specifiedSize = text->style()->fontDescription().specifiedSize();
-    float newSize = specifiedSize * multiplier; // FIXME: This is overly simplistic.
-
-    RefPtr<RenderStyle> style = RenderStyle::clone(text->style());
-    FontDescription fontDescription(style->fontDescription());
-    fontDescription.setComputedSize(newSize);
-    style->setFontDescription(fontDescription);
-    style->font().update(style->font().fontSelector());
-    text->setStyle(style.release());
-
-    // FIXME: Increase computed line height proportionately.
-    // FIXME: Increase list marker size proportionately.
+    RefPtr<RenderStyle> newStyle = RenderStyle::clone(renderer->style());
+    newStyle->setTextAutosizingMultiplier(multiplier);
+    renderer->setStyle(newStyle.release());
 }
 
 }
 
-bool TextAutosizer::treatAsInline(const RenderObject* renderer)
+float TextAutosizer::computeAutosizedFontSize(float specifiedSize, float multiplier)
 {
 {
-    return !renderer->isRenderBlock() || renderer->isListItem() || renderer->isInlineBlockOrInlineTable();
+    // Somewhat arbitrary "pleasant" font size.
+    const float pleasantSize = 16;
+
+    // Multiply fonts that the page author has specified to be larger than
+    // pleasantSize by less and less, until huge fonts are not increased at all.
+    // For specifiedSize between 0 and pleasantSize we directly apply the
+    // multiplier; hence for specifiedSize == pleasantSize, computedSize will be
+    // multiplier * pleasantSize. For greater specifiedSizes we want to
+    // gradually fade out the multiplier, so for every 1px increase in
+    // specifiedSize beyond pleasantSize we will only increase computedSize
+    // by gradientAfterPleasantSize px until we meet the
+    // computedSize = specifiedSize line, after which we stay on that line (so
+    // then every 1px increase in specifiedSize increases computedSize by 1px).
+    const float gradientAfterPleasantSize = 0.5;
+
+    float computedSize;
+    if (specifiedSize <= pleasantSize)
+        computedSize = multiplier * specifiedSize;
+    else {
+        computedSize = multiplier * pleasantSize + gradientAfterPleasantSize * (specifiedSize - pleasantSize);
+        if (computedSize < specifiedSize)
+            computedSize = specifiedSize;
+    }
+    return computedSize;
 }
 
 }
 
-// FIXME: Consider making this a method on RenderObject if it remains this generic.
-RenderObject* TextAutosizer::traverseNext(RenderObject* current, const RenderObject* stayWithin, RenderObjectFilter filter)
+bool TextAutosizer::isNotAnAutosizingContainer(const RenderObject* renderer)
 {
 {
-    for (RenderObject* child = current->firstChild(); child; child = child->nextSibling()) {
-        if (!filter || filter(child)) {
-            ASSERT(!stayWithin || child->isDescendantOf(stayWithin));
+    // "Autosizing containers" are the smallest unit for which we can enable/disable
+    // Text Autosizing. A uniform text size multiplier is enforced within them.
+    // - Must be RenderBoxes since they need a well-defined logicalWidth().
+    // - Must not be inline, as different multipliers on one line looks terrible.
+    // - Must not be list items, as items in the same list should look consistent.
+    return !renderer->isBox() || renderer->isInline() || renderer->isListItem();
+}
+
+RenderObject* TextAutosizer::nextInPreOrderMatchingFilter(RenderObject* current, const RenderObject* stayWithin, RenderObjectFilterFunctor filter)
+{
+    for (RenderObject* child = current->firstChild(); child; child = child->nextSibling())
+        if (filter(child))
             return child;
             return child;
-        }
-    }
 
 
-    for (RenderObject* ancestor = current; ancestor; ancestor = ancestor->parent()) {
+    for (const RenderObject* ancestor = current; ancestor; ancestor = ancestor->parent()) {
         if (ancestor == stayWithin)
             return 0;
         if (ancestor == stayWithin)
             return 0;
-        for (RenderObject* sibling = ancestor->nextSibling(); sibling; sibling = sibling->nextSibling()) {
-            if (!filter || filter(sibling)) {
-                ASSERT(!stayWithin || sibling->isDescendantOf(stayWithin));
+        for (RenderObject* sibling = ancestor->nextSibling(); sibling; sibling = sibling->nextSibling())
+            if (filter(sibling))
                 return sibling;
                 return sibling;
-            }
-        }
     }
 
     return 0;
     }
 
     return 0;