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
51namespace blink {
52
53#ifdef AUTOSIZING_DOM_DEBUG_INFO
54class WriteDebugInfoTask : public ExecutionContextTask {
55public:
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
67private:
68    RefPtrWillBePersistent<Element> m_element;
69    AtomicString m_value;
70};
71
72static 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
84void 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
116static 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
130static 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
140static 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
162static 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
180static 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
217static 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
237static 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.
252static 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
271static 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
278TextAutosizer::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
292void 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
306void 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
323TextAutosizer::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
341void 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
357void 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
373void 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
391void 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
419void 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
438float 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
479bool TextAutosizer::shouldHandleLayout() const
480{
481    return m_pageInfo.m_settingEnabled && m_pageInfo.m_pageNeedsAutosizing && !m_updatePageInfoDeferred;
482}
483
484void 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
496void 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
547void 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
559void 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
569TextAutosizer::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
593bool TextAutosizer::clusterWouldHaveEnoughTextToAutosize(const RenderBlock* root, const RenderBlock* widthProvider)
594{
595    Cluster hypotheticalCluster(root, classifyBlock(root), 0);
596    return clusterHasEnoughTextToAutosize(&hypotheticalCluster, widthProvider);
597}
598
599bool 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
648TextAutosizer::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
658TextAutosizer::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
694TextAutosizer::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
717TextAutosizer::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
736float 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
764bool 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
780float 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
792const RenderBlock* TextAutosizer::clusterWidthProvider(const RenderBlock* root) const
793{
794    if (root->isTable() || root->isTableCell())
795        return root;
796
797    return deepestBlockContainingAllText(root);
798}
799
800const 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
820float 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
853float 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
867const 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.
876const 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
920const 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
946void 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
977bool 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
1004TextAutosizer::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)
1011void 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
1027void 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
1037void 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
1050bool 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
1070TextAutosizer::Fingerprint TextAutosizer::FingerprintMapper::get(const RenderObject* renderer)
1071{
1072    return m_fingerprints.get(renderer);
1073}
1074
1075TextAutosizer::BlockSet* TextAutosizer::FingerprintMapper::getTentativeClusterRoots(Fingerprint fingerprint)
1076{
1077    return m_blocksForFingerprint.get(fingerprint);
1078}
1079
1080TextAutosizer::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
1093TextAutosizer::LayoutScope::~LayoutScope()
1094{
1095    if (m_textAutosizer)
1096        m_textAutosizer->endLayout(m_block);
1097}
1098
1099
1100TextAutosizer::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
1109TextAutosizer::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
1118TextAutosizer::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
1127float 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
1155void TextAutosizer::trace(Visitor* visitor)
1156{
1157    visitor->trace(m_document);
1158}
1159
1160} // namespace blink
1161