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 bd8979ab203b0323eba79b481873d18ef48f7ffd..b9ef156a163ef8dbc2c704381c3652a74a89f516 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> |
| @@ -583,9 +584,30 @@ void LayoutGrid::repeatTracksSizingIfNeeded(GridSizingData& sizingData, |
| LayoutUnit availableSpaceForRows) { |
| DCHECK(sizingData.sizingState > GridSizingData::RowSizingFirstIteration); |
| + // Baseline alignment may change item's intrinsic size, hence changing its |
| + // min-content contribution. |
| + // https://drafts.csswg.org/css-align-3/#baseline-align-content |
| + // https://drafts.csswg.org/css-align-3/#baseline-align-self |
| + bool baselineAffectIntrinsicWidth = baselineMayAffectIntrinsicWidth(); |
| + bool baselineAffectIntrinsicHeight = baselineMayAffectIntrinsicHeight(); |
| + |
| // In orthogonal flow cases column track's size is determined by using the |
| // computed row track's size, which it was estimated during the first cycle of |
| // the sizing algorithm. |
| + bool intrinsicSizeMayHaveChanged = |
| + baselineAffectIntrinsicWidth || baselineAffectIntrinsicHeight || |
| + sizingData.grid().hasAnyOrthogonalGridItem(); |
| + |
|
svillar
2017/02/09 13:10:09
Can't we just early return here if !intrinsicSizeM
jfernandez
2017/02/09 14:16:03
Acknowledged.
|
| + // Compute again the intrinsic size if baseline changed item's min-content |
| + // contribution. |
| + // TODO (lajava): Should we do the same because if orthogonal items ? Perhaps |
| + // after executing again the track sizing algorithm ? |
|
svillar
2017/02/09 13:10:09
I think we can take advantage of this change and d
jfernandez
2017/02/09 14:16:03
Acknowledged.
|
| + if (baselineAffectIntrinsicWidth) { |
| + setPreferredLogicalWidthsDirty(MarkOnlyThis); |
|
svillar
2017/02/09 13:10:09
Not sure if MarkOnlyThis is enough for orthogonal
jfernandez
2017/02/09 14:16:03
Umm, I think we must prevent infinite-iterations,
|
| + updateLogicalWidth(); |
| + availableSpaceForColumns = availableLogicalWidth(); |
| + } |
| + |
| // Hence we need to repeat computeUsedBreadthOfGridTracks for both, columns |
| // and rows, to determine the final values. |
| // TODO (lajava): orthogonal flows is just one of the cases which may require |
| @@ -593,12 +615,19 @@ void LayoutGrid::repeatTracksSizingIfNeeded(GridSizingData& sizingData, |
| // all the cases with orthogonal flows require this extra cycle; we need a |
| // more specific condition to detect whether child's min-content contribution |
| // has changed or not. |
| - if (sizingData.grid().hasAnyOrthogonalGridItem()) { |
| + if (intrinsicSizeMayHaveChanged) { |
| computeTrackSizesForDefiniteSize(ForColumns, sizingData, |
| availableSpaceForColumns); |
| computeTrackSizesForDefiniteSize(ForRows, sizingData, |
| availableSpaceForRows); |
| } |
| + |
| + if (baselineAffectIntrinsicHeight) { |
| + LayoutUnit trackBasedLogicalHeight = |
| + computeTrackBasedLogicalHeight(sizingData) + |
| + borderAndPaddingLogicalHeight() + scrollbarLogicalHeight(); |
| + setLogicalHeight(trackBasedLogicalHeight); |
|
svillar
2017/02/09 13:10:09
Perhaps you could omit the trackBasedLogicalHeight
jfernandez
2017/02/09 14:16:03
Acknowledged.
|
| + } |
| } |
| void LayoutGrid::layoutBlock(bool relayoutChildren) { |
| @@ -611,6 +640,9 @@ void LayoutGrid::layoutBlock(bool relayoutChildren) { |
| simplifiedLayout()) |
| return; |
| + m_rowAxisAlignmentContext.clear(); |
| + m_colAxisAlignmentContext.clear(); |
| + |
| SubtreeLayoutScope layoutScope(*this); |
| { |
| @@ -681,6 +713,15 @@ void LayoutGrid::layoutBlock(bool relayoutChildren) { |
| if (!cachedHasDefiniteLogicalHeight()) |
| sizingData.freeSpace(ForRows) = logicalHeight() - trackBasedLogicalHeight; |
| + // TODO (lajava): We need to compute baselines after step 2 so |
| + // items with a relative size (percentages) can resolve it before |
| + // determining its baseline. However, we only set item's grid area |
| + // (via override sizes) as part of the content-sized tracks sizing |
| + // logic. Hence, items located at fixed or flexible tracks can't |
| + // resolve correctly their size at this stage, which may lead to |
| + // determining incorrect their shared context's baseline. |
| + computeBaselineAlignmentContext(sizingData); |
|
svillar
2017/02/09 13:10:09
Not sure why you insert the computation in the mid
jfernandez
2017/02/09 14:16:03
We must do it here to so that when repeating the t
svillar
2017/02/13 15:18:46
Sorry about this. Yes it's propertly placed. I was
jfernandez
2017/02/13 23:32:37
I meant step 2 as described here:
https://drafts.
|
| + |
| // 3- If the min-content contribution of any grid items have changed based |
| // on the row sizes calculated in step 2, steps 1 and 2 are repeated with |
| // the new min-content contribution (once only). |
| @@ -1324,6 +1365,13 @@ LayoutUnit LayoutGrid::logicalHeightForChild(LayoutBox& child, |
| child.clearOverrideLogicalContentHeight(); |
| child.layoutIfNeeded(); |
| + GridAxis baselineAxis = |
| + isOrthogonalChild(child) ? GridRowAxis : GridColumnAxis; |
| + if (isBaselineAlignmentForChild(child, baselineAxis) && |
| + isBaselineContextComputed(baselineAxis)) { |
| + auto& group = getBaselineGroupForChild(child, sizingData, baselineAxis); |
| + return group.maxAscent() + group.maxDescent(); |
| + } |
| return child.logicalHeight() + child.marginLogicalHeight(); |
| } |
| @@ -1427,6 +1475,11 @@ LayoutUnit LayoutGrid::minContentForChild(LayoutBox& child, |
| if (direction == ForColumns && |
| sizingData.sizingOperation == IntrinsicSizeComputation) { |
| DCHECK(isOrthogonalChild(child)); |
| + if (isBaselineAlignmentForChild(child, GridRowAxis) && |
| + isBaselineContextComputed(GridRowAxis)) { |
| + auto& group = getBaselineGroupForChild(child, sizingData, GridRowAxis); |
| + return group.maxAscent() + group.maxDescent(); |
| + } |
| return child.logicalHeight() + child.marginLogicalHeight(); |
| } |
| @@ -3089,8 +3142,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, |
| @@ -3104,11 +3155,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; |
| @@ -3121,8 +3167,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. |
|
svillar
2017/02/09 13:10:09
Time to remove this comment?
jfernandez
2017/02/09 14:16:03
Umm, I don't think we can do it now. We must imple
svillar
2017/02/13 15:18:46
Acknowledged.
|
| baselineChild = child; |
| @@ -3172,6 +3218,189 @@ 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; |
| + const auto& span = isRowAxisContext ? grid.gridItemSpan(child, ForRows) |
| + : grid.gridItemSpan(child, ForColumns); |
| + auto& contextsMap = |
| + isRowAxisContext ? m_rowAxisAlignmentContext : m_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) { |
| + 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; |
| +} |
| + |
| +bool LayoutGrid::isBaselineContextComputed(GridAxis baselineAxis) const { |
| + return baselineAxis == GridColumnAxis ? !m_rowAxisAlignmentContext.isEmpty() |
| + : !m_colAxisAlignmentContext.isEmpty(); |
| +} |
| + |
| +bool LayoutGrid::baselineMayAffectIntrinsicWidth() const { |
| + if (!styleRef().logicalWidth().isIntrinsicOrAuto()) |
| + return false; |
| + for (const auto& context : m_colAxisAlignmentContext) { |
| + for (const auto& group : context.value->sharedGroups()) { |
| + if (group.size() > 1) |
| + return true; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| +bool LayoutGrid::baselineMayAffectIntrinsicHeight() const { |
| + if (!styleRef().logicalHeight().isIntrinsicOrAuto()) |
| + return false; |
| + for (const auto& context : m_rowAxisAlignmentContext) { |
| + for (const auto& group : context.value->sharedGroups()) { |
| + if (group.size() > 1) |
| + return true; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| +void LayoutGrid::computeBaselineAlignmentContext(GridSizingData& sizingData) { |
| + 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) { |
| + DCHECK_EQ(sizingData.sizingOperation, TrackSizing); |
| + if (!isBaselineAlignmentForChild(child, baselineAxis)) |
| + return; |
| + |
| + child.layoutIfNeeded(); |
| + |
| + // 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)) |
| + std::swap(ascent, descent); |
| + |
| + // Looking up for a shared alignment context perpendicular to the |
| + // baseline axis. |
| + auto& grid = sizingData.grid(); |
| + bool isColumnAxisBaseline = baselineAxis == GridColumnAxis; |
| + bool isRowAxisContext = isColumnAxisBaseline; |
| + const auto& span = isRowAxisContext ? grid.gridItemSpan(child, ForRows) |
| + : grid.gridItemSpan(child, ForColumns); |
| + auto& contextsMap = |
| + isRowAxisContext ? m_rowAxisAlignmentContext : m_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) |
| + return group.maxAscent() - logicalAscentForChild(child, GridRowAxis); |
| + return LayoutUnit(); |
| +} |
| + |
| GridAxisPosition LayoutGrid::columnAxisPositionForChild( |
| const LayoutBox& child) const { |
| bool hasSameWritingMode = |
| @@ -3241,8 +3470,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: |
| @@ -3320,8 +3547,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: |
| @@ -3344,7 +3569,8 @@ LayoutUnit LayoutGrid::columnAxisOffsetForChild( |
| GridAxisPosition axisPosition = columnAxisPositionForChild(child); |
| switch (axisPosition) { |
| case GridAxisStart: |
| - return startPosition; |
| + return startPosition + |
| + columnAxisBaselineOffsetForChild(child, sizingData); |
|
svillar
2017/02/09 13:10:09
Maybe this is an stupid question, but why just on
jfernandez
2017/02/09 14:16:03
because 'baseline' value is managed as 'start', in
|
| case GridAxisEnd: |
| case GridAxisCenter: { |
| size_t childEndLine = rowsSpan.endLine(); |
| @@ -3388,7 +3614,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(); |