Chromium Code Reviews| Index: third_party/WebKit/Source/core/layout/LayoutGrid.cpp |
| diff --git a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp |
| index eef54fb123031d0c59c34df4a23d138abee928e3..97bd60d359f5c4b5cf0867474b4588c02ec81e06 100644 |
| --- a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp |
| +++ b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp |
| @@ -33,6 +33,7 @@ |
| #include "core/style/ComputedStyle.h" |
| #include "core/style/GridArea.h" |
| #include "platform/LengthFunctions.h" |
| +#include "platform/text/WritingMode.h" |
| #include "wtf/PtrUtil.h" |
| #include <algorithm> |
| #include <memory> |
| @@ -43,6 +44,11 @@ static const int infinity = -1; |
| class GridItemWithSpan; |
| +static inline bool isBaselinePosition(ItemPosition position) { |
| + return position == ItemPositionBaseline || |
| + position == ItemPositionLastBaseline; |
| +} |
| + |
| size_t LayoutGrid::Grid::numTracks(GridTrackSizingDirection direction) const { |
| if (direction == ForRows) |
| return m_grid.size(); |
| @@ -282,6 +288,110 @@ struct ContentAlignmentData { |
| LayoutUnit distributionOffset = LayoutUnit(-1); |
| }; |
| +class BaselineGroup { |
| + public: |
| + BaselineGroup(WritingMode blockFlow, ItemPosition childPreference) |
| + : m_maxAscent(0), m_maxDescent(0), m_items() { |
| + m_blockFlow = blockFlow; |
| + m_preference = childPreference; |
| + } |
| + |
| + void update(const LayoutBox& child, LayoutUnit ascent, LayoutUnit descent) { |
| + if (m_items.add(&child).isNewEntry) { |
| + m_maxAscent = std::max(m_maxAscent, ascent); |
| + m_maxDescent = std::max(m_maxDescent, descent); |
| + } |
| + } |
| + bool isCompatible(WritingMode childBlockFlow, |
| + ItemPosition childPreference) const { |
| + DCHECK(isBaselinePosition(childPreference)); |
| + DCHECK_GT(size(), 0); |
| + return ((m_blockFlow == childBlockFlow || |
| + isOrthogonalBlockFlow(childBlockFlow)) && |
| + m_preference == childPreference) || |
| + (isOppositeBlockFlow(childBlockFlow) && |
| + m_preference != childPreference); |
| + } |
| + |
| + LayoutUnit maxAscent() const { return m_maxAscent; } |
| + LayoutUnit maxDescent() const { return m_maxDescent; } |
| + int size() const { return m_items.size(); } |
| + |
| + private: |
| + bool isOppositeBlockFlow(WritingMode blockFlow) const { |
| + switch (blockFlow) { |
| + case WritingMode::kHorizontalTb: |
| + return false; |
| + case WritingMode::kVerticalLr: |
| + return m_blockFlow == WritingMode::kVerticalRl; |
| + case WritingMode::kVerticalRl: |
| + return m_blockFlow == WritingMode::kVerticalLr; |
| + default: |
| + NOTREACHED(); |
| + return false; |
| + } |
| + } |
| + bool isOrthogonalBlockFlow(WritingMode blockFlow) const { |
| + switch (blockFlow) { |
| + case WritingMode::kHorizontalTb: |
| + return m_blockFlow != WritingMode::kHorizontalTb; |
| + case WritingMode::kVerticalLr: |
| + case WritingMode::kVerticalRl: |
| + return m_blockFlow == WritingMode::kHorizontalTb; |
| + default: |
| + NOTREACHED(); |
| + return false; |
| + } |
| + } |
| + |
| + WritingMode m_blockFlow; |
| + ItemPosition m_preference; |
| + LayoutUnit m_maxAscent; |
| + LayoutUnit m_maxDescent; |
| + HashSet<const LayoutBox*> m_items; |
| +}; |
| + |
| +class BaselineContext { |
| + public: |
| + BaselineContext(const LayoutBox& child, |
| + ItemPosition preference, |
| + LayoutUnit ascent, |
| + LayoutUnit descent) { |
| + DCHECK(isBaselinePosition(preference)); |
| + updateSharedGroup(child, preference, ascent, descent); |
| + } |
| + const BaselineGroup& getSharedGroup(const LayoutBox& child, |
| + ItemPosition preference) const { |
| + DCHECK(isBaselinePosition(preference)); |
| + return const_cast<BaselineContext*>(this)->findCompatibleSharedGroup( |
| + child, preference); |
| + } |
| + void updateSharedGroup(const LayoutBox& child, |
| + ItemPosition preference, |
| + LayoutUnit ascent, |
| + LayoutUnit descent) { |
| + DCHECK(isBaselinePosition(preference)); |
| + BaselineGroup& group = findCompatibleSharedGroup(child, preference); |
| + group.update(child, ascent, descent); |
| + } |
| + |
| + private: |
| + // TODO Properly implement baseline-group compatibility |
| + // See https://github.com/w3c/csswg-drafts/issues/721 |
| + BaselineGroup& findCompatibleSharedGroup(const LayoutBox& child, |
| + ItemPosition preference) { |
| + WritingMode blockDirection = child.styleRef().getWritingMode(); |
| + for (auto& group : m_sharedGroups) { |
| + if (group.isCompatible(blockDirection, preference)) |
| + return group; |
| + } |
| + m_sharedGroups.prepend(BaselineGroup(blockDirection, preference)); |
| + return m_sharedGroups[0]; |
| + } |
| + |
| + Vector<BaselineGroup> m_sharedGroups; |
| +}; |
|
svillar
2017/01/19 09:41:25
These two classes have nothing to do with Grid, th
jfernandez
2017/01/19 16:31:48
Acknowledged.
|
| + |
| enum TrackSizeRestriction { |
| AllowInfinity, |
| ForbidInfinity, |
| @@ -389,6 +499,12 @@ class LayoutGrid::GridIterator { |
| size_t m_childIndex; |
| }; |
| +typedef HashMap<unsigned, |
| + std::unique_ptr<BaselineContext>, |
| + DefaultHash<unsigned>::Hash, |
| + WTF::UnsignedWithZeroKeyHashTraits<unsigned>> |
| + BaselineContextsMap; |
| + |
| struct LayoutGrid::GridSizingData { |
| WTF_MAKE_NONCOPYABLE(GridSizingData); |
| STACK_ALLOCATED(); |
| @@ -401,6 +517,9 @@ struct LayoutGrid::GridSizingData { |
| Vector<GridTrack> rowTracks; |
| Vector<size_t> contentSizedTracksIndex; |
| + BaselineContextsMap rowAxisAlignmentContext; |
| + BaselineContextsMap colAxisAlignmentContext; |
| + |
| // Performance optimization: hold onto these Vectors until the end of Layout |
| // to avoid repeated malloc / free. |
| Vector<GridTrack*> filteredTracks; |
| @@ -645,6 +764,8 @@ void LayoutGrid::layoutBlock(bool relayoutChildren) { |
| GridSizingData sizingData(numTracks(ForColumns, m_grid), |
| numTracks(ForRows, m_grid), m_grid); |
| + computeBaselineAlignmentContext(sizingData); |
| + |
| // 1- First, the track sizing algorithm is used to resolve the sizes of the |
| // grid columns. |
| // At this point the logical width is always definite as the above call to |
| @@ -806,6 +927,7 @@ void LayoutGrid::computeIntrinsicLogicalWidths( |
| GridSizingData sizingData(numTracks(ForColumns, grid), |
| numTracks(ForRows, grid), grid); |
| + computeBaselineAlignmentContext(sizingData); |
| computeTrackSizesForIndefiniteSize(ForColumns, sizingData, minLogicalWidth, |
| maxLogicalWidth); |
| @@ -1324,6 +1446,12 @@ LayoutUnit LayoutGrid::logicalHeightForChild(LayoutBox& child, |
| child.clearOverrideLogicalContentHeight(); |
| child.layoutIfNeeded(); |
| + GridAxis baselineAxis = |
| + isOrthogonalChild(child) ? GridRowAxis : GridColumnAxis; |
| + if (isBaselineAlignmentForChild(child, baselineAxis)) { |
| + auto group = getBaselineGroupForChild(child, sizingData, baselineAxis); |
| + return group.maxAscent() + group.maxDescent(); |
| + } |
| return child.logicalHeight() + child.marginLogicalHeight(); |
| } |
| @@ -1427,6 +1555,10 @@ LayoutUnit LayoutGrid::minContentForChild(LayoutBox& child, |
| if (direction == ForColumns && |
| sizingData.sizingOperation == IntrinsicSizeComputation) { |
| DCHECK(isOrthogonalChild(child)); |
| + if (isBaselineAlignmentForChild(child, GridRowAxis)) { |
| + auto group = getBaselineGroupForChild(child, sizingData, GridRowAxis); |
| + return group.maxAscent() + group.maxDescent(); |
| + } |
| return child.logicalHeight() + child.marginLogicalHeight(); |
| } |
| @@ -3088,8 +3220,6 @@ static int synthesizedBaselineFromBorderBox(const LayoutBox& box, |
| .toInt(); |
| } |
| -// TODO(lajava): This logic is shared by LayoutFlexibleBox, so it might be |
| -// refactored somehow. |
| int LayoutGrid::baselinePosition(FontBaseline, |
| bool, |
| LineDirectionMode direction, |
| @@ -3103,11 +3233,6 @@ int LayoutGrid::baselinePosition(FontBaseline, |
| return baseline + beforeMarginInLineDirection(direction); |
| } |
| -bool LayoutGrid::isInlineBaselineAlignedChild(const LayoutBox* child) const { |
| - return alignSelfForChild(*child).position() == ItemPositionBaseline && |
| - !isOrthogonalChild(*child) && !hasAutoMarginsInColumnAxis(*child); |
| -} |
| - |
| int LayoutGrid::firstLineBoxBaseline() const { |
| if (isWritingModeRoot() || !m_grid.hasGridItems()) |
| return -1; |
| @@ -3120,8 +3245,8 @@ int LayoutGrid::firstLineBoxBaseline() const { |
| for (size_t index = 0; index < m_grid.cell(0, column).size(); index++) { |
| const LayoutBox* child = m_grid.cell(0, column)[index]; |
| DCHECK(!child->isOutOfFlowPositioned()); |
| - // If an item participates in baseline alignmen, we select such item. |
| - if (isInlineBaselineAlignedChild(child)) { |
| + // If an item participates in baseline alignment, we select such item. |
| + if (isBaselineAlignmentForChild(*child)) { |
| // TODO (lajava): self-baseline and content-baseline alignment |
| // still not implemented. |
| baselineChild = child; |
| @@ -3171,6 +3296,163 @@ int LayoutGrid::inlineBlockBaseline(LineDirectionMode direction) const { |
| return synthesizedBaselineFromContentBox(*this, direction) + marginHeight; |
| } |
| +bool LayoutGrid::isHorizontalGridAxis(GridAxis axis) const { |
| + return axis == GridRowAxis ? isHorizontalWritingMode() |
| + : !isHorizontalWritingMode(); |
| +} |
| + |
| +bool LayoutGrid::isParallelToBlockAxisForChild(const LayoutBox& child, |
| + GridAxis axis) const { |
| + return axis == GridColumnAxis ? !isOrthogonalChild(child) |
| + : isOrthogonalChild(child); |
| +} |
| + |
| +bool LayoutGrid::isDescentBaselineForChild(const LayoutBox& child, |
| + GridAxis baselineAxis) const { |
| + return isHorizontalGridAxis(baselineAxis) && |
| + !styleRef().isFlippedBlocksWritingMode(); |
| +} |
| + |
| +bool LayoutGrid::isBaselineAlignmentForChild(const LayoutBox& child, |
| + GridAxis baselineAxis) const { |
| + bool isColumnAxisBaseline = baselineAxis == GridColumnAxis; |
| + ItemPosition align = isColumnAxisBaseline |
| + ? alignSelfForChild(child).position() |
| + : justifySelfForChild(child).position(); |
| + bool hasAutoMargins = isColumnAxisBaseline ? hasAutoMarginsInColumnAxis(child) |
| + : hasAutoMarginsInRowAxis(child); |
| + return isBaselinePosition(align) && !hasAutoMargins; |
| +} |
| + |
| +const BaselineGroup& LayoutGrid::getBaselineGroupForChild( |
| + const LayoutBox& child, |
| + const GridSizingData& sizingData, |
| + GridAxis baselineAxis) const { |
| + DCHECK(isBaselineAlignmentForChild(child, baselineAxis)); |
| + auto& grid = sizingData.grid(); |
| + bool isColumnAxisBaseline = baselineAxis == GridColumnAxis; |
| + bool isRowAxisContext = isColumnAxisBaseline; |
| + auto&& span = isRowAxisContext ? grid.gridItemSpan(child, ForRows) |
|
svillar
2017/01/19 09:41:25
auto&& ???
jfernandez
2017/01/19 16:31:48
Since gridItemSpan returns a temporary object, we
|
| + : grid.gridItemSpan(child, ForColumns); |
| + auto& contextsMap = isRowAxisContext ? sizingData.rowAxisAlignmentContext |
| + : sizingData.colAxisAlignmentContext; |
| + auto* context = contextsMap.get(span.startLine()); |
| + DCHECK(context); |
| + ItemPosition align = isColumnAxisBaseline |
| + ? alignSelfForChild(child).position() |
| + : justifySelfForChild(child).position(); |
| + return context->getSharedGroup(child, align); |
| +} |
| + |
| +LayoutUnit LayoutGrid::marginOverForChild(const LayoutBox& child, |
| + GridAxis axis) const { |
| + return isHorizontalGridAxis(axis) ? child.marginRight() : child.marginTop(); |
| +} |
| + |
| +LayoutUnit LayoutGrid::logicalAscentForChild(const LayoutBox& child, |
| + GridAxis baselineAxis) const { |
| + LayoutUnit ascent = ascentForChild(child, baselineAxis); |
| + return isDescentBaselineForChild(child, baselineAxis) |
| + ? descentForChild(child, ascent, baselineAxis) |
| + : ascent; |
| +} |
| + |
| +LayoutUnit LayoutGrid::ascentForChild(const LayoutBox& child, |
| + GridAxis baselineAxis) const { |
| + int baseline = isParallelToBlockAxisForChild(child, baselineAxis) |
| + ? child.firstLineBoxBaseline() |
| + : -1; |
| + // We take border-box's bottom if no valid baseline. |
| + if (baseline == -1) { |
|
svillar
2017/01/19 09:41:25
Unfortunate that we use the magic -1 but I guess i
jfernandez
2017/01/19 16:31:48
Yes, we can implement optional, as we do in WebKit
|
| + baseline = isHorizontalGridAxis(baselineAxis) |
| + ? child.size().width().toInt() |
| + : child.size().height().toInt(); |
| + } |
| + return LayoutUnit(baseline) + marginOverForChild(child, baselineAxis); |
| +} |
| + |
| +LayoutUnit LayoutGrid::descentForChild(const LayoutBox& child, |
| + LayoutUnit ascent, |
| + GridAxis baselineAxis) const { |
| + if (isParallelToBlockAxisForChild(child, baselineAxis)) |
| + return child.marginLogicalHeight() + child.logicalHeight() - ascent; |
| + return child.marginLogicalWidth() + child.logicalWidth() - ascent; |
| +} |
| + |
| +void LayoutGrid::computeBaselineAlignmentContext( |
| + GridSizingData& sizingData) const { |
| + for (auto* child = firstInFlowChildBox(); child; |
| + child = child->nextInFlowSiblingBox()) { |
| + updateBaselineAlignmentContextIfNeeded(*child, sizingData, GridRowAxis); |
| + updateBaselineAlignmentContextIfNeeded(*child, sizingData, GridColumnAxis); |
| + } |
| +} |
| + |
| +void LayoutGrid::updateBaselineAlignmentContextIfNeeded( |
| + LayoutBox& child, |
| + GridSizingData& sizingData, |
| + GridAxis baselineAxis) const { |
| + if (!isBaselineAlignmentForChild(child, baselineAxis)) |
| + return; |
| + |
| + child.layoutIfNeeded(); |
|
svillar
2017/01/19 09:41:25
This method is called by computeBaselineAlignmentC
jfernandez
2017/01/19 16:31:48
I don't know why it doesn't.
Anyway, what I can d
|
| + |
| + // Determine Ascent and Descent values of this child with respect to |
| + // its grid container. |
| + LayoutUnit ascent = ascentForChild(child, baselineAxis); |
| + LayoutUnit descent = descentForChild(child, ascent, baselineAxis); |
| + if (isDescentBaselineForChild(child, baselineAxis)) { |
| + LayoutUnit temp(ascent); |
| + ascent = descent; |
| + descent = temp; |
| + } |
| + |
| + // Looking up for a shared alignment context perpendicular to the |
| + // baseline axis. |
| + auto& grid = sizingData.grid(); |
| + bool isColumnAxisBaseline = baselineAxis == GridColumnAxis; |
| + bool isRowAxisContext = isColumnAxisBaseline; |
| + auto&& span = isRowAxisContext ? grid.gridItemSpan(child, ForRows) |
|
svillar
2017/01/19 09:41:25
auto&& ?
jfernandez
2017/01/19 16:31:48
Already replied before.
|
| + : grid.gridItemSpan(child, ForColumns); |
| + auto& contextsMap = isRowAxisContext ? sizingData.rowAxisAlignmentContext |
| + : sizingData.colAxisAlignmentContext; |
| + auto addResult = contextsMap.add(span.startLine(), nullptr); |
| + |
| + // Looking for a compatible baseline-sharing group. |
| + ItemPosition align = isColumnAxisBaseline |
| + ? alignSelfForChild(child).position() |
| + : justifySelfForChild(child).position(); |
| + if (addResult.isNewEntry) { |
| + addResult.storedValue->value = |
| + WTF::makeUnique<BaselineContext>(child, align, ascent, descent); |
| + } else { |
| + auto* context = addResult.storedValue->value.get(); |
| + context->updateSharedGroup(child, align, ascent, descent); |
| + } |
| +} |
| + |
| +LayoutUnit LayoutGrid::columnAxisBaselineOffsetForChild( |
| + const LayoutBox& child, |
| + const GridSizingData& sizingData) const { |
| + if (!isBaselineAlignmentForChild(child, GridColumnAxis)) |
| + return LayoutUnit(); |
| + auto& group = getBaselineGroupForChild(child, sizingData, GridColumnAxis); |
| + if (group.size() > 1) |
| + return group.maxAscent() - logicalAscentForChild(child, GridColumnAxis); |
| + return LayoutUnit(); |
| +} |
| + |
| +LayoutUnit LayoutGrid::rowAxisBaselineOffsetForChild( |
| + const LayoutBox& child, |
| + const GridSizingData& sizingData) const { |
| + if (!isBaselineAlignmentForChild(child, GridRowAxis)) |
| + return LayoutUnit(); |
| + auto& group = getBaselineGroupForChild(child, sizingData, GridRowAxis); |
| + if (group.size() > 1) |
| + group.maxAscent() - logicalAscentForChild(child, GridRowAxis); |
| + return LayoutUnit(); |
| +} |
| + |
| GridAxisPosition LayoutGrid::columnAxisPositionForChild( |
| const LayoutBox& child) const { |
| bool hasSameWritingMode = |
| @@ -3240,8 +3522,6 @@ GridAxisPosition LayoutGrid::columnAxisPositionForChild( |
| return GridAxisStart; |
| case ItemPositionBaseline: |
| case ItemPositionLastBaseline: |
| - // FIXME: These two require implementing Baseline Alignment. For now, we |
| - // always 'start' align the child. crbug.com/234191 |
| return GridAxisStart; |
| case ItemPositionAuto: |
| case ItemPositionNormal: |
| @@ -3319,8 +3599,6 @@ GridAxisPosition LayoutGrid::rowAxisPositionForChild( |
| return GridAxisStart; |
| case ItemPositionBaseline: |
| case ItemPositionLastBaseline: |
| - // FIXME: These two require implementing Baseline Alignment. For now, we |
| - // always 'start' align the child. crbug.com/234191 |
| return GridAxisStart; |
| case ItemPositionAuto: |
| case ItemPositionNormal: |
| @@ -3343,7 +3621,8 @@ LayoutUnit LayoutGrid::columnAxisOffsetForChild( |
| GridAxisPosition axisPosition = columnAxisPositionForChild(child); |
| switch (axisPosition) { |
| case GridAxisStart: |
| - return startPosition; |
| + return startPosition + |
| + columnAxisBaselineOffsetForChild(child, sizingData); |
| case GridAxisEnd: |
| case GridAxisCenter: { |
| size_t childEndLine = rowsSpan.endLine(); |
| @@ -3387,7 +3666,7 @@ LayoutUnit LayoutGrid::rowAxisOffsetForChild(const LayoutBox& child, |
| GridAxisPosition axisPosition = rowAxisPositionForChild(child); |
| switch (axisPosition) { |
| case GridAxisStart: |
| - return startPosition; |
| + return startPosition + rowAxisBaselineOffsetForChild(child, sizingData); |
| case GridAxisEnd: |
| case GridAxisCenter: { |
| size_t childEndLine = columnsSpan.endLine(); |