LayoutUnit RootInlineBox::selectionBottom() const { LayoutUnit selectionBottom = m_lineBottom; if (m_hasAnnotationsAfter) selectionBottom += !renderer().style().isFlippedLinesWritingMode() ? computeUnderAnnotationAdjustment(m_lineBottom) : computeOverAnnotationAdjustment(m_lineBottom); if (!renderer().style().isFlippedLinesWritingMode() || !nextRootBox()) return selectionBottom; LayoutUnit nextTop = nextRootBox()->selectionTop(); if (nextTop > selectionBottom && blockFlow().containsFloats()) { // The next line has actually been moved further over, probably from a large line-height, but possibly because the // line was forced to clear floats. If so, let's check the offsets, and only be willing to use the next // line's top if the offsets are greater on both sides. LayoutUnit nextLeft = blockFlow().logicalLeftOffsetForLine(nextTop, false); LayoutUnit nextRight = blockFlow().logicalRightOffsetForLine(nextTop, false); LayoutUnit newLeft = blockFlow().logicalLeftOffsetForLine(selectionBottom, false); LayoutUnit newRight = blockFlow().logicalRightOffsetForLine(selectionBottom, false); if (nextLeft > newLeft || nextRight < newRight) return selectionBottom; } return nextTop; }
RootInlineBox::~RootInlineBox() { detachEllipsisBox(); if (blockFlow().flowThreadContainingBlock()) containingRegionMap(blockFlow()).remove(this); }
void RootInlineBox::clearContainingRegion() { ASSERT(!isDirty()); if (!containingRegionMap(blockFlow()).contains(this)) return; containingRegionMap(blockFlow()).remove(this); }
GapRects RootInlineBox::lineSelectionGap(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, LayoutUnit selTop, LayoutUnit selHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) { RenderObject::SelectionState lineState = selectionState(); bool leftGap, rightGap; blockFlow().getSelectionGapInfo(lineState, leftGap, rightGap); GapRects result; InlineBox* firstBox = firstSelectedBox(); InlineBox* lastBox = lastSelectedBox(); if (leftGap) { result.uniteLeft(blockFlow().logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, &firstBox->parent()->renderer(), firstBox->logicalLeft(), selTop, selHeight, cache, paintInfo)); } if (rightGap) { result.uniteRight(blockFlow().logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, &lastBox->parent()->renderer(), lastBox->logicalRight(), selTop, selHeight, cache, paintInfo)); } // When dealing with bidi text, a non-contiguous selection region is possible. // e.g. The logical text aaaAAAbbb (capitals denote RTL text and non-capitals LTR) is layed out // visually as 3 text runs |aaa|bbb|AAA| if we select 4 characters from the start of the text the // selection will look like (underline denotes selection): // |aaa|bbb|AAA| // ___ _ // We can see that the |bbb| run is not part of the selection while the runs around it are. if (firstBox && firstBox != lastBox) { // Now fill in any gaps on the line that occurred between two selected elements. LayoutUnit lastLogicalLeft = firstBox->logicalRight(); bool isPreviousBoxSelected = firstBox->selectionState() != RenderObject::SelectionNone; for (InlineBox* box = firstBox->nextLeafChild(); box; box = box->nextLeafChild()) { if (box->selectionState() != RenderObject::SelectionNone) { LayoutRect logicalRect(lastLogicalLeft, selTop, box->logicalLeft() - lastLogicalLeft, selHeight); logicalRect.move(renderer().isHorizontalWritingMode() ? offsetFromRootBlock : LayoutSize(offsetFromRootBlock.height(), offsetFromRootBlock.width())); LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect); if (isPreviousBoxSelected && gapRect.width() > 0 && gapRect.height() > 0) { if (paintInfo && box->parent()->renderer().style().visibility() == VISIBLE) paintInfo->context->fillRect(gapRect, box->parent()->renderer().selectionBackgroundColor(), box->parent()->renderer().style().colorSpace()); // VisibleSelection may be non-contiguous, see comment above. result.uniteCenter(gapRect); } lastLogicalLeft = box->logicalRight(); } if (box == lastBox) break; isPreviousBoxSelected = box->selectionState() != RenderObject::SelectionNone; } } return result; }
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 SVGRootInlineBox::computePerCharacterLayoutInformation() { RenderSVGText* textRoot = toRenderSVGText(&blockFlow()); ASSERT(textRoot); Vector<SVGTextLayoutAttributes*>& layoutAttributes = textRoot->layoutAttributes(); if (layoutAttributes.isEmpty()) return; if (textRoot->needsReordering()) reorderValueLists(layoutAttributes); // Perform SVG text layout phase two (see SVGTextLayoutEngine for details). SVGTextLayoutEngine characterLayout(layoutAttributes); layoutCharactersInTextBoxes(this, characterLayout); // Perform SVG text layout phase three (see SVGTextChunkBuilder for details). characterLayout.finishLayout(); // Perform SVG text layout phase four // Position & resize all SVGInlineText/FlowBoxes in the inline box tree, resize the root box as well as the RenderSVGText parent block. FloatRect childRect; layoutChildBoxes(this, &childRect); layoutRootBox(childRect); }
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; }
RenderRegion* RootInlineBox::containingRegion() const { ContainingRegionMap& regionMap = containingRegionMap(blockFlow()); bool hasContainingRegion = regionMap.contains(this); RenderRegion* region = hasContainingRegion ? regionMap.get(this) : nullptr; #ifndef NDEBUG if (hasContainingRegion) { RenderFlowThread* flowThread = blockFlow().flowThreadContainingBlock(); const RenderRegionList& regionList = flowThread->renderRegionList(); ASSERT_WITH_SECURITY_IMPLICATION(regionList.contains(region)); } #endif return region; }
IntRect EllipsisBox::selectionRect() { const RenderStyle& lineStyle = this->lineStyle(); const Font& font = lineStyle.font(); const RootInlineBox& rootBox = root(); // FIXME: Why is this always LTR? Fix by passing correct text run flags below. return enclosingIntRect(font.selectionRectForText(RenderBlock::constructTextRun(&blockFlow(), font, m_str, lineStyle, TextRun::AllowTrailingExpansion), IntPoint(x(), y() + rootBox.selectionTopAdjustedForPrecedingBlock()), rootBox.selectionHeightAdjustedForPrecedingBlock())); }
InlineBox* EllipsisBox::markupBox() const { if (!m_shouldPaintMarkupBox) return 0; RootInlineBox* lastLine = blockFlow().lineAtIndex(blockFlow().lineCount() - 1); if (!lastLine) return 0; // If the last line-box on the last line of a block is a link, -webkit-line-clamp paints that box after the ellipsis. // It does not actually move the link. InlineBox* anchorBox = lastLine->lastChild(); if (!anchorBox || !anchorBox->renderer().style().isLink()) return 0; return anchorBox; }
LayoutUnit RootInlineBox::alignBoxesInBlockDirection(LayoutUnit heightOfBlock, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache) { // SVG will handle vertical alignment on its own. if (isSVGRootInlineBox()) return 0; LayoutUnit maxPositionTop = 0; LayoutUnit maxPositionBottom = 0; int maxAscent = 0; int maxDescent = 0; bool setMaxAscent = false; bool setMaxDescent = false; // Figure out if we're in no-quirks mode. bool noQuirksMode = renderer().document().inNoQuirksMode(); m_baselineType = requiresIdeographicBaseline(textBoxDataMap) ? IdeographicBaseline : AlphabeticBaseline; computeLogicalBoxHeights(this, maxPositionTop, maxPositionBottom, maxAscent, maxDescent, setMaxAscent, setMaxDescent, noQuirksMode, textBoxDataMap, baselineType(), verticalPositionCache); if (maxAscent + maxDescent < std::max(maxPositionTop, maxPositionBottom)) adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); LayoutUnit maxHeight = maxAscent + maxDescent; LayoutUnit lineTop = heightOfBlock; LayoutUnit lineBottom = heightOfBlock; LayoutUnit lineTopIncludingMargins = heightOfBlock; LayoutUnit lineBottomIncludingMargins = heightOfBlock; bool setLineTop = false; bool hasAnnotationsBefore = false; bool hasAnnotationsAfter = false; placeBoxesInBlockDirection(heightOfBlock, maxHeight, maxAscent, noQuirksMode, lineTop, lineBottom, setLineTop, lineTopIncludingMargins, lineBottomIncludingMargins, hasAnnotationsBefore, hasAnnotationsAfter, baselineType()); m_hasAnnotationsBefore = hasAnnotationsBefore; m_hasAnnotationsAfter = hasAnnotationsAfter; maxHeight = std::max<LayoutUnit>(0, maxHeight); // FIXME: Is this really necessary? setLineTopBottomPositions(lineTop, lineBottom, heightOfBlock, heightOfBlock + maxHeight); setPaginatedLineWidth(blockFlow().availableLogicalWidthForContent(heightOfBlock)); LayoutUnit annotationsAdjustment = beforeAnnotationsAdjustment(); if (annotationsAdjustment) { // FIXME: Need to handle pagination here. We might have to move to the next page/column as a result of the // ruby expansion. adjustBlockDirectionPosition(annotationsAdjustment); heightOfBlock += annotationsAdjustment; } LayoutUnit gridSnapAdjustment = lineSnapAdjustment(); if (gridSnapAdjustment) { adjustBlockDirectionPosition(gridSnapAdjustment); heightOfBlock += gridSnapAdjustment; } return heightOfBlock + maxHeight; }
IntRect EllipsisBox::selectionRect() { const RenderStyle& lineStyle = this->lineStyle(); const FontCascade& font = lineStyle.fontCascade(); const RootInlineBox& rootBox = root(); // FIXME: Why is this always LTR? Fix by passing correct text run flags below. LayoutRect selectionRect = LayoutRect(x(), y() + rootBox.selectionTopAdjustedForPrecedingBlock(), 0, rootBox.selectionHeightAdjustedForPrecedingBlock()); font.adjustSelectionRectForText(RenderBlock::constructTextRun(&blockFlow(), font, m_str, lineStyle, AllowTrailingExpansion), selectionRect); // FIXME: use directional pixel snapping instead. return enclosingIntRect(selectionRect); }
void EllipsisBox::paintSelection(GraphicsContext& context, const LayoutPoint& paintOffset, const RenderStyle& style, const FontCascade& font) { Color textColor = style.visitedDependentColor(CSSPropertyColor); Color c = blockFlow().selectionBackgroundColor(); if (!c.isValid() || !c.alpha()) return; // If the text color ends up being the same as the selection background, invert the selection // background. if (textColor == c) c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); const RootInlineBox& rootBox = root(); GraphicsContextStateSaver stateSaver(context); // FIXME: Why is this always LTR? Fix by passing correct text run flags below. LayoutRect selectionRect = LayoutRect(x() + paintOffset.x(), y() + paintOffset.y() + rootBox.selectionTop(), 0, rootBox.selectionHeight()); TextRun run = RenderBlock::constructTextRun(&blockFlow(), font, m_str, style, AllowTrailingExpansion); font.adjustSelectionRectForText(run, selectionRect, 0, -1); context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), run.ltr()), c); }
LayoutUnit RootInlineBox::selectionTop() const { LayoutUnit selectionTop = m_lineTop; if (m_hasAnnotationsBefore) selectionTop -= !renderer().style().isFlippedLinesWritingMode() ? computeOverAnnotationAdjustment(m_lineTop) : computeUnderAnnotationAdjustment(m_lineTop); if (renderer().style().isFlippedLinesWritingMode()) return selectionTop; LayoutUnit prevBottom = prevRootBox() ? prevRootBox()->selectionBottom() : blockFlow().borderAndPaddingBefore(); if (prevBottom < selectionTop && blockFlow().containsFloats()) { // This line has actually been moved further down, probably from a large line-height, but possibly because the // line was forced to clear floats. If so, let's check the offsets, and only be willing to use the previous // line's bottom if the offsets are greater on both sides. LayoutUnit prevLeft = blockFlow().logicalLeftOffsetForLine(prevBottom, false); LayoutUnit prevRight = blockFlow().logicalRightOffsetForLine(prevBottom, false); LayoutUnit newLeft = blockFlow().logicalLeftOffsetForLine(selectionTop, false); LayoutUnit newRight = blockFlow().logicalRightOffsetForLine(selectionTop, false); if (prevLeft > newLeft || prevRight < newRight) return selectionTop; } return prevBottom; }
int maxflow(int n,int m ,int s,int t,int head[],int tail[],int up[]) { for(int i=0;i<m;i++) aa[i<<1]=head[i],bb[i<<1]=tail[i],cap[i<<1]=up[i], aa[(i<<1)^1]=tail[i],bb[(i<<1)^1]=head[i],cap[(i<<1)^1]=0; m<<=1; memset(flow,0,sizeof(int)*m); forwardStar(n,m); int re=0; while(layerNetwork(n,s,t)) re+=blockFlow(n,s,t); return re; }
void EllipsisBox::paintSelection(GraphicsContext* context, const LayoutPoint& paintOffset, const RenderStyle& style, const Font& font) { Color textColor = style.visitedDependentColor(CSSPropertyColor); Color c = blockFlow().selectionBackgroundColor(); if (!c.isValid() || !c.alpha()) return; // If the text color ends up being the same as the selection background, invert the selection // background. if (textColor == c) c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); const RootInlineBox& rootBox = root(); LayoutUnit top = rootBox.selectionTop(); LayoutUnit h = rootBox.selectionHeight(); FloatRect clipRect(x() + paintOffset.x(), top + paintOffset.y(), m_logicalWidth, h); alignSelectionRectToDevicePixels(clipRect); GraphicsContextStateSaver stateSaver(*context); context->clip(clipRect); // FIXME: Why is this always LTR? Fix by passing correct text run flags below. context->drawHighlightForText(font, RenderBlock::constructTextRun(&blockFlow(), font, m_str, style, TextRun::AllowTrailingExpansion), roundedIntPoint(LayoutPoint(x() + paintOffset.x(), y() + paintOffset.y() + top)), h, c, style.colorSpace()); }
bool EllipsisBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom, HitTestAction hitTestAction) { LayoutPoint adjustedLocation = accumulatedOffset + LayoutPoint(topLeft()); // Hit test the markup box. if (InlineBox* markupBox = this->markupBox()) { const RenderStyle& lineStyle = this->lineStyle(); LayoutUnit mtx = adjustedLocation.x() + m_logicalWidth - markupBox->x(); LayoutUnit mty = adjustedLocation.y() + lineStyle.fontMetrics().ascent() - (markupBox->y() + markupBox->lineStyle().fontMetrics().ascent()); if (markupBox->nodeAtPoint(request, result, locationInContainer, LayoutPoint(mtx, mty), lineTop, lineBottom, hitTestAction)) { blockFlow().updateHitTestResult(result, locationInContainer.point() - LayoutSize(mtx, mty)); return true; } } LayoutRect boundsRect(adjustedLocation, LayoutSize(m_logicalWidth, m_height)); if (visibleToHitTesting() && boundsRect.intersects(HitTestLocation::rectForPoint(locationInContainer.point(), 0, 0, 0, 0))) { blockFlow().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); if (!result.addNodeToRectBasedTestResult(blockFlow().element(), request, locationInContainer, boundsRect)) return true; } return false; }
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { GraphicsContext* context = paintInfo.context; const RenderStyle& lineStyle = this->lineStyle(); Color textColor = lineStyle.visitedDependentColor(CSSPropertyWebkitTextFillColor); if (textColor != context->fillColor()) context->setFillColor(textColor, lineStyle.colorSpace()); bool setShadow = false; if (lineStyle.textShadow()) { context->setShadow(LayoutSize(lineStyle.textShadow()->x(), lineStyle.textShadow()->y()), lineStyle.textShadow()->radius(), lineStyle.textShadow()->color(), lineStyle.colorSpace()); setShadow = true; } const Font& font = lineStyle.font(); if (selectionState() != RenderObject::SelectionNone) { paintSelection(context, paintOffset, lineStyle, font); // Select the correct color for painting the text. Color foreground = paintInfo.forceBlackText() ? Color::black : blockFlow().selectionForegroundColor(); if (foreground.isValid() && foreground != textColor) context->setFillColor(foreground, lineStyle.colorSpace()); } // FIXME: Why is this always LTR? Fix by passing correct text run flags below. context->drawText(font, RenderBlock::constructTextRun(&blockFlow(), font, m_str, lineStyle, TextRun::AllowTrailingExpansion), LayoutPoint(x() + paintOffset.x(), y() + paintOffset.y() + lineStyle.fontMetrics().ascent())); // Restore the regular fill color. if (textColor != context->fillColor()) context->setFillColor(textColor, lineStyle.colorSpace()); if (setShadow) context->clearShadow(); paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, lineStyle); }
int maxflow(int s,int t) { int m=head.size(); int n=max(s,t); for(int i=0;i<m;i++) aa[i<<1]=head[i],bb[i<<1]=tail[i],cap[i<<1]=up[i], aa[(i<<1)^1]=tail[i],bb[(i<<1)^1]=head[i],cap[(i<<1)^1]=0, n=max(n,head[i]),n=max(n,tail[i]); n++; m<<=1; memset(flow,0,sizeof(int)*m); forwardStar(n,m); int re=0; while(layerNetwork(n,s,t)) re+=blockFlow(n,s,t); return re; }
LayoutUnit RootInlineBox::beforeAnnotationsAdjustment() const { LayoutUnit result = 0; if (!renderer().style().isFlippedLinesWritingMode()) { // Annotations under the previous line may push us down. if (prevRootBox() && prevRootBox()->hasAnnotationsAfter()) result = prevRootBox()->computeUnderAnnotationAdjustment(lineTop()); if (!hasAnnotationsBefore()) return result; // Annotations over this line may push us further down. LayoutUnit highestAllowedPosition = prevRootBox() ? std::min(prevRootBox()->lineBottom(), lineTop()) + result : blockFlow().borderBefore(); result = computeOverAnnotationAdjustment(highestAllowedPosition); } else { // Annotations under this line may push us up. if (hasAnnotationsBefore()) result = computeUnderAnnotationAdjustment(prevRootBox() ? prevRootBox()->lineBottom() : blockFlow().borderBefore()); if (!prevRootBox() || !prevRootBox()->hasAnnotationsAfter()) return result; // We have to compute the expansion for annotations over the previous line to see how much we should move. LayoutUnit lowestAllowedPosition = std::max(prevRootBox()->lineBottom(), lineTop()) - result; result = prevRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition); } return result; }
InlineBox* RootInlineBox::closestLeafChildForPoint(const IntPoint& pointInContents, bool onlyEditableLeaves) { return closestLeafChildForLogicalLeftPosition(blockFlow().isHorizontalWritingMode() ? pointInContents.x() : pointInContents.y(), onlyEditableLeaves); }
void RootInlineBox::setContainingRegion(RenderRegion& region) { ASSERT(!isDirty()); containingRegionMap(blockFlow()).set(this, ®ion); }
RenderSVGText& SVGRootInlineBox::renderSVGText() { return downcast<RenderSVGText>(blockFlow()); }
void RootInlineBox::extractLineBoxFromRenderObject() { blockFlow().lineBoxes().extractLineBox(this); }
int RootInlineBox::blockDirectionPointInLine() const { return !blockFlow().style().isFlippedBlocksWritingMode() ? std::max(lineTop(), selectionTop()) : std::min(lineBottom(), selectionBottom()); }
RenderSVGText& SVGRootInlineBox::renderSVGText() { return toRenderSVGText(blockFlow()); }
void RootInlineBox::attachLineBoxToRenderObject() { blockFlow().lineBoxes().attachLineBox(this); }
void RootInlineBox::removeLineBoxFromRenderObject() { blockFlow().lineBoxes().removeLineBox(this); }
LayoutUnit RootInlineBox::lineSnapAdjustment(LayoutUnit delta) const { // If our block doesn't have snapping turned on, do nothing. // FIXME: Implement bounds snapping. if (blockFlow().style().lineSnap() == LineSnapNone) return 0; // Get the current line grid and offset. LayoutState* layoutState = blockFlow().view().layoutState(); RenderBlockFlow* lineGrid = layoutState->lineGrid(); LayoutSize lineGridOffset = layoutState->lineGridOffset(); if (!lineGrid || lineGrid->style().writingMode() != blockFlow().style().writingMode()) return 0; // Get the hypothetical line box used to establish the grid. RootInlineBox* lineGridBox = lineGrid->lineGridBox(); if (!lineGridBox) return 0; LayoutUnit lineGridBlockOffset = lineGrid->isHorizontalWritingMode() ? lineGridOffset.height() : lineGridOffset.width(); LayoutUnit blockOffset = blockFlow().isHorizontalWritingMode() ? layoutState->layoutOffset().height() : layoutState->layoutOffset().width(); // Now determine our position on the grid. Our baseline needs to be adjusted to the nearest baseline multiple // as established by the line box. // FIXME: Need to handle crazy line-box-contain values that cause the root line box to not be considered. I assume // the grid should honor line-box-contain. LayoutUnit gridLineHeight = lineGridBox->lineBottomWithLeading() - lineGridBox->lineTopWithLeading(); if (!gridLineHeight) return 0; LayoutUnit lineGridFontAscent = lineGrid->style().fontMetrics().ascent(baselineType()); LayoutUnit lineGridFontHeight = lineGridBox->logicalHeight(); LayoutUnit firstTextTop = lineGridBlockOffset + lineGridBox->logicalTop(); LayoutUnit firstLineTopWithLeading = lineGridBlockOffset + lineGridBox->lineTopWithLeading(); LayoutUnit firstBaselinePosition = firstTextTop + lineGridFontAscent; LayoutUnit currentTextTop = blockOffset + logicalTop() + delta; LayoutUnit currentFontAscent = blockFlow().style().fontMetrics().ascent(baselineType()); LayoutUnit currentBaselinePosition = currentTextTop + currentFontAscent; LayoutUnit lineGridPaginationOrigin = isHorizontal() ? layoutState->lineGridPaginationOrigin().height() : layoutState->lineGridPaginationOrigin().width(); // If we're paginated, see if we're on a page after the first one. If so, the grid resets on subsequent pages. // FIXME: If the grid is an ancestor of the pagination establisher, then this is incorrect. LayoutUnit pageLogicalTop = 0; if (layoutState->isPaginated() && layoutState->pageLogicalHeight()) { pageLogicalTop = blockFlow().pageLogicalTopForOffset(lineTopWithLeading() + delta); if (pageLogicalTop > firstLineTopWithLeading) firstTextTop = pageLogicalTop + lineGridBox->logicalTop() - lineGrid->borderAndPaddingBefore() + lineGridPaginationOrigin; } if (blockFlow().style().lineSnap() == LineSnapContain) { // Compute the desired offset from the text-top of a grid line. // Look at our height (logicalHeight()). // Look at the total available height. It's going to be (textBottom - textTop) + (n-1)*(multiple with leading) // where n is number of grid lines required to enclose us. if (logicalHeight() <= lineGridFontHeight) firstTextTop += (lineGridFontHeight - logicalHeight()) / 2; else { LayoutUnit numberOfLinesWithLeading = ceilf(static_cast<float>(logicalHeight() - lineGridFontHeight) / gridLineHeight); LayoutUnit totalHeight = lineGridFontHeight + numberOfLinesWithLeading * gridLineHeight; firstTextTop += (totalHeight - logicalHeight()) / 2; } firstBaselinePosition = firstTextTop + currentFontAscent; } else firstBaselinePosition = firstTextTop + lineGridFontAscent; // If we're above the first line, just push to the first line. if (currentBaselinePosition < firstBaselinePosition) return delta + firstBaselinePosition - currentBaselinePosition; // Otherwise we're in the middle of the grid somewhere. Just push to the next line. LayoutUnit baselineOffset = currentBaselinePosition - firstBaselinePosition; LayoutUnit remainder = roundToInt(baselineOffset) % roundToInt(gridLineHeight); LayoutUnit result = delta; if (remainder) result += gridLineHeight - remainder; // If we aren't paginated we can return the result. if (!layoutState->isPaginated() || !layoutState->pageLogicalHeight() || result == delta) return result; // We may end up shifted to a new page. We need to do a re-snap when that happens. LayoutUnit newPageLogicalTop = blockFlow().pageLogicalTopForOffset(lineBottomWithLeading() + result); if (newPageLogicalTop == pageLogicalTop) return result; // Put ourselves at the top of the next page to force a snap onto the new grid established by that page. return lineSnapAdjustment(newPageLogicalTop - (blockOffset + lineTopWithLeading())); }