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