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 05a01b709fbfee65abbd54397c163e4667c2d148..4a875da30a31861c3789dca318362ac7192c66ab 100644 |
| --- a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp |
| +++ b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp |
| @@ -32,6 +32,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> |
| @@ -140,6 +141,66 @@ struct ContentAlignmentData { |
| LayoutUnit distributionOffset = LayoutUnit(-1); |
| }; |
| +class BaselineGroup { |
| + public: |
| + BaselineGroup() {} |
| + |
| + void update(LayoutUnit ascent, LayoutUnit descent) { |
| + m_maxAscent = std::max(m_maxAscent, ascent); |
| + m_maxDescent = std::max(m_maxDescent, descent); |
| + m_size++; |
| + } |
| + LayoutUnit maxAscent() const { return m_maxAscent; } |
| + LayoutUnit maxDescent() const { return m_maxDescent; } |
| + LayoutUnit size() const { return m_size; } |
| + |
| + private: |
| + LayoutUnit m_maxAscent; |
| + LayoutUnit m_maxDescent; |
| + LayoutUnit m_size; |
| +}; |
| + |
| +class BaselineContext { |
| + public: |
| + BaselineContext(const LayoutBox& child, |
| + ItemPosition preference, |
| + LayoutUnit ascent, |
| + LayoutUnit descent) { |
| + updateSharedGroup(child, preference, ascent, descent); |
| + } |
| + |
| + const BaselineGroup& getSharedGroup(const LayoutBox& child, |
| + ItemPosition preference) const { |
| + if (isAscentBaseline(child.styleRef().getWritingMode(), preference)) |
| + return m_ascentSharedGroup; |
| + return m_descentSharedGroup; |
| + } |
| + |
| + void updateSharedGroup(const LayoutBox& child, |
| + ItemPosition preference, |
| + LayoutUnit ascent, |
| + LayoutUnit descent) { |
| + if (isAscentBaseline(child.styleRef().getWritingMode(), preference)) |
| + m_ascentSharedGroup.update(ascent, descent); |
| + else |
| + m_descentSharedGroup.update(ascent, descent); |
| + } |
| + |
| + private: |
| + bool isAscentBaseline(WritingMode blockDirection, |
| + ItemPosition preference) const { |
| + return ((blockDirection == TopToBottomWritingMode && |
| + preference == ItemPositionBaseline) || |
| + (blockDirection == LeftToRightWritingMode && |
| + preference == ItemPositionBaseline) || |
| + (blockDirection == RightToLeftWritingMode && |
| + preference == ItemPositionLastBaseline)); |
| + } |
| + |
| + BaselineGroup m_ascentSharedGroup; |
| + BaselineGroup m_descentSharedGroup; |
| +}; |
| + |
| enum TrackSizeRestriction { |
| AllowInfinity, |
| ForbidInfinity, |
| @@ -247,6 +308,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(); |
| @@ -259,6 +326,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; |
| @@ -790,8 +860,18 @@ void LayoutGrid::computeUsedBreadthOfGridTracks( |
| track.setGrowthLimitCap(valueForLength(gridLength.length(), maxSize)); |
| } |
| - if (trackSize.isContentSized()) |
| + if (trackSize.isContentSized()) { |
| sizingData.contentSizedTracksIndex.append(i); |
| + } else if (!m_gridItemArea.isEmpty()) { |
| + GridIterator iterator(m_grid, direction, i); |
| + while (LayoutBox* gridItem = iterator.nextGridItem()) { |
| + if (isBaselineAlignment(*gridItem) && |
| + (sizingData.sizingOperation != IntrinsicSizeComputation || |
| + direction == ForRows)) |
| + gridItem->layoutIfNeeded(); |
| + updateBaselineAlignmentContextIfNeeded(*gridItem, sizingData); |
| + } |
| + } |
| if (trackSize.maxTrackBreadth().isFlex()) |
| flexibleSizedTracksIndex.append(i); |
| } |
| @@ -1104,7 +1184,8 @@ GridTrackSize LayoutGrid::gridTrackSize(GridTrackSizingDirection direction, |
| } |
| } |
| - // Flex sizes are invalid as a min sizing function. However we still can have |
| + // Flex sizes Laare invalid as a min sizing function. However we still can |
|
kojii
2016/11/15 04:21:15
typo?
jfernandez
2016/11/16 11:51:24
Done.
|
| + // have |
| // a flexible |minTrackBreadth| if the track had a flex size directly (e.g. |
| // "1fr"), the spec says that in this case it implies an automatic minimum. |
| if (minTrackBreadth.isFlex()) |
| @@ -1136,7 +1217,11 @@ LayoutUnit LayoutGrid::logicalHeightForChild(LayoutBox& child, |
| child.clearOverrideLogicalContentHeight(); |
| child.layoutIfNeeded(); |
| - return child.logicalHeight() + child.marginLogicalHeight(); |
| + LayoutUnit baselineOffset = |
| + updateBaselineAlignmentContextIfNeeded(child, sizingData); |
| + return baselineOffset > 0 |
| + ? baselineOffset |
| + : child.logicalHeight() + child.marginLogicalHeight(); |
| } |
| GridTrackSizingDirection LayoutGrid::flowAwareDirectionForChild( |
| @@ -1232,7 +1317,11 @@ LayoutUnit LayoutGrid::minContentForChild(LayoutBox& child, |
| if (direction == ForColumns && |
| sizingData.sizingOperation == IntrinsicSizeComputation) { |
| DCHECK(isOrthogonalChild(child)); |
| - return child.logicalHeight() + child.marginLogicalHeight(); |
| + LayoutUnit baselineOffset = |
| + updateBaselineAlignmentContextIfNeeded(child, sizingData); |
| + return baselineOffset > 0 |
| + ? baselineOffset |
| + : child.logicalHeight() + child.marginLogicalHeight(); |
| } |
| if (updateOverrideContainingBlockContentSizeForChild( |
| @@ -2746,6 +2835,11 @@ LayoutUnit LayoutGrid::marginLogicalHeightForChild( |
| return isHorizontalWritingMode() ? child.marginHeight() : child.marginWidth(); |
| } |
| +LayoutUnit LayoutGrid::marginLogicalWidthForChild( |
| + const LayoutBox& child) const { |
| + return isHorizontalWritingMode() ? child.marginWidth() : child.marginHeight(); |
| +} |
| + |
| LayoutUnit LayoutGrid::computeMarginLogicalSizeForChild( |
| MarginDirection forDirection, |
| const LayoutBox& child) const { |
| @@ -2929,6 +3023,110 @@ static int synthesizedBaselineFromBorderBox(const LayoutBox& box, |
| // TODO(lajava): This logic is shared by LayoutFlexibleBox, so it might be |
| // refactored somehow. |
| +bool LayoutGrid::isBaselineAlignment(const LayoutBox& child) const { |
| + bool isChildInlineFlowAlongRowAxis = !isOrthogonalChild(child); |
| + ItemPosition align = isChildInlineFlowAlongRowAxis |
| + ? alignSelfForChild(child).position() |
| + : justifySelfForChild(child).position(); |
| + bool hasAutoMargins = isChildInlineFlowAlongRowAxis |
| + ? hasAutoMarginsInColumnAxis(child) |
| + : hasAutoMarginsInRowAxis(child); |
| + return ( |
| + (align == ItemPositionBaseline || align == ItemPositionLastBaseline) && |
| + !hasAutoMargins); |
| +} |
| + |
| +LayoutUnit LayoutGrid::updateBaselineAlignmentContextIfNeeded( |
| + LayoutBox& child, |
| + GridSizingData& sizingData) const { |
| + if (!isBaselineAlignment(child)) |
| + return LayoutUnit(); |
| + |
| + bool isChildInlineFlowAlongRowAxis = !isOrthogonalChild(child); |
| + ItemPosition align = isChildInlineFlowAlongRowAxis |
| + ? alignSelfForChild(child).position() |
| + : justifySelfForChild(child).position(); |
| + const GridSpan& span = isChildInlineFlowAlongRowAxis |
| + ? cachedGridSpan(child, ForRows) |
| + : cachedGridSpan(child, ForColumns); |
| + LayoutUnit ascent = firstLineBoxBaselineForChild(child); |
| + LayoutUnit descent = descentBaselineForChild(child, ascent); |
| + BaselineContextsMap& baselineContexts = |
| + isChildInlineFlowAlongRowAxis ? sizingData.rowAxisAlignmentContext |
| + : sizingData.colAxisAlignmentContext; |
| + BaselineContext* context = nullptr; |
| + BaselineContextsMap::AddResult addResult = baselineContexts.add( |
| + span.startLine(), std::unique_ptr<BaselineContext>()); |
| + if (addResult.isNewEntry) { |
| + context = new BaselineContext(child, align, ascent, descent); |
| + addResult.storedValue->value = wrapUnique(context); |
| + } else { |
| + context = addResult.storedValue->value.get(); |
| + context->updateSharedGroup(child, align, ascent, descent); |
| + } |
| + auto group = context->getSharedGroup(child, align); |
| + return group.maxAscent() + group.maxDescent(); |
| +} |
| + |
| +LayoutUnit LayoutGrid::firstLineBoxBaselineForChild( |
| + const LayoutBox& child) const { |
| + LayoutUnit baseline(child.firstLineBoxBaseline()); |
| + // We take content-box's bottom if no valid baseline. |
| + if (baseline == -1) |
| + baseline = LayoutBlock::logicalHeightForChild(child); |
| + return baseline + marginBeforeForChild(child); |
| +} |
| + |
| +LayoutUnit LayoutGrid::descentBaselineForChild(const LayoutBox& child, |
| + LayoutUnit ascent) const { |
| + bool isChildInlineFlowAlongRowAxis = !isOrthogonalChild(child); |
| + return isChildInlineFlowAlongRowAxis |
| + ? (marginLogicalHeightForChild(child) + |
| + LayoutBlock::logicalHeightForChild(child)) - |
| + ascent |
| + : (marginLogicalWidthForChild(child) + |
| + logicalWidthForChild(child)) - |
| + ascent; |
| +} |
| + |
| +LayoutUnit LayoutGrid::columnAxisBaselineOffsetForChild( |
| + const LayoutBox& child, |
| + const GridSizingData& sizingData) const { |
| + // only parallel items participating in baseline alignment along |
| + // the grid's row axis have an offset in the column axis. |
| + if (!isInlineBaselineAlignedChild(child)) |
| + return LayoutUnit(); |
| + |
| + const GridSpan& span = cachedGridSpan(child, ForRows); |
| + const BaselineContext* context = |
| + sizingData.rowAxisAlignmentContext.get(span.startLine()); |
| + DCHECK(context); |
| + const BaselineGroup& group = |
| + context->getSharedGroup(child, alignSelfForChild(child).position()); |
| + // TODO (lajava): We could just return LayoutUnit() if there is only |
| + // 1 child in the group. |
| + return group.maxAscent() - firstLineBoxBaselineForChild(child); |
| +} |
| + |
| +LayoutUnit LayoutGrid::rowAxisBaselineOffsetForChild( |
| + const LayoutBox& child, |
| + const GridSizingData& sizingData) const { |
| + // only orthogonal items participating in baseline alignment along |
| + // the grid's column axis have an offset in the row axis. |
| + if (!isBlockBaselineAlignedChild(child)) |
| + return LayoutUnit(); |
| + |
| + const GridSpan& span = cachedGridSpan(child, ForColumns); |
| + const BaselineContext* context = |
| + sizingData.colAxisAlignmentContext.get(span.startLine()); |
| + DCHECK(context); |
| + const BaselineGroup& group = |
| + context->getSharedGroup(child, justifySelfForChild(child).position()); |
| + // TODO (lajava): We could just return LayoutUnit() if there is only |
| + // 1 child in the group. |
| + return group.maxAscent() - firstLineBoxBaselineForChild(child); |
| +} |
| + |
| int LayoutGrid::baselinePosition(FontBaseline, |
| bool, |
| LineDirectionMode direction, |
| @@ -2942,9 +3140,12 @@ int LayoutGrid::baselinePosition(FontBaseline, |
| return baseline + beforeMarginInLineDirection(direction); |
| } |
| -bool LayoutGrid::isInlineBaselineAlignedChild(const LayoutBox* child) const { |
| - return alignSelfForChild(*child).position() == ItemPositionBaseline && |
| - !isOrthogonalChild(*child) && !hasAutoMarginsInColumnAxis(*child); |
| +bool LayoutGrid::isInlineBaselineAlignedChild(const LayoutBox& child) const { |
| + return !isOrthogonalChild(child) && isBaselineAlignment(child); |
| +} |
| + |
| +bool LayoutGrid::isBlockBaselineAlignedChild(const LayoutBox& child) const { |
| + return isOrthogonalChild(child) && isBaselineAlignment(child); |
| } |
| int LayoutGrid::firstLineBoxBaseline() const { |
| @@ -2959,8 +3160,8 @@ int LayoutGrid::firstLineBoxBaseline() const { |
| for (size_t index = 0; index < m_grid[0][column].size(); index++) { |
| const LayoutBox* child = m_grid[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 (isInlineBaselineAlignedChild(*child)) { |
| // TODO (lajava): self-baseline and content-baseline alignment |
| // still not implemented. |
| baselineChild = child; |
| @@ -3079,8 +3280,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: |
| @@ -3158,8 +3357,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: |
| @@ -3182,7 +3379,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(); |
| @@ -3225,7 +3423,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(); |