LayoutRect LayoutMultiColumnSet::flowThreadPortionRect() const
{
    LayoutRect portionRect(LayoutUnit(), logicalTopInFlowThread(), pageLogicalWidth(), logicalHeightInFlowThread());
    if (!isHorizontalWritingMode())
        return portionRect.transposedRect();
    return portionRect;
}
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);
}
LayoutRect MultiColumnFragmentainerGroup::columnRectAt(unsigned columnIndex) const
{
    LayoutUnit columnLogicalWidth = m_columnSet.pageLogicalWidth();
    LayoutUnit columnLogicalHeight = m_columnHeight;
    LayoutUnit columnLogicalTop;
    LayoutUnit columnLogicalLeft;
    LayoutUnit columnGap = m_columnSet.columnGap();
    LayoutUnit portionOutsideFlowThread = logicalTopInFlowThread() + (columnIndex + 1) * columnLogicalHeight - logicalBottomInFlowThread();
    if (portionOutsideFlowThread > 0) {
        // The last column may not be using all available space.
        ASSERT(columnIndex + 1 == actualColumnCount());
        columnLogicalHeight -= portionOutsideFlowThread;
        ASSERT(columnLogicalHeight >= 0);
    }

    if (m_columnSet.multiColumnFlowThread()->progressionIsInline()) {
        if (m_columnSet.style()->isLeftToRightDirection())
            columnLogicalLeft += columnIndex * (columnLogicalWidth + columnGap);
        else
            columnLogicalLeft += m_columnSet.contentLogicalWidth() - columnLogicalWidth - columnIndex * (columnLogicalWidth + columnGap);
    } else {
        columnLogicalTop += columnIndex * (m_columnHeight + columnGap);
    }

    LayoutRect columnRect(columnLogicalLeft, columnLogicalTop, columnLogicalWidth, columnLogicalHeight);
    if (!m_columnSet.isHorizontalWritingMode())
        return columnRect.transposedRect();
    return columnRect;
}
Esempio n. 4
0
void ColumnBalancer::traverseLines(const LayoutBlockFlow& blockFlow) {
  for (const RootInlineBox* line = blockFlow.firstRootBox(); line;
       line = line->nextRootBox()) {
    LayoutUnit lineTopInFlowThread =
        m_flowThreadOffset + line->lineTopWithLeading();
    if (lineTopInFlowThread < logicalTopInFlowThread())
      continue;
    if (lineTopInFlowThread >= logicalBottomInFlowThread())
      break;
    examineLine(*line);
  }
}
Esempio n. 5
0
LayoutUnit InitialColumnHeightFinder::initialMinimalBalancedHeight() const {
  LayoutUnit rowLogicalTop;
  if (m_contentRuns.size() > columnSet().usedColumnCount()) {
    // We have not inserted additional fragmentainer groups yet (because we
    // aren't able to calculate their constraints yet), but we already know for
    // sure that there'll be more than one of them, due to the number of forced
    // breaks in a nested multicol container. We will now attempt to take all
    // the imaginary rows into account and calculate a minimal balanced logical
    // height for everything.
    unsigned stride = columnSet().usedColumnCount();
    LayoutUnit rowStartOffset = logicalTopInFlowThread();
    for (unsigned i = 0; i < firstContentRunIndexInLastRow(); i += stride) {
      LayoutUnit rowEndOffset = m_contentRuns[i + stride - 1].breakOffset();
      float rowHeight = float(rowEndOffset - rowStartOffset) / float(stride);
      rowLogicalTop += LayoutUnit::fromFloatCeil(rowHeight);
      rowStartOffset = rowEndOffset;
    }
  }
  unsigned index = contentRunIndexWithTallestColumns();
  LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset()
                                     : logicalTopInFlowThread();
  LayoutUnit height = m_contentRuns[index].columnLogicalHeight(startOffset);
  return rowLogicalTop + std::max(height, m_tallestUnbreakableLogicalHeight);
}
bool MultiColumnFragmentainerGroup::recalculateColumnHeight(
    LayoutMultiColumnSet& columnSet) {
  LayoutUnit oldColumnHeight = m_columnHeight;

  m_maxColumnHeight = calculateMaxColumnHeight();

  // Only the last row may have auto height, and thus be balanced. There are no
  // good reasons to balance the preceding rows, and that could potentially lead
  // to an insane number of layout passes as well.
  if (isLastGroup() && columnSet.heightIsAuto()) {
    LayoutUnit newColumnHeight;
    if (!columnSet.isInitialHeightCalculated()) {
      // Initial balancing: Start with the lowest imaginable column height. Also
      // calculate the height of the tallest piece of unbreakable content.
      // Columns should never get any shorter than that (unless constrained by
      // max-height). Propagate this to our containing column set, in case there
      // is an outer multicol container that also needs to balance. After having
      // calculated the initial column height, the multicol container needs
      // another layout pass with the column height that we just calculated.
      InitialColumnHeightFinder initialHeightFinder(
          columnSet, logicalTopInFlowThread(), logicalBottomInFlowThread());
      LayoutUnit tallestUnbreakableLogicalHeight =
          initialHeightFinder.tallestUnbreakableLogicalHeight();
      columnSet.propagateTallestUnbreakableLogicalHeight(
          tallestUnbreakableLogicalHeight);
      newColumnHeight =
          std::max(initialHeightFinder.initialMinimalBalancedHeight(),
                   tallestUnbreakableLogicalHeight);
    } else {
      // Rebalancing: After having laid out again, we'll need to rebalance if
      // the height wasn't enough and we're allowed to stretch it, and then
      // re-lay out.  There are further details on the column balancing
      // machinery in ColumnBalancer and its derivates.
      newColumnHeight = rebalanceColumnHeightIfNeeded();
    }
    setAndConstrainColumnHeight(newColumnHeight);
  } else {
    // The position of the column set may have changed, in which case height
    // available for columns may have changed as well.
    setAndConstrainColumnHeight(m_columnHeight);
  }

  if (m_columnHeight == oldColumnHeight)
    return false;  // No change. We're done.

  return true;  // Need another pass.
}
Esempio n. 7
0
unsigned InitialColumnHeightFinder::contentRunIndexWithTallestColumns() const {
  unsigned indexWithLargestHeight = 0;
  LayoutUnit largestHeight;
  LayoutUnit previousOffset = logicalTopInFlowThread();
  size_t runCount = m_contentRuns.size();
  ASSERT(runCount);
  for (size_t i = firstContentRunIndexInLastRow(); i < runCount; i++) {
    const ContentRun& run = m_contentRuns[i];
    LayoutUnit height = run.columnLogicalHeight(previousOffset);
    if (largestHeight < height) {
      largestHeight = height;
      indexWithLargestHeight = i;
    }
    previousOffset = run.breakOffset();
  }
  return indexWithLargestHeight;
}
Esempio n. 8
0
unsigned RenderMultiColumnSet::findRunWithTallestColumns() const
{
    unsigned indexWithLargestHeight = 0;
    LayoutUnit largestHeight;
    LayoutUnit previousOffset = logicalTopInFlowThread();
    size_t runCount = m_contentRuns.size();
    ASSERT(runCount);
    for (size_t i = 0; i < runCount; i++) {
        const ContentRun& run = m_contentRuns[i];
        LayoutUnit height = run.columnLogicalHeight(previousOffset);
        if (largestHeight < height) {
            largestHeight = height;
            indexWithLargestHeight = i;
        }
        previousOffset = run.breakOffset();
    }
    return indexWithLargestHeight;
}
LayoutUnit MultiColumnFragmentainerGroup::rebalanceColumnHeightIfNeeded()
    const {
  if (actualColumnCount() <= m_columnSet.usedColumnCount()) {
    // With the current column height, the content fits without creating
    // overflowing columns. We're done.
    return m_columnHeight;
  }

  if (m_columnHeight >= m_maxColumnHeight) {
    // We cannot stretch any further. We'll just have to live with the
    // overflowing columns. This typically happens if the max column height is
    // less than the height of the tallest piece of unbreakable content (e.g.
    // lines).
    return m_columnHeight;
  }

  MinimumSpaceShortageFinder shortageFinder(
      columnSet(), logicalTopInFlowThread(), logicalBottomInFlowThread());

  if (shortageFinder.forcedBreaksCount() + 1 >= m_columnSet.usedColumnCount()) {
    // Too many forced breaks to allow any implicit breaks. Initial balancing
    // should already have set a good height. There's nothing more we should do.
    return m_columnHeight;
  }

  // If the initial guessed column height wasn't enough, stretch it now. Stretch
  // by the lowest amount of space.
  LayoutUnit minSpaceShortage = shortageFinder.minimumSpaceShortage();

  ASSERT(minSpaceShortage > 0);  // We should never _shrink_ the height!
  ASSERT(minSpaceShortage !=
         LayoutUnit::max());  // If this happens, we probably have a bug.
  if (minSpaceShortage == LayoutUnit::max())
    return m_columnHeight;  // So bail out rather than looping infinitely.

  return m_columnHeight + minSpaceShortage;
}
Esempio n. 10
0
void ColumnBalancer::traverseChildren(const LayoutObject& object) {
  // The break-after value from the previous in-flow block-level object to be
  // joined with the break-before value of the next in-flow block-level sibling.
  EBreak previousBreakAfterValue = BreakAuto;

  for (const LayoutObject* child = object.slowFirstChild(); child;
       child = child->nextSibling()) {
    if (!child->isBox()) {
      // Keep traversing inside inlines. There may be floats there.
      if (child->isLayoutInline())
        traverseChildren(*child);
      continue;
    }

    const LayoutBox& childBox = toLayoutBox(*child);

    LayoutUnit borderEdgeOffset;
    LayoutUnit logicalTop = childBox.logicalTop();
    LayoutUnit logicalHeight = childBox.logicalHeightWithVisibleOverflow();
    // Floats' margins don't collapse with column boundaries, and we don't want
    // to break inside them, or separate them from the float's border box. Set
    // the offset to the margin-before edge (rather than border-before edge),
    // and include the block direction margins in the child height.
    if (childBox.isFloating()) {
      LayoutUnit marginBefore = childBox.marginBefore(object.style());
      LayoutUnit marginAfter = childBox.marginAfter(object.style());
      logicalHeight =
          std::max(logicalHeight, childBox.logicalHeight() + marginAfter);
      logicalTop -= marginBefore;
      logicalHeight += marginBefore;

      // As soon as we want to process content inside this child, though, we
      // need to get to its border-before edge.
      borderEdgeOffset = marginBefore;
    }

    if (m_flowThreadOffset + logicalTop + logicalHeight <=
        logicalTopInFlowThread()) {
      // This child is fully above the flow thread portion we're examining.
      continue;
    }
    if (m_flowThreadOffset + logicalTop >= logicalBottomInFlowThread()) {
      // This child is fully below the flow thread portion we're examining. We
      // cannot just stop here, though, thanks to negative margins.
      // So keep looking.
      continue;
    }
    if (childBox.isOutOfFlowPositioned() || childBox.isColumnSpanAll())
      continue;

    // Tables are wicked. Both table rows and table cells are relative to their
    // table section.
    LayoutUnit offsetForThisChild =
        childBox.isTableRow() ? LayoutUnit() : logicalTop;

    m_flowThreadOffset += offsetForThisChild;

    examineBoxAfterEntering(childBox, logicalHeight, previousBreakAfterValue);
    // Unless the child is unsplittable, or if the child establishes an inner
    // multicol container, we descend into its subtree for further examination.
    if (childBox.getPaginationBreakability() != LayoutBox::ForbidBreaks &&
        (!childBox.isLayoutBlockFlow() ||
         !toLayoutBlockFlow(childBox).multiColumnFlowThread())) {
      // We need to get to the border edge before processing content inside
      // this child. If the child is floated, we're currently at the margin
      // edge.
      m_flowThreadOffset += borderEdgeOffset;
      traverseSubtree(childBox);
      m_flowThreadOffset -= borderEdgeOffset;
    }
    previousBreakAfterValue = childBox.breakAfter();
    examineBoxBeforeLeaving(childBox, logicalHeight);

    m_flowThreadOffset -= offsetForThisChild;
  }
}
Esempio n. 11
0
LayoutUnit InitialColumnHeightFinder::initialMinimalBalancedHeight() const {
  unsigned index = contentRunIndexWithTallestColumns();
  LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset()
                                     : logicalTopInFlowThread();
  return m_contentRuns[index].columnLogicalHeight(startOffset);
}
Esempio n. 12
0
LayoutUnit RenderMultiColumnSet::pageLogicalTopForOffset(LayoutUnit offset) const
{
    unsigned columnIndex = columnIndexAtOffset(offset, AssumeNewColumns);
    return logicalTopInFlowThread() + columnIndex * pageLogicalHeight();
}
Esempio n. 13
0
LayoutUnit RenderMultiColumnSet::calculateColumnHeight(BalancedHeightCalculation calculationMode) const
{
    if (calculationMode == GuessFromFlowThreadPortion) {
        // Initial balancing. Start with the lowest imaginable column height. We use the tallest
        // content run (after having "inserted" implicit breaks), and find its start offset (by
        // looking at the previous run's end offset, or, if there's no previous run, the set's start
        // offset in the flow thread).
        unsigned index = findRunWithTallestColumns();
        LayoutUnit startOffset = index > 0 ? m_contentRuns[index - 1].breakOffset() : logicalTopInFlowThread();
        return std::max<LayoutUnit>(m_contentRuns[index].columnLogicalHeight(startOffset), m_minimumColumnHeight);
    }

    if (actualColumnCount() <= usedColumnCount()) {
        // With the current column height, the content fits without creating overflowing columns. We're done.
        return m_columnHeight;
    }

    if (m_contentRuns.size() >= usedColumnCount()) {
        // Too many forced breaks to allow any implicit breaks. Initial balancing should already
        // have set a good height. There's nothing more we should do.
        return m_columnHeight;
    }

    // If the initial guessed column height wasn't enough, stretch it now. Stretch by the lowest
    // amount of space shortage found during layout.

    ASSERT(m_minSpaceShortage > 0); // We should never _shrink_ the height!
    ASSERT(m_minSpaceShortage != RenderFlowThread::maxLogicalHeight()); // If this happens, we probably have a bug.
    if (m_minSpaceShortage == RenderFlowThread::maxLogicalHeight())
        return m_columnHeight; // So bail out rather than looping infinitely.

    return m_columnHeight + m_minSpaceShortage;
}
Esempio n. 14
0
void ColumnBalancer::traverseSubtree(const LayoutBox& box) {
  if (box.childrenInline() && box.isLayoutBlockFlow()) {
    // Look for breaks between lines.
    for (const RootInlineBox* line = toLayoutBlockFlow(box).firstRootBox();
         line; line = line->nextRootBox()) {
      LayoutUnit lineTopInFlowThread =
          m_flowThreadOffset + line->lineTopWithLeading();
      if (lineTopInFlowThread < logicalTopInFlowThread())
        continue;
      if (lineTopInFlowThread >= logicalBottomInFlowThread())
        break;
      examineLine(*line);
    }
  }

  const LayoutFlowThread* flowThread = columnSet().flowThread();
  bool isHorizontalWritingMode = flowThread->isHorizontalWritingMode();

  // The break-after value from the previous in-flow block-level object to be
  // joined with the break-before value of the next in-flow block-level sibling.
  EBreak previousBreakAfterValue = BreakAuto;

  // Look for breaks between and inside block-level children. Even if this is a
  // block flow with inline children, there may be interesting floats to examine
  // here.
  for (const LayoutObject* child = box.slowFirstChild(); child;
       child = child->nextSibling()) {
    if (!child->isBox() || child->isInline())
      continue;
    const LayoutBox& childBox = toLayoutBox(*child);
    LayoutRect overflowRect = childBox.layoutOverflowRect();
    LayoutUnit childLogicalBottomWithOverflow =
        childBox.logicalTop() +
        (isHorizontalWritingMode ? overflowRect.maxY() : overflowRect.maxX());
    if (m_flowThreadOffset + childLogicalBottomWithOverflow <=
        logicalTopInFlowThread()) {
      // This child is fully above the flow thread portion we're examining.
      continue;
    }
    LayoutUnit childLogicalTopWithOverflow =
        childBox.logicalTop() +
        (isHorizontalWritingMode ? overflowRect.y() : overflowRect.x());
    if (m_flowThreadOffset + childLogicalTopWithOverflow >=
        logicalBottomInFlowThread()) {
      // This child is fully below the flow thread portion we're examining. We
      // cannot just stop here, though, thanks to negative margins.
      // So keep looking.
      continue;
    }
    if (childBox.isOutOfFlowPositioned() || childBox.isColumnSpanAll())
      continue;

    // Tables are wicked. Both table rows and table cells are relative to their
    // table section.
    LayoutUnit offsetForThisChild =
        childBox.isTableRow() ? LayoutUnit() : childBox.logicalTop();
    m_flowThreadOffset += offsetForThisChild;

    examineBoxAfterEntering(childBox, previousBreakAfterValue);
    // Unless the child is unsplittable, or if the child establishes an inner
    // multicol container, we descend into its subtree for further examination.
    if (childBox.getPaginationBreakability() != LayoutBox::ForbidBreaks &&
        (!childBox.isLayoutBlockFlow() ||
         !toLayoutBlockFlow(childBox).multiColumnFlowThread()))
      traverseSubtree(childBox);
    previousBreakAfterValue = childBox.breakAfter();
    examineBoxBeforeLeaving(childBox);

    m_flowThreadOffset -= offsetForThisChild;
  }
}