2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "core/rendering/FastTextAutosizer.h"
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/page/Page.h"
39 #include "core/rendering/InlineIterator.h"
40 #include "core/rendering/RenderBlock.h"
41 #include "core/rendering/RenderListItem.h"
42 #include "core/rendering/RenderListMarker.h"
43 #include "core/rendering/RenderTableCell.h"
44 #include "core/rendering/RenderView.h"
46 #ifdef AUTOSIZING_DOM_DEBUG_INFO
47 #include "core/dom/ExecutionContextTask.h"
54 #ifdef AUTOSIZING_DOM_DEBUG_INFO
55 class WriteDebugInfoTask : public ExecutionContextTask {
57 WriteDebugInfoTask(PassRefPtr<Element> element, AtomicString value)
63 virtual void performTask(ExecutionContext*)
65 m_element->setAttribute("data-autosizing", m_value, ASSERT_NO_EXCEPTION);
69 RefPtr<Element> m_element;
73 static void writeDebugInfo(RenderObject* renderer, const AtomicString& output)
75 Node* node = renderer->node();
78 if (node->isDocumentNode())
79 node = toDocument(node)->documentElement();
80 if (!node->isElementNode())
82 node->document().postTask(adoptPtr(new WriteDebugInfoTask(toElement(node), output)));
85 void FastTextAutosizer::writeClusterDebugInfo(Cluster* cluster)
87 String explanation = "";
88 if (cluster->m_flags & SUPPRESSING) {
89 explanation = "[suppressed]";
90 } else if (!(cluster->m_flags & (INDEPENDENT | WIDER_OR_NARROWER))) {
91 explanation = "[inherited]";
92 } else if (cluster->m_supercluster) {
93 explanation = "[supercluster]";
94 } else if (!clusterHasEnoughTextToAutosize(cluster)) {
95 explanation = "[insufficient-text]";
97 const RenderBlock* widthProvider = clusterWidthProvider(cluster->m_root);
98 if (cluster->m_hasTableAncestor && cluster->m_multiplier < multiplierFromBlock(widthProvider)) {
99 explanation = "[table-ancestor-limited]";
101 explanation = String::format("[from width %d of %s]",
102 static_cast<int>(widthFromBlock(widthProvider)), widthProvider->debugName().utf8().data());
105 String pageInfo = "";
106 if (cluster->m_root->isRenderView()) {
107 pageInfo = String::format("; pageinfo: bm %f * (lw %d / fw %d)",
108 m_pageInfo.m_baseMultiplier, m_pageInfo.m_layoutWidth, m_pageInfo.m_frameWidth);
110 float multiplier = cluster->m_flags & SUPPRESSING ? 1.0 : cluster->m_multiplier;
111 writeDebugInfo(const_cast<RenderBlock*>(cluster->m_root),
112 AtomicString(String::format("cluster: %f %s%s", multiplier,
113 explanation.utf8().data(), pageInfo.utf8().data())));
117 static const RenderObject* parentElementRenderer(const RenderObject* renderer)
119 // At style recalc, the renderer's parent may not be attached,
120 // so we need to obtain this from the DOM tree.
121 const Node* node = renderer->node();
125 while ((node = node->parentNode())) {
126 if (node->isElementNode())
127 return node->renderer();
132 static bool isFormInput(const Element* element)
134 DEFINE_STATIC_LOCAL(Vector<QualifiedName>, formInputTags, ());
135 if (formInputTags.isEmpty()) {
136 formInputTags.append(HTMLNames::inputTag);
137 formInputTags.append(HTMLNames::buttonTag);
138 formInputTags.append(HTMLNames::selectTag);
140 return formInputTags.contains(element->tagQName());
143 static bool isPotentialClusterRoot(const RenderObject* renderer)
145 // "Potential cluster roots" are the smallest unit for which we can
146 // enable/disable text autosizing.
147 // - Must not be inline, as different multipliers on one line looks terrible.
148 // Exceptions are inline-block and alike elements (inline-table, -webkit-inline-*),
149 // as they often contain entire multi-line columns of text.
150 // - Must not be list items, as items in the same list should look consistent (*).
151 // - Must not be normal list items, as items in the same list should look
152 // consistent, unless they are floating or position:absolute/fixed.
153 Node* node = renderer->generatingNode();
154 if (node && !node->hasChildren())
156 if (!renderer->isRenderBlock())
158 if (renderer->isInline() && !renderer->style()->isDisplayReplacedType())
160 if (renderer->isListItem())
161 return (renderer->isFloating() || renderer->isOutOfFlowPositioned());
162 // Avoid creating containers for text within text controls, buttons, or <select> buttons.
163 Node* parentNode = renderer->parent() ? renderer->parent()->generatingNode() : 0;
164 if (parentNode && parentNode->isElementNode() && isFormInput(toElement(parentNode)))
170 static bool isIndependentDescendant(const RenderBlock* renderer)
172 ASSERT(isPotentialClusterRoot(renderer));
174 RenderBlock* containingBlock = renderer->containingBlock();
175 return renderer->isRenderView()
176 || renderer->isFloating()
177 || renderer->isOutOfFlowPositioned()
178 || renderer->isTableCell()
179 || renderer->isTableCaption()
180 || renderer->isFlexibleBoxIncludingDeprecated()
181 || renderer->hasColumns()
182 || (containingBlock && containingBlock->isHorizontalWritingMode() != renderer->isHorizontalWritingMode())
183 || renderer->style()->isDisplayReplacedType()
184 || renderer->isTextArea()
185 || renderer->style()->userModify() != READ_ONLY;
186 // FIXME: Tables need special handling to multiply all their columns by
187 // the same amount even if they're different widths; so do hasColumns()
188 // containers, and probably flexboxes...
191 static bool blockIsRowOfLinks(const RenderBlock* block)
193 // A "row of links" is a block for which:
194 // 1. It does not contain non-link text elements longer than 3 characters
195 // 2. It contains a minimum of 3 inline links and all links should
196 // have the same specified font size.
197 // 3. It should not contain <br> elements.
198 // 4. It should contain only inline elements unless they are containers,
199 // children of link elements or children of sub-containers.
201 RenderObject* renderer = block->nextInPreOrder(block);
202 float matchingFontSize = -1;
205 if (!isPotentialClusterRoot(renderer)) {
206 if (renderer->isText() && toRenderText(renderer)->text().impl()->stripWhiteSpace()->length() > 3)
208 if (!renderer->isInline() || renderer->isBR())
211 if (renderer->style()->isLink()) {
213 if (matchingFontSize < 0)
214 matchingFontSize = renderer->style()->specifiedFontSize();
215 else if (matchingFontSize != renderer->style()->specifiedFontSize())
218 // Skip traversing descendants of the link.
219 renderer = renderer->nextInPreOrderAfterChildren(block);
222 renderer = renderer->nextInPreOrder(block);
225 return (linkCount >= 3);
228 static bool blockHeightConstrained(const RenderBlock* block)
230 // FIXME: Propagate constrainedness down the tree, to avoid inefficiently walking back up from each box.
231 // FIXME: This code needs to take into account vertical writing modes.
232 // FIXME: Consider additional heuristics, such as ignoring fixed heights if the content is already overflowing before autosizing kicks in.
233 for (; block; block = block->containingBlock()) {
234 RenderStyle* style = block->style();
235 if (style->overflowY() >= OSCROLL)
237 if (style->height().isSpecified() || style->maxHeight().isSpecified() || block->isOutOfFlowPositioned()) {
238 // Some sites (e.g. wikipedia) set their html and/or body elements to height:100%,
239 // without intending to constrain the height of the content within them.
240 return !block->isDocumentElement() && !block->isBody();
242 if (block->isFloating())
248 static bool blockContainsFormInput(const RenderBlock* block)
250 const RenderObject* renderer = block;
252 const Node* node = renderer->node();
253 if (node && node->isElementNode() && isFormInput(toElement(node)))
255 if (renderer == block)
256 renderer = renderer->nextInPreOrder(block);
258 renderer = renderer->nextInPreOrderAfterChildren(block);
264 // Some blocks are not autosized even if their parent cluster wants them to.
265 static bool blockSuppressesAutosizing(const RenderBlock* block)
267 if (blockContainsFormInput(block))
270 if (blockIsRowOfLinks(block))
273 // Don't autosize block-level text that can't wrap (as it's likely to
274 // expand sideways and break the page's layout).
275 if (!block->style()->autoWrap())
278 if (blockHeightConstrained(block))
284 static bool hasExplicitWidth(const RenderBlock* block)
286 // FIXME: This heuristic may need to be expanded to other ways a block can be wider or narrower
287 // than its parent containing block.
288 return block->style() && block->style()->width().isSpecified();
291 FastTextAutosizer::FastTextAutosizer(const Document* document)
292 : m_document(document)
293 , m_firstBlockToBeginLayout(0)
295 , m_blocksThatHaveBegunLayout()
299 , m_fingerprintMapper()
301 , m_updatePageInfoDeferred(false)
305 void FastTextAutosizer::record(const RenderBlock* block)
307 if (!m_pageInfo.m_settingEnabled)
310 ASSERT(!m_blocksThatHaveBegunLayout.contains(block));
312 if (!classifyBlock(block, INDEPENDENT | EXPLICIT_WIDTH))
315 if (Fingerprint fingerprint = computeFingerprint(block))
316 m_fingerprintMapper.addTentativeClusterRoot(block, fingerprint);
319 void FastTextAutosizer::destroy(const RenderBlock* block)
321 if (!m_pageInfo.m_settingEnabled)
324 ASSERT(!m_blocksThatHaveBegunLayout.contains(block));
326 if (m_fingerprintMapper.remove(block) && m_firstBlockToBeginLayout) {
327 // RenderBlock with a fingerprint was destroyed during layout.
328 // Clear the cluster stack and the supercluster map to avoid stale pointers.
329 // Speculative fix for http://crbug.com/369485.
330 m_firstBlockToBeginLayout = 0;
331 m_clusterStack.clear();
332 m_superclusters.clear();
336 FastTextAutosizer::BeginLayoutBehavior FastTextAutosizer::prepareForLayout(const RenderBlock* block)
339 m_blocksThatHaveBegunLayout.add(block);
342 if (!m_firstBlockToBeginLayout) {
343 m_firstBlockToBeginLayout = block;
344 prepareClusterStack(block->parent());
345 } else if (block == currentCluster()->m_root) {
346 // Ignore beginLayout on the same block twice.
347 // This can happen with paginated overflow.
351 return ContinueLayout;
354 void FastTextAutosizer::prepareClusterStack(const RenderObject* renderer)
358 prepareClusterStack(renderer->parent());
360 if (renderer->isRenderBlock()) {
361 const RenderBlock* block = toRenderBlock(renderer);
363 m_blocksThatHaveBegunLayout.add(block);
365 if (Cluster* cluster = maybeCreateCluster(block))
366 m_clusterStack.append(adoptPtr(cluster));
370 void FastTextAutosizer::beginLayout(RenderBlock* block)
372 ASSERT(shouldHandleLayout());
374 if (prepareForLayout(block) == StopLayout)
377 if (Cluster* cluster = maybeCreateCluster(block)) {
378 m_clusterStack.append(adoptPtr(cluster));
379 if (block->isTable())
380 inflateTable(toRenderTable(block));
383 if (block->childrenInline() && block->firstChild())
387 void FastTextAutosizer::inflateListItem(RenderListItem* listItem, RenderListMarker* listItemMarker)
389 if (!shouldHandleLayout())
391 ASSERT(listItem && listItemMarker);
393 if (prepareForLayout(listItem) == StopLayout)
396 // Force the LI to be inside the DBCAT when computing the multiplier.
397 // This guarantees that the DBCAT has entered layout, so we can ask for its width.
398 // It also makes sense because the list marker is autosized like a text node.
399 float multiplier = clusterMultiplier(currentCluster());
401 applyMultiplier(listItem, multiplier);
402 applyMultiplier(listItemMarker, multiplier);
405 bool FastTextAutosizer::shouldDescendForTableInflation(RenderObject* child)
407 if (!child->needsLayout())
410 if (!child->isRenderBlock() || child->isTableCell())
413 return !classifyBlock(child, INDEPENDENT | SUPPRESSING);
416 void FastTextAutosizer::inflateTable(RenderTable* table)
419 ASSERT(table->containingBlock());
421 Cluster* cluster = currentCluster();
422 ASSERT(cluster->m_root->isTable());
424 // Pre-inflate cells that have enough text so that their inflated preferred widths will be used
425 // for column sizing.
426 // The multiplier used for cell descendants represents the maximum we can ever inflate
427 // descendants without overflowing the cell width computed by the table layout. Therefore,
428 // descendants of cells cannot use a multiplier higher than the table's multiplier.
429 float multiplier = clusterMultiplier(cluster);
430 for (RenderObject* section = table->firstChild(); section; section = section->nextSibling()) {
431 if (!section->isTableSection())
433 for (RenderObject* row = section->firstChild(); row; row = row->nextSibling()) {
434 if (!row->isTableRow())
436 for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) {
437 if (!cell->isTableCell() || !cell->needsLayout())
439 RenderTableCell* renderTableCell = toRenderTableCell(cell);
442 if (blockSuppressesAutosizing(renderTableCell))
443 shouldAutosize = false;
444 else if (Supercluster* supercluster = getSupercluster(renderTableCell))
445 shouldAutosize = superclusterHasEnoughTextToAutosize(supercluster, table);
447 shouldAutosize = clusterWouldHaveEnoughTextToAutosize(renderTableCell, table);
449 if (shouldAutosize) {
450 RenderObject* child = cell;
452 if (shouldDescendForTableInflation(child)) {
453 if (child->isText()) {
454 applyMultiplier(child, multiplier);
455 applyMultiplier(child->parent(), multiplier); // Parent handles line spacing.
457 child = child->nextInPreOrder(cell);
459 // Skip inflation of this subtree.
460 child = child->nextInPreOrderAfterChildren(cell);
469 void FastTextAutosizer::endLayout(RenderBlock* block)
471 ASSERT(shouldHandleLayout());
473 if (block == m_firstBlockToBeginLayout) {
474 m_firstBlockToBeginLayout = 0;
475 m_clusterStack.clear();
476 m_superclusters.clear();
477 m_stylesRetainedDuringLayout.clear();
479 m_blocksThatHaveBegunLayout.clear();
481 } else if (currentCluster()->m_root == block) {
482 m_clusterStack.removeLast();
486 void FastTextAutosizer::inflate(RenderBlock* block)
488 Cluster* cluster = currentCluster();
489 float multiplier = 0;
490 RenderObject* descendant = block->nextInPreOrder();
492 // Skip block descendants because they will be inflate()'d on their own.
493 if (descendant->isRenderBlock()) {
494 descendant = descendant->nextInPreOrderAfterChildren(block);
497 if (descendant->isText()) {
498 // We only calculate this multiplier on-demand to ensure the parent block of this text
499 // has entered layout.
501 multiplier = cluster->m_flags & SUPPRESSING ? 1.0f : clusterMultiplier(cluster);
502 applyMultiplier(descendant, multiplier);
503 applyMultiplier(descendant->parent(), multiplier); // Parent handles line spacing.
504 // FIXME: Investigate why MarkOnlyThis is sufficient.
505 if (descendant->parent()->isRenderInline())
506 descendant->setPreferredLogicalWidthsDirty(MarkOnlyThis);
508 descendant = descendant->nextInPreOrder(block);
512 bool FastTextAutosizer::shouldHandleLayout() const
514 return m_pageInfo.m_settingEnabled && m_pageInfo.m_pageNeedsAutosizing && !m_updatePageInfoDeferred;
517 void FastTextAutosizer::updatePageInfoInAllFrames()
519 ASSERT(!m_document->frame() || m_document->frame()->isMainFrame());
521 for (LocalFrame* frame = m_document->frame(); frame; frame = frame->tree().traverseNext()) {
522 if (FastTextAutosizer* textAutosizer = frame->document()->fastTextAutosizer())
523 textAutosizer->updatePageInfo();
527 void FastTextAutosizer::updatePageInfo()
529 if (m_updatePageInfoDeferred || !m_document->page() || !m_document->settings())
532 PageInfo previousPageInfo(m_pageInfo);
533 m_pageInfo.m_settingEnabled = m_document->settings()->textAutosizingEnabled();
535 if (!m_pageInfo.m_settingEnabled || m_document->printing()) {
536 m_pageInfo.m_pageNeedsAutosizing = false;
538 RenderView* renderView = toRenderView(m_document->renderer());
539 bool horizontalWritingMode = isHorizontalWritingMode(renderView->style()->writingMode());
541 LocalFrame* mainFrame = m_document->page()->mainFrame();
542 IntSize frameSize = m_document->settings()->textAutosizingWindowSizeOverride();
543 if (frameSize.isEmpty())
544 frameSize = mainFrame->view()->unscaledVisibleContentSize(IncludeScrollbars);
545 m_pageInfo.m_frameWidth = horizontalWritingMode ? frameSize.width() : frameSize.height();
547 IntSize layoutSize = mainFrame->view()->layoutSize();
548 m_pageInfo.m_layoutWidth = horizontalWritingMode ? layoutSize.width() : layoutSize.height();
550 // Compute the base font scale multiplier based on device and accessibility settings.
551 m_pageInfo.m_baseMultiplier = m_document->settings()->accessibilityFontScaleFactor();
552 // If the page has a meta viewport or @viewport, don't apply the device scale adjustment.
553 const ViewportDescription& viewportDescription = mainFrame->document()->viewportDescription();
554 if (!viewportDescription.isSpecifiedByAuthor()) {
555 float deviceScaleAdjustment = m_document->settings()->deviceScaleAdjustment();
556 m_pageInfo.m_baseMultiplier *= deviceScaleAdjustment;
559 m_pageInfo.m_pageNeedsAutosizing = !!m_pageInfo.m_frameWidth
560 && (m_pageInfo.m_baseMultiplier * (static_cast<float>(m_pageInfo.m_layoutWidth) / m_pageInfo.m_frameWidth) > 1.0f);
563 if (m_pageInfo.m_pageNeedsAutosizing) {
564 // If page info has changed, multipliers may have changed. Force a layout to recompute them.
565 if (m_pageInfo.m_frameWidth != previousPageInfo.m_frameWidth
566 || m_pageInfo.m_layoutWidth != previousPageInfo.m_layoutWidth
567 || m_pageInfo.m_baseMultiplier != previousPageInfo.m_baseMultiplier
568 || m_pageInfo.m_settingEnabled != previousPageInfo.m_settingEnabled)
569 setAllTextNeedsLayout();
570 } else if (previousPageInfo.m_hasAutosized) {
571 // If we are no longer autosizing the page, we won't do anything during the next layout.
572 // Set all the multipliers back to 1 now.
574 m_pageInfo.m_hasAutosized = false;
578 void FastTextAutosizer::resetMultipliers()
580 RenderObject* renderer = m_document->renderer();
582 if (RenderStyle* style = renderer->style()) {
583 if (style->textAutosizingMultiplier() != 1)
584 applyMultiplier(renderer, 1, LayoutNeeded);
586 renderer = renderer->nextInPreOrder();
590 void FastTextAutosizer::setAllTextNeedsLayout()
592 RenderObject* renderer = m_document->renderer();
594 if (renderer->isText())
595 renderer->setNeedsLayout();
596 renderer = renderer->nextInPreOrder();
600 FastTextAutosizer::BlockFlags FastTextAutosizer::classifyBlock(const RenderObject* renderer, BlockFlags mask)
602 if (!renderer->isRenderBlock())
605 const RenderBlock* block = toRenderBlock(renderer);
606 BlockFlags flags = 0;
608 if (isPotentialClusterRoot(block)) {
609 if (mask & POTENTIAL_ROOT)
610 flags |= POTENTIAL_ROOT;
612 if ((mask & INDEPENDENT) && (isIndependentDescendant(block) || block->isTable()))
613 flags |= INDEPENDENT;
615 if ((mask & EXPLICIT_WIDTH) && hasExplicitWidth(block))
616 flags |= EXPLICIT_WIDTH;
618 if ((mask & SUPPRESSING) && blockSuppressesAutosizing(block))
619 flags |= SUPPRESSING;
624 bool FastTextAutosizer::clusterWouldHaveEnoughTextToAutosize(const RenderBlock* root, const RenderBlock* widthProvider)
626 Cluster hypotheticalCluster(root, classifyBlock(root), 0);
627 return clusterHasEnoughTextToAutosize(&hypotheticalCluster, widthProvider);
630 bool FastTextAutosizer::clusterHasEnoughTextToAutosize(Cluster* cluster, const RenderBlock* widthProvider)
632 if (cluster->m_hasEnoughTextToAutosize != UnknownAmountOfText)
633 return cluster->m_hasEnoughTextToAutosize == HasEnoughText;
635 const RenderBlock* root = cluster->m_root;
637 widthProvider = clusterWidthProvider(root);
639 // TextAreas and user-modifiable areas get a free pass to autosize regardless of text content.
640 if (root->isTextArea() || (root->style() && root->style()->userModify() != READ_ONLY)) {
641 cluster->m_hasEnoughTextToAutosize = HasEnoughText;
645 if (cluster->m_flags & SUPPRESSING) {
646 cluster->m_hasEnoughTextToAutosize = NotEnoughText;
650 // 4 lines of text is considered enough to autosize.
651 float minimumTextLengthToAutosize = widthFromBlock(widthProvider) * 4;
654 RenderObject* descendant = root->nextInPreOrder(root);
656 if (descendant->isRenderBlock()) {
657 if (!(descendant->isTableCell() || (root->isTableCell() && descendant->isTable()))
658 && classifyBlock(descendant, INDEPENDENT | SUPPRESSING)) {
659 descendant = descendant->nextInPreOrderAfterChildren(root);
662 } else if (descendant->isText()) {
663 // Note: Using text().stripWhiteSpace().length() instead of renderedTextLength() because
664 // the lineboxes will not be built until layout. These values can be different.
665 // Note: This is an approximation assuming each character is 1em wide.
666 length += toRenderText(descendant)->text().stripWhiteSpace().length() * descendant->style()->specifiedFontSize();
668 if (length >= minimumTextLengthToAutosize) {
669 cluster->m_hasEnoughTextToAutosize = HasEnoughText;
673 descendant = descendant->nextInPreOrder(root);
676 cluster->m_hasEnoughTextToAutosize = NotEnoughText;
680 FastTextAutosizer::Fingerprint FastTextAutosizer::getFingerprint(const RenderObject* renderer)
682 Fingerprint result = m_fingerprintMapper.get(renderer);
684 result = computeFingerprint(renderer);
685 m_fingerprintMapper.add(renderer, result);
690 FastTextAutosizer::Fingerprint FastTextAutosizer::computeFingerprint(const RenderObject* renderer)
692 Node* node = renderer->generatingNode();
693 if (!node || !node->isElementNode())
696 FingerprintSourceData data;
697 if (const RenderObject* parent = parentElementRenderer(renderer))
698 data.m_parentHash = getFingerprint(parent);
700 data.m_qualifiedNameHash = QualifiedNameHash::hash(toElement(node)->tagQName());
702 if (RenderStyle* style = renderer->style()) {
703 data.m_packedStyleProperties = style->direction();
704 data.m_packedStyleProperties |= (style->position() << 1);
705 data.m_packedStyleProperties |= (style->floating() << 4);
706 data.m_packedStyleProperties |= (style->display() << 6);
707 data.m_packedStyleProperties |= (style->width().type() << 11);
708 // packedStyleProperties effectively using 15 bits now.
710 // consider for adding: writing mode, padding.
712 data.m_width = style->width().getFloatValue();
715 // Use nodeIndex as a rough approximation of column number
716 // (it's too early to call RenderTableCell::col).
717 // FIXME: account for colspan
718 if (renderer->isTableCell())
719 data.m_column = renderer->node()->nodeIndex();
721 return StringHasher::computeHash<UChar>(
722 static_cast<const UChar*>(static_cast<const void*>(&data)),
723 sizeof data / sizeof(UChar));
726 FastTextAutosizer::Cluster* FastTextAutosizer::maybeCreateCluster(const RenderBlock* block)
728 BlockFlags flags = classifyBlock(block);
729 if (!(flags & POTENTIAL_ROOT))
732 Cluster* parentCluster = m_clusterStack.isEmpty() ? 0 : currentCluster();
733 ASSERT(parentCluster || block->isRenderView());
735 // If a non-independent block would not alter the SUPPRESSING flag, it doesn't need to be a cluster.
736 bool parentSuppresses = parentCluster && (parentCluster->m_flags & SUPPRESSING);
737 if (!(flags & INDEPENDENT) && !(flags & EXPLICIT_WIDTH) && !!(flags & SUPPRESSING) == parentSuppresses)
740 Cluster* cluster = new Cluster(block, flags, parentCluster, getSupercluster(block));
741 #ifdef AUTOSIZING_DOM_DEBUG_INFO
742 // Non-SUPPRESSING clusters are annotated in clusterMultiplier.
743 if (flags & SUPPRESSING)
744 writeClusterDebugInfo(cluster);
749 FastTextAutosizer::Supercluster* FastTextAutosizer::getSupercluster(const RenderBlock* block)
751 Fingerprint fingerprint = m_fingerprintMapper.get(block);
755 BlockSet* roots = &m_fingerprintMapper.getTentativeClusterRoots(fingerprint);
756 if (!roots || roots->size() < 2 || !roots->contains(block))
759 SuperclusterMap::AddResult addResult = m_superclusters.add(fingerprint, PassOwnPtr<Supercluster>());
760 if (!addResult.isNewEntry)
761 return addResult.storedValue->value.get();
763 Supercluster* supercluster = new Supercluster(roots);
764 addResult.storedValue->value = adoptPtr(supercluster);
768 float FastTextAutosizer::clusterMultiplier(Cluster* cluster)
770 if (cluster->m_multiplier)
771 return cluster->m_multiplier;
773 // FIXME: why does isWiderOrNarrowerDescendant crash on independent clusters?
774 if (!(cluster->m_flags & INDEPENDENT) && isWiderOrNarrowerDescendant(cluster))
775 cluster->m_flags |= WIDER_OR_NARROWER;
777 if (cluster->m_flags & (INDEPENDENT | WIDER_OR_NARROWER)) {
778 if (cluster->m_supercluster) {
779 cluster->m_multiplier = superclusterMultiplier(cluster);
780 } else if (clusterHasEnoughTextToAutosize(cluster)) {
781 cluster->m_multiplier = multiplierFromBlock(clusterWidthProvider(cluster->m_root));
782 // Do not inflate table descendants above the table's multiplier. See inflateTable(...) for details.
783 if (cluster->m_hasTableAncestor)
784 cluster->m_multiplier = min(cluster->m_multiplier, clusterMultiplier(cluster->m_parent));
786 cluster->m_multiplier = 1.0f;
789 cluster->m_multiplier = cluster->m_parent ? clusterMultiplier(cluster->m_parent) : 1.0f;
792 #ifdef AUTOSIZING_DOM_DEBUG_INFO
793 writeClusterDebugInfo(cluster);
796 ASSERT(cluster->m_multiplier);
797 return cluster->m_multiplier;
800 bool FastTextAutosizer::superclusterHasEnoughTextToAutosize(Supercluster* supercluster, const RenderBlock* widthProvider)
802 if (supercluster->m_hasEnoughTextToAutosize != UnknownAmountOfText)
803 return supercluster->m_hasEnoughTextToAutosize == HasEnoughText;
805 BlockSet::iterator end = supercluster->m_roots->end();
806 for (BlockSet::iterator it = supercluster->m_roots->begin(); it != end; ++it) {
807 if (clusterWouldHaveEnoughTextToAutosize(*it, widthProvider)) {
808 supercluster->m_hasEnoughTextToAutosize = HasEnoughText;
812 supercluster->m_hasEnoughTextToAutosize = NotEnoughText;
816 float FastTextAutosizer::superclusterMultiplier(Cluster* cluster)
818 Supercluster* supercluster = cluster->m_supercluster;
819 if (!supercluster->m_multiplier) {
820 const RenderBlock* widthProvider = maxClusterWidthProvider(cluster->m_supercluster, cluster->m_root);
821 supercluster->m_multiplier = superclusterHasEnoughTextToAutosize(supercluster, widthProvider)
822 ? multiplierFromBlock(widthProvider) : 1.0f;
824 ASSERT(supercluster->m_multiplier);
825 return supercluster->m_multiplier;
828 const RenderBlock* FastTextAutosizer::clusterWidthProvider(const RenderBlock* root)
830 if (root->isTable() || root->isTableCell())
833 return deepestBlockContainingAllText(root);
836 const RenderBlock* FastTextAutosizer::maxClusterWidthProvider(const Supercluster* supercluster, const RenderBlock* currentRoot)
838 const RenderBlock* result = clusterWidthProvider(currentRoot);
839 float maxWidth = widthFromBlock(result);
841 const BlockSet* roots = supercluster->m_roots;
842 for (BlockSet::iterator it = roots->begin(); it != roots->end(); ++it) {
843 const RenderBlock* widthProvider = clusterWidthProvider(*it);
844 if (widthProvider->needsLayout())
846 float width = widthFromBlock(widthProvider);
847 if (width > maxWidth) {
849 result = widthProvider;
852 RELEASE_ASSERT(result);
856 float FastTextAutosizer::widthFromBlock(const RenderBlock* block)
858 RELEASE_ASSERT(block);
859 RELEASE_ASSERT(block->style());
860 if (block->isTable() || block->isListItem()) {
861 RenderBlock* containingBlock = block->containingBlock();
862 // containingBlock should only be null in detached subtrees.
863 if (!containingBlock)
865 if (block->style()->logicalWidth().isSpecified())
866 return floatValueForLength(block->style()->logicalWidth(), containingBlock->contentLogicalWidth().toFloat());
867 return containingBlock->contentLogicalWidth().toFloat();
869 return block->contentLogicalWidth().toFloat();
872 float FastTextAutosizer::multiplierFromBlock(const RenderBlock* block)
874 // If block->needsLayout() is false, it does not need to be in m_blocksThatHaveBegunLayout.
875 // This can happen during layout of a positioned object if the cluster's DBCAT is deeper
876 // than the positioned object's containing block, and wasn't marked as needing layout.
877 ASSERT(m_blocksThatHaveBegunLayout.contains(block) || !block->needsLayout());
879 // Block width, in CSS pixels.
880 float blockWidth = widthFromBlock(block);
881 float multiplier = m_pageInfo.m_frameWidth ? min(blockWidth, static_cast<float>(m_pageInfo.m_layoutWidth)) / m_pageInfo.m_frameWidth : 1.0f;
883 return max(m_pageInfo.m_baseMultiplier * multiplier, 1.0f);
886 const RenderBlock* FastTextAutosizer::deepestBlockContainingAllText(Cluster* cluster)
888 if (!cluster->m_deepestBlockContainingAllText)
889 cluster->m_deepestBlockContainingAllText = deepestBlockContainingAllText(cluster->m_root);
891 return cluster->m_deepestBlockContainingAllText;
894 // FIXME: Refactor this to look more like FastTextAutosizer::deepestCommonAncestor. This is copied
895 // from TextAutosizer::findDeepestBlockContainingAllText.
896 const RenderBlock* FastTextAutosizer::deepestBlockContainingAllText(const RenderBlock* root)
898 size_t firstDepth = 0;
899 const RenderObject* firstTextLeaf = findTextLeaf(root, firstDepth, First);
903 size_t lastDepth = 0;
904 const RenderObject* lastTextLeaf = findTextLeaf(root, lastDepth, Last);
905 ASSERT(lastTextLeaf);
907 // Equalize the depths if necessary. Only one of the while loops below will get executed.
908 const RenderObject* firstNode = firstTextLeaf;
909 const RenderObject* lastNode = lastTextLeaf;
910 while (firstDepth > lastDepth) {
911 firstNode = firstNode->parent();
914 while (lastDepth > firstDepth) {
915 lastNode = lastNode->parent();
919 // Go up from both nodes until the parent is the same. Both pointers will point to the LCA then.
920 while (firstNode != lastNode) {
921 firstNode = firstNode->parent();
922 lastNode = lastNode->parent();
925 if (firstNode->isRenderBlock())
926 return toRenderBlock(firstNode);
928 // containingBlock() should never leave the cluster, since it only skips ancestors when finding
929 // the container of position:absolute/fixed blocks, and those cannot exist between a cluster and
930 // its text node's lowest common ancestor as isAutosizingCluster would have made them into their
931 // own independent cluster.
932 const RenderBlock* containingBlock = firstNode->containingBlock();
933 if (!containingBlock)
936 ASSERT(containingBlock->isDescendantOf(root));
937 return containingBlock;
940 const RenderObject* FastTextAutosizer::findTextLeaf(const RenderObject* parent, size_t& depth, TextLeafSearch firstOrLast)
942 // List items are treated as text due to the marker.
943 // The actual renderer for the marker (RenderListMarker) may not be in the tree yet since it is added during layout.
944 if (parent->isListItem())
947 if (parent->isText())
951 const RenderObject* child = (firstOrLast == First) ? parent->firstChild() : parent->lastChild();
953 // Note: At this point clusters may not have been created for these blocks so we cannot rely
954 // on m_clusters. Instead, we use a best-guess about whether the block will become a cluster.
955 if (!classifyBlock(child, INDEPENDENT)) {
956 if (const RenderObject* leaf = findTextLeaf(child, depth, firstOrLast))
959 child = (firstOrLast == First) ? child->nextSibling() : child->previousSibling();
966 void FastTextAutosizer::applyMultiplier(RenderObject* renderer, float multiplier, RelayoutBehavior relayoutBehavior)
968 ASSERT(renderer && renderer->style());
969 RenderStyle* currentStyle = renderer->style();
970 if (currentStyle->textAutosizingMultiplier() == multiplier)
973 // We need to clone the render style to avoid breaking style sharing.
974 RefPtr<RenderStyle> style = RenderStyle::clone(currentStyle);
975 style->setTextAutosizingMultiplier(multiplier);
978 switch (relayoutBehavior) {
979 case AlreadyInLayout:
980 // Don't free currentStyle until the end of the layout pass. This allows other parts of the system
981 // to safely hold raw RenderStyle* pointers during layout, e.g. BreakingContext::m_currentStyle.
982 m_stylesRetainedDuringLayout.append(currentStyle);
984 renderer->setStyleInternal(style.release());
985 renderer->setNeedsLayout();
986 if (renderer->isRenderBlock())
987 toRenderBlock(renderer)->invalidateLineHeight();
991 renderer->setStyle(style.release());
996 m_pageInfo.m_hasAutosized = true;
999 bool FastTextAutosizer::isWiderOrNarrowerDescendant(Cluster* cluster)
1001 // FIXME: Why do we return true when hasExplicitWidth returns false??
1002 if (!cluster->m_parent || !hasExplicitWidth(cluster->m_root))
1005 const RenderBlock* parentDeepestBlockContainingAllText = deepestBlockContainingAllText(cluster->m_parent);
1006 ASSERT(m_blocksThatHaveBegunLayout.contains(cluster->m_root));
1007 ASSERT(m_blocksThatHaveBegunLayout.contains(parentDeepestBlockContainingAllText));
1009 float contentWidth = cluster->m_root->contentLogicalWidth().toFloat();
1010 float clusterTextWidth = parentDeepestBlockContainingAllText->contentLogicalWidth().toFloat();
1012 // Clusters with a root that is wider than the deepestBlockContainingAllText of their parent
1013 // autosize independently of their parent.
1014 if (contentWidth > clusterTextWidth)
1017 // Clusters with a root that is significantly narrower than the deepestBlockContainingAllText of
1018 // their parent autosize independently of their parent.
1019 static float narrowWidthDifference = 200;
1020 if (clusterTextWidth - contentWidth > narrowWidthDifference)
1026 FastTextAutosizer::Cluster* FastTextAutosizer::currentCluster() const
1028 ASSERT_WITH_SECURITY_IMPLICATION(!m_clusterStack.isEmpty());
1029 return m_clusterStack.last().get();
1033 void FastTextAutosizer::FingerprintMapper::assertMapsAreConsistent()
1035 // For each fingerprint -> block mapping in m_blocksForFingerprint we should have an associated
1036 // map from block -> fingerprint in m_fingerprints.
1037 ReverseFingerprintMap::iterator end = m_blocksForFingerprint.end();
1038 for (ReverseFingerprintMap::iterator fingerprintIt = m_blocksForFingerprint.begin(); fingerprintIt != end; ++fingerprintIt) {
1039 Fingerprint fingerprint = fingerprintIt->key;
1040 BlockSet* blocks = fingerprintIt->value.get();
1041 for (BlockSet::iterator blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt) {
1042 const RenderBlock* block = (*blockIt);
1043 ASSERT(m_fingerprints.get(block) == fingerprint);
1049 void FastTextAutosizer::FingerprintMapper::add(const RenderObject* renderer, Fingerprint fingerprint)
1053 m_fingerprints.set(renderer, fingerprint);
1055 assertMapsAreConsistent();
1059 void FastTextAutosizer::FingerprintMapper::addTentativeClusterRoot(const RenderBlock* block, Fingerprint fingerprint)
1061 add(block, fingerprint);
1063 ReverseFingerprintMap::AddResult addResult = m_blocksForFingerprint.add(fingerprint, PassOwnPtr<BlockSet>());
1064 if (addResult.isNewEntry)
1065 addResult.storedValue->value = adoptPtr(new BlockSet);
1066 addResult.storedValue->value->add(block);
1068 assertMapsAreConsistent();
1072 bool FastTextAutosizer::FingerprintMapper::remove(const RenderObject* renderer)
1074 Fingerprint fingerprint = m_fingerprints.take(renderer);
1075 if (!fingerprint || !renderer->isRenderBlock())
1078 ReverseFingerprintMap::iterator blocksIter = m_blocksForFingerprint.find(fingerprint);
1079 if (blocksIter == m_blocksForFingerprint.end())
1082 BlockSet& blocks = *blocksIter->value;
1083 blocks.remove(toRenderBlock(renderer));
1084 if (blocks.isEmpty())
1085 m_blocksForFingerprint.remove(blocksIter);
1087 assertMapsAreConsistent();
1092 FastTextAutosizer::Fingerprint FastTextAutosizer::FingerprintMapper::get(const RenderObject* renderer)
1094 return m_fingerprints.get(renderer);
1097 FastTextAutosizer::BlockSet& FastTextAutosizer::FingerprintMapper::getTentativeClusterRoots(Fingerprint fingerprint)
1099 return *m_blocksForFingerprint.get(fingerprint);
1102 FastTextAutosizer::LayoutScope::LayoutScope(RenderBlock* block)
1103 : m_textAutosizer(block->document().fastTextAutosizer())
1106 if (!m_textAutosizer)
1109 if (m_textAutosizer->shouldHandleLayout())
1110 m_textAutosizer->beginLayout(m_block);
1112 m_textAutosizer = 0;
1115 FastTextAutosizer::LayoutScope::~LayoutScope()
1117 if (m_textAutosizer)
1118 m_textAutosizer->endLayout(m_block);
1121 FastTextAutosizer::DeferUpdatePageInfo::DeferUpdatePageInfo(Page* page)
1122 : m_mainFrame(page->mainFrame())
1124 if (FastTextAutosizer* textAutosizer = m_mainFrame->document()->fastTextAutosizer()) {
1125 ASSERT(!textAutosizer->m_updatePageInfoDeferred);
1126 textAutosizer->m_updatePageInfoDeferred = true;
1130 FastTextAutosizer::DeferUpdatePageInfo::~DeferUpdatePageInfo()
1132 if (FastTextAutosizer* textAutosizer = m_mainFrame->document()->fastTextAutosizer()) {
1133 ASSERT(textAutosizer->m_updatePageInfoDeferred);
1134 textAutosizer->m_updatePageInfoDeferred = false;
1135 textAutosizer->updatePageInfoInAllFrames();
1139 } // namespace WebCore