Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 31 #include "config.h" | 31 #include "config.h" |
| 32 #include "core/rendering/FastTextAutosizer.h" | 32 #include "core/rendering/FastTextAutosizer.h" |
| 33 | 33 |
| 34 #include "core/dom/Document.h" | 34 #include "core/dom/Document.h" |
| 35 #include "core/frame/Frame.h" | 35 #include "core/frame/Frame.h" |
| 36 #include "core/frame/FrameView.h" | 36 #include "core/frame/FrameView.h" |
| 37 #include "core/frame/Settings.h" | 37 #include "core/frame/Settings.h" |
| 38 #include "core/page/Page.h" | 38 #include "core/page/Page.h" |
| 39 #include "core/rendering/InlineIterator.h" | 39 #include "core/rendering/InlineIterator.h" |
| 40 #include "core/rendering/RenderBlock.h" | 40 #include "core/rendering/RenderBlock.h" |
| 41 #include "core/rendering/RenderView.h" | |
| 41 #include "core/rendering/TextAutosizer.h" | 42 #include "core/rendering/TextAutosizer.h" |
| 42 | 43 |
| 43 using namespace std; | 44 using namespace std; |
| 44 | 45 |
| 45 namespace WebCore { | 46 namespace WebCore { |
| 46 | 47 |
| 47 FastTextAutosizer::FastTextAutosizer(Document* document) | 48 FastTextAutosizer::FastTextAutosizer(Document* document) |
| 48 : m_document(document) | 49 : m_document(document) |
| 49 { | 50 { |
| 50 } | 51 } |
| 51 | 52 |
| 52 void FastTextAutosizer::prepareForLayout() | 53 void FastTextAutosizer::record(RenderBlock* block) |
| 53 { | 54 { |
| 54 if (!m_document->settings() | 55 if (!enabled()) |
| 55 || !m_document->settings()->textAutosizingEnabled() | |
| 56 || m_document->printing() | |
| 57 || !m_document->page()) | |
| 58 return; | 56 return; |
| 59 bool windowWidthChanged = updateWindowWidth(); | |
| 60 | 57 |
| 61 // If needed, set existing clusters as needing their multiplier recalculated . | 58 if (!shouldBeClusterRoot(block)) |
| 62 for (FingerprintToClusterMap::iterator it = m_clusterForFingerprint.begin(), end = m_clusterForFingerprint.end(); it != end; ++it) { | 59 return; |
| 63 Cluster* cluster = it->value.get(); | |
| 64 ASSERT(cluster); | |
| 65 WTF::HashSet<RenderBlock*>& blocks = cluster->m_blocks; | |
| 66 | 60 |
| 67 if (windowWidthChanged) { | 61 AtomicString fingerprint = computeFingerprint(block); |
| 68 // Clusters depend on the window width. Changes to the width should cause all clusters to recalc. | 62 if (fingerprint.isNull()) |
| 69 cluster->setNeedsClusterRecalc(); | 63 return; |
| 70 } else { | |
| 71 // If any of the cluster's blocks need a layout, mark the entire clu ster as needing a recalc. | |
| 72 for (WTF::HashSet<RenderBlock*>::iterator block = blocks.begin(); bl ock != blocks.end(); ++block) { | |
| 73 if ((*block)->needsLayout()) { | |
| 74 cluster->setNeedsClusterRecalc(); | |
| 75 break; | |
| 76 } | |
| 77 } | |
| 78 } | |
| 79 | 64 |
| 80 // If the cluster needs a recalc, mark all blocks as needing a layout so they pick up the new cluster info. | 65 m_fingerprints.set(block, fingerprint); |
| 81 if (cluster->needsClusterRecalc()) { | 66 |
| 82 for (WTF::HashSet<RenderBlock*>::iterator block = blocks.begin(); bl ock != blocks.end(); ++block) | 67 ReverseFingerprintMap::AddResult addResult = m_blocksForFingerprint.add(fing erprint, PassOwnPtr<BlockSet>()); |
| 83 (*block)->setNeedsLayout(); | 68 if (addResult.isNewEntry) |
| 84 } | 69 addResult.iterator->value = adoptPtr(new BlockSet); |
| 70 addResult.iterator->value->add(block); | |
| 71 } | |
| 72 | |
| 73 void FastTextAutosizer::destroy(RenderBlock* block) | |
| 74 { | |
| 75 AtomicString fingerprint = m_fingerprints.take(block); | |
| 76 if (fingerprint.isNull()) | |
| 77 return; | |
| 78 | |
| 79 ReverseFingerprintMap::iterator blocksIter = m_blocksForFingerprint.find(fin gerprint); | |
| 80 WTF::HashSet<RenderBlock*>& blocks = *blocksIter->value; | |
| 81 blocks.remove(block); | |
| 82 if (blocks.isEmpty()) | |
| 83 m_blocksForFingerprint.remove(blocksIter); | |
| 84 } | |
| 85 | |
| 86 void FastTextAutosizer::beginLayout(RenderBlock* block) | |
| 87 { | |
| 88 if (!enabled()) | |
| 89 return; | |
| 90 | |
| 91 if (block->isRenderView()) | |
| 92 prepareWindowInfo(toRenderView(block)); | |
| 93 | |
| 94 if (shouldBeClusterRoot(block)) | |
| 95 m_clusterStack.append(getCluster(block)); | |
| 96 } | |
| 97 | |
| 98 void FastTextAutosizer::inflate(RenderBlock* block) | |
| 99 { | |
| 100 if (m_clusterStack.isEmpty()) | |
| 101 return; | |
| 102 Cluster* cluster = m_clusterStack.last(); | |
| 103 | |
| 104 applyMultiplier(block, cluster->m_multiplier); | |
| 105 | |
| 106 // FIXME: Add an optimization to not do this walk if it's not needed. | |
| 107 for (InlineWalker walker(block); !walker.atEnd(); walker.advance()) { | |
| 108 RenderObject* inlineObj = walker.current(); | |
| 109 if (inlineObj->isText()) | |
| 110 applyMultiplier(inlineObj, cluster->m_multiplier); | |
| 85 } | 111 } |
| 86 } | 112 } |
| 87 | 113 |
| 88 bool FastTextAutosizer::updateWindowWidth() | 114 void FastTextAutosizer::endLayout(RenderBlock* block) |
| 89 { | 115 { |
|
pdr.
2013/12/21 01:46:57
Could you add a preventative if(!enabled) check he
skobes
2014/01/07 03:12:37
Done.
| |
| 90 int originalWindowWidth = m_windowWidth; | 116 if (!m_clusterStack.isEmpty() && m_clusterStack.last()->m_root == block) |
| 117 m_clusterStack.removeLast(); | |
| 118 } | |
| 119 | |
| 120 bool FastTextAutosizer::enabled() | |
| 121 { | |
| 122 return m_document->settings() | |
| 123 && m_document->settings()->textAutosizingEnabled() | |
| 124 && !m_document->printing() | |
| 125 && m_document->page(); | |
| 126 } | |
| 127 | |
| 128 void FastTextAutosizer::prepareWindowInfo(RenderView* renderView) | |
| 129 { | |
| 130 bool horizontalWritingMode = isHorizontalWritingMode(renderView->style()->wr itingMode()); | |
| 91 | 131 |
| 92 Frame* mainFrame = m_document->page()->mainFrame(); | 132 Frame* mainFrame = m_document->page()->mainFrame(); |
| 93 IntSize windowSize = m_document->settings()->textAutosizingWindowSizeOverrid e(); | 133 IntSize windowSize = m_document->settings()->textAutosizingWindowSizeOverrid e(); |
| 94 if (windowSize.isEmpty()) | 134 if (windowSize.isEmpty()) |
| 95 windowSize = mainFrame->view()->unscaledVisibleContentSize(ScrollableAre a::IncludeScrollbars); | 135 windowSize = mainFrame->view()->unscaledVisibleContentSize(ScrollableAre a::IncludeScrollbars); |
| 96 m_windowWidth = windowSize.width(); | 136 m_windowWidth = horizontalWritingMode ? windowSize.width() : windowSize.heig ht(); |
| 97 | 137 |
| 98 return m_windowWidth != originalWindowWidth; | 138 IntSize layoutSize = m_document->page()->mainFrame()->view()->layoutSize(); |
| 139 m_layoutWidth = horizontalWritingMode ? layoutSize.width() : layoutSize.heig ht(); | |
| 99 } | 140 } |
| 100 | 141 |
| 101 void FastTextAutosizer::record(RenderBlock* block) | 142 bool FastTextAutosizer::shouldBeClusterRoot(RenderBlock* block) |
| 102 { | 143 { |
| 103 if (!m_document->settings() | 144 // FIXME: move the logic out of TextAutosizer.cpp into this class. |
| 104 || !m_document->settings()->textAutosizingEnabled() | 145 // FIXME: why does isIndependentDescendant hang here? |
| 105 || m_document->printing() | 146 return block->isRenderView() |
| 106 || !m_document->page()) | 147 || (TextAutosizer::isAutosizingContainer(block) |
| 107 return; | 148 /* && TextAutosizer::isIndependentDescendant(block) */); |
| 108 | |
| 109 if (!TextAutosizer::isAutosizingContainer(block)) | |
| 110 return; | |
| 111 | |
| 112 AtomicString blockFingerprint = fingerprint(block); | |
| 113 HashMap<AtomicString, OwnPtr<Cluster> >::AddResult result = | |
| 114 m_clusterForFingerprint.add(blockFingerprint, PassOwnPtr<Cluster>()); | |
| 115 | |
| 116 if (result.isNewEntry) | |
| 117 result.iterator->value = adoptPtr(new Cluster(blockFingerprint)); | |
| 118 | |
| 119 Cluster* cluster = result.iterator->value.get(); | |
| 120 cluster->addBlock(block); | |
| 121 | |
| 122 m_clusterForBlock.set(block, cluster); | |
| 123 } | 149 } |
| 124 | 150 |
| 125 void FastTextAutosizer::destroy(RenderBlock* block) | 151 bool FastTextAutosizer::clusterWantsAutosizing(RenderBlock* root) |
| 126 { | 152 { |
| 127 Cluster* cluster = m_clusterForBlock.take(block); | 153 // FIXME: this should be slightly different. |
| 128 if (!cluster) | 154 return TextAutosizer::containerShouldBeAutosized(root); |
| 129 return; | |
| 130 cluster->m_blocks.remove(block); | |
| 131 if (cluster->m_blocks.isEmpty()) { | |
| 132 // This deletes the Cluster. | |
| 133 m_clusterForFingerprint.remove(cluster->m_fingerprint); | |
| 134 return; | |
| 135 } | |
| 136 cluster->setNeedsClusterRecalc(); | |
| 137 } | 155 } |
| 138 | 156 |
| 139 static void applyMultiplier(RenderObject* renderer, float multiplier) | 157 AtomicString FastTextAutosizer::computeFingerprint(RenderBlock* block) |
| 140 { | 158 { |
| 141 RenderStyle* currentStyle = renderer->style(); | 159 // FIXME(crbug.com/322340): Implement a fingerprinting algorithm. |
| 160 return nullAtom; | |
| 161 } | |
| 162 | |
| 163 FastTextAutosizer::Cluster* FastTextAutosizer::getCluster(RenderBlock* block) | |
|
pdr.
2013/12/21 01:46:57
I was surprised to learn clusters are created in g
skobes
2014/01/07 03:12:37
Done.
| |
| 164 { | |
| 165 ClusterMap::AddResult addResult = m_clusters.add(block, PassOwnPtr<Cluster>( )); | |
| 166 if (!addResult.isNewEntry) | |
| 167 return addResult.iterator->value.get(); | |
| 168 | |
| 169 AtomicString fingerprint = m_fingerprints.get(block); | |
| 170 if (fingerprint.isNull()) { | |
| 171 addResult.iterator->value = adoptPtr(createCluster(block)); | |
| 172 return addResult.iterator->value.get(); | |
| 173 } | |
| 174 return addSupercluster(*m_blocksForFingerprint.find(fingerprint)->value, blo ck); | |
| 175 } | |
| 176 | |
| 177 FastTextAutosizer::Cluster* FastTextAutosizer::createCluster(RenderBlock* block) | |
| 178 { | |
| 179 float multiplier = clusterWantsAutosizing(block) ? computeMultiplier(block) : 1.0f; | |
| 180 return new Cluster(block, multiplier); | |
| 181 } | |
| 182 | |
| 183 FastTextAutosizer::Cluster* FastTextAutosizer::addSupercluster(WTF::HashSet<Rend erBlock*>& roots, RenderBlock* returnFor) | |
| 184 { | |
| 185 RenderBlock* superRoot = deepestCommonAncestor(roots); | |
| 186 | |
| 187 bool shouldAutosize = false; | |
| 188 for (WTF::HashSet<RenderBlock*>::iterator it = roots.begin(); it != roots.en d(); ++it) | |
| 189 shouldAutosize |= clusterWantsAutosizing(*it); | |
| 190 | |
| 191 float multiplier = shouldAutosize ? computeMultiplier(superRoot) : 1.0f; | |
| 192 | |
| 193 Cluster* result = 0; | |
| 194 for (WTF::HashSet<RenderBlock*>::iterator it = roots.begin(); it != roots.en d(); ++it) { | |
| 195 Cluster* cluster = new Cluster(*it, multiplier); | |
| 196 m_clusters.set(*it, adoptPtr(cluster)); | |
| 197 | |
| 198 if (*it == returnFor) | |
| 199 result = cluster; | |
| 200 } | |
| 201 return result; | |
| 202 } | |
| 203 | |
| 204 RenderBlock* FastTextAutosizer::deepestCommonAncestor(WTF::HashSet<RenderBlock*> & blocks) | |
| 205 { | |
| 206 // Find the lowest common ancestor of blocks. | |
| 207 // Note: this could be improved to not be O(b*h) for b blocks and tree heigh t h. | |
| 208 HashCountedSet<RenderObject*> ancestors; | |
| 209 for (WTF::HashSet<RenderBlock*>::iterator it = blocks.begin(); it != blocks. end(); ++it) { | |
| 210 for (RenderBlock* block = (*it); block; block = block->containingBlock() ) { | |
| 211 ancestors.add(block); | |
| 212 // The first ancestor that has all of the blocks as children wins. | |
| 213 if (ancestors.count(block) == blocks.size()) | |
| 214 return block; | |
| 215 } | |
| 216 } | |
| 217 ASSERT_NOT_REACHED(); | |
| 218 return 0; | |
| 219 } | |
| 220 | |
| 221 float FastTextAutosizer::computeMultiplier(RenderBlock* block) | |
| 222 { | |
| 223 // Block width, in CSS pixels. | |
| 224 float blockWidth = block->contentLogicalWidth(); | |
| 225 | |
| 226 // FIXME: incorporate font scale factor. | |
| 227 // FIXME: incorporate device scale adjustment. | |
| 228 return max(min(blockWidth, (float) m_layoutWidth) / m_windowWidth, 1.0f); | |
| 229 } | |
| 230 | |
| 231 void FastTextAutosizer::applyMultiplier(RenderObject* renderer, float multiplier ) | |
| 232 { | |
| 233 RenderStyle* currentStyle = renderer->style(); | |
| 142 if (currentStyle->textAutosizingMultiplier() == multiplier) | 234 if (currentStyle->textAutosizingMultiplier() == multiplier) |
| 143 return; | 235 return; |
| 144 | 236 |
| 145 // We need to clone the render style to avoid breaking style sharing. | 237 // We need to clone the render style to avoid breaking style sharing. |
| 146 RefPtr<RenderStyle> style = RenderStyle::clone(currentStyle); | 238 RefPtr<RenderStyle> style = RenderStyle::clone(currentStyle); |
| 147 style->setTextAutosizingMultiplier(multiplier); | 239 style->setTextAutosizingMultiplier(multiplier); |
| 148 style->setUnique(); | 240 style->setUnique(); |
| 149 renderer->setStyleInternal(style.release()); | 241 renderer->setStyleInternal(style.release()); |
| 150 } | 242 } |
| 151 | 243 |
| 152 void FastTextAutosizer::inflate(RenderBlock* block) | |
| 153 { | |
| 154 Cluster* cluster = 0; | |
| 155 for (const RenderObject* clusterBlock = block; clusterBlock && !cluster; clu sterBlock = clusterBlock->parent()) { | |
| 156 if (clusterBlock->isRenderBlock()) | |
| 157 cluster = m_clusterForBlock.get(toRenderBlock(clusterBlock)); | |
| 158 } | |
| 159 if (!cluster) | |
| 160 return; | |
| 161 | |
| 162 recalcClusterIfNeeded(cluster); | |
| 163 | |
| 164 applyMultiplier(block, cluster->m_multiplier); | |
| 165 | |
| 166 // FIXME: Add an optimization to not do this walk if it's not needed. | |
| 167 for (InlineWalker walker(block); !walker.atEnd(); walker.advance()) { | |
| 168 RenderObject* inlineObj = walker.current(); | |
| 169 if (inlineObj->isRenderBlock() && m_clusterForBlock.contains(toRenderBlo ck(inlineObj))) | |
| 170 continue; | |
| 171 | |
| 172 applyMultiplier(inlineObj, cluster->m_multiplier); | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 AtomicString FastTextAutosizer::fingerprint(const RenderBlock* block) | |
| 177 { | |
| 178 // FIXME(crbug.com/322340): Implement a better fingerprinting algorithm. | |
| 179 return String::number((unsigned long long) block); | |
| 180 } | |
| 181 | |
| 182 void FastTextAutosizer::recalcClusterIfNeeded(FastTextAutosizer::Cluster* cluste r) | |
| 183 { | |
| 184 ASSERT(m_windowWidth > 0); | |
| 185 if (!cluster->needsClusterRecalc()) | |
| 186 return; | |
| 187 | |
| 188 WTF::HashSet<RenderBlock*>& blocks = cluster->m_blocks; | |
| 189 | |
| 190 bool shouldAutosize = false; | |
| 191 for (WTF::HashSet<RenderBlock*>::iterator it = blocks.begin(); it != blocks. end(); ++it) | |
| 192 shouldAutosize |= TextAutosizer::containerShouldBeAutosized(*it); | |
| 193 | |
| 194 if (!shouldAutosize) { | |
| 195 cluster->m_multiplier = 1.0f; | |
| 196 return; | |
| 197 } | |
| 198 | |
| 199 // Find the lowest common ancestor of blocks. | |
| 200 // Note: this could be improved to not be O(b*h) for b blocks and tree heigh t h. | |
| 201 cluster->m_clusterRoot = 0; | |
| 202 HashCountedSet<const RenderObject*> ancestors; | |
| 203 for (WTF::HashSet<RenderBlock*>::iterator it = blocks.begin(); !cluster->m_c lusterRoot && it != blocks.end(); ++it) { | |
| 204 const RenderObject* renderer = (*it); | |
| 205 while (renderer && (renderer = renderer->parent())) { | |
| 206 ancestors.add(renderer); | |
| 207 // The first ancestor that has all of the blocks as children wins an d is crowned the cluster root. | |
| 208 if (ancestors.count(renderer) == blocks.size()) { | |
| 209 cluster->m_clusterRoot = renderer->isRenderBlock() ? renderer : renderer->containingBlock(); | |
| 210 break; | |
| 211 } | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 ASSERT(cluster->m_clusterRoot); | |
| 216 bool horizontalWritingMode = isHorizontalWritingMode(cluster->m_clusterRoot- >style()->writingMode()); | |
| 217 | |
| 218 // Largest area of block that can be visible at once (assuming the main | |
| 219 // frame doesn't get scaled to less than overview scale), in CSS pixels. | |
| 220 IntSize layoutSize = m_document->page()->mainFrame()->view()->layoutSize(); | |
| 221 float layoutWidth = horizontalWritingMode ? layoutSize.width() : layoutSize. height(); | |
| 222 | |
| 223 // Cluster root layout width, in CSS pixels. | |
| 224 float rootWidth = toRenderBlock(cluster->m_clusterRoot)->contentLogicalWidth (); | |
| 225 | |
| 226 // FIXME: incorporate font scale factor. | |
| 227 float multiplier = min(rootWidth, layoutWidth) / m_windowWidth; | |
| 228 cluster->m_multiplier = max(multiplier, 1.0f); | |
| 229 } | |
| 230 | |
| 231 } // namespace WebCore | 244 } // namespace WebCore |
| OLD | NEW |