Index: Source/core/rendering/FastTextAutosizer.cpp |
diff --git a/Source/core/rendering/FastTextAutosizer.cpp b/Source/core/rendering/FastTextAutosizer.cpp |
index 85322761c96cc65e153cecf63528e1cceadced6f..2888f16994725ba42ddb4ffb1768cb3b4b7342aa 100644 |
--- a/Source/core/rendering/FastTextAutosizer.cpp |
+++ b/Source/core/rendering/FastTextAutosizer.cpp |
@@ -42,7 +42,6 @@ |
#include "core/rendering/RenderListMarker.h" |
#include "core/rendering/RenderTableCell.h" |
#include "core/rendering/RenderView.h" |
-#include "core/rendering/TextAutosizer.h" |
using namespace std; |
@@ -64,6 +63,182 @@ static const RenderObject* parentElementRenderer(const RenderObject* renderer) |
return 0; |
} |
+static const Vector<QualifiedName>& formInputTags() |
+{ |
+ // Returns the tags for the form input elements. |
+ DEFINE_STATIC_LOCAL(Vector<QualifiedName>, formInputTags, ()); |
+ if (formInputTags.isEmpty()) { |
+ formInputTags.append(HTMLNames::inputTag); |
+ formInputTags.append(HTMLNames::buttonTag); |
+ formInputTags.append(HTMLNames::selectTag); |
+ } |
+ return formInputTags; |
+} |
+ |
+static bool isAutosizingContainer(const RenderObject* renderer) |
+{ |
+ // "Autosizing containers" are the smallest unit for which we can |
+ // enable/disable Text Autosizing. |
+ // - Must not be inline, as different multipliers on one line looks terrible. |
+ // Exceptions are inline-block and alike elements (inline-table, -webkit-inline-*), |
+ // as they often contain entire multi-line columns of text. |
+ // - Must not be list items, as items in the same list should look consistent (*). |
+ // - Must not be normal list items, as items in the same list should look |
+ // consistent, unless they are floating or position:absolute/fixed. |
+ Node* node = renderer->generatingNode(); |
+ if ((node && !node->hasChildren()) |
+ || !renderer->isRenderBlock() |
+ || (renderer->isInline() && !renderer->style()->isDisplayReplacedType())) |
+ return false; |
+ if (renderer->isListItem()) |
+ return renderer->isFloating() || renderer->isOutOfFlowPositioned(); |
+ // Avoid creating containers for text within text controls, buttons, or <select> buttons. |
+ Node* parentNode = renderer->parent() ? renderer->parent()->generatingNode() : 0; |
+ if (parentNode && parentNode->isElementNode() && formInputTags().contains(toElement(parentNode)->tagQName())) |
+ return false; |
+ |
+ return true; |
+} |
+ |
+static RenderObject* nextInPreOrderSkippingDescendantsOfContainers(const RenderObject* current, const RenderObject* stayWithin) |
+{ |
+ if (current == stayWithin || !isAutosizingContainer(current)) |
+ return current->nextInPreOrder(stayWithin); |
+ return current->nextInPreOrderAfterChildren(stayWithin); |
+} |
+ |
+static bool isIndependentDescendant(const RenderBlock* renderer) |
+{ |
+ ASSERT(isAutosizingContainer(renderer)); |
+ |
+ // "Autosizing clusters" are special autosizing containers within which we |
+ // want to enforce a uniform text size multiplier, in the hopes of making |
+ // the major sections of the page look internally consistent. |
+ // All their descendants (including other autosizing containers) must share |
+ // the same multiplier, except for subtrees which are themselves clusters, |
+ // and some of their descendant containers might not be autosized at all |
+ // (for example if their height is constrained). |
+ // Additionally, clusterShouldBeAutosized requires each cluster to contain a |
+ // minimum amount of text, without which it won't be autosized. |
+ // |
+ // Clusters are chosen using very similar criteria to CSS flow roots, aka |
+ // block formatting contexts (http://w3.org/TR/css3-box/#flow-root), since |
+ // flow roots correspond to box containers that behave somewhat |
+ // independently from their parent (for example they don't overlap floats). |
+ // The definition of a flow root also conveniently includes most of the |
+ // ways that a box and its children can have significantly different width |
+ // from the box's parent (we want to avoid having significantly different |
+ // width blocks within a cluster, since the narrower blocks would end up |
+ // larger than would otherwise be necessary). |
+ RenderBlock* containingBlock = renderer->containingBlock(); |
+ return renderer->isRenderView() |
+ || renderer->isFloating() |
+ || renderer->isOutOfFlowPositioned() |
+ || renderer->isTableCell() |
+ || renderer->isTableCaption() |
+ || renderer->isFlexibleBoxIncludingDeprecated() |
+ || renderer->hasColumns() |
+ || (containingBlock && containingBlock->isHorizontalWritingMode() != renderer->isHorizontalWritingMode()) |
+ || renderer->style()->isDisplayReplacedType() |
+ || renderer->isTextArea() |
+ || renderer->style()->userModify() != READ_ONLY; |
+ // FIXME: Tables need special handling to multiply all their columns by |
+ // the same amount even if they're different widths; so do hasColumns() |
+ // containers, and probably flexboxes... |
+} |
+ |
+static bool containerIsRowOfLinks(const RenderObject* container) |
+{ |
+ // A "row of links" is a container for which holds: |
+ // 1. it should not contain non-link text elements longer than 3 characters |
+ // 2. it should contain min. 3 inline links and all links should |
+ // have the same specified font size |
+ // 3. it should not contain <br> elements |
+ // 4. it should contain only inline elements unless they are containers, |
+ // children of link elements or children of sub-containers. |
+ int linkCount = 0; |
+ RenderObject* renderer = container->nextInPreOrder(container); |
+ float matchingFontSize = -1; |
+ |
+ while (renderer) { |
+ if (!isAutosizingContainer(renderer)) { |
+ if (renderer->isText() && toRenderText(renderer)->text().impl()->stripWhiteSpace()->length() > 3) |
+ return false; |
+ if (!renderer->isInline()) |
+ return false; |
+ if (renderer->isBR()) |
+ return false; |
+ } |
+ if (renderer->style()->isLink()) { |
+ if (matchingFontSize < 0) { |
+ matchingFontSize = renderer->style()->specifiedFontSize(); |
+ } else { |
+ if (matchingFontSize != renderer->style()->specifiedFontSize()) |
+ return false; |
+ } |
+ |
+ linkCount++; |
+ // Skip traversing descendants of the link. |
+ renderer = renderer->nextInPreOrderAfterChildren(container); |
+ } else { |
+ renderer = nextInPreOrderSkippingDescendantsOfContainers(renderer, container); |
+ } |
+ } |
+ |
+ return (linkCount >= 3); |
+} |
+ |
+static bool contentHeightIsConstrained(const RenderBlock* container) |
+{ |
+ // FIXME: Propagate constrainedness down the tree, to avoid inefficiently walking back up from each box. |
+ // FIXME: This code needs to take into account vertical writing modes. |
+ // FIXME: Consider additional heuristics, such as ignoring fixed heights if the content is already overflowing before autosizing kicks in. |
+ for (; container; container = container->containingBlock()) { |
+ RenderStyle* style = container->style(); |
+ if (style->overflowY() >= OSCROLL) |
+ return false; |
+ if (style->height().isSpecified() || style->maxHeight().isSpecified() || container->isOutOfFlowPositioned()) { |
+ // Some sites (e.g. wikipedia) set their html and/or body elements to height:100%, |
+ // without intending to constrain the height of the content within them. |
+ return !container->isRoot() && !container->isBody(); |
+ } |
+ if (container->isFloating()) |
+ return false; |
+ } |
+ return false; |
+} |
+ |
+static bool containerContainsOneOfTags(const RenderBlock* container, const Vector<QualifiedName>& tags) |
+{ |
+ const RenderObject* renderer = container; |
+ while (renderer) { |
+ const Node* rendererNode = renderer->node(); |
+ if (rendererNode && rendererNode->isElementNode()) { |
+ if (tags.contains(toElement(rendererNode)->tagQName())) |
+ return true; |
+ } |
+ renderer = nextInPreOrderSkippingDescendantsOfContainers(renderer, container); |
+ } |
+ |
+ return false; |
+} |
+ |
+static bool containerShouldBeAutosized(const RenderBlock* container) |
+{ |
+ if (containerContainsOneOfTags(container, formInputTags())) |
+ return false; |
+ |
+ if (containerIsRowOfLinks(container)) |
+ return false; |
+ |
+ // Don't autosize block-level text that can't wrap (as it's likely to |
+ // expand sideways and break the page's layout). |
+ if (!container->style()->autoWrap()) |
+ return false; |
+ |
+ return !contentHeightIsConstrained(container); |
+} |
+ |
FastTextAutosizer::FastTextAutosizer(const Document* document) |
: m_document(document) |
, m_frameWidth(0) |
@@ -189,7 +364,7 @@ void FastTextAutosizer::inflateTable(RenderTable* table) |
RenderTableCell* renderTableCell = toRenderTableCell(cell); |
bool shouldAutosize; |
- if (!TextAutosizer::containerShouldBeAutosized(renderTableCell)) |
+ if (!containerShouldBeAutosized(renderTableCell)) |
shouldAutosize = false; |
else if (Supercluster* supercluster = getSupercluster(renderTableCell)) |
shouldAutosize = anyClusterHasEnoughTextToAutosize(supercluster->m_roots, table); |
@@ -285,8 +460,8 @@ bool FastTextAutosizer::isFingerprintingCandidate(const RenderBlock* block) |
{ |
// FIXME: move the logic out of TextAutosizer.cpp into this class. |
return block->isRenderView() |
- || (TextAutosizer::isAutosizingContainer(block) |
- && (TextAutosizer::isIndependentDescendant(block) |
+ || (isAutosizingContainer(block) |
+ && (isIndependentDescendant(block) |
|| mightBeWiderOrNarrowerDescendant(block))); |
} |
@@ -311,7 +486,7 @@ bool FastTextAutosizer::clusterHasEnoughTextToAutosize(Cluster* cluster, const R |
return true; |
} |
- if (!TextAutosizer::containerShouldBeAutosized(root)) { |
+ if (!containerShouldBeAutosized(root)) { |
cluster->m_hasEnoughTextToAutosize = NotEnoughText; |
return false; |
} |
@@ -324,12 +499,12 @@ bool FastTextAutosizer::clusterHasEnoughTextToAutosize(Cluster* cluster, const R |
while (descendant) { |
if (descendant->isRenderBlock()) { |
RenderBlock* block = toRenderBlock(descendant); |
- if (TextAutosizer::isAutosizingContainer(block)) { |
+ if (isAutosizingContainer(block)) { |
// Note: Ideally we would check isWiderOrNarrowerDescendant here but we only know that |
// after the block has entered layout, which may not be the case. |
- bool isAutosizingClusterRoot = TextAutosizer::isIndependentDescendant(block) || block->isTable(); |
+ bool isAutosizingClusterRoot = isIndependentDescendant(block) || block->isTable(); |
if ((isAutosizingClusterRoot && !block->isTableCell()) |
- || !TextAutosizer::containerShouldBeAutosized(block)) { |
+ || !containerShouldBeAutosized(block)) { |
descendant = descendant->nextInPreOrderAfterChildren(root); |
continue; |
} |
@@ -405,18 +580,18 @@ FastTextAutosizer::Fingerprint FastTextAutosizer::computeFingerprint(const Rende |
FastTextAutosizer::Cluster* FastTextAutosizer::maybeCreateCluster(const RenderBlock* block) |
{ |
- if (!TextAutosizer::isAutosizingContainer(block)) |
+ if (!isAutosizingContainer(block)) |
return 0; |
Cluster* parentCluster = m_clusterStack.isEmpty() ? 0 : currentCluster(); |
ASSERT(parentCluster || block->isRenderView()); |
// Create clusters to suppress / unsuppress autosizing based on containerShouldBeAutosized. |
- bool containerCanAutosize = TextAutosizer::containerShouldBeAutosized(block); |
+ bool containerCanAutosize = containerShouldBeAutosized(block); |
bool parentClusterCanAutosize = parentCluster && parentCluster->m_autosize; |
bool createClusterThatMightAutosize = block->isRenderView() |
|| mightBeWiderOrNarrowerDescendant(block) |
- || TextAutosizer::isIndependentDescendant(block) |
+ || isIndependentDescendant(block) |
|| block->isTable(); |
// If the container would not alter the m_autosize bit, it doesn't need to be a cluster. |
@@ -467,7 +642,7 @@ float FastTextAutosizer::clusterMultiplier(Cluster* cluster) |
ASSERT(m_renderViewInfoPrepared); |
if (!cluster->m_multiplier) { |
if (cluster->m_root->isTable() |
- || TextAutosizer::isIndependentDescendant(cluster->m_root) |
+ || isIndependentDescendant(cluster->m_root) |
|| isWiderOrNarrowerDescendant(cluster)) { |
if (cluster->m_supercluster) { |
@@ -621,7 +796,7 @@ const RenderObject* FastTextAutosizer::findTextLeaf(const RenderObject* parent, |
while (child) { |
// Note: At this point clusters may not have been created for these blocks so we cannot rely |
// on m_clusters. Instead, we use a best-guess about whether the block will become a cluster. |
- if (!TextAutosizer::isAutosizingContainer(child) || !TextAutosizer::isIndependentDescendant(toRenderBlock(child))) { |
+ if (!isAutosizingContainer(child) || !isIndependentDescendant(toRenderBlock(child))) { |
const RenderObject* leaf = findTextLeaf(child, depth, firstOrLast); |
if (leaf) |
return leaf; |