1 /* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "core/rendering/TextAutosizer.h" 33 34 #include "core/dom/Document.h" 35 #include "core/frame/FrameView.h" 36 #include "core/frame/LocalFrame.h" 37 #include "core/frame/Settings.h" 38 #include "core/html/HTMLTextAreaElement.h" 39 #include "core/page/Page.h" 40 #include "core/rendering/InlineIterator.h" 41 #include "core/rendering/RenderBlock.h" 42 #include "core/rendering/RenderListItem.h" 43 #include "core/rendering/RenderListMarker.h" 44 #include "core/rendering/RenderTableCell.h" 45 #include "core/rendering/RenderView.h" 46 47 #ifdef AUTOSIZING_DOM_DEBUG_INFO 48 #include "core/dom/ExecutionContextTask.h" 49 #endif 50 51 namespace blink { 52 53 #ifdef AUTOSIZING_DOM_DEBUG_INFO 54 class WriteDebugInfoTask : public ExecutionContextTask { 55 public: 56 WriteDebugInfoTask(PassRefPtrWillBeRawPtr<Element> element, AtomicString value) 57 : m_element(element) 58 , m_value(value) 59 { 60 } 61 62 virtual void performTask(ExecutionContext*) 63 { 64 m_element->setAttribute("data-autosizing", m_value, ASSERT_NO_EXCEPTION); 65 } 66 67 private: 68 RefPtrWillBePersistent<Element> m_element; 69 AtomicString m_value; 70 }; 71 72 static void writeDebugInfo(RenderObject* renderer, const AtomicString& output) 73 { 74 Node* node = renderer->node(); 75 if (!node) 76 return; 77 if (node->isDocumentNode()) 78 node = toDocument(node)->documentElement(); 79 if (!node->isElementNode()) 80 return; 81 node->document().postTask(adoptPtr(new WriteDebugInfoTask(toElement(node), output))); 82 } 83 84 void TextAutosizer::writeClusterDebugInfo(Cluster* cluster) 85 { 86 String explanation = ""; 87 if (cluster->m_flags & SUPPRESSING) { 88 explanation = "[suppressed]"; 89 } else if (!(cluster->m_flags & (INDEPENDENT | WIDER_OR_NARROWER))) { 90 explanation = "[inherited]"; 91 } else if (cluster->m_supercluster) { 92 explanation = "[supercluster]"; 93 } else if (!clusterHasEnoughTextToAutosize(cluster)) { 94 explanation = "[insufficient-text]"; 95 } else { 96 const RenderBlock* widthProvider = clusterWidthProvider(cluster->m_root); 97 if (cluster->m_hasTableAncestor && cluster->m_multiplier < multiplierFromBlock(widthProvider)) { 98 explanation = "[table-ancestor-limited]"; 99 } else { 100 explanation = String::format("[from width %d of %s]", 101 static_cast<int>(widthFromBlock(widthProvider)), widthProvider->debugName().utf8().data()); 102 } 103 } 104 String pageInfo = ""; 105 if (cluster->m_root->isRenderView()) { 106 pageInfo = String::format("; pageinfo: bm %f * (lw %d / fw %d)", 107 m_pageInfo.m_baseMultiplier, m_pageInfo.m_layoutWidth, m_pageInfo.m_frameWidth); 108 } 109 float multiplier = cluster->m_flags & SUPPRESSING ? 1.0 : cluster->m_multiplier; 110 writeDebugInfo(const_cast<RenderBlock*>(cluster->m_root), 111 AtomicString(String::format("cluster: %f %s%s", multiplier, 112 explanation.utf8().data(), pageInfo.utf8().data()))); 113 } 114 #endif 115 116 static const RenderObject* parentElementRenderer(const RenderObject* renderer) 117 { 118 // At style recalc, the renderer's parent may not be attached, 119 // so we need to obtain this from the DOM tree. 120 const Node* node = renderer->node(); 121 if (!node) 122 return 0; 123 124 // FIXME: This should be using NodeRenderingTraversal::parent(). 125 if (Element* parent = node->parentElement()) 126 return parent->renderer(); 127 return 0; 128 } 129 130 static bool isNonTextAreaFormControl(const RenderObject* renderer) 131 { 132 const Node* node = renderer ? renderer->node() : 0; 133 if (!node || !node->isElementNode()) 134 return false; 135 const Element* element = toElement(node); 136 137 return (element->isFormControlElement() && !isHTMLTextAreaElement(element)); 138 } 139 140 static bool isPotentialClusterRoot(const RenderObject* renderer) 141 { 142 // "Potential cluster roots" are the smallest unit for which we can 143 // enable/disable text autosizing. 144 // - Must not be inline, as different multipliers on one line looks terrible. 145 // Exceptions are inline-block and alike elements (inline-table, -webkit-inline-*), 146 // as they often contain entire multi-line columns of text. 147 // - Must not be normal list items, as items in the same list should look 148 // consistent, unless they are floating or position:absolute/fixed. 149 Node* node = renderer->generatingNode(); 150 if (node && !node->hasChildren()) 151 return false; 152 if (!renderer->isRenderBlock()) 153 return false; 154 if (renderer->isInline() && !renderer->style()->isDisplayReplacedType()) 155 return false; 156 if (renderer->isListItem()) 157 return (renderer->isFloating() || renderer->isOutOfFlowPositioned()); 158 159 return true; 160 } 161 162 static bool isIndependentDescendant(const RenderBlock* renderer) 163 { 164 ASSERT(isPotentialClusterRoot(renderer)); 165 166 RenderBlock* containingBlock = renderer->containingBlock(); 167 return renderer->isRenderView() 168 || renderer->isFloating() 169 || renderer->isOutOfFlowPositioned() 170 || renderer->isTableCell() 171 || renderer->isTableCaption() 172 || renderer->isFlexibleBoxIncludingDeprecated() 173 || renderer->hasColumns() 174 || (containingBlock && containingBlock->isHorizontalWritingMode() != renderer->isHorizontalWritingMode()) 175 || renderer->style()->isDisplayReplacedType() 176 || renderer->isTextArea() 177 || renderer->style()->userModify() != READ_ONLY; 178 } 179 180 static bool blockIsRowOfLinks(const RenderBlock* block) 181 { 182 // A "row of links" is a block for which: 183 // 1. It does not contain non-link text elements longer than 3 characters 184 // 2. It contains a minimum of 3 inline links and all links should 185 // have the same specified font size. 186 // 3. It should not contain <br> elements. 187 // 4. It should contain only inline elements unless they are containers, 188 // children of link elements or children of sub-containers. 189 int linkCount = 0; 190 RenderObject* renderer = block->firstChild(); 191 float matchingFontSize = -1; 192 193 while (renderer) { 194 if (!isPotentialClusterRoot(renderer)) { 195 if (renderer->isText() && toRenderText(renderer)->text().impl()->stripWhiteSpace()->length() > 3) 196 return false; 197 if (!renderer->isInline() || renderer->isBR()) 198 return false; 199 } 200 if (renderer->style()->isLink()) { 201 linkCount++; 202 if (matchingFontSize < 0) 203 matchingFontSize = renderer->style()->specifiedFontSize(); 204 else if (matchingFontSize != renderer->style()->specifiedFontSize()) 205 return false; 206 207 // Skip traversing descendants of the link. 208 renderer = renderer->nextInPreOrderAfterChildren(block); 209 continue; 210 } 211 renderer = renderer->nextInPreOrder(block); 212 } 213 214 return (linkCount >= 3); 215 } 216 217 static bool blockHeightConstrained(const RenderBlock* block) 218 { 219 // FIXME: Propagate constrainedness down the tree, to avoid inefficiently walking back up from each box. 220 // FIXME: This code needs to take into account vertical writing modes. 221 // FIXME: Consider additional heuristics, such as ignoring fixed heights if the content is already overflowing before autosizing kicks in. 222 for (; block; block = block->containingBlock()) { 223 RenderStyle* style = block->style(); 224 if (style->overflowY() >= OSCROLL) 225 return false; 226 if (style->height().isSpecified() || style->maxHeight().isSpecified() || block->isOutOfFlowPositioned()) { 227 // Some sites (e.g. wikipedia) set their html and/or body elements to height:100%, 228 // without intending to constrain the height of the content within them. 229 return !block->isDocumentElement() && !block->isBody(); 230 } 231 if (block->isFloating()) 232 return false; 233 } 234 return false; 235 } 236 237 static bool blockOrImmediateChildrenAreFormControls(const RenderBlock* block) 238 { 239 if (isNonTextAreaFormControl(block)) 240 return true; 241 const RenderObject* renderer = block->firstChild(); 242 while (renderer) { 243 if (isNonTextAreaFormControl(renderer)) 244 return true; 245 renderer = renderer->nextSibling(); 246 } 247 248 return false; 249 } 250 251 // Some blocks are not autosized even if their parent cluster wants them to. 252 static bool blockSuppressesAutosizing(const RenderBlock* block) 253 { 254 if (blockOrImmediateChildrenAreFormControls(block)) 255 return true; 256 257 if (blockIsRowOfLinks(block)) 258 return true; 259 260 // Don't autosize block-level text that can't wrap (as it's likely to 261 // expand sideways and break the page's layout). 262 if (!block->style()->autoWrap()) 263 return true; 264 265 if (blockHeightConstrained(block)) 266 return true; 267 268 return false; 269 } 270 271 static bool hasExplicitWidth(const RenderBlock* block) 272 { 273 // FIXME: This heuristic may need to be expanded to other ways a block can be wider or narrower 274 // than its parent containing block. 275 return block->style() && block->style()->width().isSpecified(); 276 } 277 278 TextAutosizer::TextAutosizer(const Document* document) 279 : m_document(document) 280 , m_firstBlockToBeginLayout(0) 281 #if ENABLE(ASSERT) 282 , m_blocksThatHaveBegunLayout() 283 #endif 284 , m_superclusters() 285 , m_clusterStack() 286 , m_fingerprintMapper() 287 , m_pageInfo() 288 , m_updatePageInfoDeferred(false) 289 { 290 } 291 292 void TextAutosizer::record(const RenderBlock* block) 293 { 294 if (!m_pageInfo.m_settingEnabled) 295 return; 296 297 ASSERT(!m_blocksThatHaveBegunLayout.contains(block)); 298 299 if (!classifyBlock(block, INDEPENDENT | EXPLICIT_WIDTH)) 300 return; 301 302 if (Fingerprint fingerprint = computeFingerprint(block)) 303 m_fingerprintMapper.addTentativeClusterRoot(block, fingerprint); 304 } 305 306 void TextAutosizer::destroy(const RenderBlock* block) 307 { 308 if (!m_pageInfo.m_settingEnabled && !m_fingerprintMapper.hasFingerprints()) 309 return; 310 311 ASSERT(!m_blocksThatHaveBegunLayout.contains(block)); 312 313 if (m_fingerprintMapper.remove(block) && m_firstBlockToBeginLayout) { 314 // RenderBlock with a fingerprint was destroyed during layout. 315 // Clear the cluster stack and the supercluster map to avoid stale pointers. 316 // Speculative fix for http://crbug.com/369485. 317 m_firstBlockToBeginLayout = 0; 318 m_clusterStack.clear(); 319 m_superclusters.clear(); 320 } 321 } 322 323 TextAutosizer::BeginLayoutBehavior TextAutosizer::prepareForLayout(const RenderBlock* block) 324 { 325 #if ENABLE(ASSERT) 326 m_blocksThatHaveBegunLayout.add(block); 327 #endif 328 329 if (!m_firstBlockToBeginLayout) { 330 m_firstBlockToBeginLayout = block; 331 prepareClusterStack(block->parent()); 332 } else if (block == currentCluster()->m_root) { 333 // Ignore beginLayout on the same block twice. 334 // This can happen with paginated overflow. 335 return StopLayout; 336 } 337 338 return ContinueLayout; 339 } 340 341 void TextAutosizer::prepareClusterStack(const RenderObject* renderer) 342 { 343 if (!renderer) 344 return; 345 prepareClusterStack(renderer->parent()); 346 347 if (renderer->isRenderBlock()) { 348 const RenderBlock* block = toRenderBlock(renderer); 349 #if ENABLE(ASSERT) 350 m_blocksThatHaveBegunLayout.add(block); 351 #endif 352 if (Cluster* cluster = maybeCreateCluster(block)) 353 m_clusterStack.append(adoptPtr(cluster)); 354 } 355 } 356 357 void TextAutosizer::beginLayout(RenderBlock* block) 358 { 359 ASSERT(shouldHandleLayout()); 360 361 if (prepareForLayout(block) == StopLayout) 362 return; 363 364 if (Cluster* cluster = maybeCreateCluster(block)) 365 m_clusterStack.append(adoptPtr(cluster)); 366 367 // Cells in auto-layout tables are handled separately by inflateAutoTable. 368 bool isAutoTableCell = block->isTableCell() && !toRenderTableCell(block)->table()->style()->isFixedTableLayout(); 369 if (!isAutoTableCell && !m_clusterStack.isEmpty()) 370 inflate(block); 371 } 372 373 void TextAutosizer::inflateListItem(RenderListItem* listItem, RenderListMarker* listItemMarker) 374 { 375 if (!shouldHandleLayout()) 376 return; 377 ASSERT(listItem && listItemMarker); 378 379 if (prepareForLayout(listItem) == StopLayout) 380 return; 381 382 // Force the LI to be inside the DBCAT when computing the multiplier. 383 // This guarantees that the DBCAT has entered layout, so we can ask for its width. 384 // It also makes sense because the list marker is autosized like a text node. 385 float multiplier = clusterMultiplier(currentCluster()); 386 387 applyMultiplier(listItem, multiplier); 388 applyMultiplier(listItemMarker, multiplier); 389 } 390 391 void TextAutosizer::inflateAutoTable(RenderTable* table) 392 { 393 ASSERT(table); 394 ASSERT(!table->style()->isFixedTableLayout()); 395 ASSERT(table->containingBlock()); 396 397 Cluster* cluster = currentCluster(); 398 if (cluster->m_root != table) 399 return; 400 401 // Pre-inflate cells that have enough text so that their inflated preferred widths will be used 402 // for column sizing. 403 for (RenderObject* section = table->firstChild(); section; section = section->nextSibling()) { 404 if (!section->isTableSection()) 405 continue; 406 for (RenderTableRow* row = toRenderTableSection(section)->firstRow(); row; row = row->nextRow()) { 407 for (RenderTableCell* cell = row->firstCell(); cell; cell = cell->nextCell()) { 408 if (!cell->needsLayout()) 409 continue; 410 411 beginLayout(cell); 412 inflate(cell, DescendToInnerBlocks); 413 endLayout(cell); 414 } 415 } 416 } 417 } 418 419 void TextAutosizer::endLayout(RenderBlock* block) 420 { 421 ASSERT(shouldHandleLayout()); 422 423 if (block == m_firstBlockToBeginLayout) { 424 m_firstBlockToBeginLayout = 0; 425 m_clusterStack.clear(); 426 m_superclusters.clear(); 427 m_stylesRetainedDuringLayout.clear(); 428 #if ENABLE(ASSERT) 429 m_blocksThatHaveBegunLayout.clear(); 430 #endif 431 // Tables can create two layout scopes for the same block so the isEmpty 432 // check below is needed to guard against endLayout being called twice. 433 } else if (!m_clusterStack.isEmpty() && currentCluster()->m_root == block) { 434 m_clusterStack.removeLast(); 435 } 436 } 437 438 float TextAutosizer::inflate(RenderObject* parent, InflateBehavior behavior, float multiplier) 439 { 440 Cluster* cluster = currentCluster(); 441 bool hasTextChild = false; 442 443 RenderObject* child = 0; 444 if (parent->isRenderBlock() && (parent->childrenInline() || behavior == DescendToInnerBlocks)) 445 child = toRenderBlock(parent)->firstChild(); 446 else if (parent->isRenderInline()) 447 child = toRenderInline(parent)->firstChild(); 448 449 while (child) { 450 if (child->isText()) { 451 hasTextChild = true; 452 // We only calculate this multiplier on-demand to ensure the parent block of this text 453 // has entered layout. 454 if (!multiplier) 455 multiplier = cluster->m_flags & SUPPRESSING ? 1.0f : clusterMultiplier(cluster); 456 applyMultiplier(child, multiplier); 457 // FIXME: Investigate why MarkOnlyThis is sufficient. 458 if (parent->isRenderInline()) 459 child->setPreferredLogicalWidthsDirty(MarkOnlyThis); 460 } else if (child->isRenderInline()) { 461 multiplier = inflate(child, behavior, multiplier); 462 } else if (child->isRenderBlock() && behavior == DescendToInnerBlocks 463 && !classifyBlock(child, INDEPENDENT | EXPLICIT_WIDTH | SUPPRESSING)) { 464 multiplier = inflate(child, behavior, multiplier); 465 } 466 child = child->nextSibling(); 467 } 468 469 if (hasTextChild) { 470 applyMultiplier(parent, multiplier); // Parent handles line spacing. 471 } else if (!parent->isListItem()) { 472 // For consistency, a block with no immediate text child should always have a 473 // multiplier of 1 (except for list items which are handled in inflateListItem). 474 applyMultiplier(parent, 1); 475 } 476 return multiplier; 477 } 478 479 bool TextAutosizer::shouldHandleLayout() const 480 { 481 return m_pageInfo.m_settingEnabled && m_pageInfo.m_pageNeedsAutosizing && !m_updatePageInfoDeferred; 482 } 483 484 void TextAutosizer::updatePageInfoInAllFrames() 485 { 486 ASSERT(!m_document->frame() || m_document->frame()->isMainFrame()); 487 488 for (Frame* frame = m_document->frame(); frame; frame = frame->tree().traverseNext()) { 489 if (!frame->isLocalFrame()) 490 continue; 491 if (TextAutosizer* textAutosizer = toLocalFrame(frame)->document()->textAutosizer()) 492 textAutosizer->updatePageInfo(); 493 } 494 } 495 496 void TextAutosizer::updatePageInfo() 497 { 498 if (m_updatePageInfoDeferred || !m_document->page() || !m_document->settings()) 499 return; 500 501 PageInfo previousPageInfo(m_pageInfo); 502 m_pageInfo.m_settingEnabled = m_document->settings()->textAutosizingEnabled(); 503 504 if (!m_pageInfo.m_settingEnabled || m_document->printing()) { 505 m_pageInfo.m_pageNeedsAutosizing = false; 506 } else { 507 RenderView* renderView = m_document->renderView(); 508 bool horizontalWritingMode = isHorizontalWritingMode(renderView->style()->writingMode()); 509 510 LocalFrame* mainFrame = m_document->page()->deprecatedLocalMainFrame(); 511 IntSize frameSize = m_document->settings()->textAutosizingWindowSizeOverride(); 512 if (frameSize.isEmpty()) 513 frameSize = mainFrame->view()->unscaledVisibleContentSize(IncludeScrollbars); 514 m_pageInfo.m_frameWidth = horizontalWritingMode ? frameSize.width() : frameSize.height(); 515 516 IntSize layoutSize = mainFrame->view()->layoutSize(); 517 m_pageInfo.m_layoutWidth = horizontalWritingMode ? layoutSize.width() : layoutSize.height(); 518 519 // Compute the base font scale multiplier based on device and accessibility settings. 520 m_pageInfo.m_baseMultiplier = m_document->settings()->accessibilityFontScaleFactor(); 521 // If the page has a meta viewport or @viewport, don't apply the device scale adjustment. 522 const ViewportDescription& viewportDescription = mainFrame->document()->viewportDescription(); 523 if (!viewportDescription.isSpecifiedByAuthor()) { 524 float deviceScaleAdjustment = m_document->settings()->deviceScaleAdjustment(); 525 m_pageInfo.m_baseMultiplier *= deviceScaleAdjustment; 526 } 527 528 m_pageInfo.m_pageNeedsAutosizing = !!m_pageInfo.m_frameWidth 529 && (m_pageInfo.m_baseMultiplier * (static_cast<float>(m_pageInfo.m_layoutWidth) / m_pageInfo.m_frameWidth) > 1.0f); 530 } 531 532 if (m_pageInfo.m_pageNeedsAutosizing) { 533 // If page info has changed, multipliers may have changed. Force a layout to recompute them. 534 if (m_pageInfo.m_frameWidth != previousPageInfo.m_frameWidth 535 || m_pageInfo.m_layoutWidth != previousPageInfo.m_layoutWidth 536 || m_pageInfo.m_baseMultiplier != previousPageInfo.m_baseMultiplier 537 || m_pageInfo.m_settingEnabled != previousPageInfo.m_settingEnabled) 538 setAllTextNeedsLayout(); 539 } else if (previousPageInfo.m_hasAutosized) { 540 // If we are no longer autosizing the page, we won't do anything during the next layout. 541 // Set all the multipliers back to 1 now. 542 resetMultipliers(); 543 m_pageInfo.m_hasAutosized = false; 544 } 545 } 546 547 void TextAutosizer::resetMultipliers() 548 { 549 RenderObject* renderer = m_document->renderView(); 550 while (renderer) { 551 if (RenderStyle* style = renderer->style()) { 552 if (style->textAutosizingMultiplier() != 1) 553 applyMultiplier(renderer, 1, LayoutNeeded); 554 } 555 renderer = renderer->nextInPreOrder(); 556 } 557 } 558 559 void TextAutosizer::setAllTextNeedsLayout() 560 { 561 RenderObject* renderer = m_document->renderView(); 562 while (renderer) { 563 if (renderer->isText()) 564 renderer->setNeedsLayoutAndFullPaintInvalidation(); 565 renderer = renderer->nextInPreOrder(); 566 } 567 } 568 569 TextAutosizer::BlockFlags TextAutosizer::classifyBlock(const RenderObject* renderer, BlockFlags mask) const 570 { 571 if (!renderer->isRenderBlock()) 572 return 0; 573 574 const RenderBlock* block = toRenderBlock(renderer); 575 BlockFlags flags = 0; 576 577 if (isPotentialClusterRoot(block)) { 578 if (mask & POTENTIAL_ROOT) 579 flags |= POTENTIAL_ROOT; 580 581 if ((mask & INDEPENDENT) && (isIndependentDescendant(block) || block->isTable())) 582 flags |= INDEPENDENT; 583 584 if ((mask & EXPLICIT_WIDTH) && hasExplicitWidth(block)) 585 flags |= EXPLICIT_WIDTH; 586 587 if ((mask & SUPPRESSING) && blockSuppressesAutosizing(block)) 588 flags |= SUPPRESSING; 589 } 590 return flags; 591 } 592 593 bool TextAutosizer::clusterWouldHaveEnoughTextToAutosize(const RenderBlock* root, const RenderBlock* widthProvider) 594 { 595 Cluster hypotheticalCluster(root, classifyBlock(root), 0); 596 return clusterHasEnoughTextToAutosize(&hypotheticalCluster, widthProvider); 597 } 598 599 bool TextAutosizer::clusterHasEnoughTextToAutosize(Cluster* cluster, const RenderBlock* widthProvider) 600 { 601 if (cluster->m_hasEnoughTextToAutosize != UnknownAmountOfText) 602 return cluster->m_hasEnoughTextToAutosize == HasEnoughText; 603 604 const RenderBlock* root = cluster->m_root; 605 if (!widthProvider) 606 widthProvider = clusterWidthProvider(root); 607 608 // TextAreas and user-modifiable areas get a free pass to autosize regardless of text content. 609 if (root->isTextArea() || (root->style() && root->style()->userModify() != READ_ONLY)) { 610 cluster->m_hasEnoughTextToAutosize = HasEnoughText; 611 return true; 612 } 613 614 if (cluster->m_flags & SUPPRESSING) { 615 cluster->m_hasEnoughTextToAutosize = NotEnoughText; 616 return false; 617 } 618 619 // 4 lines of text is considered enough to autosize. 620 float minimumTextLengthToAutosize = widthFromBlock(widthProvider) * 4; 621 622 float length = 0; 623 RenderObject* descendant = root->firstChild(); 624 while (descendant) { 625 if (descendant->isRenderBlock()) { 626 if (classifyBlock(descendant, INDEPENDENT | SUPPRESSING)) { 627 descendant = descendant->nextInPreOrderAfterChildren(root); 628 continue; 629 } 630 } else if (descendant->isText()) { 631 // Note: Using text().stripWhiteSpace().length() instead of renderedTextLength() because 632 // the lineboxes will not be built until layout. These values can be different. 633 // Note: This is an approximation assuming each character is 1em wide. 634 length += toRenderText(descendant)->text().stripWhiteSpace().length() * descendant->style()->specifiedFontSize(); 635 636 if (length >= minimumTextLengthToAutosize) { 637 cluster->m_hasEnoughTextToAutosize = HasEnoughText; 638 return true; 639 } 640 } 641 descendant = descendant->nextInPreOrder(root); 642 } 643 644 cluster->m_hasEnoughTextToAutosize = NotEnoughText; 645 return false; 646 } 647 648 TextAutosizer::Fingerprint TextAutosizer::getFingerprint(const RenderObject* renderer) 649 { 650 Fingerprint result = m_fingerprintMapper.get(renderer); 651 if (!result) { 652 result = computeFingerprint(renderer); 653 m_fingerprintMapper.add(renderer, result); 654 } 655 return result; 656 } 657 658 TextAutosizer::Fingerprint TextAutosizer::computeFingerprint(const RenderObject* renderer) 659 { 660 Node* node = renderer->generatingNode(); 661 if (!node || !node->isElementNode()) 662 return 0; 663 664 FingerprintSourceData data; 665 if (const RenderObject* parent = parentElementRenderer(renderer)) 666 data.m_parentHash = getFingerprint(parent); 667 668 data.m_qualifiedNameHash = QualifiedNameHash::hash(toElement(node)->tagQName()); 669 670 if (RenderStyle* style = renderer->style()) { 671 data.m_packedStyleProperties = style->direction(); 672 data.m_packedStyleProperties |= (style->position() << 1); 673 data.m_packedStyleProperties |= (style->floating() << 4); 674 data.m_packedStyleProperties |= (style->display() << 6); 675 data.m_packedStyleProperties |= (style->width().type() << 11); 676 // packedStyleProperties effectively using 15 bits now. 677 678 // consider for adding: writing mode, padding. 679 680 data.m_width = style->width().getFloatValue(); 681 } 682 683 // Use nodeIndex as a rough approximation of column number 684 // (it's too early to call RenderTableCell::col). 685 // FIXME: account for colspan 686 if (renderer->isTableCell()) 687 data.m_column = renderer->node()->nodeIndex(); 688 689 return StringHasher::computeHash<UChar>( 690 static_cast<const UChar*>(static_cast<const void*>(&data)), 691 sizeof data / sizeof(UChar)); 692 } 693 694 TextAutosizer::Cluster* TextAutosizer::maybeCreateCluster(const RenderBlock* block) 695 { 696 BlockFlags flags = classifyBlock(block); 697 if (!(flags & POTENTIAL_ROOT)) 698 return 0; 699 700 Cluster* parentCluster = m_clusterStack.isEmpty() ? 0 : currentCluster(); 701 ASSERT(parentCluster || block->isRenderView()); 702 703 // If a non-independent block would not alter the SUPPRESSING flag, it doesn't need to be a cluster. 704 bool parentSuppresses = parentCluster && (parentCluster->m_flags & SUPPRESSING); 705 if (!(flags & INDEPENDENT) && !(flags & EXPLICIT_WIDTH) && !!(flags & SUPPRESSING) == parentSuppresses) 706 return 0; 707 708 Cluster* cluster = new Cluster(block, flags, parentCluster, getSupercluster(block)); 709 #ifdef AUTOSIZING_DOM_DEBUG_INFO 710 // Non-SUPPRESSING clusters are annotated in clusterMultiplier. 711 if (flags & SUPPRESSING) 712 writeClusterDebugInfo(cluster); 713 #endif 714 return cluster; 715 } 716 717 TextAutosizer::Supercluster* TextAutosizer::getSupercluster(const RenderBlock* block) 718 { 719 Fingerprint fingerprint = m_fingerprintMapper.get(block); 720 if (!fingerprint) 721 return 0; 722 723 BlockSet* roots = m_fingerprintMapper.getTentativeClusterRoots(fingerprint); 724 if (!roots || roots->size() < 2 || !roots->contains(block)) 725 return 0; 726 727 SuperclusterMap::AddResult addResult = m_superclusters.add(fingerprint, PassOwnPtr<Supercluster>()); 728 if (!addResult.isNewEntry) 729 return addResult.storedValue->value.get(); 730 731 Supercluster* supercluster = new Supercluster(roots); 732 addResult.storedValue->value = adoptPtr(supercluster); 733 return supercluster; 734 } 735 736 float TextAutosizer::clusterMultiplier(Cluster* cluster) 737 { 738 if (cluster->m_multiplier) 739 return cluster->m_multiplier; 740 741 // FIXME: why does isWiderOrNarrowerDescendant crash on independent clusters? 742 if (!(cluster->m_flags & INDEPENDENT) && isWiderOrNarrowerDescendant(cluster)) 743 cluster->m_flags |= WIDER_OR_NARROWER; 744 745 if (cluster->m_flags & (INDEPENDENT | WIDER_OR_NARROWER)) { 746 if (cluster->m_supercluster) 747 cluster->m_multiplier = superclusterMultiplier(cluster); 748 else if (clusterHasEnoughTextToAutosize(cluster)) 749 cluster->m_multiplier = multiplierFromBlock(clusterWidthProvider(cluster->m_root)); 750 else 751 cluster->m_multiplier = 1.0f; 752 } else { 753 cluster->m_multiplier = cluster->m_parent ? clusterMultiplier(cluster->m_parent) : 1.0f; 754 } 755 756 #ifdef AUTOSIZING_DOM_DEBUG_INFO 757 writeClusterDebugInfo(cluster); 758 #endif 759 760 ASSERT(cluster->m_multiplier); 761 return cluster->m_multiplier; 762 } 763 764 bool TextAutosizer::superclusterHasEnoughTextToAutosize(Supercluster* supercluster, const RenderBlock* widthProvider) 765 { 766 if (supercluster->m_hasEnoughTextToAutosize != UnknownAmountOfText) 767 return supercluster->m_hasEnoughTextToAutosize == HasEnoughText; 768 769 BlockSet::iterator end = supercluster->m_roots->end(); 770 for (BlockSet::iterator it = supercluster->m_roots->begin(); it != end; ++it) { 771 if (clusterWouldHaveEnoughTextToAutosize(*it, widthProvider)) { 772 supercluster->m_hasEnoughTextToAutosize = HasEnoughText; 773 return true; 774 } 775 } 776 supercluster->m_hasEnoughTextToAutosize = NotEnoughText; 777 return false; 778 } 779 780 float TextAutosizer::superclusterMultiplier(Cluster* cluster) 781 { 782 Supercluster* supercluster = cluster->m_supercluster; 783 if (!supercluster->m_multiplier) { 784 const RenderBlock* widthProvider = maxClusterWidthProvider(cluster->m_supercluster, cluster->m_root); 785 supercluster->m_multiplier = superclusterHasEnoughTextToAutosize(supercluster, widthProvider) 786 ? multiplierFromBlock(widthProvider) : 1.0f; 787 } 788 ASSERT(supercluster->m_multiplier); 789 return supercluster->m_multiplier; 790 } 791 792 const RenderBlock* TextAutosizer::clusterWidthProvider(const RenderBlock* root) const 793 { 794 if (root->isTable() || root->isTableCell()) 795 return root; 796 797 return deepestBlockContainingAllText(root); 798 } 799 800 const RenderBlock* TextAutosizer::maxClusterWidthProvider(const Supercluster* supercluster, const RenderBlock* currentRoot) const 801 { 802 const RenderBlock* result = clusterWidthProvider(currentRoot); 803 float maxWidth = widthFromBlock(result); 804 805 const BlockSet* roots = supercluster->m_roots; 806 for (BlockSet::iterator it = roots->begin(); it != roots->end(); ++it) { 807 const RenderBlock* widthProvider = clusterWidthProvider(*it); 808 if (widthProvider->needsLayout()) 809 continue; 810 float width = widthFromBlock(widthProvider); 811 if (width > maxWidth) { 812 maxWidth = width; 813 result = widthProvider; 814 } 815 } 816 RELEASE_ASSERT(result); 817 return result; 818 } 819 820 float TextAutosizer::widthFromBlock(const RenderBlock* block) const 821 { 822 RELEASE_ASSERT(block); 823 RELEASE_ASSERT(block->style()); 824 825 if (!(block->isTable() || block->isTableCell() || block->isListItem())) 826 return block->contentLogicalWidth().toFloat(); 827 828 if (!block->containingBlock()) 829 return 0; 830 831 // Tables may be inflated before computing their preferred widths. Try several methods to 832 // obtain a width, and fall back on a containing block's width. 833 for (; block; block = block->containingBlock()) { 834 float width; 835 Length specifiedWidth = block->isTableCell() 836 ? toRenderTableCell(block)->styleOrColLogicalWidth() : block->style()->logicalWidth(); 837 if (specifiedWidth.isFixed()) { 838 if ((width = specifiedWidth.value()) > 0) 839 return width; 840 } 841 if (specifiedWidth.isPercent()) { 842 if (float containerWidth = block->containingBlock()->contentLogicalWidth().toFloat()) { 843 if ((width = floatValueForLength(specifiedWidth, containerWidth)) > 0) 844 return width; 845 } 846 } 847 if ((width = block->contentLogicalWidth().toFloat()) > 0) 848 return width; 849 } 850 return 0; 851 } 852 853 float TextAutosizer::multiplierFromBlock(const RenderBlock* block) 854 { 855 // If block->needsLayout() is false, it does not need to be in m_blocksThatHaveBegunLayout. 856 // This can happen during layout of a positioned object if the cluster's DBCAT is deeper 857 // than the positioned object's containing block, and wasn't marked as needing layout. 858 ASSERT(m_blocksThatHaveBegunLayout.contains(block) || !block->needsLayout()); 859 860 // Block width, in CSS pixels. 861 float blockWidth = widthFromBlock(block); 862 float multiplier = m_pageInfo.m_frameWidth ? std::min(blockWidth, static_cast<float>(m_pageInfo.m_layoutWidth)) / m_pageInfo.m_frameWidth : 1.0f; 863 864 return std::max(m_pageInfo.m_baseMultiplier * multiplier, 1.0f); 865 } 866 867 const RenderBlock* TextAutosizer::deepestBlockContainingAllText(Cluster* cluster) 868 { 869 if (!cluster->m_deepestBlockContainingAllText) 870 cluster->m_deepestBlockContainingAllText = deepestBlockContainingAllText(cluster->m_root); 871 872 return cluster->m_deepestBlockContainingAllText; 873 } 874 875 // FIXME: Refactor this to look more like TextAutosizer::deepestCommonAncestor. 876 const RenderBlock* TextAutosizer::deepestBlockContainingAllText(const RenderBlock* root) const 877 { 878 size_t firstDepth = 0; 879 const RenderObject* firstTextLeaf = findTextLeaf(root, firstDepth, First); 880 if (!firstTextLeaf) 881 return root; 882 883 size_t lastDepth = 0; 884 const RenderObject* lastTextLeaf = findTextLeaf(root, lastDepth, Last); 885 ASSERT(lastTextLeaf); 886 887 // Equalize the depths if necessary. Only one of the while loops below will get executed. 888 const RenderObject* firstNode = firstTextLeaf; 889 const RenderObject* lastNode = lastTextLeaf; 890 while (firstDepth > lastDepth) { 891 firstNode = firstNode->parent(); 892 --firstDepth; 893 } 894 while (lastDepth > firstDepth) { 895 lastNode = lastNode->parent(); 896 --lastDepth; 897 } 898 899 // Go up from both nodes until the parent is the same. Both pointers will point to the LCA then. 900 while (firstNode != lastNode) { 901 firstNode = firstNode->parent(); 902 lastNode = lastNode->parent(); 903 } 904 905 if (firstNode->isRenderBlock()) 906 return toRenderBlock(firstNode); 907 908 // containingBlock() should never leave the cluster, since it only skips ancestors when finding 909 // the container of position:absolute/fixed blocks, and those cannot exist between a cluster and 910 // its text node's lowest common ancestor as isAutosizingCluster would have made them into their 911 // own independent cluster. 912 const RenderBlock* containingBlock = firstNode->containingBlock(); 913 if (!containingBlock) 914 return root; 915 916 ASSERT(containingBlock->isDescendantOf(root)); 917 return containingBlock; 918 } 919 920 const RenderObject* TextAutosizer::findTextLeaf(const RenderObject* parent, size_t& depth, TextLeafSearch firstOrLast) const 921 { 922 // List items are treated as text due to the marker. 923 // The actual renderer for the marker (RenderListMarker) may not be in the tree yet since it is added during layout. 924 if (parent->isListItem()) 925 return parent; 926 927 if (parent->isText()) 928 return parent; 929 930 ++depth; 931 const RenderObject* child = (firstOrLast == First) ? parent->slowFirstChild() : parent->slowLastChild(); 932 while (child) { 933 // Note: At this point clusters may not have been created for these blocks so we cannot rely 934 // on m_clusters. Instead, we use a best-guess about whether the block will become a cluster. 935 if (!classifyBlock(child, INDEPENDENT)) { 936 if (const RenderObject* leaf = findTextLeaf(child, depth, firstOrLast)) 937 return leaf; 938 } 939 child = (firstOrLast == First) ? child->nextSibling() : child->previousSibling(); 940 } 941 --depth; 942 943 return 0; 944 } 945 946 void TextAutosizer::applyMultiplier(RenderObject* renderer, float multiplier, RelayoutBehavior relayoutBehavior) 947 { 948 ASSERT(renderer && renderer->style()); 949 RenderStyle* currentStyle = renderer->style(); 950 if (currentStyle->textAutosizingMultiplier() == multiplier) 951 return; 952 953 // We need to clone the render style to avoid breaking style sharing. 954 RefPtr<RenderStyle> style = RenderStyle::clone(currentStyle); 955 style->setTextAutosizingMultiplier(multiplier); 956 style->setUnique(); 957 958 switch (relayoutBehavior) { 959 case AlreadyInLayout: 960 // Don't free currentStyle until the end of the layout pass. This allows other parts of the system 961 // to safely hold raw RenderStyle* pointers during layout, e.g. BreakingContext::m_currentStyle. 962 m_stylesRetainedDuringLayout.append(currentStyle); 963 964 renderer->setStyleInternal(style.release()); 965 renderer->setNeedsLayoutAndFullPaintInvalidation(); 966 break; 967 968 case LayoutNeeded: 969 renderer->setStyle(style.release()); 970 break; 971 } 972 973 if (multiplier != 1) 974 m_pageInfo.m_hasAutosized = true; 975 } 976 977 bool TextAutosizer::isWiderOrNarrowerDescendant(Cluster* cluster) 978 { 979 // FIXME: Why do we return true when hasExplicitWidth returns false?? 980 if (!cluster->m_parent || !hasExplicitWidth(cluster->m_root)) 981 return true; 982 983 const RenderBlock* parentDeepestBlockContainingAllText = deepestBlockContainingAllText(cluster->m_parent); 984 ASSERT(m_blocksThatHaveBegunLayout.contains(cluster->m_root)); 985 ASSERT(m_blocksThatHaveBegunLayout.contains(parentDeepestBlockContainingAllText)); 986 987 float contentWidth = cluster->m_root->contentLogicalWidth().toFloat(); 988 float clusterTextWidth = parentDeepestBlockContainingAllText->contentLogicalWidth().toFloat(); 989 990 // Clusters with a root that is wider than the deepestBlockContainingAllText of their parent 991 // autosize independently of their parent. 992 if (contentWidth > clusterTextWidth) 993 return true; 994 995 // Clusters with a root that is significantly narrower than the deepestBlockContainingAllText of 996 // their parent autosize independently of their parent. 997 static float narrowWidthDifference = 200; 998 if (clusterTextWidth - contentWidth > narrowWidthDifference) 999 return true; 1000 1001 return false; 1002 } 1003 1004 TextAutosizer::Cluster* TextAutosizer::currentCluster() const 1005 { 1006 ASSERT_WITH_SECURITY_IMPLICATION(!m_clusterStack.isEmpty()); 1007 return m_clusterStack.last().get(); 1008 } 1009 1010 #if ENABLE(ASSERT) 1011 void TextAutosizer::FingerprintMapper::assertMapsAreConsistent() 1012 { 1013 // For each fingerprint -> block mapping in m_blocksForFingerprint we should have an associated 1014 // map from block -> fingerprint in m_fingerprints. 1015 ReverseFingerprintMap::iterator end = m_blocksForFingerprint.end(); 1016 for (ReverseFingerprintMap::iterator fingerprintIt = m_blocksForFingerprint.begin(); fingerprintIt != end; ++fingerprintIt) { 1017 Fingerprint fingerprint = fingerprintIt->key; 1018 BlockSet* blocks = fingerprintIt->value.get(); 1019 for (BlockSet::iterator blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt) { 1020 const RenderBlock* block = (*blockIt); 1021 ASSERT(m_fingerprints.get(block) == fingerprint); 1022 } 1023 } 1024 } 1025 #endif 1026 1027 void TextAutosizer::FingerprintMapper::add(const RenderObject* renderer, Fingerprint fingerprint) 1028 { 1029 remove(renderer); 1030 1031 m_fingerprints.set(renderer, fingerprint); 1032 #if ENABLE(ASSERT) 1033 assertMapsAreConsistent(); 1034 #endif 1035 } 1036 1037 void TextAutosizer::FingerprintMapper::addTentativeClusterRoot(const RenderBlock* block, Fingerprint fingerprint) 1038 { 1039 add(block, fingerprint); 1040 1041 ReverseFingerprintMap::AddResult addResult = m_blocksForFingerprint.add(fingerprint, PassOwnPtr<BlockSet>()); 1042 if (addResult.isNewEntry) 1043 addResult.storedValue->value = adoptPtr(new BlockSet); 1044 addResult.storedValue->value->add(block); 1045 #if ENABLE(ASSERT) 1046 assertMapsAreConsistent(); 1047 #endif 1048 } 1049 1050 bool TextAutosizer::FingerprintMapper::remove(const RenderObject* renderer) 1051 { 1052 Fingerprint fingerprint = m_fingerprints.take(renderer); 1053 if (!fingerprint || !renderer->isRenderBlock()) 1054 return false; 1055 1056 ReverseFingerprintMap::iterator blocksIter = m_blocksForFingerprint.find(fingerprint); 1057 if (blocksIter == m_blocksForFingerprint.end()) 1058 return false; 1059 1060 BlockSet& blocks = *blocksIter->value; 1061 blocks.remove(toRenderBlock(renderer)); 1062 if (blocks.isEmpty()) 1063 m_blocksForFingerprint.remove(blocksIter); 1064 #if ENABLE(ASSERT) 1065 assertMapsAreConsistent(); 1066 #endif 1067 return true; 1068 } 1069 1070 TextAutosizer::Fingerprint TextAutosizer::FingerprintMapper::get(const RenderObject* renderer) 1071 { 1072 return m_fingerprints.get(renderer); 1073 } 1074 1075 TextAutosizer::BlockSet* TextAutosizer::FingerprintMapper::getTentativeClusterRoots(Fingerprint fingerprint) 1076 { 1077 return m_blocksForFingerprint.get(fingerprint); 1078 } 1079 1080 TextAutosizer::LayoutScope::LayoutScope(RenderBlock* block) 1081 : m_textAutosizer(block->document().textAutosizer()) 1082 , m_block(block) 1083 { 1084 if (!m_textAutosizer) 1085 return; 1086 1087 if (m_textAutosizer->shouldHandleLayout()) 1088 m_textAutosizer->beginLayout(m_block); 1089 else 1090 m_textAutosizer = 0; 1091 } 1092 1093 TextAutosizer::LayoutScope::~LayoutScope() 1094 { 1095 if (m_textAutosizer) 1096 m_textAutosizer->endLayout(m_block); 1097 } 1098 1099 1100 TextAutosizer::TableLayoutScope::TableLayoutScope(RenderTable* table) 1101 : LayoutScope(table) 1102 { 1103 if (m_textAutosizer) { 1104 ASSERT(m_textAutosizer->shouldHandleLayout()); 1105 m_textAutosizer->inflateAutoTable(table); 1106 } 1107 } 1108 1109 TextAutosizer::DeferUpdatePageInfo::DeferUpdatePageInfo(Page* page) 1110 : m_mainFrame(page->deprecatedLocalMainFrame()) 1111 { 1112 if (TextAutosizer* textAutosizer = m_mainFrame->document()->textAutosizer()) { 1113 ASSERT(!textAutosizer->m_updatePageInfoDeferred); 1114 textAutosizer->m_updatePageInfoDeferred = true; 1115 } 1116 } 1117 1118 TextAutosizer::DeferUpdatePageInfo::~DeferUpdatePageInfo() 1119 { 1120 if (TextAutosizer* textAutosizer = m_mainFrame->document()->textAutosizer()) { 1121 ASSERT(textAutosizer->m_updatePageInfoDeferred); 1122 textAutosizer->m_updatePageInfoDeferred = false; 1123 textAutosizer->updatePageInfoInAllFrames(); 1124 } 1125 } 1126 1127 float TextAutosizer::computeAutosizedFontSize(float specifiedSize, float multiplier) 1128 { 1129 // Somewhat arbitrary "pleasant" font size. 1130 const float pleasantSize = 16; 1131 1132 // Multiply fonts that the page author has specified to be larger than 1133 // pleasantSize by less and less, until huge fonts are not increased at all. 1134 // For specifiedSize between 0 and pleasantSize we directly apply the 1135 // multiplier; hence for specifiedSize == pleasantSize, computedSize will be 1136 // multiplier * pleasantSize. For greater specifiedSizes we want to 1137 // gradually fade out the multiplier, so for every 1px increase in 1138 // specifiedSize beyond pleasantSize we will only increase computedSize 1139 // by gradientAfterPleasantSize px until we meet the 1140 // computedSize = specifiedSize line, after which we stay on that line (so 1141 // then every 1px increase in specifiedSize increases computedSize by 1px). 1142 const float gradientAfterPleasantSize = 0.5; 1143 1144 float computedSize; 1145 if (specifiedSize <= pleasantSize) { 1146 computedSize = multiplier * specifiedSize; 1147 } else { 1148 computedSize = multiplier * pleasantSize + gradientAfterPleasantSize * (specifiedSize - pleasantSize); 1149 if (computedSize < specifiedSize) 1150 computedSize = specifiedSize; 1151 } 1152 return computedSize; 1153 } 1154 1155 void TextAutosizer::trace(Visitor* visitor) 1156 { 1157 visitor->trace(m_document); 1158 } 1159 1160 } // namespace blink 1161