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/frame/LocalFrame.h"
     28 #include "core/frame/Settings.h"
     29 #include "core/frame/UseCounter.h"
     30 #include "core/html/HTMLElement.h"
     31 #include "core/page/Page.h"
     32 #include "core/rendering/RenderListItem.h"
     33 #include "core/rendering/RenderObject.h"
     34 #include "core/rendering/RenderText.h"
     35 #include "core/rendering/RenderView.h"
     36 #include "core/rendering/style/RenderStyle.h"
     37 #include "core/rendering/style/StyleInheritedData.h"
     38 #include "platform/TraceEvent.h"
     39 #include "platform/geometry/IntSize.h"
     40 #include "wtf/StdLibExtras.h"
     41 
     42 namespace WebCore {
     43 
     44 #define AUTOSIZING_CLUSTER_HASH
     45 
     46 using namespace HTMLNames;
     47 
     48 struct TextAutosizingWindowInfo {
     49     IntSize windowSize;
     50     IntSize minLayoutSize;
     51 };
     52 
     53 // Represents a POD of a selection of fields for hashing. The fields are selected to detect similar
     54 // nodes in the Render Tree from the viewpoint of text autosizing.
     55 struct RenderObjectPodForHash {
     56     RenderObjectPodForHash()
     57         : qualifiedNameHash(0)
     58         , packedStyleProperties(0)
     59         , width(0)
     60     {
     61     }
     62     ~RenderObjectPodForHash() { }
     63 
     64     unsigned qualifiedNameHash;
     65 
     66     // Style specific selection of signals
     67     unsigned packedStyleProperties;
     68     float width;
     69 };
     70 // To allow for efficient hashing using StringHasher.
     71 COMPILE_ASSERT(!(sizeof(RenderObjectPodForHash) % sizeof(UChar)), RenderObjectPodForHashMultipleOfUchar);
     72 
     73 #ifdef AUTOSIZING_DOM_DEBUG_INFO
     74 static void writeDebugInfo(RenderObject* renderObject, const AtomicString& output)
     75 {
     76     Node* node = renderObject->node();
     77     if (node && node->isElementNode())
     78         toElement(node)->setAttribute("data-autosizing", output, ASSERT_NO_EXCEPTION);
     79 }
     80 #endif
     81 
     82 static const Vector<QualifiedName>& formInputTags()
     83 {
     84     // Returns the tags for the form input elements.
     85     DEFINE_STATIC_LOCAL(Vector<QualifiedName>, formInputTags, ());
     86     if (formInputTags.isEmpty()) {
     87         formInputTags.append(inputTag);
     88         formInputTags.append(buttonTag);
     89         formInputTags.append(selectTag);
     90     }
     91     return formInputTags;
     92 }
     93 
     94 static RenderListItem* getAncestorListItem(const RenderObject* renderer)
     95 {
     96     RenderObject* ancestor = renderer->parent();
     97     while (ancestor && (ancestor->isRenderInline() || ancestor->isAnonymousBlock()))
     98         ancestor = ancestor->parent();
     99 
    100     return (ancestor && ancestor->isListItem()) ? toRenderListItem(ancestor) : 0;
    101 }
    102 
    103 static RenderObject* getAncestorList(const RenderObject* renderer)
    104 {
    105     // FIXME: Add support for <menu> elements as a possible ancestor of an <li> element,
    106     // see http://www.whatwg.org/specs/web-apps/current-work/multipage/grouping-content.html#the-li-element
    107     for (RenderObject* ancestor = renderer->parent(); ancestor; ancestor = ancestor->parent()) {
    108         Node* parentNode = ancestor->generatingNode();
    109         if (parentNode && (isHTMLOListElement(*parentNode) || isHTMLUListElement(*parentNode)))
    110             return ancestor;
    111     }
    112     return 0;
    113 }
    114 
    115 static Node* getGeneratingElementNode(const RenderObject* renderer)
    116 {
    117     Node* node = renderer->generatingNode();
    118     return (node && node->isElementNode()) ? node : 0;
    119 }
    120 
    121 static unsigned hashMemory(const void* data, size_t length)
    122 {
    123     return StringHasher::computeHash<UChar>(static_cast<const UChar*>(data), length / sizeof(UChar));
    124 }
    125 
    126 static unsigned computeLocalHash(const RenderObject* renderer)
    127 {
    128     Node* generatingElementNode = getGeneratingElementNode(renderer);
    129     ASSERT(generatingElementNode);
    130 
    131     RenderObjectPodForHash podForHash;
    132     podForHash.qualifiedNameHash = QualifiedNameHash::hash(toElement(generatingElementNode)->tagQName());
    133 
    134     if (RenderStyle* style = renderer->style()) {
    135         podForHash.packedStyleProperties = style->direction();
    136         podForHash.packedStyleProperties |= (style->position() << 1);
    137         podForHash.packedStyleProperties |= (style->floating() << 4);
    138         podForHash.packedStyleProperties |= (style->display() << 6);
    139         podForHash.packedStyleProperties |= (style->width().type() << 11);
    140         // packedStyleProperties effectively using 15 bits now.
    141 
    142         // consider for adding: writing mode, padding.
    143 
    144         podForHash.width = style->width().getFloatValue();
    145     }
    146 
    147     return hashMemory(&podForHash, sizeof(podForHash));
    148 }
    149 
    150 TextAutosizer::TextAutosizer(Document* document)
    151     : m_document(document)
    152     , m_previouslyAutosized(false)
    153 {
    154 }
    155 
    156 unsigned TextAutosizer::getCachedHash(const RenderObject* renderer, bool putInCacheIfAbsent)
    157 {
    158     HashMap<const RenderObject*, unsigned>::const_iterator it = m_hashCache.find(renderer);
    159     if (it != m_hashCache.end())
    160         return it->value;
    161 
    162     RenderObject* rendererParent = renderer->parent();
    163     while (rendererParent && !getGeneratingElementNode(rendererParent))
    164         rendererParent = rendererParent->parent();
    165 
    166     const unsigned parentHashValue = rendererParent ? getCachedHash(rendererParent, true) : 0;
    167     const unsigned hashes[2] = { parentHashValue, computeLocalHash(renderer) };
    168     const unsigned combinedHashValue = hashMemory(hashes, sizeof(hashes));
    169     if (putInCacheIfAbsent)
    170         m_hashCache.add(renderer, combinedHashValue);
    171     return combinedHashValue;
    172 }
    173 
    174 bool TextAutosizer::isApplicable() const
    175 {
    176     return m_document->settings()
    177         && m_document->settings()->textAutosizingEnabled()
    178         && m_document->page()
    179         && m_document->page()->mainFrame()
    180         && m_document->page()->deprecatedLocalMainFrame()->loader().stateMachine()->committedFirstRealDocumentLoad();
    181 }
    182 
    183 void TextAutosizer::recalculateMultipliers()
    184 {
    185     if (!isApplicable() && !m_previouslyAutosized)
    186         return;
    187 
    188     RenderObject* renderer = m_document->renderView();
    189     while (renderer) {
    190         if (renderer->style() && renderer->style()->textAutosizingMultiplier() != 1)
    191             setMultiplier(renderer, 1);
    192         renderer = renderer->nextInPreOrder();
    193     }
    194     m_previouslyAutosized = false;
    195 }
    196 
    197 bool TextAutosizer::processSubtree(RenderObject* layoutRoot)
    198 {
    199     TRACE_EVENT0("webkit", "TextAutosizer: check if needed");
    200 
    201     if (!isApplicable() || layoutRoot->view()->document().printing())
    202         return false;
    203 
    204     LocalFrame* mainFrame = m_document->page()->deprecatedLocalMainFrame();
    205     TextAutosizingWindowInfo windowInfo;
    206 
    207     // Window area, in logical (density-independent) pixels.
    208     windowInfo.windowSize = m_document->settings()->textAutosizingWindowSizeOverride();
    209     if (windowInfo.windowSize.isEmpty())
    210         windowInfo.windowSize = mainFrame->view()->unscaledVisibleContentSize(IncludeScrollbars);
    211 
    212     // Largest area of block that can be visible at once (assuming the main
    213     // frame doesn't get scaled to less than overview scale), in CSS pixels.
    214     windowInfo.minLayoutSize = mainFrame->view()->layoutSize();
    215     for (Frame* frame = m_document->frame(); frame; frame = frame->tree().parent()) {
    216         windowInfo.minLayoutSize = windowInfo.minLayoutSize.shrunkTo(toLocalFrame(frame)->view()->layoutSize());
    217     }
    218 
    219     // The layoutRoot could be neither a container nor a cluster, so walk up the tree till we find each of these.
    220     RenderBlock* container = layoutRoot->isRenderBlock() ? toRenderBlock(layoutRoot) : layoutRoot->containingBlock();
    221     while (container && !isAutosizingContainer(container))
    222         container = container->containingBlock();
    223 
    224     RenderBlock* cluster = container;
    225     while (cluster && (!isAutosizingContainer(cluster) || !isIndependentDescendant(cluster)))
    226         cluster = cluster->containingBlock();
    227 
    228     // Skip autosizing for orphaned trees, or if it will have no effect.
    229     // Note: this might suppress autosizing of an inner cluster with a different writing mode.
    230     // It's not clear what the correct behavior is for mixed writing modes anyway.
    231     if (!cluster || clusterMultiplier(cluster->style()->writingMode(), windowInfo,
    232         std::numeric_limits<float>::infinity()) == 1.0f)
    233         return false;
    234 
    235     TRACE_EVENT0("webkit", "TextAutosizer: process root cluster");
    236     UseCounter::count(*m_document, UseCounter::TextAutosizing);
    237 
    238     TextAutosizingClusterInfo clusterInfo(cluster);
    239     processCluster(clusterInfo, container, layoutRoot, windowInfo);
    240 
    241 #ifdef AUTOSIZING_CLUSTER_HASH
    242     // Second pass to autosize stale non-autosized clusters for consistency.
    243     secondPassProcessStaleNonAutosizedClusters();
    244     m_hashCache.clear();
    245     m_hashToMultiplier.clear();
    246     m_hashesToAutosizeSecondPass.clear();
    247     m_nonAutosizedClusters.clear();
    248 #endif
    249     m_previouslyAutosized = true;
    250     return true;
    251 }
    252 
    253 float TextAutosizer::clusterMultiplier(WritingMode writingMode, const TextAutosizingWindowInfo& windowInfo, float textWidth) const
    254 {
    255     int logicalWindowWidth = isHorizontalWritingMode(writingMode) ? windowInfo.windowSize.width() : windowInfo.windowSize.height();
    256     int logicalLayoutWidth = isHorizontalWritingMode(writingMode) ? windowInfo.minLayoutSize.width() : windowInfo.minLayoutSize.height();
    257     // Ignore box width in excess of the layout width, to avoid extreme multipliers.
    258     float logicalClusterWidth = std::min<float>(textWidth, logicalLayoutWidth);
    259 
    260     float multiplier = logicalClusterWidth / logicalWindowWidth;
    261     multiplier *= m_document->settings()->accessibilityFontScaleFactor();
    262 
    263     // If the page has a meta viewport or @viewport, don't apply the device scale adjustment.
    264     const ViewportDescription& viewportDescription = m_document->page()->deprecatedLocalMainFrame()->document()->viewportDescription();
    265     if (!viewportDescription.isSpecifiedByAuthor()) {
    266         float deviceScaleAdjustment = m_document->settings()->deviceScaleAdjustment();
    267         multiplier *= deviceScaleAdjustment;
    268     }
    269     return std::max(1.0f, multiplier);
    270 }
    271 
    272 void TextAutosizer::processClusterInternal(TextAutosizingClusterInfo& clusterInfo, RenderBlock* container, RenderObject* subtreeRoot, const TextAutosizingWindowInfo& windowInfo, float multiplier)
    273 {
    274     processContainer(multiplier, container, clusterInfo, subtreeRoot, windowInfo);
    275 #ifdef AUTOSIZING_DOM_DEBUG_INFO
    276     writeDebugInfo(clusterInfo.root, AtomicString(String::format("cluster:%f", multiplier)));
    277 #endif
    278 
    279     Vector<Vector<TextAutosizingClusterInfo> > narrowDescendantsGroups;
    280     getNarrowDescendantsGroupedByWidth(clusterInfo, narrowDescendantsGroups);
    281     for (size_t i = 0; i < narrowDescendantsGroups.size(); ++i)
    282         processCompositeCluster(narrowDescendantsGroups[i], windowInfo);
    283 }
    284 
    285 unsigned TextAutosizer::computeCompositeClusterHash(Vector<TextAutosizingClusterInfo>& clusterInfos)
    286 {
    287     if (clusterInfos.size() == 1 && getGeneratingElementNode(clusterInfos[0].root))
    288         return getCachedHash(clusterInfos[0].root, false);
    289 
    290     // FIXME: consider hashing clusters for which clusterInfos.size() > 1
    291     return 0;
    292 }
    293 
    294 void TextAutosizer::addNonAutosizedCluster(unsigned key, TextAutosizingClusterInfo& value)
    295 {
    296     HashMap<unsigned, OwnPtr<Vector<TextAutosizingClusterInfo> > >::const_iterator it = m_nonAutosizedClusters.find(key);
    297     if (it == m_nonAutosizedClusters.end()) {
    298         m_nonAutosizedClusters.add(key, adoptPtr(new Vector<TextAutosizingClusterInfo>(1, value)));
    299         return;
    300     }
    301     it->value->append(value);
    302 }
    303 
    304 float TextAutosizer::computeMultiplier(Vector<TextAutosizingClusterInfo>& clusterInfos, const TextAutosizingWindowInfo& windowInfo, float textWidth)
    305 {
    306 #ifdef AUTOSIZING_CLUSTER_HASH
    307     // When hashing is enabled this function returns a multiplier based on previously seen clusters.
    308     // It will return a non-unit multiplier if a cluster with the same hash value has been previously
    309     // autosized.
    310     unsigned clusterHash = computeCompositeClusterHash(clusterInfos);
    311 #else
    312     unsigned clusterHash = 0;
    313 #endif
    314 
    315     if (clusterHash) {
    316         HashMap<unsigned, float>::iterator it = m_hashToMultiplier.find(clusterHash);
    317         if (it != m_hashToMultiplier.end())
    318             return it->value;
    319     }
    320 
    321     if (compositeClusterShouldBeAutosized(clusterInfos, textWidth)) {
    322         float multiplier = clusterMultiplier(clusterInfos[0].root->style()->writingMode(), windowInfo, textWidth);
    323         if (clusterHash) {
    324             if (multiplier > 1 && m_nonAutosizedClusters.contains(clusterHash))
    325                 m_hashesToAutosizeSecondPass.append(clusterHash);
    326             m_hashToMultiplier.add(clusterHash, multiplier);
    327         }
    328         return multiplier;
    329     }
    330 
    331     if (clusterHash)
    332         addNonAutosizedCluster(clusterHash, clusterInfos[0]);
    333 
    334     return 1.0f;
    335 }
    336 
    337 void TextAutosizer::processCluster(TextAutosizingClusterInfo& clusterInfo, RenderBlock* container, RenderObject* subtreeRoot, const TextAutosizingWindowInfo& windowInfo)
    338 {
    339     // Many pages set a max-width on their content. So especially for the RenderView, instead of
    340     // just taking the width of |cluster| we find the lowest common ancestor of the first and last
    341     // descendant text node of the cluster (i.e. the deepest wrapper block that contains all the
    342     // text), and use its width instead.
    343     clusterInfo.blockContainingAllText = findDeepestBlockContainingAllText(clusterInfo.root);
    344     float textWidth = clusterInfo.blockContainingAllText->contentLogicalWidth().toFloat();
    345 
    346     Vector<TextAutosizingClusterInfo> clusterInfos(1, clusterInfo);
    347     float multiplier = computeMultiplier(clusterInfos, windowInfo, textWidth);
    348 
    349     processClusterInternal(clusterInfo, container, subtreeRoot, windowInfo, multiplier);
    350 }
    351 
    352 void TextAutosizer::processCompositeCluster(Vector<TextAutosizingClusterInfo>& clusterInfos, const TextAutosizingWindowInfo& windowInfo)
    353 {
    354     if (clusterInfos.isEmpty())
    355         return;
    356 
    357     float maxTextWidth = 0;
    358     for (size_t i = 0; i < clusterInfos.size(); ++i) {
    359         TextAutosizingClusterInfo& clusterInfo = clusterInfos[i];
    360         clusterInfo.blockContainingAllText = findDeepestBlockContainingAllText(clusterInfo.root);
    361         maxTextWidth = max<float>(maxTextWidth, clusterInfo.blockContainingAllText->contentLogicalWidth().toFloat());
    362     }
    363 
    364     float multiplier =  computeMultiplier(clusterInfos, windowInfo, maxTextWidth);
    365     for (size_t i = 0; i < clusterInfos.size(); ++i) {
    366         ASSERT(clusterInfos[i].root->style()->writingMode() == clusterInfos[0].root->style()->writingMode());
    367         processClusterInternal(clusterInfos[i], clusterInfos[i].root, clusterInfos[i].root, windowInfo, multiplier);
    368     }
    369 }
    370 
    371 void TextAutosizer::secondPassProcessStaleNonAutosizedClusters()
    372 {
    373     for (size_t i = 0; i < m_hashesToAutosizeSecondPass.size(); ++i) {
    374         unsigned hash = m_hashesToAutosizeSecondPass[i];
    375         float multiplier = m_hashToMultiplier.get(hash);
    376         Vector<TextAutosizingClusterInfo>* val = m_nonAutosizedClusters.get(hash);
    377         for (Vector<TextAutosizingClusterInfo>::iterator it2 = val->begin(); it2 != val->end(); ++it2)
    378             processStaleContainer(multiplier, (*it2).root, *it2);
    379     }
    380 }
    381 
    382 void TextAutosizer::processStaleContainer(float multiplier, RenderBlock* cluster, TextAutosizingClusterInfo& clusterInfo)
    383 {
    384     ASSERT(isAutosizingContainer(cluster));
    385 
    386     // This method is different from processContainer() mainly in that it does not recurse into sub-clusters.
    387     // Multiplier updates are restricted to the specified cluster only. Also the multiplier > 1 by construction
    388     // of m_hashesToAutosizeSecondPass, so we don't need to check it explicitly.
    389     float localMultiplier = containerShouldBeAutosized(cluster) ? multiplier : 1;
    390 
    391     RenderObject* descendant = nextInPreOrderSkippingDescendantsOfContainers(cluster, cluster);
    392     while (descendant) {
    393         if (descendant->isText()) {
    394             if (localMultiplier != 1 && descendant->style()->textAutosizingMultiplier() == 1) {
    395                 setMultiplier(descendant, localMultiplier);
    396                 setMultiplier(descendant->parent(), localMultiplier); // Parent does line spacing.
    397             }
    398         } else if (isAutosizingContainer(descendant)) {
    399             RenderBlock* descendantBlock = toRenderBlock(descendant);
    400             if (!isAutosizingCluster(descendantBlock, clusterInfo))
    401                 processStaleContainer(multiplier, descendantBlock, clusterInfo);
    402         }
    403         descendant = nextInPreOrderSkippingDescendantsOfContainers(descendant, cluster);
    404     }
    405 }
    406 
    407 void TextAutosizer::processContainer(float multiplier, RenderBlock* container, TextAutosizingClusterInfo& clusterInfo, RenderObject* subtreeRoot, const TextAutosizingWindowInfo& windowInfo)
    408 {
    409     ASSERT(isAutosizingContainer(container));
    410 #ifdef AUTOSIZING_DOM_DEBUG_INFO
    411     writeDebugInfo(container, "container");
    412 #endif
    413 
    414     float localMultiplier = (multiplier > 1 && containerShouldBeAutosized(container)) ? multiplier: 1;
    415 
    416     RenderObject* descendant = nextInPreOrderSkippingDescendantsOfContainers(subtreeRoot, subtreeRoot);
    417     while (descendant) {
    418         if (descendant->isText()) {
    419             if (localMultiplier != 1 && descendant->style()->textAutosizingMultiplier() == 1) {
    420                 setMultiplier(descendant, localMultiplier);
    421                 setMultiplier(descendant->parent(), localMultiplier); // Parent does line spacing.
    422 
    423                 if (RenderListItem* listItemAncestor = getAncestorListItem(descendant)) {
    424                     if (RenderObject* list = getAncestorList(listItemAncestor)) {
    425                         if (list->style()->textAutosizingMultiplier() == 1)
    426                             setMultiplierForList(list, localMultiplier);
    427                     }
    428                 }
    429             }
    430         } else if (isAutosizingContainer(descendant)) {
    431             RenderBlock* descendantBlock = toRenderBlock(descendant);
    432             TextAutosizingClusterInfo descendantClusterInfo(descendantBlock);
    433             if (isWiderDescendant(descendantBlock, clusterInfo) || isIndependentDescendant(descendantBlock))
    434                 processCluster(descendantClusterInfo, descendantBlock, descendantBlock, windowInfo);
    435             else if (isNarrowDescendant(descendantBlock, clusterInfo)) {
    436                 // Narrow descendants are processed together later to be able to apply the same multiplier
    437                 // to each of them if necessary.
    438                 clusterInfo.narrowDescendants.append(descendantClusterInfo);
    439             } else
    440                 processContainer(multiplier, descendantBlock, clusterInfo, descendantBlock, windowInfo);
    441         }
    442         descendant = nextInPreOrderSkippingDescendantsOfContainers(descendant, subtreeRoot);
    443     }
    444 }
    445 
    446 void TextAutosizer::setMultiplier(RenderObject* renderer, float multiplier)
    447 {
    448     RefPtr<RenderStyle> newStyle = RenderStyle::clone(renderer->style());
    449     newStyle->setTextAutosizingMultiplier(multiplier);
    450     newStyle->setUnique();
    451     renderer->setStyle(newStyle.release());
    452 }
    453 
    454 void TextAutosizer::setMultiplierForList(RenderObject* renderer, float multiplier)
    455 {
    456 #ifndef NDEBUG
    457     Node* parentNode = renderer->generatingNode();
    458     ASSERT(parentNode);
    459     ASSERT(isHTMLOListElement(parentNode) || isHTMLUListElement(parentNode));
    460 #endif
    461     setMultiplier(renderer, multiplier);
    462 
    463     // Make sure all list items are autosized consistently.
    464     for (RenderObject* child = renderer->slowFirstChild(); child; child = child->nextSibling()) {
    465         if (child->isListItem() && child->style()->textAutosizingMultiplier() == 1)
    466             setMultiplier(child, multiplier);
    467     }
    468 }
    469 
    470 float TextAutosizer::computeAutosizedFontSize(float specifiedSize, float multiplier)
    471 {
    472     // Somewhat arbitrary "pleasant" font size.
    473     const float pleasantSize = 16;
    474 
    475     // Multiply fonts that the page author has specified to be larger than
    476     // pleasantSize by less and less, until huge fonts are not increased at all.
    477     // For specifiedSize between 0 and pleasantSize we directly apply the
    478     // multiplier; hence for specifiedSize == pleasantSize, computedSize will be
    479     // multiplier * pleasantSize. For greater specifiedSizes we want to
    480     // gradually fade out the multiplier, so for every 1px increase in
    481     // specifiedSize beyond pleasantSize we will only increase computedSize
    482     // by gradientAfterPleasantSize px until we meet the
    483     // computedSize = specifiedSize line, after which we stay on that line (so
    484     // then every 1px increase in specifiedSize increases computedSize by 1px).
    485     const float gradientAfterPleasantSize = 0.5;
    486 
    487     float computedSize;
    488     if (specifiedSize <= pleasantSize)
    489         computedSize = multiplier * specifiedSize;
    490     else {
    491         computedSize = multiplier * pleasantSize + gradientAfterPleasantSize * (specifiedSize - pleasantSize);
    492         if (computedSize < specifiedSize)
    493             computedSize = specifiedSize;
    494     }
    495     return computedSize;
    496 }
    497 
    498 bool TextAutosizer::isAutosizingContainer(const RenderObject* renderer)
    499 {
    500     // "Autosizing containers" are the smallest unit for which we can
    501     // enable/disable Text Autosizing.
    502     // - Must not be inline, as different multipliers on one line looks terrible.
    503     //   Exceptions are inline-block and alike elements (inline-table, -webkit-inline-*),
    504     //   as they often contain entire multi-line columns of text.
    505     // - Must not be list items, as items in the same list should look consistent (*).
    506     // - Must not be normal list items, as items in the same list should look
    507     //   consistent, unless they are floating or position:absolute/fixed.
    508     Node* node = renderer->generatingNode();
    509     if ((node && !node->hasChildren())
    510         || !renderer->isRenderBlock()
    511         || (renderer->isInline() && !renderer->style()->isDisplayReplacedType()))
    512         return false;
    513     if (renderer->isListItem())
    514         return renderer->isFloating() || renderer->isOutOfFlowPositioned();
    515     // Avoid creating containers for text within text controls, buttons, or <select> buttons.
    516     Node* parentNode = renderer->parent() ? renderer->parent()->generatingNode() : 0;
    517     if (parentNode && parentNode->isElementNode() && formInputTags().contains(toElement(parentNode)->tagQName()))
    518         return false;
    519 
    520     return true;
    521 }
    522 
    523 bool TextAutosizer::isNarrowDescendant(const RenderBlock* renderer, TextAutosizingClusterInfo& parentClusterInfo)
    524 {
    525     ASSERT(isAutosizingContainer(renderer));
    526 
    527     // Autosizing containers that are significantly narrower than the |blockContainingAllText| of
    528     // their enclosing cluster may be acting as separate columns, hence must be autosized
    529     // separately. For example the 2nd div in:
    530     // <body>
    531     //     <div style="float: right; width: 50%"></div>
    532     //     <div style="width: 50%"></div>
    533     // <body>
    534     // is the left column, and should be autosized differently from the body.
    535     // If however the container is only narrower by 150px or less, it's considered part of
    536     // the enclosing cluster. This 150px limit is adjusted whenever a descendant container is
    537     // less than 50px narrower than the current limit.
    538     const float differenceFromMaxWidthDifference = 50;
    539     LayoutUnit contentWidth = renderer->contentLogicalWidth();
    540     LayoutUnit clusterTextWidth = parentClusterInfo.blockContainingAllText->contentLogicalWidth();
    541     LayoutUnit widthDifference = clusterTextWidth - contentWidth;
    542 
    543     if (widthDifference - parentClusterInfo.maxAllowedDifferenceFromTextWidth > differenceFromMaxWidthDifference)
    544         return true;
    545 
    546     parentClusterInfo.maxAllowedDifferenceFromTextWidth = std::max(widthDifference.toFloat(), parentClusterInfo.maxAllowedDifferenceFromTextWidth);
    547     return false;
    548 }
    549 
    550 bool TextAutosizer::isWiderDescendant(const RenderBlock* renderer, const TextAutosizingClusterInfo& parentClusterInfo)
    551 {
    552     ASSERT(isAutosizingContainer(renderer));
    553 
    554     // Autosizing containers that are wider than the |blockContainingAllText| of their enclosing
    555     // cluster are treated the same way as autosizing clusters to be autosized separately.
    556     LayoutUnit contentWidth = renderer->contentLogicalWidth();
    557     LayoutUnit clusterTextWidth = parentClusterInfo.blockContainingAllText->contentLogicalWidth();
    558     return contentWidth > clusterTextWidth;
    559 }
    560 
    561 bool TextAutosizer::isIndependentDescendant(const RenderBlock* renderer)
    562 {
    563     ASSERT(isAutosizingContainer(renderer));
    564 
    565     // "Autosizing clusters" are special autosizing containers within which we
    566     // want to enforce a uniform text size multiplier, in the hopes of making
    567     // the major sections of the page look internally consistent.
    568     // All their descendants (including other autosizing containers) must share
    569     // the same multiplier, except for subtrees which are themselves clusters,
    570     // and some of their descendant containers might not be autosized at all
    571     // (for example if their height is constrained).
    572     // Additionally, clusterShouldBeAutosized requires each cluster to contain a
    573     // minimum amount of text, without which it won't be autosized.
    574     //
    575     // Clusters are chosen using very similar criteria to CSS flow roots, aka
    576     // block formatting contexts (http://w3.org/TR/css3-box/#flow-root), since
    577     // flow roots correspond to box containers that behave somewhat
    578     // independently from their parent (for example they don't overlap floats).
    579     // The definition of a flow root also conveniently includes most of the
    580     // ways that a box and its children can have significantly different width
    581     // from the box's parent (we want to avoid having significantly different
    582     // width blocks within a cluster, since the narrower blocks would end up
    583     // larger than would otherwise be necessary).
    584     RenderBlock* containingBlock = renderer->containingBlock();
    585     return renderer->isRenderView()
    586         || renderer->isFloating()
    587         || renderer->isOutOfFlowPositioned()
    588         || renderer->isTableCell()
    589         || renderer->isTableCaption()
    590         || renderer->isFlexibleBoxIncludingDeprecated()
    591         || renderer->hasColumns()
    592         || (containingBlock && containingBlock->isHorizontalWritingMode() != renderer->isHorizontalWritingMode())
    593         || renderer->style()->isDisplayReplacedType()
    594         || renderer->isTextArea()
    595         || renderer->style()->userModify() != READ_ONLY;
    596     // FIXME: Tables need special handling to multiply all their columns by
    597     // the same amount even if they're different widths; so do hasColumns()
    598     // containers, and probably flexboxes...
    599 }
    600 
    601 bool TextAutosizer::isAutosizingCluster(const RenderBlock* renderer, TextAutosizingClusterInfo& parentClusterInfo)
    602 {
    603     ASSERT(isAutosizingContainer(renderer));
    604 
    605     return isNarrowDescendant(renderer, parentClusterInfo)
    606         || isWiderDescendant(renderer, parentClusterInfo)
    607         || isIndependentDescendant(renderer);
    608 }
    609 
    610 bool TextAutosizer::containerShouldBeAutosized(const RenderBlock* container)
    611 {
    612     if (containerContainsOneOfTags(container, formInputTags()))
    613         return false;
    614 
    615     if (containerIsRowOfLinks(container))
    616         return false;
    617 
    618     // Don't autosize block-level text that can't wrap (as it's likely to
    619     // expand sideways and break the page's layout).
    620     if (!container->style()->autoWrap())
    621         return false;
    622 
    623     return !contentHeightIsConstrained(container);
    624 }
    625 
    626 bool TextAutosizer::containerContainsOneOfTags(const RenderBlock* container, const Vector<QualifiedName>& tags)
    627 {
    628     const RenderObject* renderer = container;
    629     while (renderer) {
    630         const Node* rendererNode = renderer->node();
    631         if (rendererNode && rendererNode->isElementNode()) {
    632             if (tags.contains(toElement(rendererNode)->tagQName()))
    633                 return true;
    634         }
    635         renderer = nextInPreOrderSkippingDescendantsOfContainers(renderer, container);
    636     }
    637 
    638     return false;
    639 }
    640 
    641 bool TextAutosizer::containerIsRowOfLinks(const RenderObject* container)
    642 {
    643     // A "row of links" is a container for which holds:
    644     //  1. it should not contain non-link text elements longer than 3 characters
    645     //  2. it should contain min. 3 inline links and all links should
    646     //     have the same specified font size
    647     //  3. it should not contain <br> elements
    648     //  4. it should contain only inline elements unless they are containers,
    649     //     children of link elements or children of sub-containers.
    650     int linkCount = 0;
    651     RenderObject* renderer = container->nextInPreOrder(container);
    652     float matchingFontSize = -1;
    653 
    654     while (renderer) {
    655         if (!isAutosizingContainer(renderer)) {
    656             if (renderer->isText() && toRenderText(renderer)->text().impl()->stripWhiteSpace()->length() > 3)
    657                 return false;
    658             if (!renderer->isInline())
    659                 return false;
    660             if (renderer->isBR())
    661                 return false;
    662         }
    663         if (renderer->style()->isLink()) {
    664             if (matchingFontSize < 0)
    665                 matchingFontSize = renderer->style()->specifiedFontSize();
    666             else {
    667                 if (matchingFontSize != renderer->style()->specifiedFontSize())
    668                     return false;
    669             }
    670 
    671             linkCount++;
    672             // Skip traversing descendants of the link.
    673             renderer = renderer->nextInPreOrderAfterChildren(container);
    674         } else
    675             renderer = nextInPreOrderSkippingDescendantsOfContainers(renderer, container);
    676     }
    677 
    678     return (linkCount >= 3);
    679 }
    680 
    681 bool TextAutosizer::contentHeightIsConstrained(const RenderBlock* container)
    682 {
    683     // FIXME: Propagate constrainedness down the tree, to avoid inefficiently walking back up from each box.
    684     // FIXME: This code needs to take into account vertical writing modes.
    685     // FIXME: Consider additional heuristics, such as ignoring fixed heights if the content is already overflowing before autosizing kicks in.
    686     for (; container; container = container->containingBlock()) {
    687         RenderStyle* style = container->style();
    688         if (style->overflowY() >= OSCROLL)
    689             return false;
    690         if (style->height().isSpecified() || style->maxHeight().isSpecified() || container->isOutOfFlowPositioned()) {
    691             // Some sites (e.g. wikipedia) set their html and/or body elements to height:100%,
    692             // without intending to constrain the height of the content within them.
    693             return !container->isDocumentElement() && !container->isBody();
    694         }
    695         if (container->isFloating())
    696             return false;
    697     }
    698     return false;
    699 }
    700 
    701 bool TextAutosizer::compositeClusterShouldBeAutosized(Vector<TextAutosizingClusterInfo>& clusterInfos, float blockWidth)
    702 {
    703     // Don't autosize clusters that contain less than 4 lines of text (in
    704     // practice less lines are required, since measureDescendantTextWidth
    705     // assumes that characters are 1em wide, but most characters are narrower
    706     // than that, so we're overestimating their contribution to the linecount).
    707     //
    708     // This is to reduce the likelihood of autosizing things like headers and
    709     // footers, which can be quite visually distracting. The rationale is that
    710     // if a cluster contains very few lines of text then it's ok to have to zoom
    711     // in and pan from side to side to read each line, since if there are very
    712     // few lines of text you'll only need to pan across once or twice.
    713     //
    714     // An exception to the 4 lines of text are the textarea and contenteditable
    715     // clusters, which are always autosized by default (i.e. threated as if they
    716     // contain more than 4 lines of text). This is to ensure that the text does
    717     // not suddenly get autosized when the user enters more than 4 lines of text.
    718     float totalTextWidth = 0;
    719     const float minLinesOfText = 4;
    720     float minTextWidth = blockWidth * minLinesOfText;
    721     for (size_t i = 0; i < clusterInfos.size(); ++i) {
    722         if (clusterInfos[i].root->isTextArea() || (clusterInfos[i].root->style() && clusterInfos[i].root->style()->userModify() != READ_ONLY))
    723             return true;
    724         measureDescendantTextWidth(clusterInfos[i].blockContainingAllText, clusterInfos[i], minTextWidth, totalTextWidth);
    725         if (totalTextWidth >= minTextWidth)
    726             return true;
    727     }
    728     return false;
    729 }
    730 
    731 void TextAutosizer::measureDescendantTextWidth(const RenderBlock* container, TextAutosizingClusterInfo& clusterInfo, float minTextWidth, float& textWidth)
    732 {
    733     bool skipLocalText = !containerShouldBeAutosized(container);
    734 
    735     RenderObject* descendant = nextInPreOrderSkippingDescendantsOfContainers(container, container);
    736     while (descendant) {
    737         if (!skipLocalText && descendant->isText()) {
    738             textWidth += toRenderText(descendant)->renderedTextLength() * descendant->style()->specifiedFontSize();
    739         } else if (isAutosizingContainer(descendant)) {
    740             RenderBlock* descendantBlock = toRenderBlock(descendant);
    741             if (!isAutosizingCluster(descendantBlock, clusterInfo))
    742                 measureDescendantTextWidth(descendantBlock, clusterInfo, minTextWidth, textWidth);
    743         }
    744         if (textWidth >= minTextWidth)
    745             return;
    746         descendant = nextInPreOrderSkippingDescendantsOfContainers(descendant, container);
    747     }
    748 }
    749 
    750 RenderObject* TextAutosizer::nextInPreOrderSkippingDescendantsOfContainers(const RenderObject* current, const RenderObject* stayWithin)
    751 {
    752     if (current == stayWithin || !isAutosizingContainer(current))
    753         return current->nextInPreOrder(stayWithin);
    754     return current->nextInPreOrderAfterChildren(stayWithin);
    755 }
    756 
    757 const RenderBlock* TextAutosizer::findDeepestBlockContainingAllText(const RenderBlock* cluster)
    758 {
    759     size_t firstDepth = 0;
    760     const RenderObject* firstTextLeaf = findFirstTextLeafNotInCluster(cluster, firstDepth, FirstToLast);
    761     if (!firstTextLeaf)
    762         return cluster;
    763 
    764     size_t lastDepth = 0;
    765     const RenderObject* lastTextLeaf = findFirstTextLeafNotInCluster(cluster, lastDepth, LastToFirst);
    766     ASSERT(lastTextLeaf);
    767 
    768     // Equalize the depths if necessary. Only one of the while loops below will get executed.
    769     const RenderObject* firstNode = firstTextLeaf;
    770     const RenderObject* lastNode = lastTextLeaf;
    771     while (firstDepth > lastDepth) {
    772         firstNode = firstNode->parent();
    773         --firstDepth;
    774     }
    775     while (lastDepth > firstDepth) {
    776         lastNode = lastNode->parent();
    777         --lastDepth;
    778     }
    779 
    780     // Go up from both nodes until the parent is the same. Both pointers will point to the LCA then.
    781     while (firstNode != lastNode) {
    782         firstNode = firstNode->parent();
    783         lastNode = lastNode->parent();
    784     }
    785 
    786     if (firstNode->isRenderBlock())
    787         return toRenderBlock(firstNode);
    788 
    789     // containingBlock() should never leave the cluster, since it only skips ancestors when finding the
    790     // container of position:absolute/fixed blocks, and those cannot exist between a cluster and its text
    791     // nodes lowest common ancestor as isAutosizingCluster would have made them into their own independent
    792     // cluster.
    793     RenderBlock* containingBlock = firstNode->containingBlock();
    794     ASSERT(containingBlock->isDescendantOf(cluster));
    795 
    796     return containingBlock;
    797 }
    798 
    799 const RenderObject* TextAutosizer::findFirstTextLeafNotInCluster(const RenderObject* parent, size_t& depth, TraversalDirection direction)
    800 {
    801     if (parent->isText())
    802         return parent;
    803 
    804     ++depth;
    805     const RenderObject* child = (direction == FirstToLast) ? parent->slowFirstChild() : parent->slowLastChild();
    806     while (child) {
    807         if (!isAutosizingContainer(child) || !isIndependentDescendant(toRenderBlock(child))) {
    808             const RenderObject* leaf = findFirstTextLeafNotInCluster(child, depth, direction);
    809             if (leaf)
    810                 return leaf;
    811         }
    812         child = (direction == FirstToLast) ? child->nextSibling() : child->previousSibling();
    813     }
    814     --depth;
    815 
    816     return 0;
    817 }
    818 
    819 namespace {
    820 
    821 // Compares the width of the specified cluster's roots in descending order.
    822 bool clusterWiderThanComparisonFn(const TextAutosizingClusterInfo& first, const TextAutosizingClusterInfo& second)
    823 {
    824     return first.root->contentLogicalWidth() > second.root->contentLogicalWidth();
    825 }
    826 
    827 } // namespace
    828 
    829 void TextAutosizer::getNarrowDescendantsGroupedByWidth(const TextAutosizingClusterInfo& parentClusterInfo, Vector<Vector<TextAutosizingClusterInfo> >& groups)
    830 {
    831     ASSERT(parentClusterInfo.blockContainingAllText);
    832     ASSERT(groups.isEmpty());
    833 
    834     Vector<TextAutosizingClusterInfo> clusterInfos(parentClusterInfo.narrowDescendants);
    835     if (clusterInfos.isEmpty())
    836         return;
    837 
    838     std::sort(clusterInfos.begin(), clusterInfos.end(), &clusterWiderThanComparisonFn);
    839     groups.grow(1);
    840 
    841     // If the width difference between two consecutive elements of |clusterInfos| is greater than
    842     // this empirically determined value, the next element should start a new group.
    843     const float maxWidthDifferenceWithinGroup = 100;
    844     for (size_t i = 0; i < clusterInfos.size(); ++i) {
    845         groups.last().append(clusterInfos[i]);
    846 
    847         if (i + 1 < clusterInfos.size()) {
    848             LayoutUnit currentWidth = clusterInfos[i].root->contentLogicalWidth();
    849             LayoutUnit nextWidth = clusterInfos[i + 1].root->contentLogicalWidth();
    850             if (currentWidth - nextWidth > maxWidthDifferenceWithinGroup)
    851                 groups.grow(groups.size() + 1);
    852         }
    853     }
    854 }
    855 
    856 } // namespace WebCore
    857