Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 2013 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "core/rendering/TextAutosizer.h"
     33 
     34 #include "core/dom/Document.h"
     35 #include "core/frame/FrameView.h"
     36 #include "core/frame/LocalFrame.h"
     37 #include "core/frame/Settings.h"
     38 #include "core/html/HTMLTextAreaElement.h"
     39 #include "core/page/Page.h"
     40 #include "core/rendering/InlineIterator.h"
     41 #include "core/rendering/RenderBlock.h"
     42 #include "core/rendering/RenderListItem.h"
     43 #include "core/rendering/RenderListMarker.h"
     44 #include "core/rendering/RenderTableCell.h"
     45 #include "core/rendering/RenderView.h"
     46 
     47 #ifdef AUTOSIZING_DOM_DEBUG_INFO
     48 #include "core/dom/ExecutionContextTask.h"
     49 #endif
     50 
     51 namespace blink {
     52 
     53 #ifdef AUTOSIZING_DOM_DEBUG_INFO
     54 class WriteDebugInfoTask : public ExecutionContextTask {
     55 public:
     56     WriteDebugInfoTask(PassRefPtrWillBeRawPtr<Element> element, AtomicString value)
     57         : m_element(element)
     58         , m_value(value)
     59     {
     60     }
     61 
     62     virtual void performTask(ExecutionContext*)
     63     {
     64         m_element->setAttribute("data-autosizing", m_value, ASSERT_NO_EXCEPTION);
     65     }
     66 
     67 private:
     68     RefPtrWillBePersistent<Element> m_element;
     69     AtomicString m_value;
     70 };
     71 
     72 static void writeDebugInfo(RenderObject* renderer, const AtomicString& output)
     73 {
     74     Node* node = renderer->node();
     75     if (!node)
     76         return;
     77     if (node->isDocumentNode())
     78         node = toDocument(node)->documentElement();
     79     if (!node->isElementNode())
     80         return;
     81     node->document().postTask(adoptPtr(new WriteDebugInfoTask(toElement(node), output)));
     82 }
     83 
     84 void TextAutosizer::writeClusterDebugInfo(Cluster* cluster)
     85 {
     86     String explanation = "";
     87     if (cluster->m_flags & SUPPRESSING) {
     88         explanation = "[suppressed]";
     89     } else if (!(cluster->m_flags & (INDEPENDENT | WIDER_OR_NARROWER))) {
     90         explanation = "[inherited]";
     91     } else if (cluster->m_supercluster) {
     92         explanation = "[supercluster]";
     93     } else if (!clusterHasEnoughTextToAutosize(cluster)) {
     94         explanation = "[insufficient-text]";
     95     } else {
     96         const RenderBlock* widthProvider = clusterWidthProvider(cluster->m_root);
     97         if (cluster->m_hasTableAncestor && cluster->m_multiplier < multiplierFromBlock(widthProvider)) {
     98             explanation = "[table-ancestor-limited]";
     99         } else {
    100             explanation = String::format("[from width %d of %s]",
    101                 static_cast<int>(widthFromBlock(widthProvider)), widthProvider->debugName().utf8().data());
    102         }
    103     }
    104     String pageInfo = "";
    105     if (cluster->m_root->isRenderView()) {
    106         pageInfo = String::format("; pageinfo: bm %f * (lw %d / fw %d)",
    107             m_pageInfo.m_baseMultiplier, m_pageInfo.m_layoutWidth, m_pageInfo.m_frameWidth);
    108     }
    109     float multiplier = cluster->m_flags & SUPPRESSING ? 1.0 : cluster->m_multiplier;
    110     writeDebugInfo(const_cast<RenderBlock*>(cluster->m_root),
    111         AtomicString(String::format("cluster: %f %s%s", multiplier,
    112             explanation.utf8().data(), pageInfo.utf8().data())));
    113 }
    114 #endif
    115 
    116 static const RenderObject* parentElementRenderer(const RenderObject* renderer)
    117 {
    118     // At style recalc, the renderer's parent may not be attached,
    119     // so we need to obtain this from the DOM tree.
    120     const Node* node = renderer->node();
    121     if (!node)
    122         return 0;
    123 
    124     // FIXME: This should be using NodeRenderingTraversal::parent().
    125     if (Element* parent = node->parentElement())
    126         return parent->renderer();
    127     return 0;
    128 }
    129 
    130 static bool isNonTextAreaFormControl(const RenderObject* renderer)
    131 {
    132     const Node* node = renderer ? renderer->node() : 0;
    133     if (!node || !node->isElementNode())
    134         return false;
    135     const Element* element = toElement(node);
    136 
    137     return (element->isFormControlElement() && !isHTMLTextAreaElement(element));
    138 }
    139 
    140 static bool isPotentialClusterRoot(const RenderObject* renderer)
    141 {
    142     // "Potential cluster roots" are the smallest unit for which we can
    143     // enable/disable text autosizing.
    144     // - Must not be inline, as different multipliers on one line looks terrible.
    145     //   Exceptions are inline-block and alike elements (inline-table, -webkit-inline-*),
    146     //   as they often contain entire multi-line columns of text.
    147     // - Must not be normal list items, as items in the same list should look
    148     //   consistent, unless they are floating or position:absolute/fixed.
    149     Node* node = renderer->generatingNode();
    150     if (node && !node->hasChildren())
    151         return false;
    152     if (!renderer->isRenderBlock())
    153         return false;
    154     if (renderer->isInline() && !renderer->style()->isDisplayReplacedType())
    155         return false;
    156     if (renderer->isListItem())
    157         return (renderer->isFloating() || renderer->isOutOfFlowPositioned());
    158 
    159     return true;
    160 }
    161 
    162 static bool isIndependentDescendant(const RenderBlock* renderer)
    163 {
    164     ASSERT(isPotentialClusterRoot(renderer));
    165 
    166     RenderBlock* containingBlock = renderer->containingBlock();
    167     return renderer->isRenderView()
    168         || renderer->isFloating()
    169         || renderer->isOutOfFlowPositioned()
    170         || renderer->isTableCell()
    171         || renderer->isTableCaption()
    172         || renderer->isFlexibleBoxIncludingDeprecated()
    173         || renderer->hasColumns()
    174         || (containingBlock && containingBlock->isHorizontalWritingMode() != renderer->isHorizontalWritingMode())
    175         || renderer->style()->isDisplayReplacedType()
    176         || renderer->isTextArea()
    177         || renderer->style()->userModify() != READ_ONLY;
    178 }
    179 
    180 static bool blockIsRowOfLinks(const RenderBlock* block)
    181 {
    182     // A "row of links" is a block for which:
    183     //  1. It does not contain non-link text elements longer than 3 characters
    184     //  2. It contains a minimum of 3 inline links and all links should
    185     //     have the same specified font size.
    186     //  3. It should not contain <br> elements.
    187     //  4. It should contain only inline elements unless they are containers,
    188     //     children of link elements or children of sub-containers.
    189     int linkCount = 0;
    190     RenderObject* renderer = block->firstChild();
    191     float matchingFontSize = -1;
    192 
    193     while (renderer) {
    194         if (!isPotentialClusterRoot(renderer)) {
    195             if (renderer->isText() && toRenderText(renderer)->text().impl()->stripWhiteSpace()->length() > 3)
    196                 return false;
    197             if (!renderer->isInline() || renderer->isBR())
    198                 return false;
    199         }
    200         if (renderer->style()->isLink()) {
    201             linkCount++;
    202             if (matchingFontSize < 0)
    203                 matchingFontSize = renderer->style()->specifiedFontSize();
    204             else if (matchingFontSize != renderer->style()->specifiedFontSize())
    205                 return false;
    206 
    207             // Skip traversing descendants of the link.
    208             renderer = renderer->nextInPreOrderAfterChildren(block);
    209             continue;
    210         }
    211         renderer = renderer->nextInPreOrder(block);
    212     }
    213 
    214     return (linkCount >= 3);
    215 }
    216 
    217 static bool blockHeightConstrained(const RenderBlock* block)
    218 {
    219     // FIXME: Propagate constrainedness down the tree, to avoid inefficiently walking back up from each box.
    220     // FIXME: This code needs to take into account vertical writing modes.
    221     // FIXME: Consider additional heuristics, such as ignoring fixed heights if the content is already overflowing before autosizing kicks in.
    222     for (; block; block = block->containingBlock()) {
    223         RenderStyle* style = block->style();
    224         if (style->overflowY() >= OSCROLL)
    225             return false;
    226         if (style->height().isSpecified() || style->maxHeight().isSpecified() || block->isOutOfFlowPositioned()) {
    227             // Some sites (e.g. wikipedia) set their html and/or body elements to height:100%,
    228             // without intending to constrain the height of the content within them.
    229             return !block->isDocumentElement() && !block->isBody();
    230         }
    231         if (block->isFloating())
    232             return false;
    233     }
    234     return false;
    235 }
    236 
    237 static bool blockOrImmediateChildrenAreFormControls(const RenderBlock* block)
    238 {
    239     if (isNonTextAreaFormControl(block))
    240         return true;
    241     const RenderObject* renderer = block->firstChild();
    242     while (renderer) {
    243         if (isNonTextAreaFormControl(renderer))
    244             return true;
    245         renderer = renderer->nextSibling();
    246     }
    247 
    248     return false;
    249 }
    250 
    251 // Some blocks are not autosized even if their parent cluster wants them to.
    252 static bool blockSuppressesAutosizing(const RenderBlock* block)
    253 {
    254     if (blockOrImmediateChildrenAreFormControls(block))
    255         return true;
    256 
    257     if (blockIsRowOfLinks(block))
    258         return true;
    259 
    260     // Don't autosize block-level text that can't wrap (as it's likely to
    261     // expand sideways and break the page's layout).
    262     if (!block->style()->autoWrap())
    263         return true;
    264 
    265     if (blockHeightConstrained(block))
    266         return true;
    267 
    268     return false;
    269 }
    270 
    271 static bool hasExplicitWidth(const RenderBlock* block)
    272 {
    273     // FIXME: This heuristic may need to be expanded to other ways a block can be wider or narrower
    274     //        than its parent containing block.
    275     return block->style() && block->style()->width().isSpecified();
    276 }
    277 
    278 TextAutosizer::TextAutosizer(const Document* document)
    279     : m_document(document)
    280     , m_firstBlockToBeginLayout(0)
    281 #if ENABLE(ASSERT)
    282     , m_blocksThatHaveBegunLayout()
    283 #endif
    284     , m_superclusters()
    285     , m_clusterStack()
    286     , m_fingerprintMapper()
    287     , m_pageInfo()
    288     , m_updatePageInfoDeferred(false)
    289 {
    290 }
    291 
    292 void TextAutosizer::record(const RenderBlock* block)
    293 {
    294     if (!m_pageInfo.m_settingEnabled)
    295         return;
    296 
    297     ASSERT(!m_blocksThatHaveBegunLayout.contains(block));
    298 
    299     if (!classifyBlock(block, INDEPENDENT | EXPLICIT_WIDTH))
    300         return;
    301 
    302     if (Fingerprint fingerprint = computeFingerprint(block))
    303         m_fingerprintMapper.addTentativeClusterRoot(block, fingerprint);
    304 }
    305 
    306 void TextAutosizer::destroy(const RenderBlock* block)
    307 {
    308     if (!m_pageInfo.m_settingEnabled && !m_fingerprintMapper.hasFingerprints())
    309         return;
    310 
    311     ASSERT(!m_blocksThatHaveBegunLayout.contains(block));
    312 
    313     if (m_fingerprintMapper.remove(block) && m_firstBlockToBeginLayout) {
    314         // RenderBlock with a fingerprint was destroyed during layout.
    315         // Clear the cluster stack and the supercluster map to avoid stale pointers.
    316         // Speculative fix for http://crbug.com/369485.
    317         m_firstBlockToBeginLayout = 0;
    318         m_clusterStack.clear();
    319         m_superclusters.clear();
    320     }
    321 }
    322 
    323 TextAutosizer::BeginLayoutBehavior TextAutosizer::prepareForLayout(const RenderBlock* block)
    324 {
    325 #if ENABLE(ASSERT)
    326     m_blocksThatHaveBegunLayout.add(block);
    327 #endif
    328 
    329     if (!m_firstBlockToBeginLayout) {
    330         m_firstBlockToBeginLayout = block;
    331         prepareClusterStack(block->parent());
    332     } else if (block == currentCluster()->m_root) {
    333         // Ignore beginLayout on the same block twice.
    334         // This can happen with paginated overflow.
    335         return StopLayout;
    336     }
    337 
    338     return ContinueLayout;
    339 }
    340 
    341 void TextAutosizer::prepareClusterStack(const RenderObject* renderer)
    342 {
    343     if (!renderer)
    344         return;
    345     prepareClusterStack(renderer->parent());
    346 
    347     if (renderer->isRenderBlock()) {
    348         const RenderBlock* block = toRenderBlock(renderer);
    349 #if ENABLE(ASSERT)
    350         m_blocksThatHaveBegunLayout.add(block);
    351 #endif
    352         if (Cluster* cluster = maybeCreateCluster(block))
    353             m_clusterStack.append(adoptPtr(cluster));
    354     }
    355 }
    356 
    357 void TextAutosizer::beginLayout(RenderBlock* block)
    358 {
    359     ASSERT(shouldHandleLayout());
    360 
    361     if (prepareForLayout(block) == StopLayout)
    362         return;
    363 
    364     if (Cluster* cluster = maybeCreateCluster(block))
    365         m_clusterStack.append(adoptPtr(cluster));
    366 
    367     // Cells in auto-layout tables are handled separately by inflateAutoTable.
    368     bool isAutoTableCell = block->isTableCell() && !toRenderTableCell(block)->table()->style()->isFixedTableLayout();
    369     if (!isAutoTableCell && !m_clusterStack.isEmpty())
    370         inflate(block);
    371 }
    372 
    373 void TextAutosizer::inflateListItem(RenderListItem* listItem, RenderListMarker* listItemMarker)
    374 {
    375     if (!shouldHandleLayout())
    376         return;
    377     ASSERT(listItem && listItemMarker);
    378 
    379     if (prepareForLayout(listItem) == StopLayout)
    380         return;
    381 
    382     // Force the LI to be inside the DBCAT when computing the multiplier.
    383     // This guarantees that the DBCAT has entered layout, so we can ask for its width.
    384     // It also makes sense because the list marker is autosized like a text node.
    385     float multiplier = clusterMultiplier(currentCluster());
    386 
    387     applyMultiplier(listItem, multiplier);
    388     applyMultiplier(listItemMarker, multiplier);
    389 }
    390 
    391 void TextAutosizer::inflateAutoTable(RenderTable* table)
    392 {
    393     ASSERT(table);
    394     ASSERT(!table->style()->isFixedTableLayout());
    395     ASSERT(table->containingBlock());
    396 
    397     Cluster* cluster = currentCluster();
    398     if (cluster->m_root != table)
    399         return;
    400 
    401     // Pre-inflate cells that have enough text so that their inflated preferred widths will be used
    402     // for column sizing.
    403     for (RenderObject* section = table->firstChild(); section; section = section->nextSibling()) {
    404         if (!section->isTableSection())
    405             continue;
    406         for (RenderTableRow* row = toRenderTableSection(section)->firstRow(); row; row = row->nextRow()) {
    407             for (RenderTableCell* cell = row->firstCell(); cell; cell = cell->nextCell()) {
    408                 if (!cell->needsLayout())
    409                     continue;
    410 
    411                 beginLayout(cell);
    412                 inflate(cell, DescendToInnerBlocks);
    413                 endLayout(cell);
    414             }
    415         }
    416     }
    417 }
    418 
    419 void TextAutosizer::endLayout(RenderBlock* block)
    420 {
    421     ASSERT(shouldHandleLayout());
    422 
    423     if (block == m_firstBlockToBeginLayout) {
    424         m_firstBlockToBeginLayout = 0;
    425         m_clusterStack.clear();
    426         m_superclusters.clear();
    427         m_stylesRetainedDuringLayout.clear();
    428 #if ENABLE(ASSERT)
    429         m_blocksThatHaveBegunLayout.clear();
    430 #endif
    431     // Tables can create two layout scopes for the same block so the isEmpty
    432     // check below is needed to guard against endLayout being called twice.
    433     } else if (!m_clusterStack.isEmpty() && currentCluster()->m_root == block) {
    434         m_clusterStack.removeLast();
    435     }
    436 }
    437 
    438 float TextAutosizer::inflate(RenderObject* parent, InflateBehavior behavior, float multiplier)
    439 {
    440     Cluster* cluster = currentCluster();
    441     bool hasTextChild = false;
    442 
    443     RenderObject* child = 0;
    444     if (parent->isRenderBlock() && (parent->childrenInline() || behavior == DescendToInnerBlocks))
    445         child = toRenderBlock(parent)->firstChild();
    446     else if (parent->isRenderInline())
    447         child = toRenderInline(parent)->firstChild();
    448 
    449     while (child) {
    450         if (child->isText()) {
    451             hasTextChild = true;
    452             // We only calculate this multiplier on-demand to ensure the parent block of this text
    453             // has entered layout.
    454             if (!multiplier)
    455                 multiplier = cluster->m_flags & SUPPRESSING ? 1.0f : clusterMultiplier(cluster);
    456             applyMultiplier(child, multiplier);
    457             // FIXME: Investigate why MarkOnlyThis is sufficient.
    458             if (parent->isRenderInline())
    459                 child->setPreferredLogicalWidthsDirty(MarkOnlyThis);
    460         } else if (child->isRenderInline()) {
    461             multiplier = inflate(child, behavior, multiplier);
    462         } else if (child->isRenderBlock() && behavior == DescendToInnerBlocks
    463             && !classifyBlock(child, INDEPENDENT | EXPLICIT_WIDTH | SUPPRESSING)) {
    464             multiplier = inflate(child, behavior, multiplier);
    465         }
    466         child = child->nextSibling();
    467     }
    468 
    469     if (hasTextChild) {
    470         applyMultiplier(parent, multiplier); // Parent handles line spacing.
    471     } else if (!parent->isListItem()) {
    472         // For consistency, a block with no immediate text child should always have a
    473         // multiplier of 1 (except for list items which are handled in inflateListItem).
    474         applyMultiplier(parent, 1);
    475     }
    476     return multiplier;
    477 }
    478 
    479 bool TextAutosizer::shouldHandleLayout() const
    480 {
    481     return m_pageInfo.m_settingEnabled && m_pageInfo.m_pageNeedsAutosizing && !m_updatePageInfoDeferred;
    482 }
    483 
    484 void TextAutosizer::updatePageInfoInAllFrames()
    485 {
    486     ASSERT(!m_document->frame() || m_document->frame()->isMainFrame());
    487 
    488     for (Frame* frame = m_document->frame(); frame; frame = frame->tree().traverseNext()) {
    489         if (!frame->isLocalFrame())
    490             continue;
    491         if (TextAutosizer* textAutosizer = toLocalFrame(frame)->document()->textAutosizer())
    492             textAutosizer->updatePageInfo();
    493     }
    494 }
    495 
    496 void TextAutosizer::updatePageInfo()
    497 {
    498     if (m_updatePageInfoDeferred || !m_document->page() || !m_document->settings())
    499         return;
    500 
    501     PageInfo previousPageInfo(m_pageInfo);
    502     m_pageInfo.m_settingEnabled = m_document->settings()->textAutosizingEnabled();
    503 
    504     if (!m_pageInfo.m_settingEnabled || m_document->printing()) {
    505         m_pageInfo.m_pageNeedsAutosizing = false;
    506     } else {
    507         RenderView* renderView = m_document->renderView();
    508         bool horizontalWritingMode = isHorizontalWritingMode(renderView->style()->writingMode());
    509 
    510         LocalFrame* mainFrame = m_document->page()->deprecatedLocalMainFrame();
    511         IntSize frameSize = m_document->settings()->textAutosizingWindowSizeOverride();
    512         if (frameSize.isEmpty())
    513             frameSize = mainFrame->view()->unscaledVisibleContentSize(IncludeScrollbars);
    514         m_pageInfo.m_frameWidth = horizontalWritingMode ? frameSize.width() : frameSize.height();
    515 
    516         IntSize layoutSize = mainFrame->view()->layoutSize();
    517         m_pageInfo.m_layoutWidth = horizontalWritingMode ? layoutSize.width() : layoutSize.height();
    518 
    519         // Compute the base font scale multiplier based on device and accessibility settings.
    520         m_pageInfo.m_baseMultiplier = m_document->settings()->accessibilityFontScaleFactor();
    521         // If the page has a meta viewport or @viewport, don't apply the device scale adjustment.
    522         const ViewportDescription& viewportDescription = mainFrame->document()->viewportDescription();
    523         if (!viewportDescription.isSpecifiedByAuthor()) {
    524             float deviceScaleAdjustment = m_document->settings()->deviceScaleAdjustment();
    525             m_pageInfo.m_baseMultiplier *= deviceScaleAdjustment;
    526         }
    527 
    528         m_pageInfo.m_pageNeedsAutosizing = !!m_pageInfo.m_frameWidth
    529             && (m_pageInfo.m_baseMultiplier * (static_cast<float>(m_pageInfo.m_layoutWidth) / m_pageInfo.m_frameWidth) > 1.0f);
    530     }
    531 
    532     if (m_pageInfo.m_pageNeedsAutosizing) {
    533         // If page info has changed, multipliers may have changed. Force a layout to recompute them.
    534         if (m_pageInfo.m_frameWidth != previousPageInfo.m_frameWidth
    535             || m_pageInfo.m_layoutWidth != previousPageInfo.m_layoutWidth
    536             || m_pageInfo.m_baseMultiplier != previousPageInfo.m_baseMultiplier
    537             || m_pageInfo.m_settingEnabled != previousPageInfo.m_settingEnabled)
    538             setAllTextNeedsLayout();
    539     } else if (previousPageInfo.m_hasAutosized) {
    540         // If we are no longer autosizing the page, we won't do anything during the next layout.
    541         // Set all the multipliers back to 1 now.
    542         resetMultipliers();
    543         m_pageInfo.m_hasAutosized = false;
    544     }
    545 }
    546 
    547 void TextAutosizer::resetMultipliers()
    548 {
    549     RenderObject* renderer = m_document->renderView();
    550     while (renderer) {
    551         if (RenderStyle* style = renderer->style()) {
    552             if (style->textAutosizingMultiplier() != 1)
    553                 applyMultiplier(renderer, 1, LayoutNeeded);
    554         }
    555         renderer = renderer->nextInPreOrder();
    556     }
    557 }
    558 
    559 void TextAutosizer::setAllTextNeedsLayout()
    560 {
    561     RenderObject* renderer = m_document->renderView();
    562     while (renderer) {
    563         if (renderer->isText())
    564             renderer->setNeedsLayoutAndFullPaintInvalidation();
    565         renderer = renderer->nextInPreOrder();
    566     }
    567 }
    568 
    569 TextAutosizer::BlockFlags TextAutosizer::classifyBlock(const RenderObject* renderer, BlockFlags mask) const
    570 {
    571     if (!renderer->isRenderBlock())
    572         return 0;
    573 
    574     const RenderBlock* block = toRenderBlock(renderer);
    575     BlockFlags flags = 0;
    576 
    577     if (isPotentialClusterRoot(block)) {
    578         if (mask & POTENTIAL_ROOT)
    579             flags |= POTENTIAL_ROOT;
    580 
    581         if ((mask & INDEPENDENT) && (isIndependentDescendant(block) || block->isTable()))
    582             flags |= INDEPENDENT;
    583 
    584         if ((mask & EXPLICIT_WIDTH) && hasExplicitWidth(block))
    585             flags |= EXPLICIT_WIDTH;
    586 
    587         if ((mask & SUPPRESSING) && blockSuppressesAutosizing(block))
    588             flags |= SUPPRESSING;
    589     }
    590     return flags;
    591 }
    592 
    593 bool TextAutosizer::clusterWouldHaveEnoughTextToAutosize(const RenderBlock* root, const RenderBlock* widthProvider)
    594 {
    595     Cluster hypotheticalCluster(root, classifyBlock(root), 0);
    596     return clusterHasEnoughTextToAutosize(&hypotheticalCluster, widthProvider);
    597 }
    598 
    599 bool TextAutosizer::clusterHasEnoughTextToAutosize(Cluster* cluster, const RenderBlock* widthProvider)
    600 {
    601     if (cluster->m_hasEnoughTextToAutosize != UnknownAmountOfText)
    602         return cluster->m_hasEnoughTextToAutosize == HasEnoughText;
    603 
    604     const RenderBlock* root = cluster->m_root;
    605     if (!widthProvider)
    606         widthProvider = clusterWidthProvider(root);
    607 
    608     // TextAreas and user-modifiable areas get a free pass to autosize regardless of text content.
    609     if (root->isTextArea() || (root->style() && root->style()->userModify() != READ_ONLY)) {
    610         cluster->m_hasEnoughTextToAutosize = HasEnoughText;
    611         return true;
    612     }
    613 
    614     if (cluster->m_flags & SUPPRESSING) {
    615         cluster->m_hasEnoughTextToAutosize = NotEnoughText;
    616         return false;
    617     }
    618 
    619     // 4 lines of text is considered enough to autosize.
    620     float minimumTextLengthToAutosize = widthFromBlock(widthProvider) * 4;
    621 
    622     float length = 0;
    623     RenderObject* descendant = root->firstChild();
    624     while (descendant) {
    625         if (descendant->isRenderBlock()) {
    626             if (classifyBlock(descendant, INDEPENDENT | SUPPRESSING)) {
    627                 descendant = descendant->nextInPreOrderAfterChildren(root);
    628                 continue;
    629             }
    630         } else if (descendant->isText()) {
    631             // Note: Using text().stripWhiteSpace().length() instead of renderedTextLength() because
    632             // the lineboxes will not be built until layout. These values can be different.
    633             // Note: This is an approximation assuming each character is 1em wide.
    634             length += toRenderText(descendant)->text().stripWhiteSpace().length() * descendant->style()->specifiedFontSize();
    635 
    636             if (length >= minimumTextLengthToAutosize) {
    637                 cluster->m_hasEnoughTextToAutosize = HasEnoughText;
    638                 return true;
    639             }
    640         }
    641         descendant = descendant->nextInPreOrder(root);
    642     }
    643 
    644     cluster->m_hasEnoughTextToAutosize = NotEnoughText;
    645     return false;
    646 }
    647 
    648 TextAutosizer::Fingerprint TextAutosizer::getFingerprint(const RenderObject* renderer)
    649 {
    650     Fingerprint result = m_fingerprintMapper.get(renderer);
    651     if (!result) {
    652         result = computeFingerprint(renderer);
    653         m_fingerprintMapper.add(renderer, result);
    654     }
    655     return result;
    656 }
    657 
    658 TextAutosizer::Fingerprint TextAutosizer::computeFingerprint(const RenderObject* renderer)
    659 {
    660     Node* node = renderer->generatingNode();
    661     if (!node || !node->isElementNode())
    662         return 0;
    663 
    664     FingerprintSourceData data;
    665     if (const RenderObject* parent = parentElementRenderer(renderer))
    666         data.m_parentHash = getFingerprint(parent);
    667 
    668     data.m_qualifiedNameHash = QualifiedNameHash::hash(toElement(node)->tagQName());
    669 
    670     if (RenderStyle* style = renderer->style()) {
    671         data.m_packedStyleProperties = style->direction();
    672         data.m_packedStyleProperties |= (style->position() << 1);
    673         data.m_packedStyleProperties |= (style->floating() << 4);
    674         data.m_packedStyleProperties |= (style->display() << 6);
    675         data.m_packedStyleProperties |= (style->width().type() << 11);
    676         // packedStyleProperties effectively using 15 bits now.
    677 
    678         // consider for adding: writing mode, padding.
    679 
    680         data.m_width = style->width().getFloatValue();
    681     }
    682 
    683     // Use nodeIndex as a rough approximation of column number
    684     // (it's too early to call RenderTableCell::col).
    685     // FIXME: account for colspan
    686     if (renderer->isTableCell())
    687         data.m_column = renderer->node()->nodeIndex();
    688 
    689     return StringHasher::computeHash<UChar>(
    690         static_cast<const UChar*>(static_cast<const void*>(&data)),
    691         sizeof data / sizeof(UChar));
    692 }
    693 
    694 TextAutosizer::Cluster* TextAutosizer::maybeCreateCluster(const RenderBlock* block)
    695 {
    696     BlockFlags flags = classifyBlock(block);
    697     if (!(flags & POTENTIAL_ROOT))
    698         return 0;
    699 
    700     Cluster* parentCluster = m_clusterStack.isEmpty() ? 0 : currentCluster();
    701     ASSERT(parentCluster || block->isRenderView());
    702 
    703     // If a non-independent block would not alter the SUPPRESSING flag, it doesn't need to be a cluster.
    704     bool parentSuppresses = parentCluster && (parentCluster->m_flags & SUPPRESSING);
    705     if (!(flags & INDEPENDENT) && !(flags & EXPLICIT_WIDTH) && !!(flags & SUPPRESSING) == parentSuppresses)
    706         return 0;
    707 
    708     Cluster* cluster = new Cluster(block, flags, parentCluster, getSupercluster(block));
    709 #ifdef AUTOSIZING_DOM_DEBUG_INFO
    710     // Non-SUPPRESSING clusters are annotated in clusterMultiplier.
    711     if (flags & SUPPRESSING)
    712         writeClusterDebugInfo(cluster);
    713 #endif
    714     return cluster;
    715 }
    716 
    717 TextAutosizer::Supercluster* TextAutosizer::getSupercluster(const RenderBlock* block)
    718 {
    719     Fingerprint fingerprint = m_fingerprintMapper.get(block);
    720     if (!fingerprint)
    721         return 0;
    722 
    723     BlockSet* roots = m_fingerprintMapper.getTentativeClusterRoots(fingerprint);
    724     if (!roots || roots->size() < 2 || !roots->contains(block))
    725         return 0;
    726 
    727     SuperclusterMap::AddResult addResult = m_superclusters.add(fingerprint, PassOwnPtr<Supercluster>());
    728     if (!addResult.isNewEntry)
    729         return addResult.storedValue->value.get();
    730 
    731     Supercluster* supercluster = new Supercluster(roots);
    732     addResult.storedValue->value = adoptPtr(supercluster);
    733     return supercluster;
    734 }
    735 
    736 float TextAutosizer::clusterMultiplier(Cluster* cluster)
    737 {
    738     if (cluster->m_multiplier)
    739         return cluster->m_multiplier;
    740 
    741     // FIXME: why does isWiderOrNarrowerDescendant crash on independent clusters?
    742     if (!(cluster->m_flags & INDEPENDENT) && isWiderOrNarrowerDescendant(cluster))
    743         cluster->m_flags |= WIDER_OR_NARROWER;
    744 
    745     if (cluster->m_flags & (INDEPENDENT | WIDER_OR_NARROWER)) {
    746         if (cluster->m_supercluster)
    747             cluster->m_multiplier = superclusterMultiplier(cluster);
    748         else if (clusterHasEnoughTextToAutosize(cluster))
    749             cluster->m_multiplier = multiplierFromBlock(clusterWidthProvider(cluster->m_root));
    750         else
    751             cluster->m_multiplier = 1.0f;
    752     } else {
    753         cluster->m_multiplier = cluster->m_parent ? clusterMultiplier(cluster->m_parent) : 1.0f;
    754     }
    755 
    756 #ifdef AUTOSIZING_DOM_DEBUG_INFO
    757     writeClusterDebugInfo(cluster);
    758 #endif
    759 
    760     ASSERT(cluster->m_multiplier);
    761     return cluster->m_multiplier;
    762 }
    763 
    764 bool TextAutosizer::superclusterHasEnoughTextToAutosize(Supercluster* supercluster, const RenderBlock* widthProvider)
    765 {
    766     if (supercluster->m_hasEnoughTextToAutosize != UnknownAmountOfText)
    767         return supercluster->m_hasEnoughTextToAutosize == HasEnoughText;
    768 
    769     BlockSet::iterator end = supercluster->m_roots->end();
    770     for (BlockSet::iterator it = supercluster->m_roots->begin(); it != end; ++it) {
    771         if (clusterWouldHaveEnoughTextToAutosize(*it, widthProvider)) {
    772             supercluster->m_hasEnoughTextToAutosize = HasEnoughText;
    773             return true;
    774         }
    775     }
    776     supercluster->m_hasEnoughTextToAutosize = NotEnoughText;
    777     return false;
    778 }
    779 
    780 float TextAutosizer::superclusterMultiplier(Cluster* cluster)
    781 {
    782     Supercluster* supercluster = cluster->m_supercluster;
    783     if (!supercluster->m_multiplier) {
    784         const RenderBlock* widthProvider = maxClusterWidthProvider(cluster->m_supercluster, cluster->m_root);
    785         supercluster->m_multiplier = superclusterHasEnoughTextToAutosize(supercluster, widthProvider)
    786             ? multiplierFromBlock(widthProvider) : 1.0f;
    787     }
    788     ASSERT(supercluster->m_multiplier);
    789     return supercluster->m_multiplier;
    790 }
    791 
    792 const RenderBlock* TextAutosizer::clusterWidthProvider(const RenderBlock* root) const
    793 {
    794     if (root->isTable() || root->isTableCell())
    795         return root;
    796 
    797     return deepestBlockContainingAllText(root);
    798 }
    799 
    800 const RenderBlock* TextAutosizer::maxClusterWidthProvider(const Supercluster* supercluster, const RenderBlock* currentRoot) const
    801 {
    802     const RenderBlock* result = clusterWidthProvider(currentRoot);
    803     float maxWidth = widthFromBlock(result);
    804 
    805     const BlockSet* roots = supercluster->m_roots;
    806     for (BlockSet::iterator it = roots->begin(); it != roots->end(); ++it) {
    807         const RenderBlock* widthProvider = clusterWidthProvider(*it);
    808         if (widthProvider->needsLayout())
    809             continue;
    810         float width = widthFromBlock(widthProvider);
    811         if (width > maxWidth) {
    812             maxWidth = width;
    813             result = widthProvider;
    814         }
    815     }
    816     RELEASE_ASSERT(result);
    817     return result;
    818 }
    819 
    820 float TextAutosizer::widthFromBlock(const RenderBlock* block) const
    821 {
    822     RELEASE_ASSERT(block);
    823     RELEASE_ASSERT(block->style());
    824 
    825     if (!(block->isTable() || block->isTableCell() || block->isListItem()))
    826         return block->contentLogicalWidth().toFloat();
    827 
    828     if (!block->containingBlock())
    829         return 0;
    830 
    831     // Tables may be inflated before computing their preferred widths. Try several methods to
    832     // obtain a width, and fall back on a containing block's width.
    833     for (; block; block = block->containingBlock()) {
    834         float width;
    835         Length specifiedWidth = block->isTableCell()
    836             ? toRenderTableCell(block)->styleOrColLogicalWidth() : block->style()->logicalWidth();
    837         if (specifiedWidth.isFixed()) {
    838             if ((width = specifiedWidth.value()) > 0)
    839                 return width;
    840         }
    841         if (specifiedWidth.isPercent()) {
    842             if (float containerWidth = block->containingBlock()->contentLogicalWidth().toFloat()) {
    843                 if ((width = floatValueForLength(specifiedWidth, containerWidth)) > 0)
    844                     return width;
    845             }
    846         }
    847         if ((width = block->contentLogicalWidth().toFloat()) > 0)
    848             return width;
    849     }
    850     return 0;
    851 }
    852 
    853 float TextAutosizer::multiplierFromBlock(const RenderBlock* block)
    854 {
    855     // If block->needsLayout() is false, it does not need to be in m_blocksThatHaveBegunLayout.
    856     // This can happen during layout of a positioned object if the cluster's DBCAT is deeper
    857     // than the positioned object's containing block, and wasn't marked as needing layout.
    858     ASSERT(m_blocksThatHaveBegunLayout.contains(block) || !block->needsLayout());
    859 
    860     // Block width, in CSS pixels.
    861     float blockWidth = widthFromBlock(block);
    862     float multiplier = m_pageInfo.m_frameWidth ? std::min(blockWidth, static_cast<float>(m_pageInfo.m_layoutWidth)) / m_pageInfo.m_frameWidth : 1.0f;
    863 
    864     return std::max(m_pageInfo.m_baseMultiplier * multiplier, 1.0f);
    865 }
    866 
    867 const RenderBlock* TextAutosizer::deepestBlockContainingAllText(Cluster* cluster)
    868 {
    869     if (!cluster->m_deepestBlockContainingAllText)
    870         cluster->m_deepestBlockContainingAllText = deepestBlockContainingAllText(cluster->m_root);
    871 
    872     return cluster->m_deepestBlockContainingAllText;
    873 }
    874 
    875 // FIXME: Refactor this to look more like TextAutosizer::deepestCommonAncestor.
    876 const RenderBlock* TextAutosizer::deepestBlockContainingAllText(const RenderBlock* root) const
    877 {
    878     size_t firstDepth = 0;
    879     const RenderObject* firstTextLeaf = findTextLeaf(root, firstDepth, First);
    880     if (!firstTextLeaf)
    881         return root;
    882 
    883     size_t lastDepth = 0;
    884     const RenderObject* lastTextLeaf = findTextLeaf(root, lastDepth, Last);
    885     ASSERT(lastTextLeaf);
    886 
    887     // Equalize the depths if necessary. Only one of the while loops below will get executed.
    888     const RenderObject* firstNode = firstTextLeaf;
    889     const RenderObject* lastNode = lastTextLeaf;
    890     while (firstDepth > lastDepth) {
    891         firstNode = firstNode->parent();
    892         --firstDepth;
    893     }
    894     while (lastDepth > firstDepth) {
    895         lastNode = lastNode->parent();
    896         --lastDepth;
    897     }
    898 
    899     // Go up from both nodes until the parent is the same. Both pointers will point to the LCA then.
    900     while (firstNode != lastNode) {
    901         firstNode = firstNode->parent();
    902         lastNode = lastNode->parent();
    903     }
    904 
    905     if (firstNode->isRenderBlock())
    906         return toRenderBlock(firstNode);
    907 
    908     // containingBlock() should never leave the cluster, since it only skips ancestors when finding
    909     // the container of position:absolute/fixed blocks, and those cannot exist between a cluster and
    910     // its text node's lowest common ancestor as isAutosizingCluster would have made them into their
    911     // own independent cluster.
    912     const RenderBlock* containingBlock = firstNode->containingBlock();
    913     if (!containingBlock)
    914         return root;
    915 
    916     ASSERT(containingBlock->isDescendantOf(root));
    917     return containingBlock;
    918 }
    919 
    920 const RenderObject* TextAutosizer::findTextLeaf(const RenderObject* parent, size_t& depth, TextLeafSearch firstOrLast) const
    921 {
    922     // List items are treated as text due to the marker.
    923     // The actual renderer for the marker (RenderListMarker) may not be in the tree yet since it is added during layout.
    924     if (parent->isListItem())
    925         return parent;
    926 
    927     if (parent->isText())
    928         return parent;
    929 
    930     ++depth;
    931     const RenderObject* child = (firstOrLast == First) ? parent->slowFirstChild() : parent->slowLastChild();
    932     while (child) {
    933         // Note: At this point clusters may not have been created for these blocks so we cannot rely
    934         //       on m_clusters. Instead, we use a best-guess about whether the block will become a cluster.
    935         if (!classifyBlock(child, INDEPENDENT)) {
    936             if (const RenderObject* leaf = findTextLeaf(child, depth, firstOrLast))
    937                 return leaf;
    938         }
    939         child = (firstOrLast == First) ? child->nextSibling() : child->previousSibling();
    940     }
    941     --depth;
    942 
    943     return 0;
    944 }
    945 
    946 void TextAutosizer::applyMultiplier(RenderObject* renderer, float multiplier, RelayoutBehavior relayoutBehavior)
    947 {
    948     ASSERT(renderer && renderer->style());
    949     RenderStyle* currentStyle = renderer->style();
    950     if (currentStyle->textAutosizingMultiplier() == multiplier)
    951         return;
    952 
    953     // We need to clone the render style to avoid breaking style sharing.
    954     RefPtr<RenderStyle> style = RenderStyle::clone(currentStyle);
    955     style->setTextAutosizingMultiplier(multiplier);
    956     style->setUnique();
    957 
    958     switch (relayoutBehavior) {
    959     case AlreadyInLayout:
    960         // Don't free currentStyle until the end of the layout pass. This allows other parts of the system
    961         // to safely hold raw RenderStyle* pointers during layout, e.g. BreakingContext::m_currentStyle.
    962         m_stylesRetainedDuringLayout.append(currentStyle);
    963 
    964         renderer->setStyleInternal(style.release());
    965         renderer->setNeedsLayoutAndFullPaintInvalidation();
    966         break;
    967 
    968     case LayoutNeeded:
    969         renderer->setStyle(style.release());
    970         break;
    971     }
    972 
    973     if (multiplier != 1)
    974         m_pageInfo.m_hasAutosized = true;
    975 }
    976 
    977 bool TextAutosizer::isWiderOrNarrowerDescendant(Cluster* cluster)
    978 {
    979     // FIXME: Why do we return true when hasExplicitWidth returns false??
    980     if (!cluster->m_parent || !hasExplicitWidth(cluster->m_root))
    981         return true;
    982 
    983     const RenderBlock* parentDeepestBlockContainingAllText = deepestBlockContainingAllText(cluster->m_parent);
    984     ASSERT(m_blocksThatHaveBegunLayout.contains(cluster->m_root));
    985     ASSERT(m_blocksThatHaveBegunLayout.contains(parentDeepestBlockContainingAllText));
    986 
    987     float contentWidth = cluster->m_root->contentLogicalWidth().toFloat();
    988     float clusterTextWidth = parentDeepestBlockContainingAllText->contentLogicalWidth().toFloat();
    989 
    990     // Clusters with a root that is wider than the deepestBlockContainingAllText of their parent
    991     // autosize independently of their parent.
    992     if (contentWidth > clusterTextWidth)
    993         return true;
    994 
    995     // Clusters with a root that is significantly narrower than the deepestBlockContainingAllText of
    996     // their parent autosize independently of their parent.
    997     static float narrowWidthDifference = 200;
    998     if (clusterTextWidth - contentWidth > narrowWidthDifference)
    999         return true;
   1000 
   1001     return false;
   1002 }
   1003 
   1004 TextAutosizer::Cluster* TextAutosizer::currentCluster() const
   1005 {
   1006     ASSERT_WITH_SECURITY_IMPLICATION(!m_clusterStack.isEmpty());
   1007     return m_clusterStack.last().get();
   1008 }
   1009 
   1010 #if ENABLE(ASSERT)
   1011 void TextAutosizer::FingerprintMapper::assertMapsAreConsistent()
   1012 {
   1013     // For each fingerprint -> block mapping in m_blocksForFingerprint we should have an associated
   1014     // map from block -> fingerprint in m_fingerprints.
   1015     ReverseFingerprintMap::iterator end = m_blocksForFingerprint.end();
   1016     for (ReverseFingerprintMap::iterator fingerprintIt = m_blocksForFingerprint.begin(); fingerprintIt != end; ++fingerprintIt) {
   1017         Fingerprint fingerprint = fingerprintIt->key;
   1018         BlockSet* blocks = fingerprintIt->value.get();
   1019         for (BlockSet::iterator blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt) {
   1020             const RenderBlock* block = (*blockIt);
   1021             ASSERT(m_fingerprints.get(block) == fingerprint);
   1022         }
   1023     }
   1024 }
   1025 #endif
   1026 
   1027 void TextAutosizer::FingerprintMapper::add(const RenderObject* renderer, Fingerprint fingerprint)
   1028 {
   1029     remove(renderer);
   1030 
   1031     m_fingerprints.set(renderer, fingerprint);
   1032 #if ENABLE(ASSERT)
   1033     assertMapsAreConsistent();
   1034 #endif
   1035 }
   1036 
   1037 void TextAutosizer::FingerprintMapper::addTentativeClusterRoot(const RenderBlock* block, Fingerprint fingerprint)
   1038 {
   1039     add(block, fingerprint);
   1040 
   1041     ReverseFingerprintMap::AddResult addResult = m_blocksForFingerprint.add(fingerprint, PassOwnPtr<BlockSet>());
   1042     if (addResult.isNewEntry)
   1043         addResult.storedValue->value = adoptPtr(new BlockSet);
   1044     addResult.storedValue->value->add(block);
   1045 #if ENABLE(ASSERT)
   1046     assertMapsAreConsistent();
   1047 #endif
   1048 }
   1049 
   1050 bool TextAutosizer::FingerprintMapper::remove(const RenderObject* renderer)
   1051 {
   1052     Fingerprint fingerprint = m_fingerprints.take(renderer);
   1053     if (!fingerprint || !renderer->isRenderBlock())
   1054         return false;
   1055 
   1056     ReverseFingerprintMap::iterator blocksIter = m_blocksForFingerprint.find(fingerprint);
   1057     if (blocksIter == m_blocksForFingerprint.end())
   1058         return false;
   1059 
   1060     BlockSet& blocks = *blocksIter->value;
   1061     blocks.remove(toRenderBlock(renderer));
   1062     if (blocks.isEmpty())
   1063         m_blocksForFingerprint.remove(blocksIter);
   1064 #if ENABLE(ASSERT)
   1065     assertMapsAreConsistent();
   1066 #endif
   1067     return true;
   1068 }
   1069 
   1070 TextAutosizer::Fingerprint TextAutosizer::FingerprintMapper::get(const RenderObject* renderer)
   1071 {
   1072     return m_fingerprints.get(renderer);
   1073 }
   1074 
   1075 TextAutosizer::BlockSet* TextAutosizer::FingerprintMapper::getTentativeClusterRoots(Fingerprint fingerprint)
   1076 {
   1077     return m_blocksForFingerprint.get(fingerprint);
   1078 }
   1079 
   1080 TextAutosizer::LayoutScope::LayoutScope(RenderBlock* block)
   1081     : m_textAutosizer(block->document().textAutosizer())
   1082     , m_block(block)
   1083 {
   1084     if (!m_textAutosizer)
   1085         return;
   1086 
   1087     if (m_textAutosizer->shouldHandleLayout())
   1088         m_textAutosizer->beginLayout(m_block);
   1089     else
   1090         m_textAutosizer = 0;
   1091 }
   1092 
   1093 TextAutosizer::LayoutScope::~LayoutScope()
   1094 {
   1095     if (m_textAutosizer)
   1096         m_textAutosizer->endLayout(m_block);
   1097 }
   1098 
   1099 
   1100 TextAutosizer::TableLayoutScope::TableLayoutScope(RenderTable* table)
   1101     : LayoutScope(table)
   1102 {
   1103     if (m_textAutosizer) {
   1104         ASSERT(m_textAutosizer->shouldHandleLayout());
   1105         m_textAutosizer->inflateAutoTable(table);
   1106     }
   1107 }
   1108 
   1109 TextAutosizer::DeferUpdatePageInfo::DeferUpdatePageInfo(Page* page)
   1110     : m_mainFrame(page->deprecatedLocalMainFrame())
   1111 {
   1112     if (TextAutosizer* textAutosizer = m_mainFrame->document()->textAutosizer()) {
   1113         ASSERT(!textAutosizer->m_updatePageInfoDeferred);
   1114         textAutosizer->m_updatePageInfoDeferred = true;
   1115     }
   1116 }
   1117 
   1118 TextAutosizer::DeferUpdatePageInfo::~DeferUpdatePageInfo()
   1119 {
   1120     if (TextAutosizer* textAutosizer = m_mainFrame->document()->textAutosizer()) {
   1121         ASSERT(textAutosizer->m_updatePageInfoDeferred);
   1122         textAutosizer->m_updatePageInfoDeferred = false;
   1123         textAutosizer->updatePageInfoInAllFrames();
   1124     }
   1125 }
   1126 
   1127 float TextAutosizer::computeAutosizedFontSize(float specifiedSize, float multiplier)
   1128 {
   1129     // Somewhat arbitrary "pleasant" font size.
   1130     const float pleasantSize = 16;
   1131 
   1132     // Multiply fonts that the page author has specified to be larger than
   1133     // pleasantSize by less and less, until huge fonts are not increased at all.
   1134     // For specifiedSize between 0 and pleasantSize we directly apply the
   1135     // multiplier; hence for specifiedSize == pleasantSize, computedSize will be
   1136     // multiplier * pleasantSize. For greater specifiedSizes we want to
   1137     // gradually fade out the multiplier, so for every 1px increase in
   1138     // specifiedSize beyond pleasantSize we will only increase computedSize
   1139     // by gradientAfterPleasantSize px until we meet the
   1140     // computedSize = specifiedSize line, after which we stay on that line (so
   1141     // then every 1px increase in specifiedSize increases computedSize by 1px).
   1142     const float gradientAfterPleasantSize = 0.5;
   1143 
   1144     float computedSize;
   1145     if (specifiedSize <= pleasantSize) {
   1146         computedSize = multiplier * specifiedSize;
   1147     } else {
   1148         computedSize = multiplier * pleasantSize + gradientAfterPleasantSize * (specifiedSize - pleasantSize);
   1149         if (computedSize < specifiedSize)
   1150             computedSize = specifiedSize;
   1151     }
   1152     return computedSize;
   1153 }
   1154 
   1155 void TextAutosizer::trace(Visitor* visitor)
   1156 {
   1157     visitor->trace(m_document);
   1158 }
   1159 
   1160 } // namespace blink
   1161