Ejemplo n.º 1
0
LayoutUnit LayoutMultiColumnSet::nextLogicalTopForUnbreakableContent(LayoutUnit flowThreadOffset, LayoutUnit contentLogicalHeight) const
{
    ASSERT(pageLogicalTopForOffset(flowThreadOffset) == flowThreadOffset);
    LayoutMultiColumnFlowThread* enclosingFlowThread = multiColumnFlowThread()->enclosingFlowThread();
    if (!enclosingFlowThread) {
        // If there's no enclosing fragmentation context, there'll ever be only one row, and all
        // columns there will have the same height.
        return flowThreadOffset;
    }

    // Assert the problematic situation. If we have no problem with the column height, why are we
    // even here?
    ASSERT(pageLogicalHeightForOffset(flowThreadOffset) < contentLogicalHeight);

    // There's a likelihood for subsequent rows to be taller than the first one.
    // TODO(mstensho): if we're doubly nested (e.g. multicol in multicol in multicol), we need to
    // look beyond the first row here.
    const MultiColumnFragmentainerGroup& firstRow = firstFragmentainerGroup();
    LayoutUnit firstRowLogicalBottomInFlowThread = firstRow.logicalTopInFlowThread() + firstRow.logicalHeight() * usedColumnCount();
    if (flowThreadOffset >= firstRowLogicalBottomInFlowThread)
        return flowThreadOffset; // We're not in the first row. Give up.
    LayoutUnit newLogicalHeight = enclosingFlowThread->pageLogicalHeightForOffset(firstRowLogicalBottomInFlowThread);
    if (contentLogicalHeight > newLogicalHeight) {
        // The next outer column or page doesn't have enough space either. Give up and stay where
        // we are.
        return flowThreadOffset;
    }
    return firstRowLogicalBottomInFlowThread;
}
LayoutUnit MultiColumnFragmentainerGroup::calculateMaxColumnHeight() const
{
    LayoutBlockFlow* multicolBlock = m_columnSet.multiColumnBlockFlow();
    const ComputedStyle& multicolStyle = multicolBlock->styleRef();
    LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread();
    LayoutUnit availableHeight = flowThread->columnHeightAvailable();
    LayoutUnit maxColumnHeight = availableHeight ? availableHeight : LayoutUnit::max();
    if (!multicolStyle.logicalMaxHeight().isMaxSizeNone()) {
        LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(MaxSize, multicolStyle.logicalMaxHeight(), -1);
        if (logicalMaxHeight != -1 && maxColumnHeight > logicalMaxHeight)
            maxColumnHeight = logicalMaxHeight;
    }
    LayoutUnit maxHeight = heightAdjustedForRowOffset(maxColumnHeight);
    if (LayoutMultiColumnFlowThread* enclosingFlowThread = flowThread->enclosingFlowThread()) {
        if (enclosingFlowThread->isPageLogicalHeightKnown()) {
            // We're nested inside another fragmentation context whose fragmentainer heights are
            // known. This constrains the max height.
            LayoutUnit remainingOuterLogicalHeight = enclosingFlowThread->pageRemainingLogicalHeightForOffset(blockOffsetInEnclosingFlowThread(), LayoutBlock::AssociateWithLatterPage);
            ASSERT(remainingOuterLogicalHeight > 0);
            if (maxHeight > remainingOuterLogicalHeight)
                maxHeight = remainingOuterLogicalHeight;
        }
    }
    return maxHeight;
}
LayoutMultiColumnFlowThread* LayoutMultiColumnFlowThread::createAnonymous(Document& document, const ComputedStyle& parentStyle)
{
    LayoutMultiColumnFlowThread* layoutObject = new LayoutMultiColumnFlowThread();
    layoutObject->setDocumentForAnonymous(&document);
    layoutObject->setStyle(ComputedStyle::createAnonymousStyleWithDisplay(parentStyle, BLOCK));
    return layoutObject;
}
LayoutSize MultiColumnFragmentainerGroup::flowThreadTranslationAtOffset(LayoutUnit offsetInFlowThread) const
{
    LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread();
    unsigned columnIndex = columnIndexAtOffset(offsetInFlowThread);
    LayoutRect portionRect(flowThreadPortionRectAt(columnIndex));
    flowThread->flipForWritingMode(portionRect);
    LayoutRect columnRect(columnRectAt(columnIndex));
    m_columnSet.flipForWritingMode(columnRect);
    LayoutSize translationRelativeToGroup = columnRect.location() - portionRect.location();

    LayoutSize enclosingTranslation;
    if (LayoutMultiColumnFlowThread* enclosingFlowThread = flowThread->enclosingFlowThread()) {
        // Translation that would map points in the coordinate space of the outermost flow thread to
        // visual points in the first column in the first fragmentainer group (row) in our multicol
        // container.
        LayoutSize enclosingTranslationOrigin = enclosingFlowThread->flowThreadTranslationAtOffset(flowThread->blockOffsetInEnclosingFragmentationContext());

        // Translation that would map points in the coordinate space of the outermost flow thread to
        // visual points in the first column in this fragmentainer group.
        enclosingTranslation = enclosingFlowThread->flowThreadTranslationAtOffset(blockOffsetInEnclosingFragmentationContext());

        // What we ultimately return from this method is a translation that maps points in the
        // coordinate space of our flow thread to a visual point in a certain column in this
        // fragmentainer group. We had to go all the way up to the outermost flow thread, since this
        // fragmentainer group may be in a different outer column than the first outer column that
        // this multicol container lives in. It's the visual distance between the first
        // fragmentainer group and this fragmentainer group that we need to add to the translation.
        enclosingTranslation -= enclosingTranslationOrigin;
    }

    return enclosingTranslation + translationRelativeToGroup + offsetFromColumnSet() + m_columnSet.topLeftLocationOffset() - flowThread->topLeftLocationOffset();
}
void MultiColumnFragmentainerGroup::expandToEncompassFlowThreadOverflow()
{
    ASSERT(isLastGroup());
    // Get the offset within the flow thread in its block progression direction. Then get the
    // flow thread's remaining logical height including its overflow and expand our rect
    // to encompass that remaining height and overflow. The idea is that we will generate
    // additional columns and pages to hold that overflow, since people do write bad
    // content like <body style="height:0px"> in multi-column layouts.
    LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread();
    LayoutRect layoutRect = flowThread->layoutOverflowRect();
    m_logicalBottomInFlowThread = flowThread->isHorizontalWritingMode() ? layoutRect.maxY() : layoutRect.maxX();
}
Ejemplo n.º 6
0
void MinimumSpaceShortageFinder::examineBoxAfterEntering(const LayoutBox& box)
{
    if (box.hasForcedBreakBefore())
        m_forcedBreaksCount++;
    if (box.hasForcedBreakAfter())
        m_forcedBreaksCount++;

    // Look for breaks before the child box.
    bool isFirstAfterBreak = this->isFirstAfterBreak(flowThreadOffset());
    ASSERT(isFirstAfterBreak || !box.paginationStrut());
    LayoutBox::PaginationBreakability breakability = box.paginationBreakability();
    if (isFirstAfterBreak && !box.hasForcedBreakBefore()) {
        // This box is first after a soft break.
        LayoutUnit strut = box.paginationStrut();
        // Figure out how much more space we would need to prevent it from being pushed to the next column.
        recordSpaceShortage(box.logicalHeight() - strut);
        if (breakability != LayoutBox::ForbidBreaks && m_pendingStrut == LayoutUnit::min()) {
            // We now want to look for the first piece of unbreakable content (e.g. a line or a
            // block-displayed image) inside this block. That ought to be a good candidate for
            // minimum space shortage; a much better one than reporting space shortage for the
            // entire block (which we'll also do (further down), in case we couldn't find anything
            // more suitable).
            m_pendingStrut = strut;
        }
    }

    if (breakability != LayoutBox::ForbidBreaks) {
        // See if this breakable box crosses column boundaries.
        LayoutUnit bottomInFlowThread = flowThreadOffset() + box.logicalHeight();
        if (isFirstAfterBreak || group().columnLogicalTopForOffset(flowThreadOffset()) != group().columnLogicalTopForOffset(bottomInFlowThread)) {
            // If the child crosses a column boundary, record space shortage, in case nothing
            // inside it has already done so. The column balancer needs to know by how much it
            // has to stretch the columns to make more content fit. If no breaks are reported
            // (but do occur), the balancer will have no clue. Only measure the space after the
            // last column boundary, in case it crosses more than one.
            LayoutUnit spaceUsedInLastColumn = bottomInFlowThread - group().columnLogicalTopForOffset(bottomInFlowThread);
            recordSpaceShortage(spaceUsedInLastColumn);
        }
    }

    // If this is an inner multicol container, look for space shortage inside it.
    if (!box.isLayoutBlockFlow())
        return;
    LayoutMultiColumnFlowThread* flowThread = toLayoutBlockFlow(box).multiColumnFlowThread();
    if (!flowThread)
        return;
    for (const LayoutMultiColumnSet* columnSet = flowThread->firstMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) {
        for (const MultiColumnFragmentainerGroup& row : columnSet->fragmentainerGroups()) {
            MinimumSpaceShortageFinder innerFinder(row);
            recordSpaceShortage(innerFinder.minimumSpaceShortage());
        }
    }
}
LayoutUnit MultiColumnFragmentainerGroup::calculateMaxColumnHeight() const
{
    LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread();
    LayoutUnit maxColumnHeight = flowThread->maxColumnLogicalHeight();
    LayoutUnit maxHeight = heightAdjustedForRowOffset(maxColumnHeight);
    if (FragmentationContext* enclosingFragmentationContext = flowThread->enclosingFragmentationContext()) {
        if (enclosingFragmentationContext->isFragmentainerLogicalHeightKnown()) {
            // We're nested inside another fragmentation context whose fragmentainer heights are
            // known. This constrains the max height.
            LayoutUnit remainingOuterLogicalHeight = enclosingFragmentationContext->remainingLogicalHeightAt(blockOffsetInEnclosingFragmentationContext());
            ASSERT(remainingOuterLogicalHeight > 0);
            if (maxHeight > remainingOuterLogicalHeight)
                maxHeight = remainingOuterLogicalHeight;
        }
    }
    return maxHeight;
}
void MultiColumnFragmentainerGroup::resetColumnHeight()
{
    m_maxColumnHeight = calculateMaxColumnHeight();

    LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread();
    if (m_columnSet.heightIsAuto()) {
        FragmentationContext* enclosingFragmentationContext = flowThread->enclosingFragmentationContext();
        if (enclosingFragmentationContext && enclosingFragmentationContext->isFragmentainerLogicalHeightKnown()) {
            // Even if height is auto, we set an initial height, in order to tell how much content
            // this MultiColumnFragmentainerGroup can hold, and when we need to append a new one.
            m_columnHeight = m_maxColumnHeight;
        } else {
            m_columnHeight = LayoutUnit();
        }
    } else {
        setAndConstrainColumnHeight(heightAdjustedForRowOffset(flowThread->columnHeightAvailable()));
    }
}
void MultiColumnFragmentainerGroup::resetColumnHeight()
{
    // Nuke previously stored minimum column height. Contents may have changed for all we know.
    m_minimumColumnHeight = 0;

    m_maxColumnHeight = calculateMaxColumnHeight();

    LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread();
    LayoutMultiColumnFlowThread* enclosingFlowThread = flowThread->enclosingFlowThread();
    if (enclosingFlowThread && enclosingFlowThread->isPageLogicalHeightKnown()) {
        // TODO(mstensho): Do this better. If height is auto here, we shouldn't set a
        // height, or forced breaks and pagination struts might mess up column balancing.
        LayoutUnit columnHeight = heightIsAuto() ? m_maxColumnHeight : heightAdjustedForRowOffset(flowThread->columnHeightAvailable());
        setAndConstrainColumnHeight(columnHeight);
    } else if (heightIsAuto()) {
        m_columnHeight = LayoutUnit();
    } else {
        setAndConstrainColumnHeight(heightAdjustedForRowOffset(flowThread->columnHeightAvailable()));
    }
}
Ejemplo n.º 10
0
void LayoutMultiColumnSet::recordSpaceShortage(LayoutUnit offsetInFlowThread, LayoutUnit spaceShortage)
{
    MultiColumnFragmentainerGroup& row = fragmentainerGroupAtFlowThreadOffset(offsetInFlowThread);
    row.recordSpaceShortage(spaceShortage);

    // Since we're at a potential break here, take the opportunity to check if we need another
    // fragmentainer group. If we've run out of columns in the last fragmentainer group (column
    // row), we need to insert another fragmentainer group to hold more columns.
    if (!row.isLastGroup())
        return;
    LayoutMultiColumnFlowThread* flowThread = multiColumnFlowThread();
    if (!flowThread->multiColumnBlockFlow()->isInsideFlowThread())
        return; // Early bail. We're not nested, so waste no more time on this.
    if (!flowThread->isInInitialLayoutPass())
        return;
    // Move the offset to where the next column starts, if we're not there already.
    offsetInFlowThread += flowThread->pageRemainingLogicalHeightForOffset(offsetInFlowThread, AssociateWithFormerPage);

    flowThread->appendNewFragmentainerGroupIfNeeded(offsetInFlowThread);
}
Ejemplo n.º 11
0
void InitialColumnHeightFinder::examineBoxAfterEntering(
    const LayoutBox& box,
    LayoutUnit childLogicalHeight,
    EBreak previousBreakAfterValue) {
  if (m_lastBreakSeen > flowThreadOffset()) {
    // We have moved backwards. We're probably in a parallel flow, caused by
    // floats, sibling table cells, etc.
    m_lastBreakSeen = LayoutUnit();
  }
  if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) {
    if (box.needsForcedBreakBefore(previousBreakAfterValue)) {
      addContentRun(flowThreadOffset());
    } else if (isFirstAfterBreak(flowThreadOffset()) &&
               m_lastBreakSeen != flowThreadOffset()) {
      // This box is first after a soft break.
      m_lastBreakSeen = flowThreadOffset();
      recordStrutBeforeOffset(flowThreadOffset(), box.paginationStrut());
    }
  }

  if (box.getPaginationBreakability() != LayoutBox::AllowAnyBreaks) {
    m_tallestUnbreakableLogicalHeight =
        std::max(m_tallestUnbreakableLogicalHeight, childLogicalHeight);
    return;
  }
  // Need to examine inner multicol containers to find their tallest unbreakable
  // piece of content.
  if (!box.isLayoutBlockFlow())
    return;
  LayoutMultiColumnFlowThread* innerFlowThread =
      toLayoutBlockFlow(box).multiColumnFlowThread();
  if (!innerFlowThread || innerFlowThread->isLayoutPagedFlowThread())
    return;
  LayoutUnit offsetInInnerFlowThread =
      flowThreadOffset() -
      innerFlowThread->blockOffsetInEnclosingFragmentationContext();
  LayoutUnit innerUnbreakableHeight =
      innerFlowThread->tallestUnbreakableLogicalHeight(offsetInInnerFlowThread);
  m_tallestUnbreakableLogicalHeight =
      std::max(m_tallestUnbreakableLogicalHeight, innerUnbreakableHeight);
}
bool LayoutMultiColumnSet::heightIsAuto() const
{
    LayoutMultiColumnFlowThread* flowThread = multiColumnFlowThread();
    if (!flowThread->isLayoutPagedFlowThread()) {
        // If support for the column-fill property isn't enabled, we want to behave as if
        // column-fill were auto, so that multicol containers with specified height don't get their
        // columns balanced (auto-height multicol containers will still get their columns balanced,
        // even if column-fill isn't 'balance' - in accordance with the spec). Pretending that
        // column-fill is auto also matches the old multicol implementation, which has no support
        // for this property.
        if (multiColumnBlockFlow()->style()->getColumnFill() == ColumnFillBalance)
            return true;
        if (LayoutBox* next = nextSiblingBox()) {
            if (next->isLayoutMultiColumnSpannerPlaceholder()) {
                // If we're followed by a spanner, we need to balance.
                return true;
            }
        }
    }
    return !flowThread->columnHeightAvailable();
}
// Find the previous layout object that has the multicol container in its containing block chain, skipping nested multicol containers.
static LayoutObject* previousInPreOrderSkippingOutOfFlow(LayoutMultiColumnFlowThread* flowThread, LayoutObject* descendant)
{
    ASSERT(descendant->isDescendantOf(flowThread));
    LayoutObject* object = descendant->previousInPreOrder(flowThread);
    while (object && object != flowThread) {
        if (object->isColumnSpanAll()) {
            LayoutMultiColumnFlowThread* placeholderFlowThread = toLayoutBox(object)->spannerPlaceholder()->flowThread();
            if (placeholderFlowThread == flowThread)
                break;
            // We're inside an inner multicol container. We have no business there. Continue on the outside.
            object = placeholderFlowThread->parent();
            ASSERT(object->isDescendantOf(flowThread));
            continue;
        }
        if (object->flowThreadContainingBlock() == flowThread) {
            LayoutObject* ancestor;
            for (ancestor = object->parent(); ; ancestor = ancestor->parent()) {
                if (ancestor == flowThread)
                    return object;
                if (isMultiColumnContainer(*ancestor)) {
                    // We're inside an inner multicol container. We have no business there.
                    break;
                }
            }
            object = ancestor;
            ASSERT(ancestor->isDescendantOf(flowThread));
            continue; // Continue on the outside of the inner flow thread.
        }
        // We're inside something that's out-of-flow. Keep looking upwards and backwards in the tree.
        object = object->previousInPreOrder(flowThread);
    }
    if (!object || object == flowThread)
        return nullptr;
#if ENABLE(ASSERT)
    // Make sure that we didn't stumble into an inner multicol container.
    for (LayoutObject* walker = object->parent(); walker && walker != flowThread; walker = walker->parent())
        ASSERT(!isMultiColumnContainer(*walker));
#endif
    return object;
}
Ejemplo n.º 14
0
void InitialColumnHeightFinder::examineBoxAfterEntering(
    const LayoutBox& box,
    EBreak previousBreakAfterValue) {
  if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) {
    if (box.needsForcedBreakBefore(previousBreakAfterValue)) {
      addContentRun(flowThreadOffset());
    } else {
      ASSERT(isFirstAfterBreak(flowThreadOffset()) || !box.paginationStrut());
      if (isFirstAfterBreak(flowThreadOffset())) {
        // This box is first after a soft break.
        recordStrutBeforeOffset(flowThreadOffset(), box.paginationStrut());
      }
    }
  }

  if (box.getPaginationBreakability() != LayoutBox::AllowAnyBreaks) {
    LayoutUnit unsplittableLogicalHeight = box.logicalHeight();
    if (box.isFloating())
      unsplittableLogicalHeight += box.marginBefore() + box.marginAfter();
    m_tallestUnbreakableLogicalHeight =
        std::max(m_tallestUnbreakableLogicalHeight, unsplittableLogicalHeight);
    return;
  }
  // Need to examine inner multicol containers to find their tallest unbreakable
  // piece of content.
  if (!box.isLayoutBlockFlow())
    return;
  LayoutMultiColumnFlowThread* innerFlowThread =
      toLayoutBlockFlow(box).multiColumnFlowThread();
  if (!innerFlowThread || innerFlowThread->isLayoutPagedFlowThread())
    return;
  LayoutUnit offsetInInnerFlowThread =
      flowThreadOffset() -
      innerFlowThread->blockOffsetInEnclosingFragmentationContext();
  LayoutUnit innerUnbreakableHeight =
      innerFlowThread->tallestUnbreakableLogicalHeight(offsetInInnerFlowThread);
  m_tallestUnbreakableLogicalHeight =
      std::max(m_tallestUnbreakableLogicalHeight, innerUnbreakableHeight);
}
void MultiColumnFragmentainerGroup::collectLayerFragments(DeprecatedPaintLayerFragments& 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.
    //
    // All other rectangles in this method are sized physically, and the inline direction coordinate
    // is physical too, but the block direction coordinate is "logical top". This is the same as
    // e.g. LayoutBox::frameRect(). These rectangles also pretend that there's only one long column,
    // i.e. they are for the flow thread.

    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()) - 1;

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

    LayoutUnit colLogicalWidth = m_columnSet.pageLogicalWidth();
    LayoutUnit colGap = m_columnSet.columnGap();
    unsigned colCount = actualColumnCount();

    bool progressionIsInline = flowThread->progressionIsInline();
    bool leftToRight = m_columnSet.style()->isLeftToRightDirection();

    LayoutUnit initialBlockOffset = m_columnSet.logicalTop() + logicalTop() - flowThread->logicalTop();

    for (unsigned i = startColumn; i <= endColumn; i++) {
        // Get the portion of the flow thread that corresponds to this column.
        LayoutRect flowThreadPortion = flowThreadPortionRectAt(i);

        // Now get the overflow rect that corresponds to the column.
        LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap);

        // In order to create a fragment we must intersect the portion painted by this column.
        LayoutRect clippedRect(layerBoundsInFlowThread);
        clippedRect.intersect(flowThreadOverflowPortion);
        if (clippedRect.isEmpty())
            continue;

        // We also need to intersect the dirty rect. We have to apply a translation and shift based off
        // our column index.
        LayoutPoint translationOffset;
        LayoutUnit inlineOffset = progressionIsInline ? i * (colLogicalWidth + colGap) : LayoutUnit();
        if (!leftToRight)
            inlineOffset = -inlineOffset;
        translationOffset.setX(inlineOffset);
        LayoutUnit blockOffset;
        if (progressionIsInline) {
            blockOffset = initialBlockOffset + (isHorizontalWritingMode ? -flowThreadPortion.y() : -flowThreadPortion.x());
        } else {
            // Column gap can apply in the block direction for page fragmentainers.
            // There is currently no spec which calls for column-gap to apply
            // for page fragmentainers at all, but it's applied here for compatibility
            // with the old multicolumn implementation.
            blockOffset = i * colGap;
        }
        if (isFlippedBlocksWritingMode(m_columnSet.style()->writingMode()))
            blockOffset = -blockOffset;
        translationOffset.setY(blockOffset);
        if (!isHorizontalWritingMode)
            translationOffset = translationOffset.transposedPoint();

        // Shift the dirty rect to be in flow thread coordinates with this translation applied.
        LayoutRect translatedDirtyRect(dirtyRect);
        translatedDirtyRect.moveBy(-translationOffset);

        // See if we intersect the dirty rect.
        clippedRect = layerBoundingBox;
        clippedRect.intersect(translatedDirtyRect);
        if (clippedRect.isEmpty())
            continue;

        // Something does need to paint in this column. Make a fragment now and supply the physical translation
        // offset and the clip rect for the column with that offset applied.
        DeprecatedPaintLayerFragment fragment;
        fragment.paginationOffset = translationOffset;

        LayoutRect flippedFlowThreadOverflowPortion(flowThreadOverflowPortion);
        // Flip it into more a physical (DeprecatedPaintLayer-style) rectangle.
        flowThread->flipForWritingMode(flippedFlowThreadOverflowPortion);
        fragment.paginationClip = flippedFlowThreadOverflowPortion;
        fragments.append(fragment);
    }
}
Ejemplo n.º 16
0
void MinimumSpaceShortageFinder::examineBoxAfterEntering(
    const LayoutBox& box,
    LayoutUnit childLogicalHeight,
    EBreak previousBreakAfterValue) {
  LayoutBox::PaginationBreakability breakability =
      box.getPaginationBreakability();

  // Look for breaks before the child box.
  if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) {
    if (box.needsForcedBreakBefore(previousBreakAfterValue)) {
      m_forcedBreaksCount++;
    } else {
      if (isFirstAfterBreak(flowThreadOffset())) {
        // This box is first after a soft break.
        LayoutUnit strut = box.paginationStrut();
        // Figure out how much more space we would need to prevent it from being
        // pushed to the next column.
        recordSpaceShortage(childLogicalHeight - strut);
        if (breakability != LayoutBox::ForbidBreaks &&
            m_pendingStrut == LayoutUnit::min()) {
          // We now want to look for the first piece of unbreakable content
          // (e.g. a line or a block-displayed image) inside this block. That
          // ought to be a good candidate for minimum space shortage; a much
          // better one than reporting space shortage for the entire block
          // (which we'll also do (further down), in case we couldn't find
          // anything more suitable).
          m_pendingStrut = strut;
        }
      }
    }
  }

  if (breakability != LayoutBox::ForbidBreaks) {
    // See if this breakable box crosses column boundaries.
    LayoutUnit bottomInFlowThread = flowThreadOffset() + childLogicalHeight;
    const MultiColumnFragmentainerGroup& group =
        groupAtOffset(flowThreadOffset());
    if (isFirstAfterBreak(flowThreadOffset()) ||
        group.columnLogicalTopForOffset(flowThreadOffset()) !=
            group.columnLogicalTopForOffset(bottomInFlowThread)) {
      // If the child crosses a column boundary, record space shortage, in case
      // nothing inside it has already done so. The column balancer needs to
      // know by how much it has to stretch the columns to make more content
      // fit. If no breaks are reported (but do occur), the balancer will have
      // no clue. Only measure the space after the last column boundary, in case
      // it crosses more than one.
      LayoutUnit spaceUsedInLastColumn =
          bottomInFlowThread -
          group.columnLogicalTopForOffset(bottomInFlowThread);
      recordSpaceShortage(spaceUsedInLastColumn);
    }
  }

  // If this is an inner multicol container, look for space shortage inside it.
  if (!box.isLayoutBlockFlow())
    return;
  LayoutMultiColumnFlowThread* flowThread =
      toLayoutBlockFlow(box).multiColumnFlowThread();
  if (!flowThread || flowThread->isLayoutPagedFlowThread())
    return;
  for (const LayoutMultiColumnSet* columnSet =
           flowThread->firstMultiColumnSet();
       columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) {
    // Establish an inner shortage finder for this column set in the inner
    // multicol container. We need to let it walk through all fragmentainer
    // groups in one go, or we'd miss the column boundaries between each
    // fragmentainer group. We need to record space shortage there too.
    MinimumSpaceShortageFinder innerFinder(
        *columnSet, columnSet->logicalTopInFlowThread(),
        columnSet->logicalBottomInFlowThread());
    recordSpaceShortage(innerFinder.minimumSpaceShortage());
  }
}
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);
    }
}
LayoutSize MultiColumnFragmentainerGroup::flowThreadTranslationAtOffset(
    LayoutUnit offsetInFlowThread,
    LayoutBox::PageBoundaryRule rule,
    CoordinateSpaceConversion mode) const {
  LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread();

  // A column out of range doesn't have a flow thread portion, so we need to
  // clamp to make sure that we stay within the actual columns. This means that
  // content in the overflow area will be mapped to the last actual column,
  // instead of being mapped to an imaginary column further ahead.
  unsigned columnIndex = offsetInFlowThread >= logicalBottomInFlowThread()
                             ? actualColumnCount() - 1
                             : columnIndexAtOffset(offsetInFlowThread, rule);

  LayoutRect portionRect(flowThreadPortionRectAt(columnIndex));
  flowThread->flipForWritingMode(portionRect);
  portionRect.moveBy(flowThread->topLeftLocation());

  LayoutRect columnRect(columnRectAt(columnIndex));
  columnRect.move(offsetFromColumnSet());
  m_columnSet.flipForWritingMode(columnRect);
  columnRect.moveBy(m_columnSet.topLeftLocation());

  LayoutSize translationRelativeToFlowThread =
      columnRect.location() - portionRect.location();
  if (mode == CoordinateSpaceConversion::Containing)
    return translationRelativeToFlowThread;

  LayoutSize enclosingTranslation;
  if (LayoutMultiColumnFlowThread* enclosingFlowThread =
          flowThread->enclosingFlowThread()) {
    const MultiColumnFragmentainerGroup& firstRow =
        flowThread->firstMultiColumnSet()->firstFragmentainerGroup();
    // Translation that would map points in the coordinate space of the
    // outermost flow thread to visual points in the first column in the first
    // fragmentainer group (row) in our multicol container.
    LayoutSize enclosingTranslationOrigin =
        enclosingFlowThread->flowThreadTranslationAtOffset(
            firstRow.blockOffsetInEnclosingFragmentationContext(),
            LayoutBox::AssociateWithLatterPage, mode);

    // Translation that would map points in the coordinate space of the
    // outermost flow thread to visual points in the first column in this
    // fragmentainer group.
    enclosingTranslation = enclosingFlowThread->flowThreadTranslationAtOffset(
        blockOffsetInEnclosingFragmentationContext(),
        LayoutBox::AssociateWithLatterPage, mode);

    // What we ultimately return from this method is a translation that maps
    // points in the coordinate space of our flow thread to a visual point in a
    // certain column in this fragmentainer group. We had to go all the way up
    // to the outermost flow thread, since this fragmentainer group may be in a
    // different outer column than the first outer column that this multicol
    // container lives in. It's the visual distance between the first
    // fragmentainer group and this fragmentainer group that we need to add to
    // the translation.
    enclosingTranslation -= enclosingTranslationOrigin;
  }

  return enclosingTranslation + translationRelativeToFlowThread;
}