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 return; |
56 || m_document->printing() | 57 |
57 || !m_document->page()) | 58 if (!shouldBeClusterRoot(block)) |
58 return; | 59 return; |
59 bool windowWidthChanged = updateWindowWidth(); | 60 |
60 | 61 AtomicString fingerprint = computeFingerprint(block); |
61 // If needed, set existing clusters as needing their multiplier recalculated . | 62 if (fingerprint.isNull()) |
62 for (FingerprintToClusterMap::iterator it = m_clusterForFingerprint.begin(), end = m_clusterForFingerprint.end(); it != end; ++it) { | 63 return; |
63 Cluster* cluster = it->value.get(); | 64 |
64 ASSERT(cluster); | 65 m_fingerprintMapper.add(block, fingerprint); |
65 WTF::HashSet<RenderBlock*>& blocks = cluster->m_blocks; | 66 } |
66 | 67 |
67 if (windowWidthChanged) { | 68 void FastTextAutosizer::destroy(RenderBlock* block) |
68 // Clusters depend on the window width. Changes to the width should cause all clusters to recalc. | 69 { |
69 cluster->setNeedsClusterRecalc(); | 70 m_fingerprintMapper.remove(block); |
70 } else { | 71 } |
71 // If any of the cluster's blocks need a layout, mark the entire clu ster as needing a recalc. | 72 |
72 for (WTF::HashSet<RenderBlock*>::iterator block = blocks.begin(); bl ock != blocks.end(); ++block) { | 73 void FastTextAutosizer::beginLayout(RenderBlock* block) |
73 if ((*block)->needsLayout()) { | 74 { |
74 cluster->setNeedsClusterRecalc(); | 75 if (!enabled()) |
75 break; | 76 return; |
76 } | 77 |
77 } | 78 if (block->isRenderView()) |
78 } | 79 prepareWindowInfo(toRenderView(block)); |
79 | 80 |
80 // If the cluster needs a recalc, mark all blocks as needing a layout so they pick up the new cluster info. | 81 if (shouldBeClusterRoot(block)) |
81 if (cluster->needsClusterRecalc()) { | 82 m_clusterStack.append(getOrCreateCluster(block)); |
82 for (WTF::HashSet<RenderBlock*>::iterator block = blocks.begin(); bl ock != blocks.end(); ++block) | 83 } |
83 (*block)->setNeedsLayout(); | 84 |
84 } | 85 void FastTextAutosizer::inflate(RenderBlock* block) |
85 } | 86 { |
86 } | 87 if (m_clusterStack.isEmpty()) |
87 | 88 return; |
88 bool FastTextAutosizer::updateWindowWidth() | 89 Cluster* cluster = m_clusterStack.last(); |
89 { | 90 |
90 int originalWindowWidth = m_windowWidth; | 91 applyMultiplier(block, cluster->m_multiplier); |
92 | |
93 // FIXME: Add an optimization to not do this walk if it's not needed. | |
94 for (InlineWalker walker(block); !walker.atEnd(); walker.advance()) { | |
95 RenderObject* inlineObj = walker.current(); | |
96 if (inlineObj->isText()) | |
97 applyMultiplier(inlineObj, cluster->m_multiplier); | |
98 } | |
99 } | |
100 | |
101 void FastTextAutosizer::endLayout(RenderBlock* block) | |
102 { | |
103 if (!enabled()) | |
104 return; | |
105 | |
106 if (!m_clusterStack.isEmpty() && m_clusterStack.last()->m_root == block) | |
107 m_clusterStack.removeLast(); | |
108 } | |
109 | |
110 bool FastTextAutosizer::enabled() | |
111 { | |
112 return m_document->settings() | |
113 && m_document->settings()->textAutosizingEnabled() | |
114 && !m_document->printing() | |
115 && m_document->page(); | |
116 } | |
117 | |
118 void FastTextAutosizer::prepareWindowInfo(RenderView* renderView) | |
119 { | |
120 bool horizontalWritingMode = isHorizontalWritingMode(renderView->style()->wr itingMode()); | |
91 | 121 |
92 Frame* mainFrame = m_document->page()->mainFrame(); | 122 Frame* mainFrame = m_document->page()->mainFrame(); |
93 IntSize windowSize = m_document->settings()->textAutosizingWindowSizeOverrid e(); | 123 IntSize windowSize = m_document->settings()->textAutosizingWindowSizeOverrid e(); |
94 if (windowSize.isEmpty()) | 124 if (windowSize.isEmpty()) |
95 windowSize = mainFrame->view()->unscaledVisibleContentSize(ScrollableAre a::IncludeScrollbars); | 125 windowSize = mainFrame->view()->unscaledVisibleContentSize(ScrollableAre a::IncludeScrollbars); |
96 m_windowWidth = windowSize.width(); | 126 m_windowWidth = horizontalWritingMode ? windowSize.width() : windowSize.heig ht(); |
97 | 127 |
98 return m_windowWidth != originalWindowWidth; | 128 IntSize layoutSize = m_document->page()->mainFrame()->view()->layoutSize(); |
99 } | 129 m_layoutWidth = horizontalWritingMode ? layoutSize.width() : layoutSize.heig ht(); |
100 | 130 } |
101 void FastTextAutosizer::record(RenderBlock* block) | 131 |
102 { | 132 bool FastTextAutosizer::shouldBeClusterRoot(RenderBlock* block) |
103 if (!m_document->settings() | 133 { |
104 || !m_document->settings()->textAutosizingEnabled() | 134 // FIXME: move the logic out of TextAutosizer.cpp into this class. |
105 || m_document->printing() | 135 return block->isRenderView() |
106 || !m_document->page()) | 136 || (TextAutosizer::isAutosizingContainer(block) |
107 return; | 137 && TextAutosizer::isIndependentDescendant(block)); |
108 | 138 } |
109 if (!TextAutosizer::isAutosizingContainer(block)) | 139 |
110 return; | 140 bool FastTextAutosizer::clusterWantsAutosizing(RenderBlock* root) |
111 | 141 { |
112 AtomicString blockFingerprint = fingerprint(block); | 142 // FIXME: this should be slightly different. |
113 HashMap<AtomicString, OwnPtr<Cluster> >::AddResult result = | 143 return TextAutosizer::containerShouldBeAutosized(root); |
114 m_clusterForFingerprint.add(blockFingerprint, PassOwnPtr<Cluster>()); | 144 } |
115 | 145 |
116 if (result.isNewEntry) | 146 AtomicString FastTextAutosizer::computeFingerprint(RenderBlock* block) |
117 result.iterator->value = adoptPtr(new Cluster(blockFingerprint)); | 147 { |
118 | 148 // FIXME(crbug.com/322340): Implement a fingerprinting algorithm. |
119 Cluster* cluster = result.iterator->value.get(); | 149 return nullAtom; |
120 cluster->addBlock(block); | 150 } |
121 | 151 |
122 m_clusterForBlock.set(block, cluster); | 152 FastTextAutosizer::Cluster* FastTextAutosizer::getOrCreateCluster(RenderBlock* b lock) |
123 } | 153 { |
124 | 154 ClusterMap::AddResult addResult = m_clusters.add(block, PassOwnPtr<Cluster>( )); |
125 void FastTextAutosizer::destroy(RenderBlock* block) | 155 if (!addResult.isNewEntry) |
126 { | 156 return addResult.iterator->value.get(); |
127 Cluster* cluster = m_clusterForBlock.take(block); | 157 |
128 if (!cluster) | 158 AtomicString fingerprint = m_fingerprintMapper.get(block); |
129 return; | 159 if (fingerprint.isNull()) { |
130 cluster->m_blocks.remove(block); | 160 addResult.iterator->value = adoptPtr(createCluster(block)); |
131 if (cluster->m_blocks.isEmpty()) { | 161 return addResult.iterator->value.get(); |
132 // This deletes the Cluster. | 162 } |
133 m_clusterForFingerprint.remove(cluster->m_fingerprint); | 163 return addSupercluster(m_fingerprintMapper.getBlocks(fingerprint), block); |
134 return; | 164 } |
135 } | 165 |
136 cluster->setNeedsClusterRecalc(); | 166 FastTextAutosizer::Cluster* FastTextAutosizer::createCluster(RenderBlock* block) |
137 } | 167 { |
138 | 168 float multiplier = clusterWantsAutosizing(block) ? computeMultiplier(block) : 1.0f; |
139 static void applyMultiplier(RenderObject* renderer, float multiplier) | 169 return new Cluster(block, multiplier); |
140 { | 170 } |
141 RenderStyle* currentStyle = renderer->style(); | 171 |
172 FastTextAutosizer::Cluster* FastTextAutosizer::addSupercluster(WTF::HashSet<Rend erBlock*>& roots, RenderBlock* returnFor) | |
pdr.
2014/01/07 19:50:27
Would it be cleaner to change this to:
FastTextAut
skobes
2014/01/07 20:10:53
How about passing the fingerprint in, and having i
| |
173 { | |
174 RenderBlock* superRoot = deepestCommonAncestor(roots); | |
175 | |
176 bool shouldAutosize = false; | |
177 for (WTF::HashSet<RenderBlock*>::iterator it = roots.begin(); it != roots.en d(); ++it) | |
178 shouldAutosize |= clusterWantsAutosizing(*it); | |
179 | |
180 float multiplier = shouldAutosize ? computeMultiplier(superRoot) : 1.0f; | |
181 | |
182 Cluster* result = 0; | |
183 for (WTF::HashSet<RenderBlock*>::iterator it = roots.begin(); it != roots.en d(); ++it) { | |
184 Cluster* cluster = new Cluster(*it, multiplier); | |
185 m_clusters.set(*it, adoptPtr(cluster)); | |
186 | |
187 if (*it == returnFor) | |
188 result = cluster; | |
189 } | |
190 return result; | |
191 } | |
192 | |
193 RenderBlock* FastTextAutosizer::deepestCommonAncestor(WTF::HashSet<RenderBlock*> & blocks) | |
pdr.
2014/01/07 19:50:27
It may be clean to pull out the typedef for BlockS
skobes
2014/01/07 20:10:53
This is a great idea :)
| |
194 { | |
195 // Find the lowest common ancestor of blocks. | |
196 // Note: this could be improved to not be O(b*h) for b blocks and tree heigh t h. | |
197 HashCountedSet<RenderObject*> ancestors; | |
198 for (WTF::HashSet<RenderBlock*>::iterator it = blocks.begin(); it != blocks. end(); ++it) { | |
199 for (RenderBlock* block = (*it); block; block = block->containingBlock() ) { | |
200 ancestors.add(block); | |
201 // The first ancestor that has all of the blocks as children wins. | |
202 if (ancestors.count(block) == blocks.size()) | |
203 return block; | |
204 } | |
205 } | |
206 ASSERT_NOT_REACHED(); | |
207 return 0; | |
208 } | |
209 | |
210 float FastTextAutosizer::computeMultiplier(RenderBlock* block) | |
211 { | |
212 // Block width, in CSS pixels. | |
213 float blockWidth = block->contentLogicalWidth(); | |
214 | |
215 // FIXME: incorporate font scale factor. | |
216 // FIXME: incorporate device scale adjustment. | |
217 return max(min(blockWidth, (float) m_layoutWidth) / m_windowWidth, 1.0f); | |
218 } | |
219 | |
220 void FastTextAutosizer::applyMultiplier(RenderObject* renderer, float multiplier ) | |
221 { | |
222 RenderStyle* currentStyle = renderer->style(); | |
142 if (currentStyle->textAutosizingMultiplier() == multiplier) | 223 if (currentStyle->textAutosizingMultiplier() == multiplier) |
143 return; | 224 return; |
144 | 225 |
145 // We need to clone the render style to avoid breaking style sharing. | 226 // We need to clone the render style to avoid breaking style sharing. |
146 RefPtr<RenderStyle> style = RenderStyle::clone(currentStyle); | 227 RefPtr<RenderStyle> style = RenderStyle::clone(currentStyle); |
147 style->setTextAutosizingMultiplier(multiplier); | 228 style->setTextAutosizingMultiplier(multiplier); |
148 style->setUnique(); | 229 style->setUnique(); |
149 renderer->setStyleInternal(style.release()); | 230 renderer->setStyleInternal(style.release()); |
150 } | 231 } |
151 | 232 |
152 void FastTextAutosizer::inflate(RenderBlock* block) | 233 void FastTextAutosizer::FingerprintMapper::add(RenderBlock* block, AtomicString fingerprint) |
153 { | 234 { |
154 Cluster* cluster = 0; | 235 m_fingerprints.set(block, fingerprint); |
155 for (const RenderObject* clusterBlock = block; clusterBlock && !cluster; clu sterBlock = clusterBlock->parent()) { | 236 |
156 if (clusterBlock->isRenderBlock()) | 237 ReverseFingerprintMap::AddResult addResult = m_blocksForFingerprint.add(fing erprint, PassOwnPtr<BlockSet>()); |
157 cluster = m_clusterForBlock.get(toRenderBlock(clusterBlock)); | 238 if (addResult.isNewEntry) |
158 } | 239 addResult.iterator->value = adoptPtr(new BlockSet); |
159 if (!cluster) | 240 addResult.iterator->value->add(block); |
160 return; | 241 } |
161 | 242 |
162 recalcClusterIfNeeded(cluster); | 243 void FastTextAutosizer::FingerprintMapper::remove(RenderBlock* block) |
163 | 244 { |
164 applyMultiplier(block, cluster->m_multiplier); | 245 AtomicString fingerprint = m_fingerprints.take(block); |
165 | 246 if (fingerprint.isNull()) |
166 // FIXME: Add an optimization to not do this walk if it's not needed. | 247 return; |
167 for (InlineWalker walker(block); !walker.atEnd(); walker.advance()) { | 248 |
168 RenderObject* inlineObj = walker.current(); | 249 ReverseFingerprintMap::iterator blocksIter = m_blocksForFingerprint.find(fin gerprint); |
169 if (inlineObj->isRenderBlock() && m_clusterForBlock.contains(toRenderBlo ck(inlineObj))) | 250 WTF::HashSet<RenderBlock*>& blocks = *blocksIter->value; |
170 continue; | 251 blocks.remove(block); |
171 | 252 if (blocks.isEmpty()) |
172 applyMultiplier(inlineObj, cluster->m_multiplier); | 253 m_blocksForFingerprint.remove(blocksIter); |
173 } | 254 } |
174 } | 255 |
175 | 256 AtomicString FastTextAutosizer::FingerprintMapper::get(RenderBlock* block) |
176 AtomicString FastTextAutosizer::fingerprint(const RenderBlock* block) | 257 { |
177 { | 258 return m_fingerprints.get(block); |
178 // FIXME(crbug.com/322340): Implement a better fingerprinting algorithm. | 259 } |
179 return AtomicString::number((unsigned long long) block); | 260 |
180 } | 261 WTF::HashSet<RenderBlock*>& FastTextAutosizer::FingerprintMapper::getBlocks(Atom icString fingerprint) |
181 | 262 { |
182 void FastTextAutosizer::recalcClusterIfNeeded(FastTextAutosizer::Cluster* cluste r) | 263 return *m_blocksForFingerprint.get(fingerprint); |
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 } | 264 } |
230 | 265 |
231 } // namespace WebCore | 266 } // namespace WebCore |
OLD | NEW |