float RootInlineBox::placeEllipsis(const AtomicString& ellipsisStr, bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, InlineBox* markupBox) { if (!gEllipsisBoxMap) gEllipsisBoxMap = new EllipsisBoxMap(); // Create an ellipsis box. auto newEllipsisBox = std::make_unique<EllipsisBox>(blockFlow(), ellipsisStr, this, ellipsisWidth - (markupBox ? markupBox->logicalWidth() : 0), logicalHeight(), y(), !prevRootBox(), isHorizontal(), markupBox); auto ellipsisBox = newEllipsisBox.get(); gEllipsisBoxMap->add(this, std::move(newEllipsisBox)); setHasEllipsisBox(true); // FIXME: Do we need an RTL version of this? if (ltr && (x() + logicalWidth() + ellipsisWidth) <= blockRightEdge) { ellipsisBox->setX(x() + logicalWidth()); return logicalWidth() + ellipsisWidth; } // Now attempt to find the nearest glyph horizontally and place just to the right (or left in RTL) // of that glyph. Mark all of the objects that intersect the ellipsis box as not painting (as being // truncated). bool foundBox = false; float truncatedWidth = 0; float position = placeEllipsisBox(ltr, blockLeftEdge, blockRightEdge, ellipsisWidth, truncatedWidth, foundBox); ellipsisBox->setX(position); return truncatedWidth; }
LayoutUnit RenderMathMLFraction::horizontalOffset(RenderBox& child, MathMLFractionElement::FractionAlignment align) { switch (align) { case MathMLFractionElement::FractionAlignmentRight: return LayoutUnit(logicalWidth() - child.logicalWidth()); case MathMLFractionElement::FractionAlignmentCenter: return LayoutUnit((logicalWidth() - child.logicalWidth()) / 2); case MathMLFractionElement::FractionAlignmentLeft: return LayoutUnit(0); } ASSERT_NOT_REACHED(); return LayoutUnit(0); }
void RenderMathMLPadded::layoutBlock(bool relayoutChildren, LayoutUnit) { ASSERT(needsLayout()); if (!relayoutChildren && simplifiedLayout()) return; // We first layout our children as a normal <mrow> element. LayoutUnit contentAscent, contentDescent, contentWidth; contentAscent = contentDescent = 0; RenderMathMLRow::computeLineVerticalStretch(contentAscent, contentDescent); RenderMathMLRow::layoutRowItems(contentAscent, contentDescent); contentWidth = logicalWidth(); // We parse the mpadded attributes using the content metrics as the default value. LayoutUnit width = contentWidth; LayoutUnit ascent = contentAscent; LayoutUnit descent = contentDescent; LayoutUnit lspace = 0; LayoutUnit voffset = 0; resolveAttributes(width, ascent, descent, lspace, voffset); // Align children on the new baseline and shift them by (lspace, -voffset) LayoutPoint contentLocation(lspace, ascent - contentAscent - voffset); for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) child->setLocation(child->location() + contentLocation); // Set the final metrics. setLogicalWidth(width); m_ascent = ascent; setLogicalHeight(ascent + descent); clearNeedsLayout(); }
VisiblePosition RenderReplaced::positionForPoint(const LayoutPoint& point, const RenderRegion* region) { // FIXME: This code is buggy if the replaced element is relative positioned. InlineBox* box = inlineBoxWrapper(); const RootInlineBox* rootBox = box ? &box->root() : 0; LayoutUnit top = rootBox ? rootBox->selectionTop() : logicalTop(); LayoutUnit bottom = rootBox ? rootBox->selectionBottom() : logicalBottom(); LayoutUnit blockDirectionPosition = isHorizontalWritingMode() ? point.y() + y() : point.x() + x(); LayoutUnit lineDirectionPosition = isHorizontalWritingMode() ? point.x() + x() : point.y() + y(); if (blockDirectionPosition < top) return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above if (blockDirectionPosition >= bottom) return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below if (element()) { if (lineDirectionPosition <= logicalLeft() + (logicalWidth() / 2)) return createVisiblePosition(0, DOWNSTREAM); return createVisiblePosition(1, DOWNSTREAM); } return RenderBox::positionForPoint(point, region); }
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { GraphicsContext* context = paintInfo.context; RenderStyle* style = renderer().style(isFirstLineStyle()); const Font& font = style->font(); FloatPoint boxOrigin = locationIncludingFlipping(); boxOrigin.moveBy(FloatPoint(paintOffset)); if (!isHorizontal()) boxOrigin.move(0, -virtualLogicalHeight()); FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), virtualLogicalHeight())); GraphicsContextStateSaver stateSaver(*context); if (!isHorizontal()) context->concatCTM(InlineTextBox::rotation(boxRect, InlineTextBox::Clockwise)); FloatPoint textOrigin(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent()); bool isPrinting = renderer().document().printing(); bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone; if (haveSelection) paintSelection(context, boxOrigin, style, font); else if (paintInfo.phase == PaintPhaseSelection) return; TextPainter::Style textStyle = TextPainter::textPaintingStyle(renderer(), style, paintInfo.forceBlackText(), isPrinting); if (haveSelection) textStyle = TextPainter::selectionPaintingStyle(renderer(), true, paintInfo.forceBlackText(), isPrinting, textStyle); TextRun textRun = constructTextRun(&renderer(), font, m_str, style, TextRun::AllowTrailingExpansion); TextPainter textPainter(context, font, textRun, textOrigin, boxRect, isHorizontal()); textPainter.paint(0, m_str.length(), m_str.length(), textStyle); paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, style); }
PositionWithAffinity LayoutReplaced::positionForPoint(const LayoutPoint& point) { // FIXME: This code is buggy if the replaced element is relative positioned. InlineBox* box = inlineBoxWrapper(); RootInlineBox* rootBox = box ? &box->root() : 0; LayoutUnit top = rootBox ? rootBox->selectionTop() : logicalTop(); LayoutUnit bottom = rootBox ? rootBox->selectionBottom() : logicalBottom(); LayoutUnit blockDirectionPosition = isHorizontalWritingMode() ? point.y() + location().y() : point.x() + location().x(); LayoutUnit lineDirectionPosition = isHorizontalWritingMode() ? point.x() + location().x() : point.y() + location().y(); if (blockDirectionPosition < top) return createPositionWithAffinity(caretMinOffset()); // coordinates are above if (blockDirectionPosition >= bottom) return createPositionWithAffinity(caretMaxOffset()); // coordinates are below if (node()) { if (lineDirectionPosition <= logicalLeft() + (logicalWidth() / 2)) return createPositionWithAffinity(0); return createPositionWithAffinity(1); } return LayoutBox::positionForPoint(point); }
bool LayoutMultiColumnFlowThread::needsNewWidth() const { LayoutUnit newWidth; unsigned dummyColumnCount; // We only care if used column-width changes. calculateColumnCountAndWidth(newWidth, dummyColumnCount); return newWidth != logicalWidth(); }
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { GraphicsContext* context = paintInfo.context; RenderStyle* style = m_renderer->style(isFirstLineStyle()); Color styleTextColor = style->visitedDependentColor(CSSPropertyWebkitTextFillColor); if (styleTextColor != context->fillColor()) context->setFillColor(styleTextColor, style->colorSpace()); Color textColor = styleTextColor; const Font& font = style->font(); if (selectionState() != RenderObject::SelectionNone) { paintSelection(context, paintOffset, style, font); // Select the correct color for painting the text. Color foreground = paintInfo.forceBlackText() ? Color::black : renderer()->selectionForegroundColor(); if (foreground.isValid() && foreground != styleTextColor) context->setFillColor(foreground, style->colorSpace()); } const ShadowData* shadow = style->textShadow(); bool hasShadow = shadow; if (hasShadow) { // FIXME: it would be better if we could get the shadows top-to-bottom from the style. Vector<const ShadowData*, 4> shadows; do { shadows.append(shadow); } while ((shadow = shadow->next())); DrawLooper drawLooper; drawLooper.addUnmodifiedContent(); for (int i = shadows.size() - 1; i >= 0; i--) { shadow = shadows[i]; int shadowX = isHorizontal() ? shadow->x() : shadow->y(); int shadowY = isHorizontal() ? shadow->y() : -shadow->x(); FloatSize offset(shadowX, shadowY); drawLooper.addShadow(offset, shadow->blur(), shadow->color(), DrawLooper::ShadowRespectsTransforms, DrawLooper::ShadowIgnoresAlpha); } context->setDrawLooper(drawLooper); } // FIXME: Why is this always LTR? Fix by passing correct text run flags below. FloatPoint boxOrigin(paintOffset); boxOrigin.move(x(), y()); FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), logicalHeight())); FloatPoint textOrigin(boxOrigin.x(), boxOrigin.y() + style->fontMetrics().ascent()); TextRun textRun = RenderBlock::constructTextRun(renderer(), font, m_str, style, TextRun::AllowTrailingExpansion); TextRunPaintInfo textRunPaintInfo(textRun); textRunPaintInfo.bounds = boxRect; context->drawText(font, textRunPaintInfo, textOrigin); // Restore the regular fill color. if (styleTextColor != context->fillColor()) context->setFillColor(styleTextColor, style->colorSpace()); if (hasShadow) context->clearDrawLooper(); paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, style); }
void RenderFlowThread::updateRegionsFlowThreadPortionRect() { LayoutUnit logicalHeight = 0; for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; LayoutUnit regionLogicalWidth = region->pageLogicalWidth(); LayoutUnit regionLogicalHeight = region->logicalHeightOfAllFlowThreadContent(); LayoutRect regionRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - regionLogicalWidth, logicalHeight, regionLogicalWidth, regionLogicalHeight); // When a flow thread has more than one auto logical height region, // we have to take into account the override logical content height value, // if computed for an auto logical height region, and use it to set the height // for the region rect. This way, the regions in the chain following the auto // logical height region, will be able to fragment the right part of their // associated flow thread content (and compute their overrideComputedLogicalHeight properly). if (view()->normalLayoutPhase()) { ASSERT(region->hasOverrideHeight() || !region->hasAutoLogicalHeight()); if (region->hasOverrideHeight()) regionLogicalHeight = region->overrideLogicalContentHeight(); regionRect.setHeight(regionLogicalHeight); } region->setFlowThreadPortionRect(isHorizontalWritingMode() ? regionRect : regionRect.transposedRect()); logicalHeight += regionLogicalHeight; } }
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { GraphicsContext* context = paintInfo.context; RenderStyle* style = renderer().style(isFirstLineStyle()); const Font& font = style->font(); FloatPoint boxOrigin = locationIncludingFlipping(); boxOrigin.moveBy(FloatPoint(paintOffset)); if (!isHorizontal()) boxOrigin.move(0, -virtualLogicalHeight()); FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), virtualLogicalHeight())); GraphicsContextStateSaver stateSaver(*context); if (!isHorizontal()) context->concatCTM(InlineTextBox::rotation(boxRect, InlineTextBox::Clockwise)); FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent()); Color styleTextColor = renderer().resolveColor(style, CSSPropertyWebkitTextFillColor); if (styleTextColor != context->fillColor()) context->setFillColor(styleTextColor); if (selectionState() != RenderObject::SelectionNone) { paintSelection(context, boxOrigin, style, font); // Select the correct color for painting the text. Color foreground = paintInfo.forceBlackText() ? Color::black : renderer().selectionForegroundColor(); if (foreground != styleTextColor) context->setFillColor(foreground); } // Text shadows are disabled when printing. http://crbug.com/258321 const ShadowList* shadowList = context->printing() ? 0 : style->textShadow(); bool hasShadow = shadowList; if (hasShadow) { OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create(); for (size_t i = shadowList->shadows().size(); i--; ) { const ShadowData& shadow = shadowList->shadows()[i]; float shadowX = isHorizontal() ? shadow.x() : shadow.y(); float shadowY = isHorizontal() ? shadow.y() : -shadow.x(); FloatSize offset(shadowX, shadowY); drawLooperBuilder->addShadow(offset, shadow.blur(), shadow.color(), DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha); } drawLooperBuilder->addUnmodifiedContent(); context->setDrawLooper(drawLooperBuilder.release()); } TextRun textRun = RenderBlockFlow::constructTextRun(&renderer(), font, m_str, style, TextRun::AllowTrailingExpansion); TextRunPaintInfo textRunPaintInfo(textRun); textRunPaintInfo.bounds = boxRect; context->drawText(font, textRunPaintInfo, textOrigin); // Restore the regular fill color. if (styleTextColor != context->fillColor()) context->setFillColor(styleTextColor); if (hasShadow) context->clearDrawLooper(); paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, style); }
void RenderView::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; if (!document().paginated()) setPageLogicalHeight(0); if (shouldUsePrintingLayout()) m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = logicalWidth(); // Use calcWidth/Height to get the new width/height, since this will take the full page zoom factor into account. bool relayoutChildren = !shouldUsePrintingLayout() && (width() != viewWidth() || height() != viewHeight()); if (relayoutChildren) { setChildNeedsLayout(MarkOnlyThis); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { if (!child->isBox()) continue; RenderBox& box = toRenderBox(*child); if (box.hasRelativeLogicalHeight() || box.hasViewportPercentageLogicalHeight() || box.style()->logicalHeight().isPercent() || box.style()->logicalMinHeight().isPercent() || box.style()->logicalMaxHeight().isPercent() || box.style()->logicalHeight().isViewportPercentage() || box.style()->logicalMinHeight().isViewportPercentage() || box.style()->logicalMaxHeight().isViewportPercentage() #if ENABLE(SVG) || box.isSVGRoot() #endif ) box.setChildNeedsLayout(MarkOnlyThis); } } ASSERT(!m_layoutState); if (!needsLayout()) return; m_layoutState = std::make_unique<LayoutState>(); bool isSeamlessAncestorInFlowThread = initializeLayoutState(*m_layoutState); m_pageLogicalHeightChanged = false; if (checkTwoPassLayoutForAutoHeightRegions()) layoutContentInAutoLogicalHeightRegions(*m_layoutState); else layoutContent(*m_layoutState); layoutContentToComputeOverflowInRegions(*m_layoutState); #ifndef NDEBUG checkLayoutState(*m_layoutState); #endif m_layoutState = nullptr; clearNeedsLayout(); if (isSeamlessAncestorInFlowThread) flowThreadController().setCurrentRenderFlowThread(0); }
void RenderView::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; if (!document()->paginated()) setPageLogicalHeight(0); if (shouldUsePrintingLayout()) m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = logicalWidth(); // Use calcWidth/Height to get the new width/height, since this will take the full page zoom factor into account. bool relayoutChildren = !shouldUsePrintingLayout() && (!m_frameView || width() != viewWidth() || height() != viewHeight()); if (relayoutChildren) { setChildNeedsLayout(true, MarkOnlyThis); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { if ((child->isBox() && toRenderBox(child)->hasRelativeLogicalHeight()) || child->style()->logicalHeight().isPercent() || child->style()->logicalMinHeight().isPercent() || child->style()->logicalMaxHeight().isPercent()) child->setChildNeedsLayout(true, MarkOnlyThis); } } ASSERT(!m_layoutState); if (!needsLayout()) return; LayoutState state; // FIXME: May be better to push a clip and avoid issuing offscreen repaints. state.m_clipped = false; state.m_pageLogicalHeight = m_pageLogicalHeight; state.m_pageLogicalHeightChanged = m_pageLogicalHeightChanged; state.m_isPaginated = state.m_pageLogicalHeight; m_pageLogicalHeightChanged = false; m_layoutState = &state; m_layoutPhase = RenderViewNormalLayout; bool needsTwoPassLayoutForAutoLogicalHeightRegions = hasRenderNamedFlowThreads() && flowThreadController()->hasAutoLogicalHeightRegions() && flowThreadController()->hasRenderNamedFlowThreadsNeedingLayout(); if (needsTwoPassLayoutForAutoLogicalHeightRegions) flowThreadController()->resetRegionsOverrideLogicalContentHeight(); layoutContent(state); if (needsTwoPassLayoutForAutoLogicalHeightRegions) { m_layoutPhase = ConstrainedFlowThreadsLayoutInAutoLogicalHeightRegions; flowThreadController()->markAutoLogicalHeightRegionsForLayout(); layoutContent(state); } #ifndef NDEBUG checkLayoutState(state); #endif m_layoutState = 0; setNeedsLayout(false); }
int LayoutTextControl::textBlockLogicalWidth() const { Element* innerEditor = innerEditorElement(); ASSERT(innerEditor); LayoutUnit unitWidth = logicalWidth() - borderAndPaddingLogicalWidth(); if (innerEditor->layoutObject()) unitWidth -= innerEditor->layoutBox()->paddingStart() + innerEditor->layoutBox()->paddingEnd(); return unitWidth.toInt(); }
bool RootInlineBox::lineCanAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth) { // First sanity-check the unoverflowed width of the whole line to see if there is sufficient room. int delta = ltr ? lineBoxEdge - blockEdge : blockEdge - lineBoxEdge; if (logicalWidth() - delta < ellipsisWidth) return false; // Next iterate over all the line boxes on the line. If we find a replaced element that intersects // then we refuse to accommodate the ellipsis. Otherwise we're ok. return InlineFlowBox::canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth); }
int RenderTextControl::textBlockLogicalWidth() const { TextControlInnerTextElement* innerText = innerTextElement(); ASSERT(innerText); LayoutUnit unitWidth = logicalWidth() - borderAndPaddingLogicalWidth(); if (innerText->renderer()) unitWidth -= innerText->renderBox()->paddingStart() + innerText->renderBox()->paddingEnd(); return unitWidth; }
bool SVGInlineTextBox::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit, LayoutUnit) { // FIXME: integrate with InlineTextBox::nodeAtPoint better. ASSERT(!isLineBreak()); PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, result.hitTestRequest(), getLineLayoutItem().style()->pointerEvents()); bool isVisible = getLineLayoutItem().style()->visibility() == EVisibility::Visible; if (isVisible || !hitRules.requireVisible) { if (hitRules.canHitBoundingBox || (hitRules.canHitStroke && (getLineLayoutItem().style()->svgStyle().hasStroke() || !hitRules.requireStroke)) || (hitRules.canHitFill && (getLineLayoutItem().style()->svgStyle().hasFill() || !hitRules.requireFill))) { LayoutRect rect(topLeft(), LayoutSize(logicalWidth(), logicalHeight())); rect.moveBy(accumulatedOffset); if (locationInContainer.intersects(rect)) { LineLayoutSVGInlineText lineLayoutItem = LineLayoutSVGInlineText(this->getLineLayoutItem()); const SimpleFontData* fontData = lineLayoutItem.scaledFont().primaryFont(); DCHECK(fontData); if (!fontData) return false; DCHECK(lineLayoutItem.scalingFactor()); float baseline = fontData->getFontMetrics().floatAscent() / lineLayoutItem.scalingFactor(); FloatPoint floatLocation = FloatPoint(locationInContainer.point()); for (const SVGTextFragment& fragment : m_textFragments) { FloatQuad fragmentQuad = fragment.boundingQuad(baseline); if (fragmentQuad.containsPoint(floatLocation)) { lineLayoutItem.updateHitTestResult( result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); if (result.addNodeToListBasedTestResult(lineLayoutItem.node(), locationInContainer, rect) == StopHitTesting) return true; } } } } } return false; }
IntRect RootInlineBox::computeCaretRect(float logicalLeftPosition, unsigned caretWidth, LayoutUnit* extraWidthToEndOfLine) const { int height = selectionHeight(); int top = selectionTop(); // Distribute the caret's width to either side of the offset. float left = logicalLeftPosition; int caretWidthLeftOfOffset = caretWidth / 2; left -= caretWidthLeftOfOffset; int caretWidthRightOfOffset = caretWidth - caretWidthLeftOfOffset; left = roundf(left); float rootLeft = logicalLeft(); float rootRight = logicalRight(); if (extraWidthToEndOfLine) *extraWidthToEndOfLine = (logicalWidth() + rootLeft) - (left + caretWidth); const RenderStyle& blockStyle = blockFlow().style(); bool rightAligned = false; switch (blockStyle.textAlign()) { case RIGHT: case WEBKIT_RIGHT: rightAligned = true; break; case LEFT: case WEBKIT_LEFT: case CENTER: case WEBKIT_CENTER: break; case JUSTIFY: case TASTART: rightAligned = !blockStyle.isLeftToRightDirection(); break; case TAEND: rightAligned = blockStyle.isLeftToRightDirection(); break; } float leftEdge = std::min<float>(0, rootLeft); float rightEdge = std::max<float>(blockFlow().logicalWidth(), rootRight); if (rightAligned) { left = std::max(left, leftEdge); left = std::min(left, rootRight - caretWidth); } else { left = std::min(left, rightEdge - caretWidthRightOfOffset); left = std::max(left, rootLeft); } return blockStyle.isHorizontalWritingMode() ? IntRect(left, top, caretWidth, height) : IntRect(top, left, height, caretWidth); }
void RenderView::layout() { if (!document().paginated()) setPageLogicalHeight(0); if (shouldUsePrintingLayout()) m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = logicalWidth(); SubtreeLayoutScope layoutScope(this); // Use calcWidth/Height to get the new width/height, since this will take the full page zoom factor into account. bool relayoutChildren = !shouldUsePrintingLayout() && (!m_frameView || width() != viewWidth() || height() != viewHeight()); if (relayoutChildren) { layoutScope.setChildNeedsLayout(this); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { if (child->isSVGRoot()) continue; if ((child->isBox() && toRenderBox(child)->hasRelativeLogicalHeight()) || child->style()->logicalHeight().isPercent() || child->style()->logicalMinHeight().isPercent() || child->style()->logicalMaxHeight().isPercent()) layoutScope.setChildNeedsLayout(child); } if (document().svgExtensions()) document().accessSVGExtensions()->invalidateSVGRootsWithRelativeLengthDescendents(&layoutScope); } ASSERT(!m_layoutState); if (!needsLayout()) return; LayoutState state; initializeLayoutState(state); m_pageLogicalHeightChanged = false; m_layoutState = &state; layoutContent(state); if (m_frameView->partialLayout().isStopping()) { m_layoutState = 0; return; } #ifndef NDEBUG checkLayoutState(state); #endif m_layoutState = 0; clearNeedsLayout(); }
int InlineTextBox::offsetForPosition(LayoutUnit lineOffset, bool includePartialGlyphs) const { if (isLineBreak()) return 0; if (lineOffset - logicalLeft() > logicalWidth()) return isLeftToRightDirection() ? len() : 0; if (lineOffset - logicalLeft() < 0) return isLeftToRightDirection() ? 0 : len(); LineLayoutText text = lineLayoutItem(); const ComputedStyle& style = text.styleRef(isFirstLineStyle()); const Font& font = style.font(); return font.offsetForPosition(constructTextRun(style, font), (lineOffset - logicalLeft()).toFloat(), includePartialGlyphs); }
void RenderMathMLRoot::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty() && needsLayout()); #ifndef NDEBUG // FIXME: Remove this once mathml stops modifying the render tree here. SetLayoutNeededForbiddenScope layoutForbiddenScope(this, false); #endif computeChildrenPreferredLogicalHeights(); int baseHeight = firstChild() ? roundToInt(preferredLogicalHeightAfterSizing(firstChild())) : style()->fontSize(); int frontWidth = lroundf(gFrontWidthEms * style()->fontSize()); // Base height above which the shape of the root changes float thresholdHeight = gThresholdBaseHeightEms * style()->fontSize(); if (baseHeight > thresholdHeight && thresholdHeight) { float shift = min<float>((baseHeight - thresholdHeight) / thresholdHeight, 1.0f); m_overbarLeftPointShift = static_cast<int>(shift * gRadicalBottomPointXFront * frontWidth); m_intrinsicPaddingAfter = lroundf(gBigRootBottomPaddingEms * style()->fontSize()); } else { m_overbarLeftPointShift = 0; m_intrinsicPaddingAfter = 0; } int rootPad = lroundf(gSpaceAboveEms * style()->fontSize()); m_intrinsicPaddingBefore = rootPad; m_indexTop = 0; if (RenderBoxModelObject* index = this->index()) { m_intrinsicPaddingStart = roundToInt(index->maxPreferredLogicalWidth()) + m_overbarLeftPointShift; int indexHeight = roundToInt(preferredLogicalHeightAfterSizing(index)); int partDipHeight = lroundf((1 - gRootRadicalDipLeftPointYPos) * baseHeight); int rootExtraTop = partDipHeight + indexHeight - (baseHeight + rootPad); if (rootExtraTop > 0) m_intrinsicPaddingBefore += rootExtraTop; else m_indexTop = - rootExtraTop; } else m_intrinsicPaddingStart = frontWidth; RenderMathMLBlock::computePreferredLogicalWidths(); // Shrink our logical width to its probable value now without triggering unnecessary relayout of our children. ASSERT(needsLayout() && logicalWidth() >= maxPreferredLogicalWidth()); setLogicalWidth(maxPreferredLogicalWidth()); }
VisiblePosition RenderTextLineBoxes::positionForPoint(const RenderText& renderer, const LayoutPoint& point) const { if (!m_first || !renderer.textLength()) return renderer.createVisiblePosition(0, DOWNSTREAM); LayoutUnit pointLineDirection = m_first->isHorizontal() ? point.x() : point.y(); LayoutUnit pointBlockDirection = m_first->isHorizontal() ? point.y() : point.x(); bool blocksAreFlipped = renderer.style().isFlippedBlocksWritingMode(); InlineTextBox* lastBox = nullptr; for (auto box = m_first; box; box = box->nextTextBox()) { if (box->isLineBreak() && !box->prevLeafChild() && box->nextLeafChild() && !box->nextLeafChild()->isLineBreak()) box = box->nextTextBox(); auto& rootBox = box->root(); LayoutUnit top = std::min(rootBox.selectionTop(), rootBox.lineTop()); if (pointBlockDirection > top || (!blocksAreFlipped && pointBlockDirection == top)) { LayoutUnit bottom = rootBox.selectionBottom(); if (rootBox.nextRootBox()) bottom = std::min(bottom, rootBox.nextRootBox()->lineTop()); if (pointBlockDirection < bottom || (blocksAreFlipped && pointBlockDirection == bottom)) { ShouldAffinityBeDownstream shouldAffinityBeDownstream; #if PLATFORM(IOS) if (pointLineDirection != box->logicalLeft() && point.x() < box->x() + box->logicalWidth()) { int half = box->x() + box->logicalWidth() / 2; EAffinity affinity = point.x() < half ? DOWNSTREAM : VP_UPSTREAM_IF_POSSIBLE; return renderer.createVisiblePosition(box->offsetForPosition(pointLineDirection) + box->start(), affinity); } #endif if (lineDirectionPointFitsInBox(pointLineDirection, *box, shouldAffinityBeDownstream)) return createVisiblePositionAfterAdjustingOffsetForBiDi(*box, box->offsetForPosition(pointLineDirection), shouldAffinityBeDownstream); } } lastBox = box; } if (lastBox) { ShouldAffinityBeDownstream shouldAffinityBeDownstream; lineDirectionPointFitsInBox(pointLineDirection, *lastBox, shouldAffinityBeDownstream); return createVisiblePositionAfterAdjustingOffsetForBiDi(*lastBox, lastBox->offsetForPosition(pointLineDirection) + lastBox->start(), shouldAffinityBeDownstream); } return renderer.createVisiblePosition(0, DOWNSTREAM); }
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { GraphicsContext* context = paintInfo.context; RenderStyle* style = renderer().style(isFirstLineStyle()); const Font& font = style->font(); FloatPoint boxOrigin = locationIncludingFlipping(); boxOrigin.moveBy(FloatPoint(paintOffset)); FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), virtualLogicalHeight())); GraphicsContextStateSaver stateSaver(*context); FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent()); Color styleTextColor = renderer().resolveColor(style, CSSPropertyWebkitTextFillColor); if (styleTextColor != context->fillColor()) context->setFillColor(styleTextColor); if (selectionState() != RenderObject::SelectionNone) { paintSelection(context, boxOrigin, style, font); // Select the correct color for painting the text. Color foreground = paintInfo.forceBlackText() ? Color::black : renderer().selectionForegroundColor(); if (foreground != styleTextColor) context->setFillColor(foreground); } const ShadowList* shadowList = style->textShadow(); bool hasShadow = shadowList; if (hasShadow) context->setDrawLooper(shadowList->createDrawLooper(DrawLooperBuilder::ShadowIgnoresAlpha)); TextRun textRun = constructTextRun(&renderer(), font, m_str, style, TextRun::AllowTrailingExpansion); TextRunPaintInfo textRunPaintInfo(textRun); textRunPaintInfo.bounds = boxRect; context->drawText(font, textRunPaintInfo, textOrigin); // Restore the regular fill color. if (styleTextColor != context->fillColor()) context->setFillColor(styleTextColor); if (hasShadow) context->clearDrawLooper(); paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, style); }
void RenderView::layout() { if (!document()->paginated()) setPageLogicalHeight(0); if (shouldUsePrintingLayout()) m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = logicalWidth(); // Use calcWidth/Height to get the new width/height, since this will take the full page zoom factor into account. bool relayoutChildren = !shouldUsePrintingLayout() && (!m_frameView || width() != viewWidth() || height() != viewHeight()); if (relayoutChildren) { setChildNeedsLayout(true, MarkOnlyThis); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { if ((child->isBox() && toRenderBox(child)->hasRelativeLogicalHeight()) || child->style()->logicalHeight().isPercent() || child->style()->logicalMinHeight().isPercent() || child->style()->logicalMaxHeight().isPercent()) child->setChildNeedsLayout(true, MarkOnlyThis); } } ASSERT(!m_layoutState); LayoutState state; // FIXME: May be better to push a clip and avoid issuing offscreen repaints. state.m_clipped = false; state.m_pageLogicalHeight = m_pageLogicalHeight; state.m_pageLogicalHeightChanged = m_pageLogicalHeightChanged; state.m_isPaginated = state.m_pageLogicalHeight; m_pageLogicalHeightChanged = false; m_layoutState = &state; if (needsLayout()) { RenderBlock::layout(); if (hasRenderNamedFlowThreads()) flowThreadController()->layoutRenderNamedFlowThreads(); } ASSERT(layoutDelta() == LayoutSize()); ASSERT(m_layoutStateDisableCount == 0); ASSERT(m_layoutState == &state); m_layoutState = 0; setNeedsLayout(false); }
void RenderFlowThread::updateRegionsFlowThreadPortionRect() { LayoutUnit logicalHeight = 0; // FIXME: Optimize not to clear the interval all the time. This implies manually managing the tree nodes lifecycle. m_multiColumnSetIntervalTree.clear(); m_multiColumnSetIntervalTree.initIfNeeded(); for (RenderMultiColumnSetList::iterator iter = m_multiColumnSetList.begin(); iter != m_multiColumnSetList.end(); ++iter) { RenderMultiColumnSet* columnSet = *iter; LayoutUnit columnSetLogicalWidth = columnSet->pageLogicalWidth(); LayoutUnit columnSetLogicalHeight = std::min<LayoutUnit>(RenderFlowThread::maxLogicalHeight() - logicalHeight, columnSet->logicalHeightOfAllFlowThreadContent()); LayoutRect columnSetRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - columnSetLogicalWidth, logicalHeight, columnSetLogicalWidth, columnSetLogicalHeight); columnSet->setFlowThreadPortionRect(isHorizontalWritingMode() ? columnSetRect : columnSetRect.transposedRect()); m_multiColumnSetIntervalTree.add(MultiColumnSetIntervalTree::createInterval(logicalHeight, logicalHeight + columnSetLogicalHeight, columnSet)); logicalHeight += columnSetLogicalHeight; } }
void RenderMultiColumnSet::updateLogicalWidth() { RenderMultiColumnFlowThread* flowThread = multiColumnFlowThread(); setComputedColumnWidthAndCount(flowThread->columnWidth(), flowThread->columnCount()); // FIXME: When we add regions support, we'll start it off at the width of the multi-column // block in that particular region. setLogicalWidth(parentBox()->contentLogicalWidth()); // If we overflow, increase our logical width. unsigned colCount = columnCount(); LayoutUnit colGap = columnGap(); LayoutUnit minimumContentLogicalWidth = colCount * computedColumnWidth() + (colCount - 1) * colGap; LayoutUnit currentContentLogicalWidth = contentLogicalWidth(); LayoutUnit delta = max(LayoutUnit(), minimumContentLogicalWidth - currentContentLogicalWidth); if (!delta) return; // Increase our logical width by the delta. setLogicalWidth(logicalWidth() + delta); }
void RenderMultiColumnSet::updateLogicalWidth() { RenderMultiColumnBlock* parentBlock = toRenderMultiColumnBlock(parent()); setComputedColumnWidthAndCount(parentBlock->columnWidth(), parentBlock->columnCount()); // FIXME: This will eventually vary if we are contained inside regions. // FIXME: When we add regions support, we'll start it off at the width of the multi-column // block in that particular region. setLogicalWidth(parentBox()->contentLogicalWidth()); // If we overflow, increase our logical width. unsigned colCount = columnCount(); if (!colCount) return; LayoutUnit colGap = columnGap(); LayoutUnit minimumContentLogicalWidth = colCount * computedColumnWidth() + (colCount - 1) * colGap; LayoutUnit currentContentLogicalWidth = contentLogicalWidth(); LayoutUnit delta = max(LayoutUnit(), minimumContentLogicalWidth - currentContentLogicalWidth); if (!delta) return; // Increase our logical width by the delta. setLogicalWidth(logicalWidth() + delta); }
void RenderMultiColumnSet::setFlowThreadPortionRect(const LayoutRect& rect) { RenderRegion::setFlowThreadPortionRect(rect); // Mutate the dimensions of the column set once our flow portion is set if the flow portion has more columns // than can fit inside our current dimensions. unsigned colCount = columnCount(); if (!colCount) return; LayoutUnit colGap = columnGap(); LayoutUnit minimumContentLogicalWidth = colCount * computedColumnWidth() + (colCount - 1) * colGap; LayoutUnit currentContentLogicalWidth = contentLogicalWidth(); LayoutUnit delta = max(LayoutUnit(), minimumContentLogicalWidth - currentContentLogicalWidth); if (!delta) return; // Increase our logical width by the delta. setLogicalWidth(logicalWidth() + delta); // Shift our position left by the delta if we are RTL. if (!style()->isLeftToRightDirection()) setLogicalLeft(logicalLeft() - delta); }
LayoutUnit InlineTextBox::placeEllipsisBox(bool flowIsLTR, LayoutUnit visibleLeftEdge, LayoutUnit visibleRightEdge, LayoutUnit ellipsisWidth, LayoutUnit &truncatedWidth, bool& foundBox) { if (foundBox) { setTruncation(cFullTruncation); return -1; } // For LTR this is the left edge of the box, for RTL, the right edge in parent coordinates. LayoutUnit ellipsisX = flowIsLTR ? visibleRightEdge - ellipsisWidth : visibleLeftEdge + ellipsisWidth; // Criteria for full truncation: // LTR: the left edge of the ellipsis is to the left of our text run. // RTL: the right edge of the ellipsis is to the right of our text run. bool ltrFullTruncation = flowIsLTR && ellipsisX <= logicalLeft(); bool rtlFullTruncation = !flowIsLTR && ellipsisX >= logicalLeft() + logicalWidth(); if (ltrFullTruncation || rtlFullTruncation) { // Too far. Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box. setTruncation(cFullTruncation); foundBox = true; return -1; } bool ltrEllipsisWithinBox = flowIsLTR && (ellipsisX < logicalRight()); bool rtlEllipsisWithinBox = !flowIsLTR && (ellipsisX > logicalLeft()); if (ltrEllipsisWithinBox || rtlEllipsisWithinBox) { foundBox = true; // The inline box may have different directionality than it's parent. Since truncation // behavior depends both on both the parent and the inline block's directionality, we // must keep track of these separately. bool ltr = isLeftToRightDirection(); if (ltr != flowIsLTR) { // Width in pixels of the visible portion of the box, excluding the ellipsis. int visibleBoxWidth = visibleRightEdge - visibleLeftEdge - ellipsisWidth; ellipsisX = ltr ? logicalLeft() + visibleBoxWidth : logicalRight() - visibleBoxWidth; } int offset = offsetForPosition(ellipsisX, false); if (offset == 0) { // No characters should be laid out. Set ourselves to full truncation and place the ellipsis at the min of our start // and the ellipsis edge. setTruncation(cFullTruncation); truncatedWidth += ellipsisWidth; return std::min(ellipsisX, logicalLeft()); } // Set the truncation index on the text run. setTruncation(offset); // If we got here that means that we were only partially truncated and we need to return the pixel offset at which // to place the ellipsis. LayoutUnit widthOfVisibleText = lineLayoutItem().width(m_start, offset, textPos(), flowIsLTR ? LTR : RTL, isFirstLineStyle()); // The ellipsis needs to be placed just after the last visible character. // Where "after" is defined by the flow directionality, not the inline // box directionality. // e.g. In the case of an LTR inline box truncated in an RTL flow then we can // have a situation such as |Hello| -> |...He| truncatedWidth += widthOfVisibleText + ellipsisWidth; if (flowIsLTR) return logicalLeft() + widthOfVisibleText; return logicalRight() - widthOfVisibleText - ellipsisWidth; } truncatedWidth += logicalWidth(); return -1; }
float InlineBox::placeEllipsisBox(bool, float, float, float, float& truncatedWidth, bool&) { // Use -1 to mean "we didn't set the position." truncatedWidth += logicalWidth(); return -1; }
void RenderFlowThread::layout() { m_pageLogicalHeightChanged = m_regionsInvalidated && everHadLayout(); if (m_regionsInvalidated) { m_regionsInvalidated = false; m_hasValidRegions = false; m_regionsHaveUniformLogicalWidth = true; m_regionsHaveUniformLogicalHeight = true; m_regionRangeMap.clear(); LayoutUnit previousRegionLogicalWidth = 0; LayoutUnit previousRegionLogicalHeight = 0; if (hasRegions()) { for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; ASSERT(!region->needsLayout()); region->deleteAllRenderBoxRegionInfo(); LayoutUnit regionLogicalWidth = region->pageLogicalWidth(); LayoutUnit regionLogicalHeight = region->pageLogicalHeight(); if (!m_hasValidRegions) m_hasValidRegions = true; else { if (m_regionsHaveUniformLogicalWidth && previousRegionLogicalWidth != regionLogicalWidth) m_regionsHaveUniformLogicalWidth = false; if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight) m_regionsHaveUniformLogicalHeight = false; } previousRegionLogicalWidth = regionLogicalWidth; } updateLogicalWidth(); // Called to get the maximum logical width for the region. LayoutUnit logicalHeight = 0; for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) { RenderRegion* region = *iter; if (!region->isValid()) continue; LayoutUnit regionLogicalWidth = region->pageLogicalWidth(); LayoutUnit regionLogicalHeight = region->logicalHeightOfAllFlowThreadContent(); LayoutRect regionRect(style()->direction() == LTR ? ZERO_LAYOUT_UNIT : logicalWidth() - regionLogicalWidth, logicalHeight, regionLogicalWidth, regionLogicalHeight); region->setFlowThreadPortionRect(isHorizontalWritingMode() ? regionRect : regionRect.transposedRect()); logicalHeight += regionLogicalHeight; } } } CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this); RenderBlock::layout(); m_pageLogicalHeightChanged = false; if (lastRegion()) lastRegion()->expandToEncompassFlowThreadContentsIfNeeded(); if (shouldDispatchRegionLayoutUpdateEvent()) dispatchRegionLayoutUpdateEvent(); }