Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 2012 Google Inc. All rights reserved.
      3  * Copyright (C) 2012 Apple Inc. All rights reserved.
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  */
     20 
     21 #include "config.h"
     22 #include "core/rendering/TextAutosizer.h"
     23 
     24 #include <algorithm>
     25 
     26 #include "core/dom/Document.h"
     27 #include "core/html/HTMLElement.h"
     28 #include "core/inspector/InspectorInstrumentation.h"
     29 #include "core/frame/Settings.h"
     30 #include "core/rendering/RenderListItem.h"
     31 #include "core/rendering/RenderObject.h"
     32 #include "core/rendering/RenderText.h"
     33 #include "core/rendering/RenderView.h"
     34 #include "core/rendering/style/RenderStyle.h"
     35 #include "core/rendering/style/StyleInheritedData.h"
     36 #include "platform/TraceEvent.h"
     37 #include "platform/geometry/IntSize.h"
     38 #include "wtf/StdLibExtras.h"
     39 #include "wtf/Vector.h"
     40 
     41 namespace WebCore {
     42 
     43 using namespace HTMLNames;
     44 
     45 struct TextAutosizingWindowInfo {
     46     IntSize windowSize;
     47     IntSize minLayoutSize;
     48 };
     49 
     50 // Represents cluster related data. Instances should not persist between calls to processSubtree.
     51 struct TextAutosizingClusterInfo {
     52     explicit TextAutosizingClusterInfo(RenderBlock* root)
     53         : root(root)
     54         , blockContainingAllText(0)
     55         , maxAllowedDifferenceFromTextWidth(150)
     56     {
     57     }
     58 
     59     RenderBlock* root;
     60     const RenderBlock* blockContainingAllText;
     61 
     62     // Upper limit on the difference between the width of the cluster's block containing all
     63     // text and that of a narrow child before the child becomes a separate cluster.
     64     float maxAllowedDifferenceFromTextWidth;
     65 
     66     // Descendants of the cluster that are narrower than the block containing all text and must be
     67     // processed together.
     68     Vector<TextAutosizingClusterInfo> narrowDescendants;
     69 };
     70 
     71 #ifdef AUTOSIZING_DOM_DEBUG_INFO
     72 static void writeDebugInfo(RenderObject* renderObject, const AtomicString& output)
     73 {
     74     Node* node = renderObject->node();
     75     if (node && node->isElementNode())
     76         toElement(node)->setAttribute("data-autosizing", output, ASSERT_NO_EXCEPTION);
     77 }
     78 #endif
     79 
     80 static const Vector<QualifiedName>& formInputTags()
     81 {
     82     // Returns the tags for the form input elements.
     83     DEFINE_STATIC_LOCAL(Vector<QualifiedName>, formInputTags, ());
     84     if (formInputTags.isEmpty()) {
     85         formInputTags.append(inputTag);
     86         formInputTags.append(buttonTag);
     87         formInputTags.append(selectTag);
     88     }
     89     return formInputTags;
     90 }
     91 
     92 static RenderListItem* getAncestorListItem(const RenderObject* renderer)
     93 {
     94     RenderObject* ancestor = renderer->parent();
     95     while (ancestor && (ancestor->isRenderInline() || ancestor->isAnonymousBlock()))
     96         ancestor = ancestor->parent();
     97 
     98     return (ancestor && ancestor->isListItem()) ? toRenderListItem(ancestor) : 0;
     99 }
    100 
    101 static RenderObject* getAncestorList(const RenderObject* renderer)
    102 {
    103     // FIXME: Add support for <menu> elements as a possible ancestor of an <li> element,
    104     // see http://www.whatwg.org/specs/web-apps/current-work/multipage/grouping-content.html#the-li-element
    105     for (RenderObject* ancestor = renderer->parent(); ancestor; ancestor = ancestor->parent()) {
    106         Node* parentNode = ancestor->generatingNode();
    107         if (parentNode && (parentNode->hasTagName(olTag) || parentNode->hasTagName(ulTag)))
    108             return ancestor;
    109     }
    110     return 0;
    111 }
    112 
    113 TextAutosizer::TextAutosizer(Document* document)
    114     : m_document(document)
    115 {
    116 }
    117 
    118 void TextAutosizer::recalculateMultipliers()
    119 {
    120     RenderObject* renderer = m_document->renderer();
    121     while (renderer) {
    122         if (renderer->style() && renderer->style()->textAutosizingMultiplier() != 1)
    123             setMultiplier(renderer, 1);
    124         renderer = renderer->nextInPreOrder();
    125     }
    126 }
    127 
    128 bool TextAutosizer::processSubtree(RenderObject* layoutRoot)
    129 {
    130     TRACE_EVENT0("webkit", "TextAutosizer::processSubtree");
    131 
    132     if (!m_document->settings() || !m_document->settings()->textAutosizingEnabled() || layoutRoot->view()->document().printing() || !m_document->page())
    133         return false;
    134 
    135     Frame* mainFrame = m_document->page()->mainFrame();
    136 
    137     TextAutosizingWindowInfo windowInfo;
    138 
    139     // Window area, in logical (density-independent) pixels.
    140     windowInfo.windowSize = m_document->settings()->textAutosizingWindowSizeOverride();
    141     if (windowInfo.windowSize.isEmpty())
    142         windowInfo.windowSize = mainFrame->view()->unscaledVisibleContentSize(ScrollableArea::IncludeScrollbars);
    143 
    144     // Largest area of block that can be visible at once (assuming the main
    145     // frame doesn't get scaled to less than overview scale), in CSS pixels.
    146     windowInfo.minLayoutSize = mainFrame->view()->layoutSize();
    147     for (Frame* frame = m_document->frame(); frame; frame = frame->tree().parent())
    148         windowInfo.minLayoutSize = windowInfo.minLayoutSize.shrunkTo(frame->view()->layoutSize());
    149 
    150     // The layoutRoot could be neither a container nor a cluster, so walk up the tree till we find each of these.
    151     RenderBlock* container = layoutRoot->isRenderBlock() ? toRenderBlock(layoutRoot) : layoutRoot->containingBlock();
    152     while (container && !isAutosizingContainer(container))
    153         container = container->containingBlock();
    154 
    155     RenderBlock* cluster = container;
    156     while (cluster && (!isAutosizingContainer(cluster) || !isIndependentDescendant(cluster)))
    157         cluster = cluster->containingBlock();
    158 
    159     // Skip autosizing for orphaned trees, or if it will have no effect.
    160     // Note: this might suppress autosizing of an inner cluster with a different writing mode.
    161     // It's not clear what the correct behavior is for mixed writing modes anyway.
    162     if (!cluster || clusterMultiplier(cluster->style()->writingMode(), windowInfo,
    163         std::numeric_limits<float>::infinity()) == 1.0f)
    164         return false;
    165 
    166     TextAutosizingClusterInfo clusterInfo(cluster);
    167     processCluster(clusterInfo, container, layoutRoot, windowInfo);
    168     InspectorInstrumentation::didAutosizeText(layoutRoot);
    169     return true;
    170 }
    171 
    172 float TextAutosizer::clusterMultiplier(WritingMode writingMode, const TextAutosizingWindowInfo& windowInfo, float textWidth) const
    173 {
    174     int logicalWindowWidth = isHorizontalWritingMode(writingMode) ? windowInfo.windowSize.width() : windowInfo.windowSize.height();
    175     int logicalLayoutWidth = isHorizontalWritingMode(writingMode) ? windowInfo.minLayoutSize.width() : windowInfo.minLayoutSize.height();
    176     // Ignore box width in excess of the layout width, to avoid extreme multipliers.
    177     float logicalClusterWidth = std::min<float>(textWidth, logicalLayoutWidth);
    178 
    179     float multiplier = logicalClusterWidth / logicalWindowWidth;
    180     multiplier *= m_document->settings()->accessibilityFontScaleFactor();
    181 
    182     // If the page has a meta viewport or @viewport, don't apply the device scale adjustment.
    183     const ViewportDescription& viewportDescription = m_document->page()->mainFrame()->document()->viewportDescription();
    184     if (!viewportDescription.isSpecifiedByAuthor()) {
    185         multiplier *= m_document->settings()->deviceScaleAdjustment();
    186     }
    187     return std::max(1.0f, multiplier);
    188 }
    189 
    190 void TextAutosizer::processClusterInternal(TextAutosizingClusterInfo& clusterInfo, RenderBlock* container, RenderObject* subtreeRoot, const TextAutosizingWindowInfo& windowInfo, float multiplier)
    191 {
    192     processContainer(multiplier, container, clusterInfo, subtreeRoot, windowInfo);
    193 #ifdef AUTOSIZING_DOM_DEBUG_INFO
    194     writeDebugInfo(clusterInfo.root, String::format("cluster:%f", multiplier));
    195 #endif
    196 
    197     Vector<Vector<TextAutosizingClusterInfo> > narrowDescendantsGroups;
    198     getNarrowDescendantsGroupedByWidth(clusterInfo, narrowDescendantsGroups);
    199     for (size_t i = 0; i < narrowDescendantsGroups.size(); ++i)
    200         processCompositeCluster(narrowDescendantsGroups[i], windowInfo);
    201 }
    202 
    203 void TextAutosizer::processCluster(TextAutosizingClusterInfo& clusterInfo, RenderBlock* container, RenderObject* subtreeRoot, const TextAutosizingWindowInfo& windowInfo)
    204 {
    205     // Many pages set a max-width on their content. So especially for the RenderView, instead of
    206     // just taking the width of |cluster| we find the lowest common ancestor of the first and last
    207     // descendant text node of the cluster (i.e. the deepest wrapper block that contains all the
    208     // text), and use its width instead.
    209     clusterInfo.blockContainingAllText = findDeepestBlockContainingAllText(clusterInfo.root);
    210     float textWidth = clusterInfo.blockContainingAllText->contentLogicalWidth();
    211     float multiplier =  1.0;
    212     if (clusterShouldBeAutosized(clusterInfo, textWidth))
    213         multiplier = clusterMultiplier(clusterInfo.root->style()->writingMode(), windowInfo, textWidth);
    214     processClusterInternal(clusterInfo, container, subtreeRoot, windowInfo, multiplier);
    215 }
    216 
    217 void TextAutosizer::processCompositeCluster(Vector<TextAutosizingClusterInfo>& clusterInfos, const TextAutosizingWindowInfo& windowInfo)
    218 {
    219     if (clusterInfos.isEmpty())
    220         return;
    221 
    222     float maxTextWidth = 0;
    223     for (size_t i = 0; i < clusterInfos.size(); ++i) {
    224         TextAutosizingClusterInfo& clusterInfo = clusterInfos[i];
    225         clusterInfo.blockContainingAllText = findDeepestBlockContainingAllText(clusterInfo.root);
    226         maxTextWidth = max<float>(maxTextWidth, clusterInfo.blockContainingAllText->contentLogicalWidth());
    227     }
    228 
    229     float multiplier = 1.0;
    230     if (compositeClusterShouldBeAutosized(clusterInfos, maxTextWidth))
    231         multiplier = clusterMultiplier(clusterInfos[0].root->style()->writingMode(), windowInfo, maxTextWidth);
    232     for (size_t i = 0; i < clusterInfos.size(); ++i) {
    233         ASSERT(clusterInfos[i].root->style()->writingMode() == clusterInfos[0].root->style()->writingMode());
    234         processClusterInternal(clusterInfos[i], clusterInfos[i].root, clusterInfos[i].root, windowInfo, multiplier);
    235     }
    236 }
    237 
    238 void TextAutosizer::processContainer(float multiplier, RenderBlock* container, TextAutosizingClusterInfo& clusterInfo, RenderObject* subtreeRoot, const TextAutosizingWindowInfo& windowInfo)
    239 {
    240     ASSERT(isAutosizingContainer(container));
    241 #ifdef AUTOSIZING_DOM_DEBUG_INFO
    242     writeDebugInfo(container, "container");
    243 #endif
    244 
    245     float localMultiplier = containerShouldBeAutosized(container) ? multiplier: 1;
    246 
    247     RenderObject* descendant = nextInPreOrderSkippingDescendantsOfContainers(subtreeRoot, subtreeRoot);
    248     while (descendant) {
    249         if (descendant->isText()) {
    250             if (localMultiplier != 1 && descendant->style()->textAutosizingMultiplier() == 1) {
    251                 setMultiplier(descendant, localMultiplier);
    252                 setMultiplier(descendant->parent(), localMultiplier); // Parent does line spacing.
    253 
    254                 if (RenderListItem* listItemAncestor = getAncestorListItem(descendant)) {
    255                     if (RenderObject* list = getAncestorList(listItemAncestor)) {
    256                         if (list->style()->textAutosizingMultiplier() == 1)
    257                             setMultiplierForList(list, localMultiplier);
    258                     }
    259                 }
    260             }
    261         } else if (isAutosizingContainer(descendant)) {
    262             RenderBlock* descendantBlock = toRenderBlock(descendant);
    263             TextAutosizingClusterInfo descendantClusterInfo(descendantBlock);
    264             if (isWiderDescendant(descendantBlock, clusterInfo) || isIndependentDescendant(descendantBlock))
    265                 processCluster(descendantClusterInfo, descendantBlock, descendantBlock, windowInfo);
    266             else if (isNarrowDescendant(descendantBlock, clusterInfo)) {
    267                 // Narrow descendants are processed together later to be able to apply the same multiplier
    268                 // to each of them if necessary.
    269                 clusterInfo.narrowDescendants.append(descendantClusterInfo);
    270             } else
    271                 processContainer(multiplier, descendantBlock, clusterInfo, descendantBlock, windowInfo);
    272         }
    273         descendant = nextInPreOrderSkippingDescendantsOfContainers(descendant, subtreeRoot);
    274     }
    275 }
    276 
    277 void TextAutosizer::setMultiplier(RenderObject* renderer, float multiplier)
    278 {
    279     RefPtr<RenderStyle> newStyle = RenderStyle::clone(renderer->style());
    280     newStyle->setTextAutosizingMultiplier(multiplier);
    281     newStyle->setUnique();
    282     renderer->setStyle(newStyle.release());
    283 }
    284 
    285 void TextAutosizer::setMultiplierForList(RenderObject* renderer, float multiplier)
    286 {
    287 #ifndef NDEBUG
    288     Node* parentNode = renderer->generatingNode();
    289     ASSERT(parentNode);
    290     ASSERT(parentNode->hasTagName(olTag) || parentNode->hasTagName(ulTag));
    291 #endif
    292     setMultiplier(renderer, multiplier);
    293 
    294     // Make sure all list items are autosized consistently.
    295     for (RenderObject* child = renderer->firstChild(); child; child = child->nextSibling()) {
    296         if (child->isListItem() && child->style()->textAutosizingMultiplier() == 1)
    297             setMultiplier(child, multiplier);
    298     }
    299 }
    300 
    301 float TextAutosizer::computeAutosizedFontSize(float specifiedSize, float multiplier)
    302 {
    303     // Somewhat arbitrary "pleasant" font size.
    304     const float pleasantSize = 16;
    305 
    306     // Multiply fonts that the page author has specified to be larger than
    307     // pleasantSize by less and less, until huge fonts are not increased at all.
    308     // For specifiedSize between 0 and pleasantSize we directly apply the
    309     // multiplier; hence for specifiedSize == pleasantSize, computedSize will be
    310     // multiplier * pleasantSize. For greater specifiedSizes we want to
    311     // gradually fade out the multiplier, so for every 1px increase in
    312     // specifiedSize beyond pleasantSize we will only increase computedSize
    313     // by gradientAfterPleasantSize px until we meet the
    314     // computedSize = specifiedSize line, after which we stay on that line (so
    315     // then every 1px increase in specifiedSize increases computedSize by 1px).
    316     const float gradientAfterPleasantSize = 0.5;
    317 
    318     float computedSize;
    319     if (specifiedSize <= pleasantSize)
    320         computedSize = multiplier * specifiedSize;
    321     else {
    322         computedSize = multiplier * pleasantSize + gradientAfterPleasantSize * (specifiedSize - pleasantSize);
    323         if (computedSize < specifiedSize)
    324             computedSize = specifiedSize;
    325     }
    326     return computedSize;
    327 }
    328 
    329 bool TextAutosizer::isAutosizingContainer(const RenderObject* renderer)
    330 {
    331     // "Autosizing containers" are the smallest unit for which we can
    332     // enable/disable Text Autosizing.
    333     // - Must not be inline, as different multipliers on one line looks terrible.
    334     //   Exceptions are inline-block and alike elements (inline-table, -webkit-inline-*),
    335     //   as they often contain entire multi-line columns of text.
    336     // - Must not be list items, as items in the same list should look consistent (*).
    337     // - Must not be normal list items, as items in the same list should look
    338     //   consistent, unless they are floating or position:absolute/fixed.
    339     Node* node = renderer->generatingNode();
    340     if ((node && !node->hasChildNodes())
    341         || !renderer->isRenderBlock()
    342         || (renderer->isInline() && !renderer->style()->isDisplayReplacedType()))
    343         return false;
    344     if (renderer->isListItem())
    345         return renderer->isFloating() || renderer->isOutOfFlowPositioned();
    346     // Avoid creating containers for text within text controls, buttons, or <select> buttons.
    347     Node* parentNode = renderer->parent() ? renderer->parent()->generatingNode() : 0;
    348     if (parentNode && parentNode->isElementNode() && formInputTags().contains(toElement(parentNode)->tagQName()))
    349         return false;
    350 
    351     return true;
    352 }
    353 
    354 bool TextAutosizer::isNarrowDescendant(const RenderBlock* renderer, TextAutosizingClusterInfo& parentClusterInfo)
    355 {
    356     ASSERT(isAutosizingContainer(renderer));
    357 
    358     // Autosizing containers that are significantly narrower than the |blockContainingAllText| of
    359     // their enclosing cluster may be acting as separate columns, hence must be autosized
    360     // separately. For example the 2nd div in:
    361     // <body>
    362     //     <div style="float: right; width: 50%"></div>
    363     //     <div style="width: 50%"></div>
    364     // <body>
    365     // is the left column, and should be autosized differently from the body.
    366     // If however the container is only narrower by 150px or less, it's considered part of
    367     // the enclosing cluster. This 150px limit is adjusted whenever a descendant container is
    368     // less than 50px narrower than the current limit.
    369     const float differenceFromMaxWidthDifference = 50;
    370     float contentWidth = renderer->contentLogicalWidth();
    371     float clusterTextWidth = parentClusterInfo.blockContainingAllText->contentLogicalWidth();
    372     float widthDifference = clusterTextWidth - contentWidth;
    373 
    374     if (widthDifference - parentClusterInfo.maxAllowedDifferenceFromTextWidth > differenceFromMaxWidthDifference)
    375         return true;
    376 
    377     parentClusterInfo.maxAllowedDifferenceFromTextWidth = std::max(widthDifference, parentClusterInfo.maxAllowedDifferenceFromTextWidth);
    378     return false;
    379 }
    380 
    381 bool TextAutosizer::isWiderDescendant(const RenderBlock* renderer, const TextAutosizingClusterInfo& parentClusterInfo)
    382 {
    383     ASSERT(isAutosizingContainer(renderer));
    384 
    385     // Autosizing containers that are wider than the |blockContainingAllText| of their enclosing
    386     // cluster are treated the same way as autosizing clusters to be autosized separately.
    387     float contentWidth = renderer->contentLogicalWidth();
    388     float clusterTextWidth = parentClusterInfo.blockContainingAllText->contentLogicalWidth();
    389     return contentWidth > clusterTextWidth;
    390 }
    391 
    392 bool TextAutosizer::isIndependentDescendant(const RenderBlock* renderer)
    393 {
    394     ASSERT(isAutosizingContainer(renderer));
    395 
    396     // "Autosizing clusters" are special autosizing containers within which we
    397     // want to enforce a uniform text size multiplier, in the hopes of making
    398     // the major sections of the page look internally consistent.
    399     // All their descendants (including other autosizing containers) must share
    400     // the same multiplier, except for subtrees which are themselves clusters,
    401     // and some of their descendant containers might not be autosized at all
    402     // (for example if their height is constrained).
    403     // Additionally, clusterShouldBeAutosized requires each cluster to contain a
    404     // minimum amount of text, without which it won't be autosized.
    405     //
    406     // Clusters are chosen using very similar criteria to CSS flow roots, aka
    407     // block formatting contexts (http://w3.org/TR/css3-box/#flow-root), since
    408     // flow roots correspond to box containers that behave somewhat
    409     // independently from their parent (for example they don't overlap floats).
    410     // The definition of a flow root also conveniently includes most of the
    411     // ways that a box and its children can have significantly different width
    412     // from the box's parent (we want to avoid having significantly different
    413     // width blocks within a cluster, since the narrower blocks would end up
    414     // larger than would otherwise be necessary).
    415     return renderer->isRenderView()
    416         || renderer->isFloating()
    417         || renderer->isOutOfFlowPositioned()
    418         || renderer->isTableCell()
    419         || renderer->isTableCaption()
    420         || renderer->isFlexibleBoxIncludingDeprecated()
    421         || renderer->hasColumns()
    422         || renderer->containingBlock()->isHorizontalWritingMode() != renderer->isHorizontalWritingMode()
    423         || renderer->style()->isDisplayReplacedType()
    424         || renderer->isTextArea()
    425         || renderer->style()->userModify() != READ_ONLY;
    426     // FIXME: Tables need special handling to multiply all their columns by
    427     // the same amount even if they're different widths; so do hasColumns()
    428     // containers, and probably flexboxes...
    429 }
    430 
    431 bool TextAutosizer::isAutosizingCluster(const RenderBlock* renderer, TextAutosizingClusterInfo& parentClusterInfo)
    432 {
    433     ASSERT(isAutosizingContainer(renderer));
    434 
    435     return isNarrowDescendant(renderer, parentClusterInfo)
    436         || isWiderDescendant(renderer, parentClusterInfo)
    437         || isIndependentDescendant(renderer);
    438 }
    439 
    440 bool TextAutosizer::containerShouldBeAutosized(const RenderBlock* container)
    441 {
    442     if (containerContainsOneOfTags(container, formInputTags()))
    443         return false;
    444 
    445     if (containerIsRowOfLinks(container))
    446         return false;
    447 
    448     // Don't autosize block-level text that can't wrap (as it's likely to
    449     // expand sideways and break the page's layout).
    450     if (!container->style()->autoWrap())
    451         return false;
    452 
    453     return !contentHeightIsConstrained(container);
    454 }
    455 
    456 bool TextAutosizer::containerContainsOneOfTags(const RenderBlock* container, const Vector<QualifiedName>& tags)
    457 {
    458     const RenderObject* renderer = container;
    459     while (renderer) {
    460         const Node* rendererNode = renderer->node();
    461         if (rendererNode && rendererNode->isElementNode()) {
    462             if (tags.contains(toElement(rendererNode)->tagQName()))
    463                 return true;
    464         }
    465         renderer = nextInPreOrderSkippingDescendantsOfContainers(renderer, container);
    466     }
    467 
    468     return false;
    469 }
    470 
    471 bool TextAutosizer::containerIsRowOfLinks(const RenderObject* container)
    472 {
    473     // A "row of links" is a container for which holds:
    474     //  1. it should not contain non-link text elements longer than 3 characters
    475     //  2. it should contain min. 3 inline links and all links should
    476     //     have the same specified font size
    477     //  3. it should not contain <br> elements
    478     //  4. it should contain only inline elements unless they are containers,
    479     //     children of link elements or children of sub-containers.
    480     int linkCount = 0;
    481     RenderObject* renderer = container->nextInPreOrder(container);
    482     float matchingFontSize = -1;
    483 
    484     while (renderer) {
    485         if (!isAutosizingContainer(renderer)) {
    486             if (renderer->isText() && toRenderText(renderer)->text().impl()->stripWhiteSpace()->length() > 3)
    487                 return false;
    488             if (!renderer->isInline())
    489                 return false;
    490             if (renderer->isBR())
    491                 return false;
    492         }
    493         if (renderer->style()->isLink()) {
    494             if (matchingFontSize < 0)
    495                 matchingFontSize = renderer->style()->specifiedFontSize();
    496             else {
    497                 if (matchingFontSize != renderer->style()->specifiedFontSize())
    498                     return false;
    499             }
    500 
    501             linkCount++;
    502             // Skip traversing descendants of the link.
    503             renderer = renderer->nextInPreOrderAfterChildren(container);
    504         } else
    505             renderer = nextInPreOrderSkippingDescendantsOfContainers(renderer, container);
    506     }
    507 
    508     return (linkCount >= 3);
    509 }
    510 
    511 bool TextAutosizer::contentHeightIsConstrained(const RenderBlock* container)
    512 {
    513     // FIXME: Propagate constrainedness down the tree, to avoid inefficiently walking back up from each box.
    514     // FIXME: This code needs to take into account vertical writing modes.
    515     // FIXME: Consider additional heuristics, such as ignoring fixed heights if the content is already overflowing before autosizing kicks in.
    516     for (; container; container = container->containingBlock()) {
    517         RenderStyle* style = container->style();
    518         if (style->overflowY() >= OSCROLL)
    519             return false;
    520         if (style->height().isSpecified() || style->maxHeight().isSpecified() || container->isOutOfFlowPositioned()) {
    521             // Some sites (e.g. wikipedia) set their html and/or body elements to height:100%,
    522             // without intending to constrain the height of the content within them.
    523             return !container->isRoot() && !container->isBody();
    524         }
    525         if (container->isFloating())
    526             return false;
    527     }
    528     return false;
    529 }
    530 
    531 bool TextAutosizer::clusterShouldBeAutosized(TextAutosizingClusterInfo& clusterInfo, float blockWidth)
    532 {
    533     Vector<TextAutosizingClusterInfo> clusterInfos(1, clusterInfo);
    534     return compositeClusterShouldBeAutosized(clusterInfos, blockWidth);
    535 }
    536 
    537 bool TextAutosizer::compositeClusterShouldBeAutosized(Vector<TextAutosizingClusterInfo>& clusterInfos, float blockWidth)
    538 {
    539     // Don't autosize clusters that contain less than 4 lines of text (in
    540     // practice less lines are required, since measureDescendantTextWidth
    541     // assumes that characters are 1em wide, but most characters are narrower
    542     // than that, so we're overestimating their contribution to the linecount).
    543     //
    544     // This is to reduce the likelihood of autosizing things like headers and
    545     // footers, which can be quite visually distracting. The rationale is that
    546     // if a cluster contains very few lines of text then it's ok to have to zoom
    547     // in and pan from side to side to read each line, since if there are very
    548     // few lines of text you'll only need to pan across once or twice.
    549     //
    550     // An exception to the 4 lines of text are the textarea and contenteditable
    551     // clusters, which are always autosized by default (i.e. threated as if they
    552     // contain more than 4 lines of text). This is to ensure that the text does
    553     // not suddenly get autosized when the user enters more than 4 lines of text.
    554     float totalTextWidth = 0;
    555     const float minLinesOfText = 4;
    556     float minTextWidth = blockWidth * minLinesOfText;
    557     for (size_t i = 0; i < clusterInfos.size(); ++i) {
    558         if (clusterInfos[i].root->isTextArea() || (clusterInfos[i].root->style() && clusterInfos[i].root->style()->userModify() != READ_ONLY))
    559             return true;
    560         measureDescendantTextWidth(clusterInfos[i].blockContainingAllText, clusterInfos[i], minTextWidth, totalTextWidth);
    561         if (totalTextWidth >= minTextWidth)
    562             return true;
    563     }
    564     return false;
    565 }
    566 
    567 void TextAutosizer::measureDescendantTextWidth(const RenderBlock* container, TextAutosizingClusterInfo& clusterInfo, float minTextWidth, float& textWidth)
    568 {
    569     bool skipLocalText = !containerShouldBeAutosized(container);
    570 
    571     RenderObject* descendant = nextInPreOrderSkippingDescendantsOfContainers(container, container);
    572     while (descendant) {
    573         if (!skipLocalText && descendant->isText()) {
    574             textWidth += toRenderText(descendant)->renderedTextLength() * descendant->style()->specifiedFontSize();
    575         } else if (isAutosizingContainer(descendant)) {
    576             RenderBlock* descendantBlock = toRenderBlock(descendant);
    577             if (!isAutosizingCluster(descendantBlock, clusterInfo))
    578                 measureDescendantTextWidth(descendantBlock, clusterInfo, minTextWidth, textWidth);
    579         }
    580         if (textWidth >= minTextWidth)
    581             return;
    582         descendant = nextInPreOrderSkippingDescendantsOfContainers(descendant, container);
    583     }
    584 }
    585 
    586 RenderObject* TextAutosizer::nextInPreOrderSkippingDescendantsOfContainers(const RenderObject* current, const RenderObject* stayWithin)
    587 {
    588     if (current == stayWithin || !isAutosizingContainer(current))
    589         return current->nextInPreOrder(stayWithin);
    590     return current->nextInPreOrderAfterChildren(stayWithin);
    591 }
    592 
    593 const RenderBlock* TextAutosizer::findDeepestBlockContainingAllText(const RenderBlock* cluster)
    594 {
    595     size_t firstDepth = 0;
    596     const RenderObject* firstTextLeaf = findFirstTextLeafNotInCluster(cluster, firstDepth, FirstToLast);
    597     if (!firstTextLeaf)
    598         return cluster;
    599 
    600     size_t lastDepth = 0;
    601     const RenderObject* lastTextLeaf = findFirstTextLeafNotInCluster(cluster, lastDepth, LastToFirst);
    602     ASSERT(lastTextLeaf);
    603 
    604     // Equalize the depths if necessary. Only one of the while loops below will get executed.
    605     const RenderObject* firstNode = firstTextLeaf;
    606     const RenderObject* lastNode = lastTextLeaf;
    607     while (firstDepth > lastDepth) {
    608         firstNode = firstNode->parent();
    609         --firstDepth;
    610     }
    611     while (lastDepth > firstDepth) {
    612         lastNode = lastNode->parent();
    613         --lastDepth;
    614     }
    615 
    616     // Go up from both nodes until the parent is the same. Both pointers will point to the LCA then.
    617     while (firstNode != lastNode) {
    618         firstNode = firstNode->parent();
    619         lastNode = lastNode->parent();
    620     }
    621 
    622     if (firstNode->isRenderBlock())
    623         return toRenderBlock(firstNode);
    624 
    625     // containingBlock() should never leave the cluster, since it only skips ancestors when finding the
    626     // container of position:absolute/fixed blocks, and those cannot exist between a cluster and its text
    627     // nodes lowest common ancestor as isAutosizingCluster would have made them into their own independent
    628     // cluster.
    629     RenderBlock* containingBlock = firstNode->containingBlock();
    630     ASSERT(containingBlock->isDescendantOf(cluster));
    631 
    632     return containingBlock;
    633 }
    634 
    635 const RenderObject* TextAutosizer::findFirstTextLeafNotInCluster(const RenderObject* parent, size_t& depth, TraversalDirection direction)
    636 {
    637     if (parent->isEmpty())
    638         return parent->isText() ? parent : 0;
    639 
    640     ++depth;
    641     const RenderObject* child = (direction == FirstToLast) ? parent->firstChild() : parent->lastChild();
    642     while (child) {
    643         if (!isAutosizingContainer(child) || !isIndependentDescendant(toRenderBlock(child))) {
    644             const RenderObject* leaf = findFirstTextLeafNotInCluster(child, depth, direction);
    645             if (leaf)
    646                 return leaf;
    647         }
    648         child = (direction == FirstToLast) ? child->nextSibling() : child->previousSibling();
    649     }
    650     --depth;
    651 
    652     return 0;
    653 }
    654 
    655 namespace {
    656 
    657 // Compares the width of the specified cluster's roots in descending order.
    658 bool clusterWiderThanComparisonFn(const TextAutosizingClusterInfo& first, const TextAutosizingClusterInfo& second)
    659 {
    660     return first.root->contentLogicalWidth() > second.root->contentLogicalWidth();
    661 }
    662 
    663 } // namespace
    664 
    665 void TextAutosizer::getNarrowDescendantsGroupedByWidth(const TextAutosizingClusterInfo& parentClusterInfo, Vector<Vector<TextAutosizingClusterInfo> >& groups)
    666 {
    667     ASSERT(parentClusterInfo.blockContainingAllText);
    668     ASSERT(groups.isEmpty());
    669 
    670     Vector<TextAutosizingClusterInfo> clusterInfos(parentClusterInfo.narrowDescendants);
    671     if (clusterInfos.isEmpty())
    672         return;
    673 
    674     std::sort(clusterInfos.begin(), clusterInfos.end(), &clusterWiderThanComparisonFn);
    675     groups.grow(1);
    676 
    677     // If the width difference between two consecutive elements of |clusterInfos| is greater than
    678     // this empirically determined value, the next element should start a new group.
    679     const float maxWidthDifferenceWithinGroup = 100;
    680     for (size_t i = 0; i < clusterInfos.size(); ++i) {
    681         groups.last().append(clusterInfos[i]);
    682 
    683         if (i + 1 < clusterInfos.size()) {
    684             float currentWidth = clusterInfos[i].root->contentLogicalWidth();
    685             float nextWidth = clusterInfos[i + 1].root->contentLogicalWidth();
    686             if (currentWidth - nextWidth > maxWidthDifferenceWithinGroup)
    687                 groups.grow(groups.size() + 1);
    688         }
    689     }
    690 }
    691 
    692 } // namespace WebCore
    693