LayoutRect MultiColumnFragmentainerGroup::fragmentsBoundingBox(const LayoutRect& boundingBoxInFlowThread) const
{
    // Find the start and end column intersected by the bounding box.
    LayoutRect flippedBoundingBoxInFlowThread(boundingBoxInFlowThread);
    LayoutFlowThread* flowThread = m_columnSet.flowThread();
    flowThread->flipForWritingMode(flippedBoundingBoxInFlowThread);
    bool isHorizontalWritingMode = m_columnSet.isHorizontalWritingMode();
    LayoutUnit boundingBoxLogicalTop = isHorizontalWritingMode ? flippedBoundingBoxInFlowThread.y() : flippedBoundingBoxInFlowThread.x();
    LayoutUnit boundingBoxLogicalBottom = isHorizontalWritingMode ? flippedBoundingBoxInFlowThread.maxY() : flippedBoundingBoxInFlowThread.maxX();
    if (boundingBoxLogicalBottom <= logicalTopInFlowThread() || boundingBoxLogicalTop >= logicalBottomInFlowThread())
        return LayoutRect(); // The bounding box doesn't intersect this fragmentainer group.
    unsigned startColumn;
    unsigned endColumn;
    columnIntervalForBlockRangeInFlowThread(boundingBoxLogicalTop, boundingBoxLogicalBottom, startColumn, endColumn);

    LayoutRect startColumnFlowThreadOverflowPortion = flowThreadPortionOverflowRectAt(startColumn);
    flowThread->flipForWritingMode(startColumnFlowThreadOverflowPortion);
    LayoutRect startColumnRect(boundingBoxInFlowThread);
    startColumnRect.intersect(startColumnFlowThreadOverflowPortion);
    startColumnRect.move(flowThreadTranslationAtOffset(logicalTopInFlowThreadAt(startColumn)));
    if (startColumn == endColumn)
        return startColumnRect; // It all takes place in one column. We're done.

    LayoutRect endColumnFlowThreadOverflowPortion = flowThreadPortionOverflowRectAt(endColumn);
    flowThread->flipForWritingMode(endColumnFlowThreadOverflowPortion);
    LayoutRect endColumnRect(boundingBoxInFlowThread);
    endColumnRect.intersect(endColumnFlowThreadOverflowPortion);
    endColumnRect.move(flowThreadTranslationAtOffset(logicalTopInFlowThreadAt(endColumn)));
    return unionRect(startColumnRect, endColumnRect);
}
LayoutPoint MultiColumnFragmentainerGroup::visualPointToFlowThreadPoint(const LayoutPoint& visualPoint) const
{
    unsigned columnIndex = columnIndexAtVisualPoint(visualPoint);
    LayoutRect columnRect = columnRectAt(columnIndex);
    LayoutPoint localPoint(visualPoint);
    localPoint.moveBy(-columnRect.location());
    // Before converting to a flow thread position, if the block direction coordinate is outside the
    // column, snap to the bounds of the column, and reset the inline direction coordinate to the
    // start position in the column. The effect of this is that if the block position is before the
    // column rectangle, we'll get to the beginning of this column, while if the block position is
    // after the column rectangle, we'll get to the beginning of the next column.
    if (!m_columnSet.isHorizontalWritingMode()) {
        LayoutUnit columnStart = m_columnSet.style()->isLeftToRightDirection() ? LayoutUnit() : columnRect.height();
        if (localPoint.x() < 0)
            localPoint = LayoutPoint(LayoutUnit(), columnStart);
        else if (localPoint.x() > logicalHeight())
            localPoint = LayoutPoint(logicalHeight(), columnStart);
        return LayoutPoint(localPoint.x() + logicalTopInFlowThreadAt(columnIndex), localPoint.y());
    }
    LayoutUnit columnStart = m_columnSet.style()->isLeftToRightDirection() ? LayoutUnit() : columnRect.width();
    if (localPoint.y() < 0)
        localPoint = LayoutPoint(columnStart, LayoutUnit());
    else if (localPoint.y() > logicalHeight())
        localPoint = LayoutPoint(columnStart, logicalHeight());
    return LayoutPoint(localPoint.x(), localPoint.y() + logicalTopInFlowThreadAt(columnIndex));
}
void MultiColumnFragmentainerGroup::columnIntervalForBlockRangeInFlowThread(LayoutUnit logicalTopInFlowThread, LayoutUnit logicalBottomInFlowThread, unsigned& firstColumn, unsigned& lastColumn) const
{
    ASSERT(logicalTopInFlowThread <= logicalBottomInFlowThread);
    firstColumn = columnIndexAtOffset(logicalTopInFlowThread);
    lastColumn = columnIndexAtOffset(logicalBottomInFlowThread);
    // logicalBottomInFlowThread is an exclusive endpoint, so some additional adjustments may be necessary.
    if (lastColumn > firstColumn && logicalTopInFlowThreadAt(lastColumn) == logicalBottomInFlowThread)
        lastColumn--;
}
LayoutRect MultiColumnFragmentainerGroup::flowThreadPortionRectAt(unsigned columnIndex) const
{
    LayoutUnit logicalTop = logicalTopInFlowThreadAt(columnIndex);
    LayoutUnit logicalBottom = logicalTop + m_columnHeight;
    if (logicalBottom > logicalBottomInFlowThread()) {
        // The last column may not be using all available space.
        ASSERT(columnIndex + 1 == actualColumnCount());
        logicalBottom = logicalBottomInFlowThread();
        ASSERT(logicalBottom >= logicalTop);
    }
    LayoutUnit portionLogicalHeight = logicalBottom - logicalTop;
    if (m_columnSet.isHorizontalWritingMode())
        return LayoutRect(LayoutUnit(), logicalTop, m_columnSet.pageLogicalWidth(), portionLogicalHeight);
    return LayoutRect(logicalTop, LayoutUnit(), portionLogicalHeight, m_columnSet.pageLogicalWidth());
}
unsigned MultiColumnFragmentainerGroup::columnIndexAtOffset(
    LayoutUnit offsetInFlowThread,
    LayoutBox::PageBoundaryRule pageBoundaryRule) const {
  // Handle the offset being out of range.
  if (offsetInFlowThread < m_logicalTopInFlowThread)
    return 0;

  if (!m_columnHeight)
    return 0;
  unsigned columnIndex =
      ((offsetInFlowThread - m_logicalTopInFlowThread) / m_columnHeight)
          .floor();
  if (pageBoundaryRule == LayoutBox::AssociateWithFormerPage &&
      columnIndex > 0 &&
      logicalTopInFlowThreadAt(columnIndex) == offsetInFlowThread) {
    // We are exactly at a column boundary, and we've been told to associate
    // offsets at column boundaries with the former column, not the latter.
    columnIndex--;
  }
  return columnIndex;
}
LayoutUnit MultiColumnFragmentainerGroup::columnLogicalTopForOffset(LayoutUnit offsetInFlowThread) const
{
    unsigned columnIndex = columnIndexAtOffset(offsetInFlowThread, AssumeNewColumns);
    return logicalTopInFlowThreadAt(columnIndex);
}
void MultiColumnFragmentainerGroup::collectLayerFragments(PaintLayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) const
{
    // |layerBoundingBox| is in the flow thread coordinate space, relative to the top/left edge of
    // the flow thread, but note that it has been converted with respect to writing mode (so that
    // it's visual/physical in that sense).
    //
    // |dirtyRect| is visual, relative to the multicol container.
    //
    // Then there's the output from this method - the stuff we put into the list of fragments. The
    // fragment.paginationOffset point is the actual visual translation required to get from a
    // location in the flow thread to a location in a given column. The fragment.paginationClip
    // rectangle, on the other hand, is in flow thread coordinates, but otherwise completely
    // physical in terms of writing mode.

    LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread();
    bool isHorizontalWritingMode = m_columnSet.isHorizontalWritingMode();

    // Put the layer bounds into flow thread-local coordinates by flipping it first. Since we're in
    // a layoutObject, most rectangles are represented this way.
    LayoutRect layerBoundsInFlowThread(layerBoundingBox);
    flowThread->flipForWritingMode(layerBoundsInFlowThread);

    // Now we can compare with the flow thread portions owned by each column. First let's
    // see if the rect intersects our flow thread portion at all.
    LayoutRect clippedRect(layerBoundsInFlowThread);
    clippedRect.intersect(m_columnSet.flowThreadPortionOverflowRect());
    if (clippedRect.isEmpty())
        return;

    // Now we know we intersect at least one column. Let's figure out the logical top and logical
    // bottom of the area we're checking.
    LayoutUnit layerLogicalTop = isHorizontalWritingMode ? layerBoundsInFlowThread.y() : layerBoundsInFlowThread.x();
    LayoutUnit layerLogicalBottom = (isHorizontalWritingMode ? layerBoundsInFlowThread.maxY() : layerBoundsInFlowThread.maxX());

    // Figure out the start and end columns for the layer and only check within that range so that
    // we don't walk the entire column row.
    unsigned startColumn;
    unsigned endColumn;
    columnIntervalForBlockRangeInFlowThread(layerLogicalTop, layerLogicalBottom, startColumn, endColumn);

    // Now intersect with the columns actually occupied by the dirty rect, to narrow it down even further.
    unsigned firstColumnInDirtyRect, lastColumnInDirtyRect;
    columnIntervalForVisualRect(dirtyRect, firstColumnInDirtyRect, lastColumnInDirtyRect);
    if (firstColumnInDirtyRect > endColumn || lastColumnInDirtyRect < startColumn)
        return; // The two column intervals are disjoint. There's nothing to collect.
    if (startColumn < firstColumnInDirtyRect)
        startColumn = firstColumnInDirtyRect;
    if (endColumn > lastColumnInDirtyRect)
        endColumn = lastColumnInDirtyRect;
    ASSERT(endColumn >= startColumn);

    for (unsigned i = startColumn; i <= endColumn; i++) {
        PaintLayerFragment fragment;

        // Set the physical translation offset.
        fragment.paginationOffset = toLayoutPoint(flowThreadTranslationAtOffset(logicalTopInFlowThreadAt(i)));

        // Set the overflow clip rect that corresponds to the column.
        fragment.paginationClip = flowThreadPortionOverflowRectAt(i);
        // Flip it into more a physical (PaintLayer-style) rectangle.
        flowThread->flipForWritingMode(fragment.paginationClip);

        fragments.append(fragment);
    }
}
LayoutUnit MultiColumnFragmentainerGroup::columnLogicalTopForOffset(
    LayoutUnit offsetInFlowThread) const {
  unsigned columnIndex = columnIndexAtOffset(
      offsetInFlowThread, LayoutBox::AssociateWithLatterPage);
  return logicalTopInFlowThreadAt(columnIndex);
}