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 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 44 #include "core/rendering/RenderView.h" | 44 #include "core/rendering/RenderView.h" |
| 45 | 45 |
| 46 using namespace std; | 46 using namespace std; |
| 47 | 47 |
| 48 namespace WebCore { | 48 namespace WebCore { |
| 49 | 49 |
| 50 static const RenderObject* parentElementRenderer(const RenderObject* renderer) | 50 static const RenderObject* parentElementRenderer(const RenderObject* renderer) |
| 51 { | 51 { |
| 52 // At style recalc, the renderer's parent may not be attached, | 52 // At style recalc, the renderer's parent may not be attached, |
| 53 // so we need to obtain this from the DOM tree. | 53 // so we need to obtain this from the DOM tree. |
| 54 | |
| 55 const Node* node = renderer->node(); | 54 const Node* node = renderer->node(); |
| 56 if (!node) | 55 if (!node) |
| 57 return 0; | 56 return 0; |
| 58 | 57 |
| 59 while ((node = node->parentNode())) { | 58 while ((node = node->parentNode())) { |
| 60 if (node->isElementNode()) | 59 if (node->isElementNode()) |
| 61 return node->renderer(); | 60 return node->renderer(); |
| 62 } | 61 } |
| 63 return 0; | 62 return 0; |
| 64 } | 63 } |
| 65 | 64 |
| 66 static const Vector<QualifiedName>& formInputTags() | 65 static bool isFormInput(const Element* element) |
| 67 { | 66 { |
| 68 // Returns the tags for the form input elements. | |
| 69 DEFINE_STATIC_LOCAL(Vector<QualifiedName>, formInputTags, ()); | 67 DEFINE_STATIC_LOCAL(Vector<QualifiedName>, formInputTags, ()); |
| 70 if (formInputTags.isEmpty()) { | 68 if (formInputTags.isEmpty()) { |
| 71 formInputTags.append(HTMLNames::inputTag); | 69 formInputTags.append(HTMLNames::inputTag); |
| 72 formInputTags.append(HTMLNames::buttonTag); | 70 formInputTags.append(HTMLNames::buttonTag); |
| 73 formInputTags.append(HTMLNames::selectTag); | 71 formInputTags.append(HTMLNames::selectTag); |
| 74 } | 72 } |
| 75 return formInputTags; | 73 return formInputTags.contains(element->tagQName()); |
| 76 } | 74 } |
| 77 | 75 |
| 78 static bool isAutosizingContainer(const RenderObject* renderer) | 76 static bool isPotentialClusterRoot(const RenderObject* renderer) |
| 79 { | 77 { |
| 80 // "Autosizing containers" are the smallest unit for which we can | 78 // "Potential cluster roots" are the smallest unit for which we can |
| 81 // enable/disable Text Autosizing. | 79 // enable/disable text autosizing. |
| 82 // - Must not be inline, as different multipliers on one line looks terrible . | 80 // - Must not be inline, as different multipliers on one line looks terrible . |
| 83 // Exceptions are inline-block and alike elements (inline-table, -webkit-i nline-*), | 81 // Exceptions are inline-block and alike elements (inline-table, -webkit-i nline-*), |
| 84 // as they often contain entire multi-line columns of text. | 82 // as they often contain entire multi-line columns of text. |
| 85 // - Must not be list items, as items in the same list should look consisten t (*). | 83 // - Must not be list items, as items in the same list should look consisten t (*). |
| 86 // - Must not be normal list items, as items in the same list should look | 84 // - Must not be normal list items, as items in the same list should look |
| 87 // consistent, unless they are floating or position:absolute/fixed. | 85 // consistent, unless they are floating or position:absolute/fixed. |
| 88 Node* node = renderer->generatingNode(); | 86 Node* node = renderer->generatingNode(); |
| 89 if ((node && !node->hasChildren()) | 87 if (node && !node->hasChildren()) |
| 90 || !renderer->isRenderBlock() | 88 return false; |
| 91 || (renderer->isInline() && !renderer->style()->isDisplayReplacedType()) ) | 89 if (!renderer->isRenderBlock()) |
| 90 return false; | |
| 91 if (renderer->isInline() && !renderer->style()->isDisplayReplacedType()) | |
| 92 return false; | 92 return false; |
| 93 if (renderer->isListItem()) | 93 if (renderer->isListItem()) |
| 94 return renderer->isFloating() || renderer->isOutOfFlowPositioned(); | 94 return (renderer->isFloating() || renderer->isOutOfFlowPositioned()); |
| 95 // Avoid creating containers for text within text controls, buttons, or <sel ect> buttons. | 95 // Avoid creating containers for text within text controls, buttons, or <sel ect> buttons. |
| 96 Node* parentNode = renderer->parent() ? renderer->parent()->generatingNode() : 0; | 96 Node* parentNode = renderer->parent() ? renderer->parent()->generatingNode() : 0; |
| 97 if (parentNode && parentNode->isElementNode() && formInputTags().contains(to Element(parentNode)->tagQName())) | 97 if (parentNode && parentNode->isElementNode() && isFormInput(toElement(paren tNode))) |
| 98 return false; | 98 return false; |
| 99 | 99 |
| 100 return true; | 100 return true; |
| 101 } | 101 } |
| 102 | 102 |
| 103 static RenderObject* nextInPreOrderSkippingDescendantsOfContainers(const RenderO bject* current, const RenderObject* stayWithin) | |
| 104 { | |
| 105 if (current == stayWithin || !isAutosizingContainer(current)) | |
| 106 return current->nextInPreOrder(stayWithin); | |
| 107 return current->nextInPreOrderAfterChildren(stayWithin); | |
| 108 } | |
| 109 | |
| 110 static bool isIndependentDescendant(const RenderBlock* renderer) | 103 static bool isIndependentDescendant(const RenderBlock* renderer) |
| 111 { | 104 { |
| 112 ASSERT(isAutosizingContainer(renderer)); | 105 ASSERT(isPotentialClusterRoot(renderer)); |
| 113 | 106 |
| 114 // "Autosizing clusters" are special autosizing containers within which we | |
| 115 // want to enforce a uniform text size multiplier, in the hopes of making | |
| 116 // the major sections of the page look internally consistent. | |
| 117 // All their descendants (including other autosizing containers) must share | |
| 118 // the same multiplier, except for subtrees which are themselves clusters, | |
| 119 // and some of their descendant containers might not be autosized at all | |
| 120 // (for example if their height is constrained). | |
| 121 // Additionally, clusterShouldBeAutosized requires each cluster to contain a | |
| 122 // minimum amount of text, without which it won't be autosized. | |
| 123 // | |
| 124 // Clusters are chosen using very similar criteria to CSS flow roots, aka | |
| 125 // block formatting contexts (http://w3.org/TR/css3-box/#flow-root), since | |
| 126 // flow roots correspond to box containers that behave somewhat | |
| 127 // independently from their parent (for example they don't overlap floats). | |
| 128 // The definition of a flow root also conveniently includes most of the | |
| 129 // ways that a box and its children can have significantly different width | |
| 130 // from the box's parent (we want to avoid having significantly different | |
| 131 // width blocks within a cluster, since the narrower blocks would end up | |
| 132 // larger than would otherwise be necessary). | |
| 133 RenderBlock* containingBlock = renderer->containingBlock(); | 107 RenderBlock* containingBlock = renderer->containingBlock(); |
| 134 return renderer->isRenderView() | 108 return renderer->isRenderView() |
| 135 || renderer->isFloating() | 109 || renderer->isFloating() |
| 136 || renderer->isOutOfFlowPositioned() | 110 || renderer->isOutOfFlowPositioned() |
| 137 || renderer->isTableCell() | 111 || renderer->isTableCell() |
| 138 || renderer->isTableCaption() | 112 || renderer->isTableCaption() |
| 139 || renderer->isFlexibleBoxIncludingDeprecated() | 113 || renderer->isFlexibleBoxIncludingDeprecated() |
| 140 || renderer->hasColumns() | 114 || renderer->hasColumns() |
| 141 || (containingBlock && containingBlock->isHorizontalWritingMode() != ren derer->isHorizontalWritingMode()) | 115 || (containingBlock && containingBlock->isHorizontalWritingMode() != ren derer->isHorizontalWritingMode()) |
| 142 || renderer->style()->isDisplayReplacedType() | 116 || renderer->style()->isDisplayReplacedType() |
| 143 || renderer->isTextArea() | 117 || renderer->isTextArea() |
| 144 || renderer->style()->userModify() != READ_ONLY; | 118 || renderer->style()->userModify() != READ_ONLY; |
| 145 // FIXME: Tables need special handling to multiply all their columns by | 119 // FIXME: Tables need special handling to multiply all their columns by |
| 146 // the same amount even if they're different widths; so do hasColumns() | 120 // the same amount even if they're different widths; so do hasColumns() |
| 147 // containers, and probably flexboxes... | 121 // containers, and probably flexboxes... |
| 148 } | 122 } |
| 149 | 123 |
| 150 static bool containerIsRowOfLinks(const RenderObject* container) | 124 static bool blockIsRowOfLinks(const RenderBlock* block) |
| 151 { | 125 { |
| 152 // A "row of links" is a container for which holds: | 126 // A "row of links" is a block for which: |
| 153 // 1. it should not contain non-link text elements longer than 3 characters | 127 // 1. It does not contain non-link text elements longer than 3 characters |
| 154 // 2. it should contain min. 3 inline links and all links should | 128 // 2. It contains a minimum of 3 inline links and all links should |
| 155 // have the same specified font size | 129 // have the same specified font size. |
| 156 // 3. it should not contain <br> elements | 130 // 3. It should not contain <br> elements. |
| 157 // 4. it should contain only inline elements unless they are containers, | 131 // 4. It should contain only inline elements unless they are containers, |
| 158 // children of link elements or children of sub-containers. | 132 // children of link elements or children of sub-containers. |
| 159 int linkCount = 0; | 133 int linkCount = 0; |
| 160 RenderObject* renderer = container->nextInPreOrder(container); | 134 RenderObject* renderer = block->nextInPreOrder(block); |
| 161 float matchingFontSize = -1; | 135 float matchingFontSize = -1; |
| 162 | 136 |
| 163 while (renderer) { | 137 while (renderer) { |
| 164 if (!isAutosizingContainer(renderer)) { | 138 if (!isPotentialClusterRoot(renderer)) { |
| 165 if (renderer->isText() && toRenderText(renderer)->text().impl()->str ipWhiteSpace()->length() > 3) | 139 if (renderer->isText() && toRenderText(renderer)->text().impl()->str ipWhiteSpace()->length() > 3) |
| 166 return false; | 140 return false; |
| 167 if (!renderer->isInline()) | 141 if (!renderer->isInline() || renderer->isBR()) |
| 168 return false; | |
| 169 if (renderer->isBR()) | |
| 170 return false; | 142 return false; |
| 171 } | 143 } |
| 172 if (renderer->style()->isLink()) { | 144 if (renderer->style()->isLink()) { |
| 173 if (matchingFontSize < 0) { | 145 linkCount++; |
| 146 if (matchingFontSize < 0) | |
| 174 matchingFontSize = renderer->style()->specifiedFontSize(); | 147 matchingFontSize = renderer->style()->specifiedFontSize(); |
| 175 } else { | 148 else if (matchingFontSize != renderer->style()->specifiedFontSize()) |
| 176 if (matchingFontSize != renderer->style()->specifiedFontSize()) | 149 return false; |
| 177 return false; | |
| 178 } | |
| 179 | 150 |
| 180 linkCount++; | |
| 181 // Skip traversing descendants of the link. | 151 // Skip traversing descendants of the link. |
| 182 renderer = renderer->nextInPreOrderAfterChildren(container); | 152 renderer = renderer->nextInPreOrderAfterChildren(block); |
| 183 } else { | 153 continue; |
| 184 renderer = nextInPreOrderSkippingDescendantsOfContainers(renderer, c ontainer); | |
| 185 } | 154 } |
| 155 renderer = renderer->nextInPreOrder(block); | |
| 186 } | 156 } |
| 187 | 157 |
| 188 return (linkCount >= 3); | 158 return (linkCount >= 3); |
| 189 } | 159 } |
| 190 | 160 |
| 191 static bool contentHeightIsConstrained(const RenderBlock* container) | 161 static bool blockHeightConstrained(const RenderBlock* block) |
| 192 { | 162 { |
| 193 // FIXME: Propagate constrainedness down the tree, to avoid inefficiently wa lking back up from each box. | 163 // FIXME: Propagate constrainedness down the tree, to avoid inefficiently wa lking back up from each box. |
| 194 // FIXME: This code needs to take into account vertical writing modes. | 164 // FIXME: This code needs to take into account vertical writing modes. |
| 195 // FIXME: Consider additional heuristics, such as ignoring fixed heights if the content is already overflowing before autosizing kicks in. | 165 // FIXME: Consider additional heuristics, such as ignoring fixed heights if the content is already overflowing before autosizing kicks in. |
| 196 for (; container; container = container->containingBlock()) { | 166 for (; block; block = block->containingBlock()) { |
| 197 RenderStyle* style = container->style(); | 167 RenderStyle* style = block->style(); |
| 198 if (style->overflowY() >= OSCROLL) | 168 if (style->overflowY() >= OSCROLL) |
| 199 return false; | 169 return false; |
| 200 if (style->height().isSpecified() || style->maxHeight().isSpecified() || container->isOutOfFlowPositioned()) { | 170 if (style->height().isSpecified() || style->maxHeight().isSpecified() || block->isOutOfFlowPositioned()) { |
| 201 // Some sites (e.g. wikipedia) set their html and/or body elements t o height:100%, | 171 // Some sites (e.g. wikipedia) set their html and/or body elements t o height:100%, |
| 202 // without intending to constrain the height of the content within t hem. | 172 // without intending to constrain the height of the content within t hem. |
| 203 return !container->isRoot() && !container->isBody(); | 173 return !block->isRoot() && !block->isBody(); |
| 204 } | 174 } |
| 205 if (container->isFloating()) | 175 if (block->isFloating()) |
| 206 return false; | 176 return false; |
| 207 } | 177 } |
| 208 return false; | 178 return false; |
| 209 } | 179 } |
| 210 | 180 |
| 211 static bool containerContainsOneOfTags(const RenderBlock* container, const Vecto r<QualifiedName>& tags) | 181 static bool blockContainsFormInput(const RenderBlock* block) |
| 212 { | 182 { |
| 213 const RenderObject* renderer = container; | 183 const RenderObject* renderer = block; |
| 214 while (renderer) { | 184 while (renderer) { |
| 215 const Node* rendererNode = renderer->node(); | 185 const Node* node = renderer->node(); |
| 216 if (rendererNode && rendererNode->isElementNode()) { | 186 if (node && node->isElementNode() && isFormInput(toElement(node))) |
| 217 if (tags.contains(toElement(rendererNode)->tagQName())) | 187 return true; |
| 218 return true; | 188 if (renderer == block) |
| 219 } | 189 renderer = renderer->nextInPreOrder(block); |
| 220 renderer = nextInPreOrderSkippingDescendantsOfContainers(renderer, conta iner); | 190 else |
| 191 renderer = renderer->nextInPreOrderAfterChildren(block); | |
| 221 } | 192 } |
| 222 | 193 |
| 223 return false; | 194 return false; |
| 224 } | 195 } |
| 225 | 196 |
| 226 static bool containerShouldBeAutosized(const RenderBlock* container) | 197 // Some blocks are not autosized even if their parent cluster wants them to. |
| 198 static bool blockSuppressesAutosizing(const RenderBlock* block) | |
| 227 { | 199 { |
| 228 if (containerContainsOneOfTags(container, formInputTags())) | 200 if (blockContainsFormInput(block)) |
| 229 return false; | 201 return true; |
| 230 | 202 |
| 231 if (containerIsRowOfLinks(container)) | 203 if (blockIsRowOfLinks(block)) |
| 232 return false; | 204 return true; |
| 233 | 205 |
| 234 // Don't autosize block-level text that can't wrap (as it's likely to | 206 // Don't autosize block-level text that can't wrap (as it's likely to |
| 235 // expand sideways and break the page's layout). | 207 // expand sideways and break the page's layout). |
| 236 if (!container->style()->autoWrap()) | 208 if (!block->style()->autoWrap()) |
| 237 return false; | 209 return true; |
| 238 | 210 |
| 239 return !contentHeightIsConstrained(container); | 211 if (blockHeightConstrained(block)) |
| 212 return true; | |
| 213 | |
| 214 return false; | |
| 215 } | |
| 216 | |
| 217 static bool mightBeWiderOrNarrowerDescendant(const RenderBlock* block) | |
| 218 { | |
| 219 // FIXME: This heuristic may need to be expanded to other ways a block can b e wider or narrower | |
| 220 // than its parent containing block. | |
| 221 return block->style() && block->style()->width().isSpecified(); | |
| 222 } | |
| 223 | |
| 224 // Before a block enters layout we don't know for sure if it will become a clust er. | |
| 225 // Note: clusters are also created for blocks that do become autosizing clusters ! | |
| 226 static bool blockMightBecomeAutosizingCluster(const RenderBlock* block) | |
| 227 { | |
| 228 ASSERT(isPotentialClusterRoot(block)); | |
| 229 return isIndependentDescendant(block) || mightBeWiderOrNarrowerDescendant(bl ock) || block->isTable(); | |
| 240 } | 230 } |
| 241 | 231 |
| 242 FastTextAutosizer::FastTextAutosizer(const Document* document) | 232 FastTextAutosizer::FastTextAutosizer(const Document* document) |
| 243 : m_document(document) | 233 : m_document(document) |
| 244 , m_frameWidth(0) | 234 , m_frameWidth(0) |
| 245 , m_layoutWidth(0) | 235 , m_layoutWidth(0) |
| 246 , m_baseMultiplier(0) | 236 , m_baseMultiplier(0) |
| 247 , m_pageAutosizingStatus(PageAutosizingStatusUnknown) | 237 , m_pageAutosizingStatus(PageAutosizingStatusUnknown) |
| 248 , m_firstBlock(0) | 238 , m_firstBlock(0) |
| 249 #ifndef NDEBUG | 239 #ifndef NDEBUG |
| 250 , m_renderViewInfoPrepared(false) | 240 , m_renderViewInfoPrepared(false) |
| 251 , m_blocksThatHaveBegunLayout() | 241 , m_blocksThatHaveBegunLayout() |
| 252 #endif | 242 #endif |
| 253 , m_superclusters() | 243 , m_superclusters() |
| 254 , m_clusterStack() | 244 , m_clusterStack() |
| 255 , m_fingerprintMapper() | 245 , m_fingerprintMapper() |
| 256 { | 246 { |
| 257 } | 247 } |
| 258 | 248 |
| 259 void FastTextAutosizer::record(const RenderBlock* block) | 249 void FastTextAutosizer::record(const RenderBlock* block) |
| 260 { | 250 { |
| 261 if (!enabled()) | 251 if (!enabled()) |
| 262 return; | 252 return; |
| 263 | 253 |
| 264 ASSERT(!m_blocksThatHaveBegunLayout.contains(block)); | 254 ASSERT(!m_blocksThatHaveBegunLayout.contains(block)); |
| 265 | 255 |
| 266 if (!isFingerprintingCandidate(block)) | 256 if (!isPotentialClusterRoot(block)) |
| 257 return; | |
| 258 | |
| 259 if (!block->isRenderView() && !blockMightBecomeAutosizingCluster(block)) | |
|
skobes
2014/03/19 00:04:33
It shouldn't be necessary to check isRenderView he
pdr.
2014/03/19 00:14:00
Good catch. I also removed a similar call in maybe
| |
| 267 return; | 260 return; |
| 268 | 261 |
| 269 if (Fingerprint fingerprint = computeFingerprint(block)) | 262 if (Fingerprint fingerprint = computeFingerprint(block)) |
| 270 m_fingerprintMapper.addTentativeClusterRoot(block, fingerprint); | 263 m_fingerprintMapper.addTentativeClusterRoot(block, fingerprint); |
| 271 } | 264 } |
| 272 | 265 |
| 273 void FastTextAutosizer::destroy(const RenderBlock* block) | 266 void FastTextAutosizer::destroy(const RenderBlock* block) |
| 274 { | 267 { |
| 275 if (!enabled()) | 268 if (!enabled()) |
| 276 return; | 269 return; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 357 continue; | 350 continue; |
| 358 for (RenderObject* row = section->firstChild(); row; row = row->nextSibl ing()) { | 351 for (RenderObject* row = section->firstChild(); row; row = row->nextSibl ing()) { |
| 359 if (!row->isTableRow()) | 352 if (!row->isTableRow()) |
| 360 continue; | 353 continue; |
| 361 for (RenderObject* cell = row->firstChild(); cell; cell = cell->next Sibling()) { | 354 for (RenderObject* cell = row->firstChild(); cell; cell = cell->next Sibling()) { |
| 362 if (!cell->isTableCell()) | 355 if (!cell->isTableCell()) |
| 363 continue; | 356 continue; |
| 364 RenderTableCell* renderTableCell = toRenderTableCell(cell); | 357 RenderTableCell* renderTableCell = toRenderTableCell(cell); |
| 365 | 358 |
| 366 bool shouldAutosize; | 359 bool shouldAutosize; |
| 367 if (!containerShouldBeAutosized(renderTableCell)) | 360 if (blockSuppressesAutosizing(renderTableCell)) |
| 368 shouldAutosize = false; | 361 shouldAutosize = false; |
| 369 else if (Supercluster* supercluster = getSupercluster(renderTabl eCell)) | 362 else if (Supercluster* supercluster = getSupercluster(renderTabl eCell)) |
| 370 shouldAutosize = anyClusterHasEnoughTextToAutosize(superclus ter->m_roots, table); | 363 shouldAutosize = anyClusterHasEnoughTextToAutosize(superclus ter->m_roots, table); |
| 371 else | 364 else |
| 372 shouldAutosize = clusterWouldHaveEnoughTextToAutosize(render TableCell, table); | 365 shouldAutosize = clusterWouldHaveEnoughTextToAutosize(render TableCell, table); |
| 373 | 366 |
| 374 if (shouldAutosize) { | 367 if (shouldAutosize) { |
| 375 for (RenderObject* child = cell; child; child = child->nextI nPreOrder(cell)) { | 368 for (RenderObject* child = cell; child; child = child->nextI nPreOrder(cell)) { |
| 376 if (child->isText()) { | 369 if (child->isText()) { |
| 377 applyMultiplier(child, multiplier); | 370 applyMultiplier(child, multiplier); |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 398 #endif | 391 #endif |
| 399 } else if (currentCluster()->m_root == block) { | 392 } else if (currentCluster()->m_root == block) { |
| 400 m_clusterStack.removeLast(); | 393 m_clusterStack.removeLast(); |
| 401 } | 394 } |
| 402 } | 395 } |
| 403 | 396 |
| 404 void FastTextAutosizer::inflate(RenderBlock* block) | 397 void FastTextAutosizer::inflate(RenderBlock* block) |
| 405 { | 398 { |
| 406 Cluster* cluster = currentCluster(); | 399 Cluster* cluster = currentCluster(); |
| 407 float multiplier = 0; | 400 float multiplier = 0; |
| 408 for (RenderObject* descendant = nextChildSkippingChildrenOfBlocks(block, blo ck); descendant; descendant = nextChildSkippingChildrenOfBlocks(descendant, bloc k)) { | 401 RenderObject* descendant = block->nextInPreOrder(); |
| 402 while (descendant) { | |
| 403 // Skip block descendants because they will be inflate()'d on their own. | |
| 404 if (descendant->isRenderBlock()) { | |
| 405 descendant = descendant->nextInPreOrderAfterChildren(block); | |
| 406 continue; | |
| 407 } | |
| 409 if (descendant->isText()) { | 408 if (descendant->isText()) { |
| 410 // We only calculate this multiplier on-demand to ensure the parent block of this text | 409 // We only calculate this multiplier on-demand to ensure the parent block of this text |
| 411 // has entered layout. | 410 // has entered layout. |
| 412 if (!multiplier) | 411 if (!multiplier) |
| 413 multiplier = cluster->m_autosize ? clusterMultiplier(cluster) : 1.0f; | 412 multiplier = cluster->m_autosize ? clusterMultiplier(cluster) : 1.0f; |
| 414 applyMultiplier(descendant, multiplier); | 413 applyMultiplier(descendant, multiplier); |
| 415 applyMultiplier(descendant->parent(), multiplier); // Parent handles line spacing. | 414 applyMultiplier(descendant->parent(), multiplier); // Parent handles line spacing. |
| 416 } | 415 } |
| 416 descendant = descendant->nextInPreOrder(block); | |
| 417 } | 417 } |
| 418 } | 418 } |
| 419 | 419 |
| 420 bool FastTextAutosizer::enabled() | 420 bool FastTextAutosizer::enabled() |
| 421 { | 421 { |
| 422 if (!m_document->settings() || !m_document->page() || m_document->printing() ) | 422 if (!m_document->settings() || !m_document->page() || m_document->printing() ) |
| 423 return false; | 423 return false; |
| 424 | 424 |
| 425 return m_document->settings()->textAutosizingEnabled(); | 425 return m_document->settings()->textAutosizingEnabled(); |
| 426 } | 426 } |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 449 } | 449 } |
| 450 | 450 |
| 451 m_pageAutosizingStatus = m_frameWidth && (m_baseMultiplier * (static_cast<fl oat>(m_layoutWidth) / m_frameWidth) > 1.0f) | 451 m_pageAutosizingStatus = m_frameWidth && (m_baseMultiplier * (static_cast<fl oat>(m_layoutWidth) / m_frameWidth) > 1.0f) |
| 452 ? PageNeedsAutosizing : PageDoesNotNeedAutosizing; | 452 ? PageNeedsAutosizing : PageDoesNotNeedAutosizing; |
| 453 | 453 |
| 454 #ifndef NDEBUG | 454 #ifndef NDEBUG |
| 455 m_renderViewInfoPrepared = true; | 455 m_renderViewInfoPrepared = true; |
| 456 #endif | 456 #endif |
| 457 } | 457 } |
| 458 | 458 |
| 459 bool FastTextAutosizer::isFingerprintingCandidate(const RenderBlock* block) | |
| 460 { | |
| 461 // FIXME: move the logic out of TextAutosizer.cpp into this class. | |
| 462 return block->isRenderView() | |
| 463 || (isAutosizingContainer(block) | |
| 464 && (isIndependentDescendant(block) | |
| 465 || mightBeWiderOrNarrowerDescendant(block))); | |
| 466 } | |
| 467 | |
| 468 bool FastTextAutosizer::clusterWouldHaveEnoughTextToAutosize(const RenderBlock* root, const RenderBlock* widthProvider) | 459 bool FastTextAutosizer::clusterWouldHaveEnoughTextToAutosize(const RenderBlock* root, const RenderBlock* widthProvider) |
| 469 { | 460 { |
| 470 Cluster hypotheticalCluster(root, true, 0); | 461 Cluster hypotheticalCluster(root, true, 0); |
| 471 return clusterHasEnoughTextToAutosize(&hypotheticalCluster, widthProvider); | 462 return clusterHasEnoughTextToAutosize(&hypotheticalCluster, widthProvider); |
| 472 } | 463 } |
| 473 | 464 |
| 474 bool FastTextAutosizer::clusterHasEnoughTextToAutosize(Cluster* cluster, const R enderBlock* widthProvider) | 465 bool FastTextAutosizer::clusterHasEnoughTextToAutosize(Cluster* cluster, const R enderBlock* widthProvider) |
| 475 { | 466 { |
| 476 if (cluster->m_hasEnoughTextToAutosize != UnknownAmountOfText) | 467 if (cluster->m_hasEnoughTextToAutosize != UnknownAmountOfText) |
| 477 return cluster->m_hasEnoughTextToAutosize == HasEnoughText; | 468 return cluster->m_hasEnoughTextToAutosize == HasEnoughText; |
| 478 | 469 |
| 479 const RenderBlock* root = cluster->m_root; | 470 const RenderBlock* root = cluster->m_root; |
| 480 if (!widthProvider) | 471 if (!widthProvider) |
| 481 widthProvider = clusterWidthProvider(root); | 472 widthProvider = clusterWidthProvider(root); |
| 482 | 473 |
| 483 // TextAreas and user-modifiable areas get a free pass to autosize regardles s of text content. | 474 // TextAreas and user-modifiable areas get a free pass to autosize regardles s of text content. |
| 484 if (root->isTextArea() || (root->style() && root->style()->userModify() != R EAD_ONLY)) { | 475 if (root->isTextArea() || (root->style() && root->style()->userModify() != R EAD_ONLY)) { |
| 485 cluster->m_hasEnoughTextToAutosize = HasEnoughText; | 476 cluster->m_hasEnoughTextToAutosize = HasEnoughText; |
| 486 return true; | 477 return true; |
| 487 } | 478 } |
| 488 | 479 |
| 489 if (!containerShouldBeAutosized(root)) { | 480 if (blockSuppressesAutosizing(root)) { |
| 490 cluster->m_hasEnoughTextToAutosize = NotEnoughText; | 481 cluster->m_hasEnoughTextToAutosize = NotEnoughText; |
| 491 return false; | 482 return false; |
| 492 } | 483 } |
| 493 | 484 |
| 494 // 4 lines of text is considered enough to autosize. | 485 // 4 lines of text is considered enough to autosize. |
| 495 float minimumTextLengthToAutosize = widthFromBlock(widthProvider) * 4; | 486 float minimumTextLengthToAutosize = widthFromBlock(widthProvider) * 4; |
| 496 | 487 |
| 497 float length = 0; | 488 float length = 0; |
| 498 RenderObject* descendant = root->nextInPreOrder(root); | 489 RenderObject* descendant = root->nextInPreOrder(root); |
| 499 while (descendant) { | 490 while (descendant) { |
| 500 if (descendant->isRenderBlock()) { | 491 if (descendant->isRenderBlock()) { |
| 501 RenderBlock* block = toRenderBlock(descendant); | 492 RenderBlock* block = toRenderBlock(descendant); |
| 502 if (isAutosizingContainer(block)) { | 493 |
| 503 // Note: Ideally we would check isWiderOrNarrowerDescendant here but we only know that | 494 // FIXME: This check wants to be blockMightBecomeAutosizingCluster b ut our tests |
|
skobes
2014/03/19 00:04:33
I think the code is correct as written, because bl
skobes
2014/03/19 00:09:59
(it will be clearer once we address http://crbug.c
pdr.
2014/03/19 00:14:00
That is what it's doing, though it's not clear to
| |
| 504 // after the block has entered layout, which may not be th e case. | 495 // are particularly sensitive to modifying this code. We shou ld refactor |
| 496 // this code and update the tests. | |
| 497 if (isPotentialClusterRoot(block)) { | |
| 505 bool isAutosizingClusterRoot = isIndependentDescendant(block) || block->isTable(); | 498 bool isAutosizingClusterRoot = isIndependentDescendant(block) || block->isTable(); |
| 506 if ((isAutosizingClusterRoot && !block->isTableCell()) | 499 if ((isAutosizingClusterRoot && !block->isTableCell()) || blockS uppressesAutosizing(block)) { |
| 507 || !containerShouldBeAutosized(block)) { | |
| 508 descendant = descendant->nextInPreOrderAfterChildren(root); | 500 descendant = descendant->nextInPreOrderAfterChildren(root); |
| 509 continue; | 501 continue; |
| 510 } | 502 } |
| 511 } | 503 } |
| 512 } else if (descendant->isText()) { | 504 } else if (descendant->isText()) { |
| 513 // Note: Using text().stripWhiteSpace().length() instead of rendered TextLength() because | 505 // Note: Using text().stripWhiteSpace().length() instead of rendered TextLength() because |
| 514 // the lineboxes will not be built until layout. These values can be different. | 506 // the lineboxes will not be built until layout. These values can be different. |
| 515 // Note: This is an approximation assuming each character is 1em wid e. | 507 // Note: This is an approximation assuming each character is 1em wid e. |
| 516 length += toRenderText(descendant)->text().stripWhiteSpace().length( ) * descendant->style()->specifiedFontSize(); | 508 length += toRenderText(descendant)->text().stripWhiteSpace().length( ) * descendant->style()->specifiedFontSize(); |
| 517 | 509 |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 573 if (renderer->isTableCell()) | 565 if (renderer->isTableCell()) |
| 574 data.m_column = renderer->node()->nodeIndex(); | 566 data.m_column = renderer->node()->nodeIndex(); |
| 575 | 567 |
| 576 return StringHasher::computeHash<UChar>( | 568 return StringHasher::computeHash<UChar>( |
| 577 static_cast<const UChar*>(static_cast<const void*>(&data)), | 569 static_cast<const UChar*>(static_cast<const void*>(&data)), |
| 578 sizeof data / sizeof(UChar)); | 570 sizeof data / sizeof(UChar)); |
| 579 } | 571 } |
| 580 | 572 |
| 581 FastTextAutosizer::Cluster* FastTextAutosizer::maybeCreateCluster(const RenderBl ock* block) | 573 FastTextAutosizer::Cluster* FastTextAutosizer::maybeCreateCluster(const RenderBl ock* block) |
| 582 { | 574 { |
| 583 if (!isAutosizingContainer(block)) | 575 if (!isPotentialClusterRoot(block)) |
| 584 return 0; | 576 return 0; |
| 585 | 577 |
| 586 Cluster* parentCluster = m_clusterStack.isEmpty() ? 0 : currentCluster(); | 578 Cluster* parentCluster = m_clusterStack.isEmpty() ? 0 : currentCluster(); |
| 587 ASSERT(parentCluster || block->isRenderView()); | 579 ASSERT(parentCluster || block->isRenderView()); |
| 588 | 580 |
| 589 // Create clusters to suppress / unsuppress autosizing based on containerSho uldBeAutosized. | 581 bool mightAutosize = block->isRenderView() || blockMightBecomeAutosizingClus ter(block); |
| 590 bool containerCanAutosize = containerShouldBeAutosized(block); | 582 bool suppressesAutosizing = blockSuppressesAutosizing(block); |
| 591 bool parentClusterCanAutosize = parentCluster && parentCluster->m_autosize; | |
| 592 bool createClusterThatMightAutosize = block->isRenderView() | |
| 593 || mightBeWiderOrNarrowerDescendant(block) | |
| 594 || isIndependentDescendant(block) | |
| 595 || block->isTable(); | |
| 596 | 583 |
| 597 // If the container would not alter the m_autosize bit, it doesn't need to b e a cluster. | 584 // If the block would not alter the m_autosize bit, it doesn't need to be a cluster. |
| 598 if (!createClusterThatMightAutosize && containerCanAutosize == parentCluster CanAutosize) | 585 bool parentSuppressesAutosizing = parentCluster && !parentCluster->m_autosiz e; |
| 586 if (!mightAutosize && suppressesAutosizing == parentSuppressesAutosizing) | |
| 599 return 0; | 587 return 0; |
| 600 | 588 |
| 601 return new Cluster(block, containerCanAutosize, parentCluster, getSuperclust er(block)); | 589 return new Cluster(block, !suppressesAutosizing, parentCluster, getSuperclus ter(block)); |
| 602 } | 590 } |
| 603 | 591 |
| 604 FastTextAutosizer::Supercluster* FastTextAutosizer::getSupercluster(const Render Block* block) | 592 FastTextAutosizer::Supercluster* FastTextAutosizer::getSupercluster(const Render Block* block) |
| 605 { | 593 { |
| 606 Fingerprint fingerprint = m_fingerprintMapper.get(block); | 594 Fingerprint fingerprint = m_fingerprintMapper.get(block); |
| 607 if (!fingerprint) | 595 if (!fingerprint) |
| 608 return 0; | 596 return 0; |
| 609 | 597 |
| 610 BlockSet* roots = &m_fingerprintMapper.getTentativeClusterRoots(fingerprint) ; | 598 BlockSet* roots = &m_fingerprintMapper.getTentativeClusterRoots(fingerprint) ; |
| 611 if (!roots || roots->size() < 2 || !roots->contains(block)) | 599 if (!roots || roots->size() < 2 || !roots->contains(block)) |
| (...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 789 return parent; | 777 return parent; |
| 790 | 778 |
| 791 if (parent->isEmpty()) | 779 if (parent->isEmpty()) |
| 792 return parent->isText() ? parent : 0; | 780 return parent->isText() ? parent : 0; |
| 793 | 781 |
| 794 ++depth; | 782 ++depth; |
| 795 const RenderObject* child = (firstOrLast == First) ? parent->firstChild() : parent->lastChild(); | 783 const RenderObject* child = (firstOrLast == First) ? parent->firstChild() : parent->lastChild(); |
| 796 while (child) { | 784 while (child) { |
| 797 // Note: At this point clusters may not have been created for these bloc ks so we cannot rely | 785 // Note: At this point clusters may not have been created for these bloc ks so we cannot rely |
| 798 // on m_clusters. Instead, we use a best-guess about whether the b lock will become a cluster. | 786 // on m_clusters. Instead, we use a best-guess about whether the b lock will become a cluster. |
| 799 if (!isAutosizingContainer(child) || !isIndependentDescendant(toRenderBl ock(child))) { | 787 if (!isPotentialClusterRoot(child) || !isIndependentDescendant(toRenderB lock(child))) { |
| 800 const RenderObject* leaf = findTextLeaf(child, depth, firstOrLast); | 788 const RenderObject* leaf = findTextLeaf(child, depth, firstOrLast); |
| 801 if (leaf) | 789 if (leaf) |
| 802 return leaf; | 790 return leaf; |
| 803 } | 791 } |
| 804 child = (firstOrLast == First) ? child->nextSibling() : child->previousS ibling(); | 792 child = (firstOrLast == First) ? child->nextSibling() : child->previousS ibling(); |
| 805 } | 793 } |
| 806 --depth; | 794 --depth; |
| 807 | 795 |
| 808 return 0; | 796 return 0; |
| 809 } | 797 } |
| 810 | 798 |
| 811 void FastTextAutosizer::applyMultiplier(RenderObject* renderer, float multiplier ) | 799 void FastTextAutosizer::applyMultiplier(RenderObject* renderer, float multiplier ) |
| 812 { | 800 { |
| 813 ASSERT(renderer); | 801 ASSERT(renderer); |
| 814 RenderStyle* currentStyle = renderer->style(); | 802 RenderStyle* currentStyle = renderer->style(); |
| 815 if (currentStyle->textAutosizingMultiplier() == multiplier) | 803 if (currentStyle->textAutosizingMultiplier() == multiplier) |
| 816 return; | 804 return; |
| 817 | 805 |
| 818 // We need to clone the render style to avoid breaking style sharing. | 806 // We need to clone the render style to avoid breaking style sharing. |
| 819 RefPtr<RenderStyle> style = RenderStyle::clone(currentStyle); | 807 RefPtr<RenderStyle> style = RenderStyle::clone(currentStyle); |
| 820 style->setTextAutosizingMultiplier(multiplier); | 808 style->setTextAutosizingMultiplier(multiplier); |
| 821 style->setUnique(); | 809 style->setUnique(); |
| 822 renderer->setStyleInternal(style.release()); | 810 renderer->setStyleInternal(style.release()); |
| 823 } | 811 } |
| 824 | 812 |
| 825 bool FastTextAutosizer::mightBeWiderOrNarrowerDescendant(const RenderBlock* bloc k) | |
| 826 { | |
| 827 // FIXME: This heuristic may need to be expanded to other ways a block can b e wider or narrower | |
| 828 // than its parent containing block. | |
| 829 return block->style() && block->style()->width().isSpecified(); | |
| 830 } | |
| 831 | |
| 832 bool FastTextAutosizer::isWiderOrNarrowerDescendant(Cluster* cluster) | 813 bool FastTextAutosizer::isWiderOrNarrowerDescendant(Cluster* cluster) |
| 833 { | 814 { |
| 834 if (!cluster->m_parent || !mightBeWiderOrNarrowerDescendant(cluster->m_root) ) | 815 if (!cluster->m_parent || !mightBeWiderOrNarrowerDescendant(cluster->m_root) ) |
| 835 return true; | 816 return true; |
| 836 | 817 |
| 837 const RenderBlock* parentDeepestBlockContainingAllText = deepestBlockContain ingAllText(cluster->m_parent); | 818 const RenderBlock* parentDeepestBlockContainingAllText = deepestBlockContain ingAllText(cluster->m_parent); |
| 838 ASSERT(m_blocksThatHaveBegunLayout.contains(cluster->m_root)); | 819 ASSERT(m_blocksThatHaveBegunLayout.contains(cluster->m_root)); |
| 839 ASSERT(m_blocksThatHaveBegunLayout.contains(parentDeepestBlockContainingAllT ext)); | 820 ASSERT(m_blocksThatHaveBegunLayout.contains(parentDeepestBlockContainingAllT ext)); |
| 840 | 821 |
| 841 float contentWidth = cluster->m_root->contentLogicalWidth().toFloat(); | 822 float contentWidth = cluster->m_root->contentLogicalWidth().toFloat(); |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 923 FastTextAutosizer::Fingerprint FastTextAutosizer::FingerprintMapper::get(const R enderObject* renderer) | 904 FastTextAutosizer::Fingerprint FastTextAutosizer::FingerprintMapper::get(const R enderObject* renderer) |
| 924 { | 905 { |
| 925 return m_fingerprints.get(renderer); | 906 return m_fingerprints.get(renderer); |
| 926 } | 907 } |
| 927 | 908 |
| 928 FastTextAutosizer::BlockSet& FastTextAutosizer::FingerprintMapper::getTentativeC lusterRoots(Fingerprint fingerprint) | 909 FastTextAutosizer::BlockSet& FastTextAutosizer::FingerprintMapper::getTentativeC lusterRoots(Fingerprint fingerprint) |
| 929 { | 910 { |
| 930 return *m_blocksForFingerprint.get(fingerprint); | 911 return *m_blocksForFingerprint.get(fingerprint); |
| 931 } | 912 } |
| 932 | 913 |
| 933 RenderObject* FastTextAutosizer::nextChildSkippingChildrenOfBlocks(const RenderO bject* current, const RenderObject* stayWithin) | |
| 934 { | |
| 935 if (current == stayWithin || !current->isRenderBlock()) | |
| 936 return current->nextInPreOrder(stayWithin); | |
| 937 return current->nextInPreOrderAfterChildren(stayWithin); | |
| 938 } | |
| 939 | |
| 940 FastTextAutosizer::LayoutScope::LayoutScope(RenderBlock* block) | 914 FastTextAutosizer::LayoutScope::LayoutScope(RenderBlock* block) |
| 941 : m_textAutosizer(block->document().fastTextAutosizer()) | 915 : m_textAutosizer(block->document().fastTextAutosizer()) |
| 942 , m_block(block) | 916 , m_block(block) |
| 943 { | 917 { |
| 944 if (!m_textAutosizer) | 918 if (!m_textAutosizer) |
| 945 return; | 919 return; |
| 946 | 920 |
| 947 if (!m_textAutosizer->enabled()) { | 921 if (!m_textAutosizer->enabled()) { |
| 948 m_textAutosizer = 0; | 922 m_textAutosizer = 0; |
| 949 return; | 923 return; |
| 950 } | 924 } |
| 951 | 925 |
| 952 if (m_textAutosizer->m_pageAutosizingStatus == PageAutosizingStatusUnknown) | 926 if (m_textAutosizer->m_pageAutosizingStatus == PageAutosizingStatusUnknown) |
| 953 m_textAutosizer->updateRenderViewInfo(); | 927 m_textAutosizer->updateRenderViewInfo(); |
| 954 | 928 |
| 955 if (m_textAutosizer->m_pageAutosizingStatus == PageNeedsAutosizing) | 929 if (m_textAutosizer->m_pageAutosizingStatus == PageNeedsAutosizing) |
| 956 m_textAutosizer->beginLayout(m_block); | 930 m_textAutosizer->beginLayout(m_block); |
| 957 else | 931 else |
| 958 m_textAutosizer = 0; | 932 m_textAutosizer = 0; |
| 959 } | 933 } |
| 960 | 934 |
| 961 FastTextAutosizer::LayoutScope::~LayoutScope() | 935 FastTextAutosizer::LayoutScope::~LayoutScope() |
| 962 { | 936 { |
| 963 if (m_textAutosizer) | 937 if (m_textAutosizer) |
| 964 m_textAutosizer->endLayout(m_block); | 938 m_textAutosizer->endLayout(m_block); |
| 965 } | 939 } |
| 966 | 940 |
| 967 } // namespace WebCore | 941 } // namespace WebCore |
| OLD | NEW |