TEST_P(PaintControllerPaintTestForSlimmingPaintV1AndV2, InlineRelayout) { setBodyInnerHTML( "<div id='div' style='width:100px; height: 200px'>AAAAAAAAAA " "BBBBBBBBBB</div>"); Element& div = *toElement(document().body()->firstChild()); LayoutBlock& divBlock = *toLayoutBlock(document().body()->firstChild()->layoutObject()); LayoutText& text = *toLayoutText(divBlock.firstChild()); InlineTextBox& firstTextBox = *text.firstTextBox(); if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { EXPECT_DISPLAY_LIST( rootPaintController().getDisplayItemList(), 6, TestDisplayItem(layoutView(), DisplayItem::kClipFrameToVisibleContentRect), TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence), TestDisplayItem(layoutView(), documentBackgroundType), TestDisplayItem(firstTextBox, foregroundType), TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence), TestDisplayItem(layoutView(), DisplayItem::clipTypeToEndClipType( DisplayItem::kClipFrameToVisibleContentRect))); } else { EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 2, TestDisplayItem(layoutView(), documentBackgroundType), TestDisplayItem(firstTextBox, foregroundType)); } div.setAttribute(HTMLNames::styleAttr, "width: 10px; height: 200px"); document().view()->updateAllLifecyclePhases(); LayoutText& newText = *toLayoutText(divBlock.firstChild()); InlineTextBox& newFirstTextBox = *newText.firstTextBox(); InlineTextBox& secondTextBox = *newText.firstTextBox()->nextTextBox(); if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { EXPECT_DISPLAY_LIST( rootPaintController().getDisplayItemList(), 7, TestDisplayItem(layoutView(), DisplayItem::kClipFrameToVisibleContentRect), TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence), TestDisplayItem(layoutView(), documentBackgroundType), TestDisplayItem(newFirstTextBox, foregroundType), TestDisplayItem(secondTextBox, foregroundType), TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence), TestDisplayItem(layoutView(), DisplayItem::clipTypeToEndClipType( DisplayItem::kClipFrameToVisibleContentRect))); } else { EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 3, TestDisplayItem(layoutView(), documentBackgroundType), TestDisplayItem(newFirstTextBox, foregroundType), TestDisplayItem(secondTextBox, foregroundType)); } }
TEST_P(PaintControllerPaintTestForSlimmingPaintV1AndV2, InlineRelayout) { setBodyInnerHTML("<div id='div' style='width:100px; height: 200px'>AAAAAAAAAA BBBBBBBBBB</div>"); PaintLayer& rootLayer = *layoutView().layer(); Element& div = *toElement(document().body()->firstChild()); LayoutBlock& divBlock = *toLayoutBlock(document().body()->firstChild()->layoutObject()); LayoutText& text = *toLayoutText(divBlock.firstChild()); InlineTextBox& firstTextBox = *text.firstTextBox(); if (RuntimeEnabledFeatures::slimmingPaintSynchronizedPaintingEnabled()) { EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 4, TestDisplayItem(rootLayer, DisplayItem::Subsequence), TestDisplayItem(layoutView(), backgroundType), TestDisplayItem(firstTextBox, foregroundType), TestDisplayItem(rootLayer, DisplayItem::EndSubsequence)); } else { GraphicsContext context(rootPaintController()); PaintLayerPaintingInfo paintingInfo(&rootLayer, LayoutRect(0, 0, 800, 600), GlobalPaintNormalPhase, LayoutSize()); PaintLayerPainter(rootLayer).paintLayerContents(&context, paintingInfo, PaintLayerPaintingCompositingAllPhases); rootPaintController().commitNewDisplayItems(); EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 2, TestDisplayItem(layoutView(), backgroundType), TestDisplayItem(firstTextBox, foregroundType)); } div.setAttribute(HTMLNames::styleAttr, "width: 10px; height: 200px"); document().view()->updateAllLifecyclePhases(); LayoutText& newText = *toLayoutText(divBlock.firstChild()); InlineTextBox& newFirstTextBox = *newText.firstTextBox(); InlineTextBox& secondTextBox = *newText.firstTextBox()->nextTextBox(); if (RuntimeEnabledFeatures::slimmingPaintSynchronizedPaintingEnabled()) { EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 5, TestDisplayItem(rootLayer, DisplayItem::Subsequence), TestDisplayItem(layoutView(), backgroundType), TestDisplayItem(newFirstTextBox, foregroundType), TestDisplayItem(secondTextBox, foregroundType), TestDisplayItem(rootLayer, DisplayItem::EndSubsequence)); } else { GraphicsContext context(rootPaintController()); PaintLayerPaintingInfo paintingInfo(&rootLayer, LayoutRect(0, 0, 800, 600), GlobalPaintNormalPhase, LayoutSize()); PaintLayerPainter(rootLayer).paintLayerContents(&context, paintingInfo, PaintLayerPaintingCompositingAllPhases); rootPaintController().commitNewDisplayItems(); EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 3, TestDisplayItem(layoutView(), backgroundType), TestDisplayItem(newFirstTextBox, foregroundType), TestDisplayItem(secondTextBox, foregroundType)); } }
TEST_F(VisualRectMappingTest, LayoutText) { setBodyInnerHTML( "<style>body { margin: 0; }</style>" "<div id='container' style='overflow: scroll; width: 50px; height: 50px'>" " <span><img style='width: 20px; height: 100px'></span>" " text text text text text text text" "</div>"); LayoutBlock* container = toLayoutBlock(getLayoutObjectByElementId("container")); LayoutText* text = toLayoutText(container->lastChild()); container->setScrollTop(LayoutUnit(50)); LayoutRect originalRect(0, 60, 20, 80); LayoutRect rect = originalRect; EXPECT_TRUE(text->mapToVisualRectInAncestorSpace(container, rect)); rect.move(-container->scrolledContentOffset()); EXPECT_EQ(rect, LayoutRect(0, 10, 20, 80)); rect = originalRect; EXPECT_TRUE(text->mapToVisualRectInAncestorSpace(&layoutView(), rect)); EXPECT_EQ(rect, LayoutRect(0, 10, 20, 40)); checkPaintInvalidationStateRectMapping(rect, originalRect, *text, layoutView(), layoutView()); rect = LayoutRect(0, 60, 80, 0); EXPECT_TRUE( text->mapToVisualRectInAncestorSpace(container, rect, EdgeInclusive)); rect.move(-container->scrolledContentOffset()); EXPECT_EQ(rect, LayoutRect(0, 10, 80, 0)); }
void LayoutAnalyzer::push(const LayoutObject& o) { increment(TotalLayoutObjectsThatWereLaidOut); if (!o.everHadLayout()) increment(LayoutObjectsThatHadNeverHadLayout); if (o.selfNeedsLayout()) increment(LayoutObjectsThatNeedLayoutForThemselves); if (o.needsPositionedMovementLayout()) increment(LayoutObjectsThatNeedPositionedMovementLayout); if (o.isOutOfFlowPositioned()) increment(LayoutObjectsThatAreOutOfFlowPositioned); if (o.isTableCell()) increment(LayoutObjectsThatAreTableCells); if (o.isFloating()) increment(LayoutObjectsThatAreFloating); if (o.style()->specifiesColumns()) increment(LayoutObjectsThatSpecifyColumns); if (o.hasLayer()) increment(LayoutObjectsThatHaveALayer); if (o.isLayoutInline() && o.alwaysCreateLineBoxesForLayoutInline()) increment(LayoutInlineObjectsThatAlwaysCreateLineBoxes); if (o.isText()) { const LayoutText& t = *toLayoutText(&o); if (t.canUseSimpleFontCodePath()) { increment(LayoutObjectsThatAreTextAndCanUseTheSimpleFontCodePath); increment(CharactersInLayoutObjectsThatAreTextAndCanUseTheSimpleFontCodePath, t.textLength()); } else { increment(LayoutObjectsThatAreTextAndCanNotUseTheSimpleFontCodePath); increment(CharactersInLayoutObjectsThatAreTextAndCanNotUseTheSimpleFontCodePath, t.textLength()); } } // This might be a root in a subtree layout, in which case the LayoutObject // has a parent but the stack is empty. If a LayoutObject subclass forgets // to call push() and is a root in a subtree layout, then this // assert would only fail if that LayoutObject instance has any children // that need layout and do call push(). // LayoutBlock::layoutPositionedObjects() hoists positioned descendants. // LayoutBlockFlow::layoutInlineChildren() walks through inlines. // LayoutTableSection::layoutRows() walks through rows. if (!o.isPositioned() && !o.isTableCell() && !o.isSVGResourceContainer() && (m_stack.size() != 0) && !(o.parent()->childrenInline() && (o.isReplaced() || o.isFloating() || o.isOutOfFlowPositioned()))) { ASSERT(o.parent() == m_stack.peek()); } m_stack.push(&o); // This refers to LayoutAnalyzer depth, not layout tree depth or DOM tree // depth. LayoutAnalyzer depth is generally closer to C++ stack recursion // depth. See above exceptions for when LayoutAnalyzer depth != layout tree // depth. if (m_stack.size() > m_counters[LayoutAnalyzerStackMaximumDepth]) m_counters[LayoutAnalyzerStackMaximumDepth] = m_stack.size(); }
static int maxOffsetIncludingCollapsedSpaces(Node* node) { int offset = caretMaxOffset(node); if (node->layoutObject() && node->layoutObject()->isText()) offset += collapsedSpaceLength(toLayoutText(node->layoutObject()), offset); return offset; }
void IntersectionObservation::initializeGeometry(IntersectionGeometry& geometry) { ASSERT(m_target); LayoutObject* targetLayoutObject = target()->layoutObject(); if (targetLayoutObject->isBoxModelObject()) geometry.targetRect = toLayoutBoxModelObject(targetLayoutObject)->visualOverflowRect(); else geometry.targetRect = toLayoutText(targetLayoutObject)->visualOverflowRect(); geometry.intersectionRect = geometry.targetRect; }
TracedLayoutObject::TracedLayoutObject(const LayoutObject& object) : m_address((unsigned long) &object) , m_isAnonymous(object.isAnonymous()) , m_isPositioned(object.isOutOfFlowPositioned()) , m_isRelPositioned(object.isRelPositioned()) , m_isStickyPositioned(object.isStickyPositioned()) , m_isFloating(object.isFloating()) , m_selfNeeds(object.selfNeedsLayout()) , m_positionedMovement(object.needsPositionedMovementLayout()) , m_childNeeds(object.normalChildNeedsLayout()) , m_posChildNeeds(object.posChildNeedsLayout()) , m_isTableCell(object.isTableCell()) , m_name(String(object.name()).isolatedCopy()) , m_absRect(object.absoluteBoundingBoxRect()) { if (Node* node = object.node()) { m_tag = String(node->nodeName()).isolatedCopy(); if (node->isElementNode()) { Element& element = toElement(*node); if (element.hasID()) m_id = String(element.getIdAttribute()).isolatedCopy(); if (element.hasClass()) { for (size_t i = 0; i < element.classNames().size(); ++i) { m_classNames.append( String(element.classNames()[i]).isolatedCopy()); } } } } // FIXME: When the fixmes in LayoutTreeAsText::writeLayoutObject() are // fixed, deduplicate it with this. if (object.isText()) { m_rect = LayoutRect(toLayoutText(object).linesBoundingBox()); } else if (object.isLayoutInline()) { m_rect = LayoutRect(toLayoutInline(object).linesBoundingBox()); } else if (object.isBox()) { m_rect = toLayoutBox(&object)->frameRect(); } if (m_isTableCell) { const LayoutTableCell& c = toLayoutTableCell(object); if (c.row() && c.row()->rowIndexWasSet() && c.hasCol() && (!c.row()->section() || !c.row()->section()->needsCellRecalc())) { m_row = c.rowIndex(); m_col = c.col(); m_rowSpan = c.rowSpan(); m_colSpan = c.colSpan(); } } for (LayoutObject* child = object.slowFirstChild(); child; child = child->nextSibling()) { m_children.append(adoptRef(new TracedLayoutObject(*child))); } }
void LayoutRubyRun::getOverhang(bool firstLine, LayoutObject* startLayoutObject, LayoutObject* endLayoutObject, int& startOverhang, int& endOverhang) const { ASSERT(!needsLayout()); startOverhang = 0; endOverhang = 0; LayoutRubyBase* rubyBase = this->rubyBase(); LayoutRubyText* rubyText = this->rubyText(); if (!rubyBase || !rubyText) return; if (!rubyBase->firstRootBox()) return; int logicalWidth = this->logicalWidth(); int logicalLeftOverhang = std::numeric_limits<int>::max(); int logicalRightOverhang = std::numeric_limits<int>::max(); for (RootInlineBox* rootInlineBox = rubyBase->firstRootBox(); rootInlineBox; rootInlineBox = rootInlineBox->nextRootBox()) { logicalLeftOverhang = std::min<int>(logicalLeftOverhang, rootInlineBox->logicalLeft()); logicalRightOverhang = std::min<int>(logicalRightOverhang, logicalWidth - rootInlineBox->logicalRight()); } startOverhang = style()->isLeftToRightDirection() ? logicalLeftOverhang : logicalRightOverhang; endOverhang = style()->isLeftToRightDirection() ? logicalRightOverhang : logicalLeftOverhang; if (!startLayoutObject || !startLayoutObject->isText() || startLayoutObject->style(firstLine)->fontSize() > rubyBase->style(firstLine)->fontSize()) startOverhang = 0; if (!endLayoutObject || !endLayoutObject->isText() || endLayoutObject->style(firstLine)->fontSize() > rubyBase->style(firstLine)->fontSize()) endOverhang = 0; // We overhang a ruby only if the neighboring layout object is a text. // We can overhang the ruby by no more than half the width of the neighboring text // and no more than half the font size. int halfWidthOfFontSize = rubyText->style(firstLine)->fontSize() / 2; if (startOverhang) startOverhang = std::min<int>(startOverhang, std::min<int>(toLayoutText(startLayoutObject)->minLogicalWidth(), halfWidthOfFontSize)); if (endOverhang) endOverhang = std::min<int>(endOverhang, std::min<int>(toLayoutText(endLayoutObject)->minLogicalWidth(), halfWidthOfFontSize)); }
TEST_P(PaintControllerPaintTestForSlimmingPaintV1AndV2, FullDocumentPaintingWithCaret) { setBodyInnerHTML( "<div id='div' contentEditable='true' style='outline:none'>XYZ</div>"); document().page()->focusController().setActive(true); document().page()->focusController().setFocused(true); Element& div = *toElement(document().body()->firstChild()); InlineTextBox& textInlineBox = *toLayoutText(div.firstChild()->layoutObject())->firstTextBox(); if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { EXPECT_DISPLAY_LIST( rootPaintController().getDisplayItemList(), 6, TestDisplayItem(layoutView(), DisplayItem::kClipFrameToVisibleContentRect), TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence), TestDisplayItem(layoutView(), documentBackgroundType), TestDisplayItem(textInlineBox, foregroundType), TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence), TestDisplayItem(layoutView(), DisplayItem::clipTypeToEndClipType( DisplayItem::kClipFrameToVisibleContentRect))); } else { EXPECT_DISPLAY_LIST(rootPaintController().getDisplayItemList(), 2, TestDisplayItem(layoutView(), documentBackgroundType), TestDisplayItem(textInlineBox, foregroundType)); } div.focus(); document().view()->updateAllLifecyclePhases(); if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { EXPECT_DISPLAY_LIST( rootPaintController().getDisplayItemList(), 7, TestDisplayItem(layoutView(), DisplayItem::kClipFrameToVisibleContentRect), TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence), TestDisplayItem(layoutView(), documentBackgroundType), TestDisplayItem(textInlineBox, foregroundType), TestDisplayItem(*document().frame()->selection().m_frameCaret, DisplayItem::kCaret), // New! TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence), TestDisplayItem(layoutView(), DisplayItem::clipTypeToEndClipType( DisplayItem::kClipFrameToVisibleContentRect))); } else { EXPECT_DISPLAY_LIST( rootPaintController().getDisplayItemList(), 3, TestDisplayItem(layoutView(), documentBackgroundType), TestDisplayItem(textInlineBox, foregroundType), TestDisplayItem(*document().frame()->selection().m_frameCaret, DisplayItem::kCaret)); // New! } }
LayoutTextFragment* LayoutQuote::findFragmentChild() const { // We walk from the end of the child list because, if we've had a first-letter // LayoutObject inserted then the remaining text will be at the end. while (LayoutObject* child = lastChild()) { if (child->isText() && toLayoutText(child)->isTextFragment()) return toLayoutTextFragment(child); } return nullptr; }
LayoutText* SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::handleFirstLetter(int& startOffset, int& offsetInNode) { LayoutText* layoutObject = toLayoutText(m_node->layoutObject()); startOffset = (m_node == m_startNode) ? m_startOffset : 0; if (!layoutObject->isTextFragment()) { offsetInNode = 0; return layoutObject; } LayoutTextFragment* fragment = toLayoutTextFragment(layoutObject); int offsetAfterFirstLetter = fragment->start(); if (startOffset >= offsetAfterFirstLetter) { ASSERT(!m_shouldHandleFirstLetter); offsetInNode = offsetAfterFirstLetter; return layoutObject; } if (!m_shouldHandleFirstLetter && offsetAfterFirstLetter < m_offset) { m_shouldHandleFirstLetter = true; offsetInNode = offsetAfterFirstLetter; return layoutObject; } m_shouldHandleFirstLetter = false; offsetInNode = 0; ASSERT(fragment->isRemainingTextLayoutObject()); ASSERT(fragment->firstLetterPseudoElement()); LayoutObject* pseudoElementLayoutObject = fragment->firstLetterPseudoElement()->layoutObject(); ASSERT(pseudoElementLayoutObject); ASSERT(pseudoElementLayoutObject->slowFirstChild()); LayoutText* firstLetterLayoutObject = toLayoutText(pseudoElementLayoutObject->slowFirstChild()); m_offset = firstLetterLayoutObject->caretMaxOffset(); m_offset += collapsedSpaceLength(firstLetterLayoutObject, m_offset); return firstLetterLayoutObject; }
bool EditCommand::isRenderedCharacter(const Position& position) { if (position.isNull()) return false; DCHECK(position.isOffsetInAnchor()) << position; if (!position.anchorNode()->isTextNode()) return false; LayoutObject* layoutObject = position.anchorNode()->layoutObject(); if (!layoutObject) return false; return toLayoutText(layoutObject) ->isRenderedCharacter(position.offsetInContainerNode()); }
TEST_P(PaintControllerPaintTestForSlimmingPaintV1AndV2, FullDocumentPaintingWithCaret) { setBodyInnerHTML("<div id='div' contentEditable='true' style='outline:none'>XYZ</div>"); document().page()->focusController().setActive(true); document().page()->focusController().setFocused(true); PaintLayer& rootLayer = *layoutView().layer(); Element& div = *toElement(document().body()->firstChild()); LayoutObject& divLayoutObject = *document().body()->firstChild()->layoutObject(); InlineTextBox& textInlineBox = *toLayoutText(div.firstChild()->layoutObject())->firstTextBox(); if (RuntimeEnabledFeatures::slimmingPaintSynchronizedPaintingEnabled()) { EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 4, TestDisplayItem(rootLayer, DisplayItem::Subsequence), TestDisplayItem(layoutView(), backgroundType), TestDisplayItem(textInlineBox, foregroundType), TestDisplayItem(rootLayer, DisplayItem::EndSubsequence)); } else { GraphicsContext context(rootPaintController()); PaintLayerPaintingInfo paintingInfo(&rootLayer, LayoutRect(0, 0, 800, 600), GlobalPaintNormalPhase, LayoutSize()); PaintLayerPainter(rootLayer).paintLayerContents(&context, paintingInfo, PaintLayerPaintingCompositingAllPhases); rootPaintController().commitNewDisplayItems(); EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 2, TestDisplayItem(layoutView(), backgroundType), TestDisplayItem(textInlineBox, foregroundType)); } div.focus(); document().view()->updateAllLifecyclePhases(); if (RuntimeEnabledFeatures::slimmingPaintSynchronizedPaintingEnabled()) { EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 5, TestDisplayItem(rootLayer, DisplayItem::Subsequence), TestDisplayItem(layoutView(), backgroundType), TestDisplayItem(textInlineBox, foregroundType), TestDisplayItem(divLayoutObject, DisplayItem::Caret), // New! TestDisplayItem(rootLayer, DisplayItem::EndSubsequence)); } else { GraphicsContext context(rootPaintController()); PaintLayerPaintingInfo paintingInfo(&rootLayer, LayoutRect(0, 0, 800, 600), GlobalPaintNormalPhase, LayoutSize()); PaintLayerPainter(rootLayer).paintLayerContents(&context, paintingInfo, PaintLayerPaintingCompositingAllPhases); rootPaintController().commitNewDisplayItems(); EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 3, TestDisplayItem(layoutView(), backgroundType), TestDisplayItem(textInlineBox, foregroundType), TestDisplayItem(divLayoutObject, DisplayItem::Caret)); // New! } }
TEST_P(PaintControllerPaintTestForSlimmingPaintV1AndV2, InlineRelayout) { setBodyInnerHTML("<div id='div' style='width:100px; height: 200px'>AAAAAAAAAA BBBBBBBBBB</div>"); Element& div = *toElement(document().body()->firstChild()); LayoutBlock& divBlock = *toLayoutBlock(document().body()->firstChild()->layoutObject()); LayoutText& text = *toLayoutText(divBlock.firstChild()); InlineTextBox& firstTextBox = *text.firstTextBox(); EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 2, TestDisplayItem(layoutView(), backgroundType), TestDisplayItem(firstTextBox, foregroundType)); div.setAttribute(HTMLNames::styleAttr, "width: 10px; height: 200px"); document().view()->updateAllLifecyclePhases(); LayoutText& newText = *toLayoutText(divBlock.firstChild()); InlineTextBox& newFirstTextBox = *newText.firstTextBox(); InlineTextBox& secondTextBox = *newText.firstTextBox()->nextTextBox(); EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 3, TestDisplayItem(layoutView(), backgroundType), TestDisplayItem(newFirstTextBox, foregroundType), TestDisplayItem(secondTextBox, foregroundType)); }
TEST_F(LayoutObjectTest, MapToVisibleRectInContainerSpace) { setBodyInnerHTML( "<div id='container' style='overflow: scroll; will-change: transform; width: 50px; height: 50px'>" " <span><img style='width: 20px; height: 100px'></span>" " text text text text text text text" "</div>"); LayoutBlock* container = toLayoutBlock(document().getElementById("container")->layoutObject()); LayoutText* text = toLayoutText(container->lastChild()); container->setScrollTop(50); LayoutRect rect(0, 60, 20, 20); text->mapToVisibleRectInAncestorSpace(container, rect, nullptr); EXPECT_TRUE(rect == LayoutRect(0, 10, 20, 20)); }
bool TextAutosizer::clusterHasEnoughTextToAutosize(Cluster* cluster, const LayoutBlock* widthProvider) { if (cluster->m_hasEnoughTextToAutosize != UnknownAmountOfText) return cluster->m_hasEnoughTextToAutosize == HasEnoughText; const LayoutBlock* root = cluster->m_root; if (!widthProvider) widthProvider = clusterWidthProvider(root); // TextAreas and user-modifiable areas get a free pass to autosize regardless of text content. if (root->isTextArea() || (root->style() && root->style()->userModify() != READ_ONLY)) { cluster->m_hasEnoughTextToAutosize = HasEnoughText; return true; } if (cluster->m_flags & SUPPRESSING) { cluster->m_hasEnoughTextToAutosize = NotEnoughText; return false; } // 4 lines of text is considered enough to autosize. float minimumTextLengthToAutosize = widthFromBlock(widthProvider) * 4; float length = 0; LayoutObject* descendant = root->firstChild(); while (descendant) { if (descendant->isLayoutBlock()) { if (classifyBlock(descendant, INDEPENDENT | SUPPRESSING)) { descendant = descendant->nextInPreOrderAfterChildren(root); continue; } } else if (descendant->isText()) { // Note: Using text().stripWhiteSpace().length() instead of resolvedTextLength() because // the lineboxes will not be built until layout. These values can be different. // Note: This is an approximation assuming each character is 1em wide. length += toLayoutText(descendant)->text().stripWhiteSpace().length() * descendant->style()->specifiedFontSize(); if (length >= minimumTextLengthToAutosize) { cluster->m_hasEnoughTextToAutosize = HasEnoughText; return true; } } descendant = descendant->nextInPreOrder(root); } cluster->m_hasEnoughTextToAutosize = NotEnoughText; return false; }
TEST_P(PaintControllerPaintTestForSlimmingPaintV1AndV2, FullDocumentPaintingWithCaret) { setBodyInnerHTML("<div id='div' contentEditable='true' style='outline:none'>XYZ</div>"); document().page()->focusController().setActive(true); document().page()->focusController().setFocused(true); Element& div = *toElement(document().body()->firstChild()); LayoutObject& divLayoutObject = *document().body()->firstChild()->layoutObject(); InlineTextBox& textInlineBox = *toLayoutText(div.firstChild()->layoutObject())->firstTextBox(); EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 2, TestDisplayItem(layoutView(), backgroundType), TestDisplayItem(textInlineBox, foregroundType)); div.focus(); document().view()->updateAllLifecyclePhases(); EXPECT_DISPLAY_LIST(rootPaintController().displayItemList(), 3, TestDisplayItem(layoutView(), backgroundType), TestDisplayItem(textInlineBox, foregroundType), TestDisplayItem(divLayoutObject, DisplayItem::Caret)); // New! }
TEST_F(VisualRectMappingTest, LayoutView) { document().setBaseURLOverride(KURL(ParsedURLString, "http://test.com")); setBodyInnerHTML( "<style>body { margin: 0; }</style>" "<div id=frameContainer>" " <iframe src='http://test.com' width='50' height='50' " "frameBorder='0'></iframe>" "</div>"); setChildFrameHTML( "<style>body { margin: 0; }</style><span><img style='width: 20px; " "height: 100px'></span>text text text"); document().view()->updateAllLifecyclePhases(); LayoutBlock* frameContainer = toLayoutBlock(getLayoutObjectByElementId("frameContainer")); LayoutBlock* frameBody = toLayoutBlock(childDocument().body()->layoutObject()); LayoutText* frameText = toLayoutText(frameBody->lastChild()); // This case involves clipping: frame height is 50, y-coordinate of result // rect is 13, so height should be clipped to (50 - 13) == 37. childDocument().view()->setScrollOffset(ScrollOffset(0, 47), ProgrammaticScroll); LayoutRect originalRect(4, 60, 20, 80); LayoutRect rect = originalRect; EXPECT_TRUE(frameText->mapToVisualRectInAncestorSpace(frameContainer, rect)); EXPECT_EQ(rect, LayoutRect(4, 13, 20, 37)); rect = originalRect; EXPECT_TRUE(frameText->mapToVisualRectInAncestorSpace(&layoutView(), rect)); EXPECT_EQ(rect, LayoutRect(4, 13, 20, 37)); checkPaintInvalidationStateRectMapping(rect, originalRect, *frameText, layoutView(), layoutView()); rect = LayoutRect(4, 60, 0, 80); EXPECT_TRUE(frameText->mapToVisualRectInAncestorSpace(frameContainer, rect, EdgeInclusive)); EXPECT_EQ(rect, LayoutRect(4, 13, 0, 37)); }
void LayoutAnalyzer::push(const LayoutObject& o) { increment(TotalLayoutObjectsThatWereLaidOut); if (!o.everHadLayout()) increment(LayoutObjectsThatHadNeverHadLayout); if (o.selfNeedsLayout()) increment(LayoutObjectsThatNeedLayoutForThemselves); if (o.needsPositionedMovementLayout()) increment(LayoutObjectsThatNeedPositionedMovementLayout); if (o.isOutOfFlowPositioned()) increment(LayoutObjectsThatAreOutOfFlowPositioned); if (o.isTableCell()) increment(LayoutObjectsThatAreTableCells); if (o.isFloating()) increment(LayoutObjectsThatAreFloating); if (o.style()->specifiesColumns()) increment(LayoutObjectsThatSpecifyColumns); if (o.hasLayer()) increment(LayoutObjectsThatHaveALayer); if (o.isLayoutInline() && o.alwaysCreateLineBoxesForLayoutInline()) increment(LayoutInlineObjectsThatAlwaysCreateLineBoxes); if (o.isText()) { const LayoutText& t = *toLayoutText(&o); if (t.canUseSimpleFontCodePath()) { increment(LayoutObjectsThatAreTextAndCanUseTheSimpleFontCodePath); increment(CharactersInLayoutObjectsThatAreTextAndCanUseTheSimpleFontCodePath, t.textLength()); } else { increment(LayoutObjectsThatAreTextAndCanNotUseTheSimpleFontCodePath); increment(CharactersInLayoutObjectsThatAreTextAndCanNotUseTheSimpleFontCodePath, t.textLength()); } } ++m_depth; // This refers to LayoutAnalyzer depth, which is generally closer to C++ // stack recursion depth, not layout tree depth or DOM tree depth. m_counters[LayoutAnalyzerStackMaximumDepth] = max(m_counters[LayoutAnalyzerStackMaximumDepth], m_depth); }
// Bounds of the LayoutObject relative to the scroller's visible content rect. static LayoutRect relativeBounds(const LayoutObject* layoutObject, const ScrollableArea* scroller) { LayoutRect localBounds; if (layoutObject->isBox()) { localBounds = toLayoutBox(layoutObject)->borderBoxRect(); if (!layoutObject->hasOverflowClip()) { // borderBoxRect doesn't include overflow content and floats. LayoutUnit maxHeight = std::max(localBounds.height(), toLayoutBox(layoutObject)->layoutOverflowRect().height()); if (layoutObject->isLayoutBlockFlow() && toLayoutBlockFlow(layoutObject)->containsFloats()) { // Note that lowestFloatLogicalBottom doesn't include floating // grandchildren. maxHeight = std::max( maxHeight, toLayoutBlockFlow(layoutObject)->lowestFloatLogicalBottom()); } localBounds.setHeight(maxHeight); } } else if (layoutObject->isText()) { // TODO(skobes): Use first and last InlineTextBox only? for (InlineTextBox* box = toLayoutText(layoutObject)->firstTextBox(); box; box = box->nextTextBox()) localBounds.unite(box->frameRect()); } else { // Only LayoutBox and LayoutText are supported. ASSERT_NOT_REACHED(); } LayoutRect relativeBounds = LayoutRect( scroller->localToVisibleContentQuad(FloatRect(localBounds), layoutObject) .boundingBox()); return relativeBounds; }
static bool blockIsRowOfLinks(const LayoutBlock* block) { // A "row of links" is a block for which: // 1. It does not contain non-link text elements longer than 3 characters // 2. It contains a minimum of 3 inline links and all links should // have the same specified font size. // 3. It should not contain <br> elements. // 4. It should contain only inline elements unless they are containers, // children of link elements or children of sub-containers. int linkCount = 0; LayoutObject* layoutObject = block->firstChild(); float matchingFontSize = -1; while (layoutObject) { if (!isPotentialClusterRoot(layoutObject)) { if (layoutObject->isText() && toLayoutText(layoutObject)->text().stripWhiteSpace().length() > 3) return false; if (!layoutObject->isInline() || layoutObject->isBR()) return false; } if (layoutObject->style()->isLink()) { linkCount++; if (matchingFontSize < 0) matchingFontSize = layoutObject->style()->specifiedFontSize(); else if (matchingFontSize != layoutObject->style()->specifiedFontSize()) return false; // Skip traversing descendants of the link. layoutObject = layoutObject->nextInPreOrderAfterChildren(block); continue; } layoutObject = layoutObject->nextInPreOrder(block); } return (linkCount >= 3); }
void LineBoxList::dirtyLinesFromChangedChild(LayoutObject* container, LayoutObject* child) { if (!container->parent() || (container->isLayoutBlock() && (container->selfNeedsLayout() || !container->isLayoutBlockFlow()))) return; LayoutInline* inlineContainer = container->isLayoutInline() ? toLayoutInline(container) : 0; InlineBox* firstBox = inlineContainer ? inlineContainer->firstLineBoxIncludingCulling() : firstLineBox(); // If we have no first line box, then just bail early. if (!firstBox) { // For an empty inline, go ahead and propagate the check up to our parent, unless the parent // is already dirty. if (container->isInline() && !container->ancestorLineBoxDirty()) { container->parent()->dirtyLinesFromChangedChild(container); container->setAncestorLineBoxDirty(); // Mark the container to avoid dirtying the same lines again across multiple destroy() calls of the same subtree. } return; } // Try to figure out which line box we belong in. First try to find a previous // line box by examining our siblings. If we didn't find a line box, then use our // parent's first line box. RootInlineBox* box = 0; LayoutObject* curr = 0; ListHashSet<LayoutObject*, 16> potentialLineBreakObjects; potentialLineBreakObjects.add(child); for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) { potentialLineBreakObjects.add(curr); if (curr->isFloatingOrOutOfFlowPositioned()) continue; if (curr->isReplaced()) { InlineBox* wrapper = toLayoutBox(curr)->inlineBoxWrapper(); if (wrapper) box = &wrapper->root(); } else if (curr->isText()) { InlineTextBox* textBox = toLayoutText(curr)->lastTextBox(); if (textBox) box = &textBox->root(); } else if (curr->isLayoutInline()) { InlineBox* lastSiblingBox = toLayoutInline(curr)->lastLineBoxIncludingCulling(); if (lastSiblingBox) box = &lastSiblingBox->root(); } if (box) break; } if (!box) { if (inlineContainer && !inlineContainer->alwaysCreateLineBoxes()) { // https://bugs.webkit.org/show_bug.cgi?id=60778 // We may have just removed a <br> with no line box that was our first child. In this case // we won't find a previous sibling, but firstBox can be pointing to a following sibling. // This isn't good enough, since we won't locate the root line box that encloses the removed // <br>. We have to just over-invalidate a bit and go up to our parent. if (!inlineContainer->ancestorLineBoxDirty()) { inlineContainer->parent()->dirtyLinesFromChangedChild(inlineContainer); inlineContainer->setAncestorLineBoxDirty(); // Mark the container to avoid dirtying the same lines again across multiple destroy() calls of the same subtree. } return; } box = &firstBox->root(); } // If we found a line box, then dirty it. if (box) { RootInlineBox* adjacentBox; box->markDirty(); // dirty the adjacent lines that might be affected // NOTE: we dirty the previous line because RootInlineBox objects cache // the address of the first object on the next line after a BR, which we may be // invalidating here. For more info, see how LayoutBlock::layoutInlineChildren // calls setLineBreakInfo with the result of findNextLineBreak. findNextLineBreak, // despite the name, actually returns the first LayoutObject after the BR. // <rdar://problem/3849947> "Typing after pasting line does not appear until after window resize." adjacentBox = box->prevRootBox(); if (adjacentBox) adjacentBox->markDirty(); adjacentBox = box->nextRootBox(); // If |child| or any of its immediately previous siblings with culled lineboxes is the object after a line-break in |box| or the linebox after it // then that means |child| actually sits on the linebox after |box| (or is its line-break object) and so we need to dirty it as well. if (adjacentBox && (potentialLineBreakObjects.contains(box->lineBreakObj()) || potentialLineBreakObjects.contains(adjacentBox->lineBreakObj()) || child->isBR() || isIsolated(container->style()->unicodeBidi()))) adjacentBox->markDirty(); } }