Chromium Code Reviews| Index: third_party/WebKit/Source/core/layout/TextAutosizer.cpp |
| diff --git a/third_party/WebKit/Source/core/layout/TextAutosizer.cpp b/third_party/WebKit/Source/core/layout/TextAutosizer.cpp |
| index 44838fff84911253c0d28db3d383ab59970e5af8..47325aedfbcccb62cf6529cea86789d6beb46080 100644 |
| --- a/third_party/WebKit/Source/core/layout/TextAutosizer.cpp |
| +++ b/third_party/WebKit/Source/core/layout/TextAutosizer.cpp |
| @@ -88,7 +88,8 @@ void TextAutosizer::writeClusterDebugInfo(Cluster* cluster) { |
| String explanation = ""; |
| if (cluster->m_flags & SUPPRESSING) { |
| explanation = "[suppressed]"; |
| - } else if (!(cluster->m_flags & (INDEPENDENT | WIDER_OR_NARROWER))) { |
| + } else if ((!(cluster->m_flags & (INDEPENDENT | WIDER_OR_NARROWER))) && |
| + isParentClusterReliable(cluster->m_parent)) { |
| explanation = "[inherited]"; |
| } else if (cluster->m_supercluster) { |
| explanation = "[supercluster]"; |
| @@ -123,7 +124,7 @@ void TextAutosizer::writeClusterDebugInfo(Cluster* cluster) { |
| } |
| #endif |
| -static const LayoutObject* parentElementLayoutObject( |
| +static LayoutObject* parentElementLayoutObject( |
| const LayoutObject* layoutObject) { |
| // At style recalc, the layoutObject's parent may not be attached, |
| // so we need to obtain this from the DOM tree. |
| @@ -295,29 +296,32 @@ TextAutosizer::TextAutosizer(const Document* document) |
| #if ENABLE(ASSERT) |
| m_blocksThatHaveBegunLayout(), |
| #endif |
| - m_superclusters(), |
| m_clusterStack(), |
| m_fingerprintMapper(), |
| + m_fingerprintsNeedCheckConsistent(), |
| m_pageInfo(), |
| m_updatePageInfoDeferred(false) { |
| } |
| TextAutosizer::~TextAutosizer() {} |
| -void TextAutosizer::record(const LayoutBlock* block) { |
| +void TextAutosizer::record(LayoutBlock* block) { |
| if (!m_pageInfo.m_settingEnabled) |
| return; |
| ASSERT(!m_blocksThatHaveBegunLayout.contains(block)); |
| - |
| - if (!classifyBlock(block, INDEPENDENT | EXPLICIT_WIDTH)) |
| + if (!classifyBlock(block, INDEPENDENT | EXPLICIT_WIDTH)) { |
| + newNodeAdded(block); |
|
pdr.
2016/11/20 06:38:13
Are these calls (newNodeAdded and needCheckConsist
cathiechentx
2016/11/22 08:14:47
Yes, it's necessary. Here is to find the nearest-c
|
| return; |
| + } |
| if (Fingerprint fingerprint = computeFingerprint(block)) |
| m_fingerprintMapper.addTentativeClusterRoot(block, fingerprint); |
| + |
| + needCheckConsistent(block); |
|
cathiechentx
2016/11/22 08:14:48
Add block to hashset. So block's children don't ha
|
| } |
| -void TextAutosizer::destroy(const LayoutBlock* block) { |
| +void TextAutosizer::destroy(LayoutBlock* block) { |
| if (!m_pageInfo.m_settingEnabled && !m_fingerprintMapper.hasFingerprints()) |
| return; |
| @@ -329,12 +333,11 @@ void TextAutosizer::destroy(const LayoutBlock* block) { |
| // Speculative fix for http://crbug.com/369485. |
| m_firstBlockToBeginLayout = nullptr; |
| m_clusterStack.clear(); |
| - m_superclusters.clear(); |
| } |
| } |
| TextAutosizer::BeginLayoutBehavior TextAutosizer::prepareForLayout( |
| - const LayoutBlock* block) { |
| + LayoutBlock* block) { |
| #if ENABLE(ASSERT) |
| m_blocksThatHaveBegunLayout.add(block); |
| #endif |
| @@ -342,6 +345,8 @@ TextAutosizer::BeginLayoutBehavior TextAutosizer::prepareForLayout( |
| if (!m_firstBlockToBeginLayout) { |
| m_firstBlockToBeginLayout = block; |
| prepareClusterStack(block->parent()); |
| + if (block->isLayoutView()) |
|
pdr.
2016/11/20 06:38:13
Is this isLayoutView check needed?
cathiechentx
2016/11/22 08:14:48
Yes, it is necessary. CheckConsistent might change
|
| + checkConsistent(); |
| } else if (block == currentCluster()->m_root) { |
| // Ignore beginLayout on the same block twice. |
| // This can happen with paginated overflow. |
| @@ -351,13 +356,13 @@ TextAutosizer::BeginLayoutBehavior TextAutosizer::prepareForLayout( |
| return ContinueLayout; |
| } |
| -void TextAutosizer::prepareClusterStack(const LayoutObject* layoutObject) { |
| +void TextAutosizer::prepareClusterStack(LayoutObject* layoutObject) { |
| if (!layoutObject) |
| return; |
| prepareClusterStack(layoutObject->parent()); |
| if (layoutObject->isLayoutBlock()) { |
| - const LayoutBlock* block = toLayoutBlock(layoutObject); |
| + LayoutBlock* block = toLayoutBlock(layoutObject); |
| #if ENABLE(ASSERT) |
| m_blocksThatHaveBegunLayout.add(block); |
| #endif |
| @@ -424,11 +429,11 @@ void TextAutosizer::endLayout(LayoutBlock* block) { |
| if (block == m_firstBlockToBeginLayout) { |
| m_firstBlockToBeginLayout = nullptr; |
| m_clusterStack.clear(); |
| - m_superclusters.clear(); |
| m_stylesRetainedDuringLayout.clear(); |
| #if ENABLE(ASSERT) |
| m_blocksThatHaveBegunLayout.clear(); |
| #endif |
| + m_fingerprintsNeedCheckConsistent.clear(); |
| // Tables can create two layout scopes for the same block so the isEmpty |
| // check below is needed to guard against endLayout being called twice. |
| } else if (!m_clusterStack.isEmpty() && currentCluster()->m_root == block) { |
| @@ -512,6 +517,65 @@ bool TextAutosizer::pageNeedsAutosizing() const { |
| return m_pageInfo.m_pageNeedsAutosizing; |
| } |
| +void TextAutosizer::newNodeAdded(LayoutObject* object) { |
| + if (!object || !shouldHandleLayout()) |
| + return; |
| + |
| + LayoutObject* parent = object->parent(); |
| + // Sometimes layoutObject haven't added to layout tree yet |
| + if (!parent && object->node() && object->node()->parentNode()) |
| + parent = object->node()->parentNode()->layoutObject(); |
| + // !everHadLayout() means the object hasn't layout yet |
| + // which means this object is new added. If parent is new added, |
| + // no need to checkConsistent its children |
| + if (!parent || !parent->everHadLayout()) |
|
cathiechentx
2016/11/22 08:14:47
The descendants of a new node will return from her
|
| + return; |
| + |
| + // Find the nearest LayoutBlock. |
| + LayoutBlock* block = nullptr; |
| + if (object->isLayoutBlock()) { |
| + // Only deal with the !everHadLayout LayoutBlock |
| + if (object->everHadLayout()) |
| + return; |
| + block = toLayoutBlock(object); |
| + } else { |
| + if (parent->isLayoutBlock()) |
| + block = toLayoutBlock(parent); |
| + else |
| + block = parent->containingBlock(); |
| + } |
| + if (!block) |
| + return; |
| + |
| + // Find the nearest block which choud be a cluster root |
| + while (block && !block->isTableCell() && |
| + !classifyBlock(block, INDEPENDENT | EXPLICIT_WIDTH)) { |
| + if (!block->parent()) |
| + return; |
| + if (block->parent()->isLayoutBlock()) |
| + block = toLayoutBlock(block->parent()); |
| + else |
| + block = block->containingBlock(); |
| + } |
| + if (!block) |
| + return; |
| + |
| + needCheckConsistent(block); |
| +} |
| + |
| +void TextAutosizer::needCheckConsistent(LayoutBlock* block) { |
| + if (!block || !shouldHandleLayout()) |
| + return; |
| + |
| + if (Fingerprint fingerprint = computeFingerprint(block)) { |
| + // If the supercluster hasn't be created yet, create the supercluster. |
| + Supercluster* supercluster = m_fingerprintMapper.getSupercluster(block); |
| + if (supercluster && |
| + supercluster->m_hasEnoughTextToAutosize != HasEnoughText) |
| + m_fingerprintsNeedCheckConsistent.add(fingerprint); |
|
pdr.
2016/11/20 06:38:13
Instead of storing a set of fingerprints, could we
cathiechentx
2016/11/22 08:14:48
Done.
|
| + } |
| +} |
| + |
| void TextAutosizer::updatePageInfoInAllFrames() { |
| ASSERT(!m_document->frame() || m_document->frame()->isMainFrame()); |
| @@ -723,7 +787,7 @@ bool TextAutosizer::clusterHasEnoughTextToAutosize( |
| } |
| TextAutosizer::Fingerprint TextAutosizer::getFingerprint( |
| - const LayoutObject* layoutObject) { |
| + LayoutObject* layoutObject) { |
| Fingerprint result = m_fingerprintMapper.get(layoutObject); |
| if (!result) { |
| result = computeFingerprint(layoutObject); |
| @@ -739,7 +803,7 @@ TextAutosizer::Fingerprint TextAutosizer::computeFingerprint( |
| return 0; |
| FingerprintSourceData data; |
| - if (const LayoutObject* parent = parentElementLayoutObject(layoutObject)) |
| + if (LayoutObject* parent = parentElementLayoutObject(layoutObject)) |
| data.m_parentHash = getFingerprint(parent); |
| data.m_qualifiedNameHash = |
| @@ -771,8 +835,7 @@ TextAutosizer::Fingerprint TextAutosizer::computeFingerprint( |
| sizeof data / sizeof(UChar)); |
| } |
| -TextAutosizer::Cluster* TextAutosizer::maybeCreateCluster( |
| - const LayoutBlock* block) { |
| +TextAutosizer::Cluster* TextAutosizer::maybeCreateCluster(LayoutBlock* block) { |
| BlockFlags flags = classifyBlock(block); |
| if (!(flags & POTENTIAL_ROOT)) |
| return nullptr; |
| @@ -789,8 +852,8 @@ TextAutosizer::Cluster* TextAutosizer::maybeCreateCluster( |
| !!(flags & SUPPRESSING) == parentSuppresses) |
| return nullptr; |
| - Cluster* cluster = |
| - new Cluster(block, flags, parentCluster, getSupercluster(block)); |
| + Cluster* cluster = new Cluster(block, flags, parentCluster, |
| + m_fingerprintMapper.getSupercluster(block)); |
| #ifdef AUTOSIZING_DOM_DEBUG_INFO |
| // Non-SUPPRESSING clusters are annotated in clusterMultiplier. |
| if (flags & SUPPRESSING) |
| @@ -799,13 +862,13 @@ TextAutosizer::Cluster* TextAutosizer::maybeCreateCluster( |
| return cluster; |
| } |
| -TextAutosizer::Supercluster* TextAutosizer::getSupercluster( |
| - const LayoutBlock* block) { |
| - Fingerprint fingerprint = m_fingerprintMapper.get(block); |
| +TextAutosizer::Supercluster* TextAutosizer::FingerprintMapper::getSupercluster( |
| + LayoutBlock* block) { |
| + Fingerprint fingerprint = get(block); |
| if (!fingerprint) |
| return nullptr; |
| - BlockSet* roots = m_fingerprintMapper.getTentativeClusterRoots(fingerprint); |
| + BlockSet* roots = getTentativeClusterRoots(fingerprint); |
| if (!roots || roots->size() < 2 || !roots->contains(block)) |
| return nullptr; |
| @@ -819,6 +882,27 @@ TextAutosizer::Supercluster* TextAutosizer::getSupercluster( |
| return supercluster; |
| } |
| +TextAutosizer::Supercluster* |
| +TextAutosizer::FingerprintMapper::getSuperclusterByFingerprint( |
| + const Fingerprint fingerprint) { |
| + Supercluster* supercluster = nullptr; |
| + SuperclusterMap::iterator superclusterIter = |
| + m_superclusters.find(fingerprint); |
| + if (superclusterIter != m_superclusters.end()) { |
| + supercluster = superclusterIter->value.get(); |
| + } |
| + return supercluster; |
| +} |
| + |
| +bool TextAutosizer::isParentClusterReliable(Cluster* cluster) { |
| + if (!cluster) |
| + return true; |
| + // Cause cluster is changed, m_supercluster's "NotEnoughText" is not reliable |
| + // now. |
| + return !(cluster->m_supercluster && |
| + cluster->m_supercluster->m_hasEnoughTextToAutosize == NotEnoughText); |
| +} |
| + |
| float TextAutosizer::clusterMultiplier(Cluster* cluster) { |
| if (cluster->m_multiplier) |
| return cluster->m_multiplier; |
| @@ -827,7 +911,8 @@ float TextAutosizer::clusterMultiplier(Cluster* cluster) { |
| if (!(cluster->m_flags & INDEPENDENT) && isWiderOrNarrowerDescendant(cluster)) |
| cluster->m_flags |= WIDER_OR_NARROWER; |
| - if (cluster->m_flags & (INDEPENDENT | WIDER_OR_NARROWER)) { |
| + if ((cluster->m_flags & (INDEPENDENT | WIDER_OR_NARROWER)) || |
| + !isParentClusterReliable(cluster->m_parent)) { |
| if (cluster->m_supercluster) |
| cluster->m_multiplier = superclusterMultiplier(cluster); |
| else if (clusterHasEnoughTextToAutosize(cluster)) |
| @@ -850,11 +935,14 @@ float TextAutosizer::clusterMultiplier(Cluster* cluster) { |
| bool TextAutosizer::superclusterHasEnoughTextToAutosize( |
| Supercluster* supercluster, |
| - const LayoutBlock* widthProvider) { |
| + const LayoutBlock* widthProvider, |
| + const bool skipLayoutedNodes) { |
| if (supercluster->m_hasEnoughTextToAutosize != UnknownAmountOfText) |
| return supercluster->m_hasEnoughTextToAutosize == HasEnoughText; |
| for (auto* root : *supercluster->m_roots) { |
| + if (skipLayoutedNodes && !root->normalChildNeedsLayout()) |
| + continue; |
| if (clusterWouldHaveEnoughTextToAutosize(root, widthProvider)) { |
| supercluster->m_hasEnoughTextToAutosize = HasEnoughText; |
| return true; |
| @@ -869,8 +957,9 @@ float TextAutosizer::superclusterMultiplier(Cluster* cluster) { |
| if (!supercluster->m_multiplier) { |
| const LayoutBlock* widthProvider = |
| maxClusterWidthProvider(cluster->m_supercluster, cluster->m_root); |
| + RELEASE_ASSERT(widthProvider); |
| supercluster->m_multiplier = |
| - superclusterHasEnoughTextToAutosize(supercluster, widthProvider) |
| + superclusterHasEnoughTextToAutosize(supercluster, widthProvider, false) |
| ? multiplierFromBlock(widthProvider) |
| : 1.0f; |
| } |
| @@ -887,10 +976,15 @@ const LayoutBlock* TextAutosizer::clusterWidthProvider( |
| } |
| const LayoutBlock* TextAutosizer::maxClusterWidthProvider( |
| - const Supercluster* supercluster, |
| + Supercluster* supercluster, |
| const LayoutBlock* currentRoot) const { |
| - const LayoutBlock* result = clusterWidthProvider(currentRoot); |
| - float maxWidth = widthFromBlock(result); |
| + const LayoutBlock* result = nullptr; |
| + if (currentRoot) |
| + result = clusterWidthProvider(currentRoot); |
| + |
| + float maxWidth = 0; |
| + if (result) |
| + maxWidth = widthFromBlock(result); |
| const BlockSet* roots = supercluster->m_roots; |
| for (const auto* root : *roots) { |
| @@ -903,7 +997,6 @@ const LayoutBlock* TextAutosizer::maxClusterWidthProvider( |
| result = widthProvider; |
| } |
| } |
| - RELEASE_ASSERT(result); |
| return result; |
| } |
| @@ -1166,7 +1259,7 @@ void TextAutosizer::FingerprintMapper::assertMapsAreConsistent() { |
| } |
| #endif |
| -void TextAutosizer::FingerprintMapper::add(const LayoutObject* layoutObject, |
| +void TextAutosizer::FingerprintMapper::add(LayoutObject* layoutObject, |
| Fingerprint fingerprint) { |
| remove(layoutObject); |
| @@ -1177,7 +1270,7 @@ void TextAutosizer::FingerprintMapper::add(const LayoutObject* layoutObject, |
| } |
| void TextAutosizer::FingerprintMapper::addTentativeClusterRoot( |
| - const LayoutBlock* block, |
| + LayoutBlock* block, |
| Fingerprint fingerprint) { |
| add(block, fingerprint); |
| @@ -1191,8 +1284,7 @@ void TextAutosizer::FingerprintMapper::addTentativeClusterRoot( |
| #endif |
| } |
| -bool TextAutosizer::FingerprintMapper::remove( |
| - const LayoutObject* layoutObject) { |
| +bool TextAutosizer::FingerprintMapper::remove(LayoutObject* layoutObject) { |
| Fingerprint fingerprint = m_fingerprints.take(layoutObject); |
| if (!fingerprint || !layoutObject->isLayoutBlock()) |
| return false; |
| @@ -1204,8 +1296,14 @@ bool TextAutosizer::FingerprintMapper::remove( |
| BlockSet& blocks = *blocksIter->value; |
| blocks.remove(toLayoutBlock(layoutObject)); |
| - if (blocks.isEmpty()) |
| + if (blocks.isEmpty()) { |
| m_blocksForFingerprint.remove(blocksIter); |
| + |
| + SuperclusterMap::iterator superclusterIter = |
| + m_superclusters.find(fingerprint); |
| + if (superclusterIter != m_superclusters.end()) |
| + m_superclusters.remove(superclusterIter); |
| + } |
| #if ENABLE(ASSERT) |
| assertMapsAreConsistent(); |
| #endif |
| @@ -1297,6 +1395,58 @@ float TextAutosizer::computeAutosizedFontSize(float specifiedSize, |
| return computedSize; |
| } |
| +void setAllTextInsideContainerNeedsLayout(LayoutBlock* container) { |
| + DCHECK(container); |
| + LayoutObject* layoutObject = container; |
| + while (layoutObject) { |
| + if (layoutObject->isText()) { |
| + layoutObject->setNeedsLayoutAndFullPaintInvalidation( |
| + LayoutInvalidationReason::TextAutosizing); |
| + } |
| + layoutObject = layoutObject->nextInPreOrder(container); |
| + } |
| +} |
| + |
| +void TextAutosizer::checkConsistent() { |
| + if (m_fingerprintsNeedCheckConsistent.isEmpty()) |
| + return; |
| + |
| + FingerprintSet::iterator end = m_fingerprintsNeedCheckConsistent.end(); |
| + for (FingerprintSet::iterator it = m_fingerprintsNeedCheckConsistent.begin(); |
| + it != end; ++it) { |
| + const Fingerprint fingerprint = *it; |
| + Supercluster* supercluster = |
| + m_fingerprintMapper.getSuperclusterByFingerprint(fingerprint); |
| + if (!supercluster) |
| + continue; |
| + |
| + if (HasEnoughText == supercluster->m_hasEnoughTextToAutosize) |
| + continue; |
| + |
| + float oldMultipiler = supercluster->m_multiplier; |
| + supercluster->m_multiplier = 0; |
| + supercluster->m_hasEnoughTextToAutosize = UnknownAmountOfText; |
| + const LayoutBlock* widthProvider = |
| + maxClusterWidthProvider(supercluster, nullptr); |
| + if (!widthProvider) |
| + continue; |
| + |
| + superclusterHasEnoughTextToAutosize(supercluster, widthProvider, true); |
| + |
| + if (HasEnoughText == supercluster->m_hasEnoughTextToAutosize) { |
| + for (auto* root : *supercluster->m_roots) { |
| + if (root->normalChildNeedsLayout()) |
| + continue; |
| + |
| + DCHECK(root); |
| + setAllTextInsideContainerNeedsLayout(root); |
| + } |
| + } else { |
| + supercluster->m_multiplier = oldMultipiler; |
| + } |
| + } |
| +} |
| + |
| DEFINE_TRACE(TextAutosizer) { |
| visitor->trace(m_document); |
| } |