LayoutRect RenderRegion::rectFlowPortionForBox(const RenderBox* box, const LayoutRect& rect) const { RenderRegion* startRegion = 0; RenderRegion* endRegion = 0; m_flowThread->getRegionRangeForBox(box, startRegion, endRegion); LayoutRect mappedRect = m_flowThread->mapFromLocalToFlowThread(box, rect); if (flowThread()->isHorizontalWritingMode()) { if (this != startRegion) mappedRect.shiftYEdgeTo(std::max<LayoutUnit>(logicalTopForFlowThreadContent(), mappedRect.y())); if (this != endRegion) mappedRect.setHeight(std::max<LayoutUnit>(0, std::min<LayoutUnit>(logicalBottomForFlowThreadContent() - mappedRect.y(), mappedRect.height()))); } else { if (this != startRegion) mappedRect.shiftXEdgeTo(std::max<LayoutUnit>(logicalTopForFlowThreadContent(), mappedRect.x())); if (this != endRegion) mappedRect.setWidth(std::max<LayoutUnit>(0, std::min<LayoutUnit>(logicalBottomForFlowThreadContent() - mappedRect.x(), mappedRect.width()))); } bool isLastRegionWithRegionFragmentBreak = (isLastRegion() && (style().regionFragment() == BreakRegionFragment)); if (hasOverflowClip() || isLastRegionWithRegionFragmentBreak) mappedRect.intersect(flowThreadPortionRect()); return mappedRect.isEmpty() ? mappedRect : m_flowThread->mapFromFlowThreadToLocal(box, mappedRect); }
LayoutRect RenderRegion::rectFlowPortionForBox(const RenderBox* box, const LayoutRect& rect) const { RenderRegion* startRegion = 0; RenderRegion* endRegion = 0; m_flowThread->getRegionRangeForBox(box, startRegion, endRegion); LayoutRect mappedRect = m_flowThread->mapFromLocalToFlowThread(box, rect); if (flowThread()->isHorizontalWritingMode()) { if (this != startRegion) mappedRect.shiftYEdgeTo(std::max<LayoutUnit>(logicalTopForFlowThreadContent(), mappedRect.y())); if (this != endRegion) mappedRect.setHeight(std::max<LayoutUnit>(0, std::min<LayoutUnit>(logicalBottomForFlowThreadContent() - mappedRect.y(), mappedRect.height()))); } else { if (this != startRegion) mappedRect.shiftXEdgeTo(std::max<LayoutUnit>(logicalTopForFlowThreadContent(), mappedRect.x())); if (this != endRegion) mappedRect.setWidth(std::max<LayoutUnit>(0, std::min<LayoutUnit>(logicalBottomForFlowThreadContent() - mappedRect.x(), mappedRect.width()))); } if (shouldClipFlowThreadContent()) { LayoutRect portionRect; if (isRenderNamedFlowFragment()) portionRect = toRenderNamedFlowFragment(this)->flowThreadPortionRectForClipping(this == startRegion, this == endRegion); else portionRect = flowThreadPortionRect(); mappedRect.intersect(portionRect); } return mappedRect.isEmpty() ? mappedRect : m_flowThread->mapFromFlowThreadToLocal(box, mappedRect); }
void RenderLineBreak::collectSelectionRects(Vector<SelectionRect>& rects, unsigned, unsigned) { ensureLineBoxes(*this); InlineElementBox* box = m_inlineBoxWrapper; if (!box) return; const RootInlineBox& rootBox = box->root(); LayoutRect rect = rootBox.computeCaretRect(box->logicalLeft(), 0, nullptr); if (rootBox.isFirstAfterPageBreak()) { if (box->isHorizontal()) rect.shiftYEdgeTo(rootBox.lineTopWithLeading()); else rect.shiftXEdgeTo(rootBox.lineTopWithLeading()); } RenderBlock* containingBlock = this->containingBlock(); // Map rect, extended left to leftOffset, and right to rightOffset, through transforms to get minX and maxX. LogicalSelectionOffsetCaches cache(*containingBlock); LayoutUnit leftOffset = containingBlock->logicalLeftSelectionOffset(*containingBlock, box->logicalTop(), cache); LayoutUnit rightOffset = containingBlock->logicalRightSelectionOffset(*containingBlock, box->logicalTop(), cache); LayoutRect extentsRect = rect; if (box->isHorizontal()) { extentsRect.setX(leftOffset); extentsRect.setWidth(rightOffset - leftOffset); } else { extentsRect.setY(leftOffset); extentsRect.setHeight(rightOffset - leftOffset); } extentsRect = localToAbsoluteQuad(FloatRect(extentsRect)).enclosingBoundingBox(); if (!box->isHorizontal()) extentsRect = extentsRect.transposedRect(); bool isFirstOnLine = !box->previousOnLineExists(); bool isLastOnLine = !box->nextOnLineExists(); if (containingBlock->isRubyBase() || containingBlock->isRubyText()) isLastOnLine = !containingBlock->containingBlock()->inlineBoxWrapper()->nextOnLineExists(); bool isFixed = false; IntRect absRect = localToAbsoluteQuad(FloatRect(rect), UseTransforms, &isFixed).enclosingBoundingBox(); bool boxIsHorizontal = !box->isSVGInlineTextBox() ? box->isHorizontal() : !style().svgStyle().isVerticalWritingMode(); // If the containing block is an inline element, we want to check the inlineBoxWrapper orientation // to determine the orientation of the block. In this case we also use the inlineBoxWrapper to // determine if the element is the last on the line. if (containingBlock->inlineBoxWrapper()) { if (containingBlock->inlineBoxWrapper()->isHorizontal() != boxIsHorizontal) { boxIsHorizontal = containingBlock->inlineBoxWrapper()->isHorizontal(); isLastOnLine = !containingBlock->inlineBoxWrapper()->nextOnLineExists(); } } rects.append(SelectionRect(absRect, box->direction(), extentsRect.x(), extentsRect.maxX(), extentsRect.maxY(), 0, box->isLineBreak(), isFirstOnLine, isLastOnLine, false, false, boxIsHorizontal, isFixed, containingBlock->isRubyText(), view().pageNumberForBlockProgressionOffset(absRect.x()))); }
LayoutRect MultiColumnFragmentainerGroup::flowThreadPortionOverflowRectAt( unsigned columnIndex) const { // This function determines the portion of the flow thread that paints for the // column. Along the inline axis, columns are unclipped at outside edges // (i.e., the first and last column in the set), and they clip to half the // column gap along interior edges. // // In the block direction, we will not clip overflow out of the top of the // first column, or out of the bottom of the last column. This applies only to // the true first column and last column across all column sets. // // FIXME: Eventually we will know overflow on a per-column basis, but we can't // do this until we have a painting mode that understands not to paint // contents from a previous column in the overflow area of a following column. bool isFirstColumnInRow = !columnIndex; bool isLastColumnInRow = columnIndex == actualColumnCount() - 1; bool isLTR = m_columnSet.style()->isLeftToRightDirection(); bool isLeftmostColumn = isLTR ? isFirstColumnInRow : isLastColumnInRow; bool isRightmostColumn = isLTR ? isLastColumnInRow : isFirstColumnInRow; LayoutRect portionRect = flowThreadPortionRectAt(columnIndex); bool isFirstColumnInMulticolContainer = isFirstColumnInRow && this == &m_columnSet.firstFragmentainerGroup() && !m_columnSet.previousSiblingMultiColumnSet(); bool isLastColumnInMulticolContainer = isLastColumnInRow && this == &m_columnSet.lastFragmentainerGroup() && !m_columnSet.nextSiblingMultiColumnSet(); // Calculate the overflow rectangle, based on the flow thread's, clipped at // column logical top/bottom unless it's the first/last column. LayoutRect overflowRect = m_columnSet.overflowRectForFlowThreadPortion( portionRect, isFirstColumnInMulticolContainer, isLastColumnInMulticolContainer); // Avoid overflowing into neighboring columns, by clipping in the middle of // adjacent column gaps. Also make sure that we avoid rounding errors. LayoutUnit columnGap = m_columnSet.columnGap(); if (m_columnSet.isHorizontalWritingMode()) { if (!isLeftmostColumn) overflowRect.shiftXEdgeTo(portionRect.x() - columnGap / 2); if (!isRightmostColumn) overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + columnGap - columnGap / 2); } else { if (!isLeftmostColumn) overflowRect.shiftYEdgeTo(portionRect.y() - columnGap / 2); if (!isRightmostColumn) overflowRect.shiftMaxYEdgeTo(portionRect.maxY() + columnGap - columnGap / 2); } return overflowRect; }
LayoutRect RootInlineBox::paddedLayoutOverflowRect(LayoutUnit endPadding) const { LayoutRect lineLayoutOverflow = layoutOverflowRect(lineTop(), lineBottom()); if (!endPadding) return lineLayoutOverflow; // FIXME: Audit whether to use pixel snapped values when not using integers for layout: https://bugs.webkit.org/show_bug.cgi?id=63656 if (isHorizontal()) { if (isLeftToRightDirection()) lineLayoutOverflow.shiftMaxXEdgeTo(std::max<LayoutUnit>(lineLayoutOverflow.maxX(), pixelSnappedLogicalRight() + endPadding)); else lineLayoutOverflow.shiftXEdgeTo(std::min<LayoutUnit>(lineLayoutOverflow.x(), pixelSnappedLogicalLeft() - endPadding)); } else { if (isLeftToRightDirection()) lineLayoutOverflow.shiftMaxYEdgeTo(std::max<LayoutUnit>(lineLayoutOverflow.maxY(), pixelSnappedLogicalRight() + endPadding)); else lineLayoutOverflow.shiftYEdgeTo(std::min<LayoutUnit>(lineLayoutOverflow.y(), pixelSnappedLogicalLeft() - endPadding)); } return lineLayoutOverflow; }
LayoutRect RenderMultiColumnSet::flowThreadPortionOverflowRect(const LayoutRect& portionRect, unsigned index, unsigned colCount, LayoutUnit colGap) { // This function determines the portion of the flow thread that paints for the column. Along the inline axis, columns are // unclipped at outside edges (i.e., the first and last column in the set), and they clip to half the column // gap along interior edges. // // In the block direction, we will not clip overflow out of the top of the first column, or out of the bottom of // the last column. This applies only to the true first column and last column across all column sets. // // FIXME: Eventually we will know overflow on a per-column basis, but we can't do this until we have a painting // mode that understands not to paint contents from a previous column in the overflow area of a following column. // This problem applies to regions and pages as well and is not unique to columns. RenderBlockFlow* parentFlow = toRenderBlockFlow(parent()); bool progressionReversed = parentFlow->multiColumnFlowThread()->progressionIsReversed(); bool isFirstColumn = !index; bool isLastColumn = index == colCount - 1; bool isLeftmostColumn = style().isLeftToRightDirection() ^ progressionReversed ? isFirstColumn : isLastColumn; bool isRightmostColumn = style().isLeftToRightDirection() ^ progressionReversed ? isLastColumn : isFirstColumn; // Calculate the overflow rectangle, based on the flow thread's, clipped at column logical // top/bottom unless it's the first/last column. LayoutRect overflowRect = overflowRectForFlowThreadPortion(portionRect, isFirstColumn && isFirstRegion(), isLastColumn && isLastRegion(), VisualOverflow); // Avoid overflowing into neighboring columns, by clipping in the middle of adjacent column // gaps. Also make sure that we avoid rounding errors. if (isHorizontalWritingMode()) { if (!isLeftmostColumn) overflowRect.shiftXEdgeTo(portionRect.x() - colGap / 2); if (!isRightmostColumn) overflowRect.shiftMaxXEdgeTo(portionRect.maxX() + colGap - colGap / 2); } else { if (!isLeftmostColumn) overflowRect.shiftYEdgeTo(portionRect.y() - colGap / 2); if (!isRightmostColumn) overflowRect.shiftMaxYEdgeTo(portionRect.maxY() + colGap - colGap / 2); } return overflowRect; }
LayoutRect RenderNamedFlowThread::decorationsClipRectForBoxInNamedFlowFragment(const RenderBox& box, RenderNamedFlowFragment& fragment) const { LayoutRect visualOverflowRect = fragment.visualOverflowRectForBox(&box); LayoutUnit initialLogicalX = style().isHorizontalWritingMode() ? visualOverflowRect.x() : visualOverflowRect.y(); // The visual overflow rect returned by visualOverflowRectForBox is already flipped but the // RenderRegion::rectFlowPortionForBox method expects it unflipped. flipForWritingModeLocalCoordinates(visualOverflowRect); visualOverflowRect = fragment.rectFlowPortionForBox(&box, visualOverflowRect); // Now flip it again. flipForWritingModeLocalCoordinates(visualOverflowRect); // Take the scrolled offset of this object's parents into consideration. IntSize scrolledContentOffset; RenderBlock* containingBlock = box.containingBlock(); while (containingBlock) { if (containingBlock->isRenderNamedFlowThread()) { // We've reached the flow thread, take the scrolled offset of the region into consideration. ASSERT(containingBlock == this); scrolledContentOffset += fragment.fragmentContainer().scrolledContentOffset(); break; } scrolledContentOffset += containingBlock->scrolledContentOffset(); containingBlock = containingBlock->containingBlock(); } if (!scrolledContentOffset.isZero()) { if (style().isFlippedBlocksWritingMode()) scrolledContentOffset = -scrolledContentOffset; visualOverflowRect.inflateX(scrolledContentOffset.width()); visualOverflowRect.inflateY(scrolledContentOffset.height()); } // Layers are in physical coordinates so the origin must be moved to the physical top-left of the flowthread. if (style().isFlippedBlocksWritingMode()) { if (style().isHorizontalWritingMode()) visualOverflowRect.moveBy(LayoutPoint(0, height())); else visualOverflowRect.moveBy(LayoutPoint(width(), 0)); } const RenderBox* iterBox = &box; while (iterBox && iterBox != this) { RenderBlock* containerBlock = iterBox->containingBlock(); // FIXME: This doesn't work properly with flipped writing modes. // https://bugs.webkit.org/show_bug.cgi?id=125149 if (iterBox->isPositioned()) { // For positioned elements, just use the layer's absolute bounding box. visualOverflowRect.moveBy(iterBox->layer()->absoluteBoundingBox().location()); break; } LayoutRect currentBoxRect = iterBox->frameRect(); if (iterBox->style().isFlippedBlocksWritingMode()) { if (iterBox->style().isHorizontalWritingMode()) currentBoxRect.setY(currentBoxRect.height() - currentBoxRect.maxY()); else currentBoxRect.setX(currentBoxRect.width() - currentBoxRect.maxX()); } if (containerBlock->style().writingMode() != iterBox->style().writingMode()) iterBox->flipForWritingMode(currentBoxRect); visualOverflowRect.moveBy(currentBoxRect.location()); iterBox = containerBlock; } // Since the purpose of this method is to make sure the borders of a fragmented // element don't overflow the region in the fragmentation direction, there's no // point in restricting the clipping rect on the logical X axis. // This also saves us the trouble of handling percent-based widths and margins // since the absolute bounding box of a positioned element would not contain // the correct coordinates relative to the region we're interested in, but rather // relative to the actual flow thread. if (style().isHorizontalWritingMode()) { if (initialLogicalX < visualOverflowRect.x()) visualOverflowRect.shiftXEdgeTo(initialLogicalX); if (visualOverflowRect.width() < frameRect().width()) visualOverflowRect.setWidth(frameRect().width()); } else { if (initialLogicalX < visualOverflowRect.y()) visualOverflowRect.shiftYEdgeTo(initialLogicalX); if (visualOverflowRect.height() < frameRect().height()) visualOverflowRect.setHeight(frameRect().height()); } return visualOverflowRect; }