bool hitTestFlow(const RenderBlockFlow& flow, const Layout& layout, const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) { if (hitTestAction != HitTestForeground) return false; if (!layout.runCount()) return false; RenderStyle& style = flow.style(); if (style.visibility() != VISIBLE || style.pointerEvents() == PE_NONE) return false; RenderObject& renderer = *flow.firstChild(); LayoutRect rangeRect = locationInContainer.boundingBox(); rangeRect.moveBy(-accumulatedOffset); auto resolver = lineResolver(flow, layout); for (FloatRect lineRect : resolver.rangeForRect(rangeRect)) { lineRect.moveBy(accumulatedOffset); if (!locationInContainer.intersects(lineRect)) continue; renderer.updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); if (!result.addNodeToRectBasedTestResult(renderer.node(), request, locationInContainer, lineRect)) return true; } return false; }
LayoutRect RenderMultiColumnSet::columnRectAt(unsigned index) const { LayoutUnit colLogicalWidth = computedColumnWidth(); LayoutUnit colLogicalHeight = computedColumnHeight(); LayoutUnit colLogicalTop = borderAndPaddingBefore(); LayoutUnit colLogicalLeft = borderAndPaddingLogicalLeft(); LayoutUnit colGap = columnGap(); RenderBlockFlow* parentFlow = toRenderBlockFlow(parent()); bool progressionReversed = parentFlow->multiColumnFlowThread()->progressionIsReversed(); bool progressionInline = parentFlow->multiColumnFlowThread()->progressionIsInline(); if (progressionInline) { if (style().isLeftToRightDirection() ^ progressionReversed) colLogicalLeft += index * (colLogicalWidth + colGap); else colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (colLogicalWidth + colGap); } else { if (!progressionReversed) colLogicalTop += index * (colLogicalHeight + colGap); else colLogicalTop += contentLogicalHeight() - colLogicalHeight - index * (colLogicalHeight + colGap); } if (isHorizontalWritingMode()) return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight); return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth); }
static inline InlineFlowBox* flowBoxForRenderer(RenderObject* renderer) { if (!renderer) return 0; if (renderer->isRenderBlock()) { // If we're given a block element, it has to be a RenderSVGText. ASSERT(renderer->isSVGText()); RenderBlockFlow* renderBlockFlow = toRenderBlockFlow(renderer); // RenderSVGText only ever contains a single line box. InlineFlowBox* flowBox = renderBlockFlow->firstLineBox(); ASSERT(flowBox == renderBlockFlow->lastLineBox()); return flowBox; } if (renderer->isRenderInline()) { // We're given a RenderSVGInline or objects that derive from it (RenderSVGTSpan / RenderSVGTextPath) RenderInline* renderInline = toRenderInline(renderer); // RenderSVGInline only ever contains a single line box. InlineFlowBox* flowBox = renderInline->firstLineBox(); ASSERT(flowBox == renderInline->lastLineBox()); return flowBox; } ASSERT_NOT_REACHED(); return 0; }
void LayoutState::establishLineGrid(RenderBlockFlow* block) { // First check to see if this grid has been established already. if (m_lineGrid) { if (m_lineGrid->style().lineGrid() == block->style().lineGrid()) return; RenderBlockFlow* currentGrid = m_lineGrid; for (LayoutState* currentState = m_next.get(); currentState; currentState = currentState->m_next.get()) { if (currentState->m_lineGrid == currentGrid) continue; currentGrid = currentState->m_lineGrid; if (!currentGrid) break; if (currentGrid->style().lineGrid() == block->style().lineGrid()) { m_lineGrid = currentGrid; m_lineGridOffset = currentState->m_lineGridOffset; return; } } } // We didn't find an already-established grid with this identifier. Our render object establishes the grid. m_lineGrid = block; m_lineGridOffset = m_layoutOffset; }
TextFragmentIterator::TextFragmentIterator(const RenderBlockFlow& flow) : m_flowContents(flow) , m_currentSegment(m_flowContents.begin()) , m_lineBreakIterator(m_currentSegment->text, flow.style().locale()) , m_style(flow.style()) { }
inline static float availableWidthAtOffset(const RenderBlockFlow& block, const LayoutUnit& offset, IndentTextOrNot shouldIndentText, float& newLineLeft, float& newLineRight, const LayoutUnit& lineHeight = 0) { newLineLeft = block.logicalLeftOffsetForLine(offset, shouldIndentText, lineHeight); newLineRight = block.logicalRightOffsetForLine(offset, shouldIndentText, lineHeight); return std::max(0.0f, newLineRight - newLineLeft); }
LayoutUnit RenderMultiColumnSet::columnGap() const { RenderBlockFlow* parentBlock = multiColumnBlockFlow(); if (parentBlock->style()->hasNormalColumnGap()) return parentBlock->style()->fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins. return parentBlock->style()->columnGap(); }
static RenderBlockFlow* createAnonymousRubyInlineBlock(RenderObject* ruby) { RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(ruby->style(), INLINE_BLOCK); RenderBlockFlow* newBlock = RenderBlockFlow::createAnonymous(&ruby->document()); newBlock->setStyle(newStyle.release()); return newBlock; }
ShapeOutsideDeltas ShapeOutsideInfo::computeDeltasForContainingBlockLine(const RenderBlockFlow& containingBlock, const FloatingObject& floatingObject, LayoutUnit lineTop, LayoutUnit lineHeight) { ASSERT(lineHeight >= 0); LayoutUnit borderBoxTop = containingBlock.logicalTopForFloat(floatingObject) + containingBlock.marginBeforeForChild(m_renderer); LayoutUnit borderBoxLineTop = lineTop - borderBoxTop; if (isShapeDirty() || !m_shapeOutsideDeltas.isForLine(borderBoxLineTop, lineHeight)) { LayoutUnit referenceBoxLineTop = borderBoxLineTop - logicalTopOffset(); LayoutUnit floatMarginBoxWidth = std::max<LayoutUnit>(LayoutUnit(), containingBlock.logicalWidthForFloat(floatingObject)); if (computedShape().lineOverlapsShapeMarginBounds(referenceBoxLineTop, lineHeight)) { LineSegment segment = computedShape().getExcludedInterval((borderBoxLineTop - logicalTopOffset()), std::min(lineHeight, shapeLogicalBottom() - borderBoxLineTop)); if (segment.isValid) { LayoutUnit logicalLeftMargin = containingBlock.style().isLeftToRightDirection() ? containingBlock.marginStartForChild(m_renderer) : containingBlock.marginEndForChild(m_renderer); LayoutUnit rawLeftMarginBoxDelta = segment.logicalLeft + logicalLeftOffset() + logicalLeftMargin; LayoutUnit leftMarginBoxDelta = clampTo<LayoutUnit>(rawLeftMarginBoxDelta, LayoutUnit(), floatMarginBoxWidth); LayoutUnit logicalRightMargin = containingBlock.style().isLeftToRightDirection() ? containingBlock.marginEndForChild(m_renderer) : containingBlock.marginStartForChild(m_renderer); LayoutUnit rawRightMarginBoxDelta = segment.logicalRight + logicalLeftOffset() - containingBlock.logicalWidthForChild(m_renderer) - logicalRightMargin; LayoutUnit rightMarginBoxDelta = clampTo<LayoutUnit>(rawRightMarginBoxDelta, -floatMarginBoxWidth, LayoutUnit()); m_shapeOutsideDeltas = ShapeOutsideDeltas(leftMarginBoxDelta, rightMarginBoxDelta, true, borderBoxLineTop, lineHeight); return m_shapeOutsideDeltas; } } // Lines that do not overlap the shape should act as if the float // wasn't there for layout purposes. So we set the deltas to remove the // entire width of the float m_shapeOutsideDeltas = ShapeOutsideDeltas(floatMarginBoxWidth, -floatMarginBoxWidth, false, borderBoxLineTop, lineHeight); } return m_shapeOutsideDeltas; }
void RenderMultiColumnFlowThread::evacuateAndDestroy() { RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); m_beingEvacuated = true; // Delete the line box tree. deleteLines(); LayoutStateDisabler layoutStateDisabler(&view()); // First promote all children of the flow thread. Before we move them to the flow thread's // container, we need to unregister the flow thread, so that they aren't just re-added again to // the flow thread that we're trying to empty. multicolContainer->setMultiColumnFlowThread(nullptr); moveAllChildrenTo(multicolContainer, true); // Move spanners back to their original DOM position in the tree, and destroy the placeholders. SpannerMap::iterator it; while ((it = m_spannerMap.begin()) != m_spannerMap.end()) { RenderBox* spanner = it->key; RenderMultiColumnSpannerPlaceholder* placeholder = it->value; RenderBlockFlow* originalContainer = toRenderBlockFlow(placeholder->parent()); multicolContainer->removeChild(*spanner); originalContainer->addChild(spanner, placeholder); placeholder->destroy(); m_spannerMap.remove(it); } // Remove all sets. while (RenderMultiColumnSet* columnSet = firstMultiColumnSet()) columnSet->destroy(); destroy(); }
LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) const { RenderBlockFlow* multicolBlock = multiColumnBlockFlow(); LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderBefore() - multicolBlock->paddingBefore(); height -= contentLogicalTop; return max(height, LayoutUnit(1)); // Let's avoid zero height, as that would probably cause an infinite amount of columns to be created. }
LayoutUnit RenderMultiColumnSet::columnGap() const { // FIXME: Eventually we will cache the column gap when the widths of columns start varying, but for now we just // go to the parent block to get the gap. RenderBlockFlow* parentBlock = toRenderBlockFlow(parent()); if (parentBlock->style().hasNormalColumnGap()) return parentBlock->style().fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins. return parentBlock->style().columnGap(); }
void RenderMultiColumnSet::updateLogicalWidth() { RenderBlockFlow* parentBlock = toRenderBlockFlow(parent()); setComputedColumnWidthAndCount(parentBlock->multiColumnFlowThread()->columnWidth(), parentBlock->multiColumnFlowThread()->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()); }
void collectFlowOverflow(RenderBlockFlow& flow, const Layout& layout) { auto resolver = lineResolver(flow, layout); for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) { auto rect = *it; flow.addLayoutOverflow(rect); flow.addVisualOverflow(rect); } }
FloatPoint InlineBox::locationIncludingFlipping() { if (!renderer()->style()->isFlippedBlocksWritingMode()) return FloatPoint(x(), y()); RenderBlockFlow* block = root()->block(); if (block->style()->isHorizontalWritingMode()) return FloatPoint(x(), block->height() - height() - y()); else return FloatPoint(block->width() - width() - x(), y()); }
void collectFlowOverflow(RenderBlockFlow& flow, const Layout& layout) { float strokeOverflow = std::ceil(flow.style().textStrokeWidth()); for (FloatRect lineRect : lineResolver(flow, layout)) { LayoutRect inflatedLineRect(lineRect); inflatedLineRect.inflate(strokeOverflow); flow.addLayoutOverflow(inflatedLineRect); flow.addVisualOverflow(inflatedLineRect); } }
void RenderMultiColumnFlowThread::populate() { RenderBlockFlow* multicolContainer = multiColumnBlockFlow(); ASSERT(!nextSibling()); // Reparent children preceding the flow thread into the flow thread. It's multicol content // now. At this point there's obviously nothing after the flow thread, but renderers (column // sets and spanners) will be inserted there as we insert elements into the flow thread. LayoutStateDisabler layoutStateDisabler(&view()); multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), this, true); }
void paintFlow(const RenderBlockFlow& flow, const Layout& layout, PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (paintInfo.phase != PaintPhaseForeground) return; RenderStyle& style = flow.style(); if (style.visibility() != VISIBLE) return; bool debugBordersEnabled = flow.frame().settings().simpleLineLayoutDebugBordersEnabled(); TextPainter textPainter(paintInfo.context()); textPainter.setFont(style.fontCascade()); textPainter.setTextPaintStyle(computeTextPaintStyle(flow.frame(), style, paintInfo)); Optional<TextDecorationPainter> textDecorationPainter; if (style.textDecorationsInEffect() != TextDecorationNone) { const RenderText* textRenderer = childrenOfType<RenderText>(flow).first(); if (textRenderer) { textDecorationPainter = TextDecorationPainter(paintInfo.context(), style.textDecorationsInEffect(), *textRenderer, false); textDecorationPainter->setFont(style.fontCascade()); textDecorationPainter->setBaseline(style.fontMetrics().ascent()); } } LayoutRect paintRect = paintInfo.rect; paintRect.moveBy(-paintOffset); auto resolver = runResolver(flow, layout); float strokeOverflow = std::ceil(flow.style().textStrokeWidth()); float deviceScaleFactor = flow.document().deviceScaleFactor(); for (auto run : resolver.rangeForRect(paintRect)) { if (run.start() == run.end()) continue; FloatRect rect = run.rect(); FloatRect visualOverflowRect = rect; visualOverflowRect.inflate(strokeOverflow); if (paintRect.y() > visualOverflowRect.maxY() || paintRect.maxY() < visualOverflowRect.y()) continue; TextRun textRun(run.text()); textRun.setTabSize(!style.collapseWhiteSpace(), style.tabSize()); // x position indicates the line offset from the rootbox. It's always 0 in case of simple line layout. textRun.setXPos(0); FloatPoint textOrigin = FloatPoint(rect.x() + paintOffset.x(), roundToDevicePixel(run.baselinePosition() + paintOffset.y(), deviceScaleFactor)); textPainter.paintText(textRun, textRun.length(), rect, textOrigin); if (textDecorationPainter) { textDecorationPainter->setWidth(rect.width()); textDecorationPainter->paintTextDecoration(textRun, textOrigin, rect.location() + paintOffset); } if (debugBordersEnabled) paintDebugBorders(paintInfo.context(), LayoutRect(run.rect()), paintOffset); } }
void collectFlowOverflow(RenderBlockFlow& flow, const Layout& layout) { auto resolver = lineResolver(flow, layout); float strokeOverflow = ceilf(flow.style().textStrokeWidth()); for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) { auto rect = LayoutRect(*it); rect.inflate(strokeOverflow); flow.addLayoutOverflow(rect); flow.addVisualOverflow(rect); } }
static bool newFloatShrinksLine(const FloatingObject& newFloat, const RenderBlockFlow& block, bool isFirstLine) { LayoutUnit blockOffset = block.logicalHeight(); if (blockOffset >= block.logicalTopForFloat(newFloat) && blockOffset < block.logicalBottomForFloat(newFloat)) return true; // initial-letter float always shrinks the first line. const auto& style = newFloat.renderer().style(); if (isFirstLine && style.styleType() == FIRST_LETTER && !style.initialLetter().isEmpty()) return true; return false; }
RunResolver::RunResolver(const RenderBlockFlow& flow, const Layout& layout) : m_flowRenderer(flow) , m_layout(layout) , m_flowContents(flow) , m_lineHeight(lineHeightFromFlow(flow)) , m_baseline(baselineFromFlow(flow)) , m_borderAndPaddingBefore(flow.borderAndPaddingBefore()) , m_ascent(flow.style().fontCascade().fontMetrics().ascent()) , m_descent(flow.style().fontCascade().fontMetrics().descent()) , m_visualOverflowOffset(visualOverflowForDecorations(flow.style(), nullptr).bottom) , m_inQuirksMode(flow.document().inQuirksMode()) { }
LayoutUnit RenderMultiColumnSet::calculateMaxColumnHeight() const { RenderBlockFlow* multicolBlock = multiColumnBlockFlow(); RenderStyle* multicolStyle = multicolBlock->style(); LayoutUnit availableHeight = multiColumnFlowThread()->columnHeightAvailable(); LayoutUnit maxColumnHeight = availableHeight ? availableHeight : RenderFlowThread::maxLogicalHeight(); if (!multicolStyle->logicalMaxHeight().isUndefined()) { LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(multicolStyle->logicalMaxHeight(), -1); if (logicalMaxHeight != -1 && maxColumnHeight > logicalMaxHeight) maxColumnHeight = logicalMaxHeight; } return heightAdjustedForSetOffset(maxColumnHeight); }
LayoutUnit RenderMultiColumnSet::initialBlockOffsetForPainting() const { RenderBlockFlow* parentFlow = toRenderBlockFlow(parent()); bool progressionReversed = parentFlow->multiColumnFlowThread()->progressionIsReversed(); bool progressionIsInline = parentFlow->multiColumnFlowThread()->progressionIsInline(); LayoutUnit result = 0; if (!progressionIsInline && progressionReversed) { LayoutRect colRect = columnRectAt(0); result = isHorizontalWritingMode() ? colRect.y() : colRect.x(); if (style().isFlippedBlocksWritingMode()) result = -result; } return result; }
RootInlineBox::RootInlineBox(RenderBlockFlow& block) : InlineFlowBox(block) , m_lineBreakPos(0) , m_lineBreakObj(nullptr) { setIsHorizontal(block.isHorizontalWritingMode()); }
FloatingObjects::FloatingObjects(const RenderBlockFlow& renderer) : m_leftObjectsCount(0) , m_rightObjectsCount(0) , m_horizontalWritingMode(renderer.isHorizontalWritingMode()) , m_renderer(renderer) { }
static AvoidanceReasonFlags canUseForFontAndText(const RenderBlockFlow& flow, FallThrough fallthrough) { AvoidanceReasonFlags reasons = NoReason; // We assume that all lines have metrics based purely on the primary font. const auto& style = flow.style(); auto& primaryFont = style.fontCascade().primaryFont(); if (primaryFont.isLoading()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowIsMissingPrimaryFont, fallthrough); if (primaryFont.isSVGFont()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowHasSVGFont, fallthrough); for (const auto& textRenderer : childrenOfType<RenderText>(flow)) { if (textRenderer.isCombineText()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowTextIsCombineText, fallthrough); if (textRenderer.isCounter()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowTextIsRenderCounter, fallthrough); if (textRenderer.isQuote()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowTextIsRenderQuote, fallthrough); if (textRenderer.isTextFragment()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowTextIsTextFragment, fallthrough); if (textRenderer.isSVGInlineText()) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowTextIsSVGInlineText, fallthrough); if (style.fontCascade().codePath(TextRun(textRenderer.text())) != FontCascade::Simple) SET_REASON_AND_RETURN_IF_NEEDED(reasons, FlowFontIsNotSimple, fallthrough); auto textReasons = canUseForText(textRenderer, primaryFont, fallthrough); if (textReasons != NoReason) SET_REASON_AND_RETURN_IF_NEEDED(reasons, textReasons, fallthrough); } return reasons; }
static void createTextRuns(Layout::RunVector& runs, RenderBlockFlow& flow, unsigned& lineCount) { LayoutUnit borderAndPaddingBefore = flow.borderAndPaddingBefore(); LayoutUnit lineHeight = lineHeightFromFlow(flow); LineState line; bool isEndOfContent = false; TextFragmentIterator textFragmentIterator = TextFragmentIterator(flow); do { flow.setLogicalHeight(lineHeight * lineCount + borderAndPaddingBefore); LineState previousLine = line; unsigned previousRunCount = runs.size(); line = LineState(); updateLineConstrains(flow, line, !lineCount); isEndOfContent = createLineRuns(line, previousLine, runs, textFragmentIterator); closeLineEndingAndAdjustRuns(line, runs, previousRunCount, lineCount, textFragmentIterator); } while (!isEndOfContent); }
LayoutUnit RenderMultiColumnSet::heightAdjustedForSetOffset(LayoutUnit height) const { // Adjust for the top offset within the content box of the multicol container (containing // block), unless this is the first set. We know that the top offset for the first set will be // zero, but if the multicol container has non-zero top border or padding, the set's top offset // (initially being 0 and relative to the border box) will be negative until it has been laid // out. Had we used this bogus offset, we would calculate the wrong height, and risk performing // a wasted layout iteration. Of course all other sets (if any) have this problem in the first // layout pass too, but there's really nothing we can do there until the flow thread has been // laid out anyway. if (previousSiblingMultiColumnSet()) { RenderBlockFlow* multicolBlock = multiColumnBlockFlow(); LayoutUnit contentLogicalTop = logicalTop() - multicolBlock->borderAndPaddingBefore(); height -= contentLogicalTop; } return max(height, LayoutUnit(1)); // Let's avoid zero height, as that would probably cause an infinite amount of columns to be created. }
void RenderMultiColumnSet::prepareForLayout() { RenderBlockFlow* multicolBlock = toRenderBlockFlow(parent()); const RenderStyle& multicolStyle = multicolBlock->style(); // Set box logical top. ASSERT(!previousSiblingBox() || !previousSiblingBox()->isRenderMultiColumnSet()); // FIXME: multiple set not implemented; need to examine previous set to calculate the correct logical top. setLogicalTop(multicolBlock->borderAndPaddingBefore()); // Set box width. updateLogicalWidth(); if (multicolBlock->multiColumnFlowThread()->requiresBalancing()) { // Set maximum column height. We will not stretch beyond this. m_maxColumnHeight = RenderFlowThread::maxLogicalHeight(); if (!multicolStyle.logicalHeight().isAuto()) { m_maxColumnHeight = multicolBlock->computeContentLogicalHeight(multicolStyle.logicalHeight()); if (m_maxColumnHeight == -1) m_maxColumnHeight = RenderFlowThread::maxLogicalHeight(); } if (!multicolStyle.logicalMaxHeight().isUndefined()) { LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(multicolStyle.logicalMaxHeight()); if (logicalMaxHeight != -1 && m_maxColumnHeight > logicalMaxHeight) m_maxColumnHeight = logicalMaxHeight; } m_maxColumnHeight = heightAdjustedForSetOffset(m_maxColumnHeight); m_computedColumnHeight = 0; // Restart balancing. } else setAndConstrainColumnHeight(heightAdjustedForSetOffset(multicolBlock->multiColumnFlowThread()->columnHeightAvailable())); clearForcedBreaks(); // Nuke previously stored minimum column height. Contents may have changed for all we know. m_minimumColumnHeight = 0; }
void RenderMultiColumnSet::adjustRegionBoundsFromFlowThreadPortionRect(const LayoutPoint& layerOffset, LayoutRect& regionBounds) { LayoutUnit layerLogicalTop = isHorizontalWritingMode() ? layerOffset.y() : layerOffset.x(); unsigned startColumn = columnIndexAtOffset(layerLogicalTop); LayoutUnit colGap = columnGap(); LayoutUnit colLogicalWidth = computedColumnWidth(); LayoutRect flowThreadPortion = flowThreadPortionRectAt(startColumn); LayoutPoint translationOffset; RenderBlockFlow* parentFlow = toRenderBlockFlow(parent()); bool progressionReversed = parentFlow->multiColumnFlowThread()->progressionIsReversed(); bool progressionIsInline = parentFlow->multiColumnFlowThread()->progressionIsInline(); LayoutUnit initialBlockOffset = initialBlockOffsetForPainting(); LayoutUnit inlineOffset = progressionIsInline ? startColumn * (colLogicalWidth + colGap) : LayoutUnit(); bool leftToRight = style().isLeftToRightDirection() ^ progressionReversed; if (!leftToRight) { inlineOffset = -inlineOffset; if (progressionReversed) inlineOffset += contentLogicalWidth() - colLogicalWidth; } translationOffset.setX(inlineOffset); LayoutUnit blockOffset = initialBlockOffset + (isHorizontalWritingMode() ? -flowThreadPortion.y() : -flowThreadPortion.x()); if (!progressionIsInline) { if (!progressionReversed) blockOffset = startColumn * colGap; else blockOffset -= startColumn * (computedColumnHeight() + colGap); } if (isFlippedBlocksWritingMode(style().writingMode())) blockOffset = -blockOffset; translationOffset.setY(blockOffset); if (!isHorizontalWritingMode()) translationOffset = translationOffset.transposedPoint(); // FIXME: The translation needs to include the multicolumn set's content offset within the // multicolumn block as well. This won't be an issue until we start creating multiple multicolumn sets. regionBounds.moveBy(roundedIntPoint(-translationOffset)); }