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 e828f0b9b611de39ddced39a99e6c766aafd4ba1..53156bc714211bedd23e489921dcc0c3593ab9f3 100644 |
| --- a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp |
| +++ b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp |
| @@ -142,6 +142,68 @@ public: |
| LayoutUnit distributionOffset = -1; |
| }; |
| +class BaselineGroup { |
|
cbiesinger
2015/10/26 22:07:16
For this class and BaselineContext, can you add a
jfernandez
2015/10/27 09:47:56
Done.
|
| +public: |
| + BaselineGroup(LayoutBox* child, LayoutUnit childAscent, ItemPosition preference) |
| + { |
| + ASSERT(preference == ItemPositionBaseline || preference == ItemPositionLastBaseline); |
| + add(child, childAscent); |
| + groupPreference = preference; |
| + groupWritingMode = child->styleRef().writingMode(); |
| + } |
| + |
| + static bool oppositeBlockDirection(WritingMode childBlockDirection, WritingMode groupBlockDirection) |
| + { |
| + return (childBlockDirection == TopToBottomWritingMode && groupBlockDirection == BottomToTopWritingMode) |
| + || (childBlockDirection == BottomToTopWritingMode && groupBlockDirection == TopToBottomWritingMode) |
| + || (childBlockDirection == LeftToRightWritingMode && groupBlockDirection == RightToLeftWritingMode) |
| + || (childBlockDirection == RightToLeftWritingMode && groupBlockDirection == LeftToRightWritingMode); |
| + } |
| + |
| + bool isCompatibleChild(LayoutBox* child, ItemPosition preference) |
| + { |
| + return (child->styleRef().writingMode() == groupWritingMode && preference == groupPreference) |
| + || (oppositeBlockDirection(child->styleRef().writingMode(), groupWritingMode) && preference != groupPreference); |
|
cbiesinger
2015/10/26 22:07:16
I don't think I fully understand what this is nece
jfernandez
2015/10/27 09:47:56
The CSS Box Alignment spec defines when a group of
cbiesinger
2015/10/27 21:52:30
Oh, thanks for the explanation. I should have chec
|
| + } |
| + |
| + void add(LayoutBox* child, LayoutUnit childAscent) |
| + { |
| + ASSERT(child); |
| + children.append(child); |
| + groupMaxAscent = std::max(groupMaxAscent, childAscent); |
| + } |
| + |
| + Vector<LayoutBox*> children; |
|
cbiesinger
2015/10/26 22:07:17
Why not the m_children style for member variables?
jfernandez
2015/10/27 09:47:56
Acknowledged.
|
| + ItemPosition groupPreference; |
| + WritingMode groupWritingMode; |
| + LayoutUnit groupMaxAscent; |
| +}; |
| + |
| +class BaselineContext { |
| +public: |
| + BaselineContext() { } |
| + |
| + BaselineContext(LayoutBox* child, LayoutUnit maxAscent, ItemPosition preference) |
| + { |
| + sharedGroups.append(BaselineGroup(child, maxAscent, preference)); |
| + } |
| + |
| + void addGroupMember(LayoutBox* child, LayoutUnit childAscent, ItemPosition preference) |
| + { |
| + bool compatibleGroupFound = false; |
| + for (auto& group : sharedGroups) { |
| + if ((compatibleGroupFound = group.isCompatibleChild(child, preference))) { |
| + group.add(child, childAscent); |
| + break; |
| + } |
| + } |
| + if (!compatibleGroupFound) |
| + sharedGroups.append(BaselineGroup(child, childAscent, preference)); |
| + } |
| + |
| + Vector<BaselineGroup> sharedGroups; |
| +}; |
| + |
| enum TrackSizeRestriction { |
| AllowInfinity, |
| ForbidInfinity, |
| @@ -226,6 +288,8 @@ private: |
| size_t m_childIndex; |
| }; |
| +typedef HashMap<unsigned, BaselineContext, DefaultHash<unsigned>::Hash, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> BaselineContextsMap; |
| + |
| struct LayoutGrid::GridSizingData { |
| WTF_MAKE_NONCOPYABLE(GridSizingData); |
| STACK_ALLOCATED(); |
| @@ -240,6 +304,8 @@ public: |
| Vector<GridTrack> rowTracks; |
| Vector<size_t> contentSizedTracksIndex; |
| + BaselineContextsMap rowAxisAlignmentContext; |
| + |
| // Performance optimization: hold onto these Vectors until the end of Layout to avoid repeated malloc / free. |
| Vector<GridTrack*> filteredTracks; |
| Vector<GridItemWithSpan> itemsSortedByIncreasingSpan; |
| @@ -1334,6 +1400,8 @@ void LayoutGrid::layoutGridItems() |
| child->layoutIfNeeded(); |
| + updateBaselineAlignmentContextInRowAxisIfNeeded(child, sizingData); |
| + |
| // We need pending layouts to be done in order to compute auto-margins properly. |
| updateAutoMarginsInColumnAxisIfNeeded(*child); |
| updateAutoMarginsInRowAxisIfNeeded(*child); |
| @@ -1352,6 +1420,8 @@ void LayoutGrid::layoutGridItems() |
| m_gridItemsOverflowingGridArea.append(child); |
| } |
| + applyBaselineAlignmentIfNeeded(sizingData); |
| + |
| for (const auto& row : sizingData.rowTracks) |
| setLogicalHeight(logicalHeight() + row.baseSize()); |
| @@ -1709,6 +1779,120 @@ void LayoutGrid::updateAutoMarginsInColumnAxisIfNeeded(LayoutBox& child) |
| } |
| } |
| +void LayoutGrid::updateBaselineAlignmentContextInRowAxisIfNeeded(LayoutBox* child, GridSizingData& sizingData) const |
| +{ |
| + ItemPosition align = ComputedStyle::resolveAlignment(styleRef(), child->styleRef(), ItemPositionStretch); |
| + if ((align != ItemPositionBaseline && align != ItemPositionLastBaseline) || hasAutoMarginsInColumnAxis(*child)) |
| + return; |
| + |
| + const GridCoordinate& coordinate = cachedGridCoordinate(*child); |
| + unsigned lineNumber = coordinate.rows.resolvedInitialPosition.toInt(); |
| + LayoutUnit ascent = marginBoxAscentFromBaselineForChild(*child); |
| + bool contextExists = sizingData.rowAxisAlignmentContext.contains(lineNumber); |
| + BaselineContext context = contextExists ? sizingData.rowAxisAlignmentContext.get(lineNumber) : BaselineContext(child, ascent, align); |
| + if (contextExists) |
| + context.addGroupMember(child, ascent, align); |
| + sizingData.rowAxisAlignmentContext.set(lineNumber, context); |
| +} |
| + |
| +// FIXME: This logic is shared by LayoutFlexibleBox, so it should be moved to LayoutBox. |
| +LayoutUnit LayoutGrid::marginBoxAscentFromBaselineForChild(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); |
| +} |
| + |
| +void LayoutGrid::applyBaselineAlignmentIfNeeded(const GridSizingData& sizingData) |
| +{ |
| + for (auto& contexts : sizingData.rowAxisAlignmentContext.values()) { |
| + for (auto& group : contexts.sharedGroups) { |
| + for (LayoutBox* child : group.children) { |
| + ItemPosition align = ComputedStyle::resolveAlignment(styleRef(), child->styleRef(), ItemPositionStretch); |
| + // TODO (lajava): implement last-baseline logic here. |
| + if (align == ItemPositionBaseline) { |
| + LayoutUnit ascent = marginBoxAscentFromBaselineForChild(*child); |
| + LayoutUnit startOffset = group.groupMaxAscent - ascent; |
| + LayoutPoint location(isHorizontalWritingMode() ? child->location() : child->location().transposedPoint()); |
| + child->setLogicalLocation(location + LayoutSize(0, startOffset)); |
| + } |
| + } |
| + } |
| + } |
| +} |
| + |
| +static int synthesizedBaselineFromContentBox(const LayoutBox& box, LineDirectionMode direction) |
| +{ |
| + if (direction == HorizontalLine) |
| + return box.size().height() - box.borderBottom() - box.paddingBottom() - box.verticalScrollbarWidth(); |
| + return box.size().width() - box.borderLeft() - box.paddingLeft() - box.horizontalScrollbarHeight(); |
| +} |
| + |
| +int LayoutGrid::baselinePosition(FontBaseline, bool, LineDirectionMode direction, LinePositionMode mode) const |
| +{ |
| + ASSERT(mode == PositionOnContainingLine); |
| + int baseline = firstLineBoxBaseline(); |
| + // We take content-box's bottom if no valid baseline. |
| + if (baseline == -1) |
| + baseline = synthesizedBaselineFromContentBox(*this, direction); |
| + |
| + return baseline + beforeMarginInLineDirection(direction); |
| +} |
| + |
| +int LayoutGrid::firstLineBoxBaseline() const |
| +{ |
| + if (isWritingModeRoot()) |
| + return -1; |
| + LayoutBox* baselineChild = nullptr; |
| + for (LayoutBox* child = m_orderIterator.first(); child; child = m_orderIterator.next()) { |
| + if (child->isOutOfFlowPositioned()) |
| + continue; |
| + const GridCoordinate& coordinate = cachedGridCoordinate(*child); |
| + // TODO (lajava): propertly identifying grid items whose areas intersect the grid container's first row/column. |
| + if (coordinate.rows.resolvedInitialPosition.toInt() > 0 && coordinate.columns.resolvedInitialPosition.toInt() > 0) |
| + break; |
| + ItemPosition align = ComputedStyle::resolveAlignment(styleRef(), child->styleRef(), ItemPositionStretch); |
| + // Orthogonal children don't participate in baseline alignment. |
| + bool isOrthogonal = child->isHorizontalWritingMode() != isHorizontalWritingMode(); |
| + if (align == ItemPositionBaseline && !isOrthogonal && !hasAutoMarginsInColumnAxis(*child)) { |
| + baselineChild = child; |
| + break; |
| + } |
| + if (!baselineChild) |
| + baselineChild = child; |
| + } |
| + |
| + if (!baselineChild) |
| + return -1; |
| + |
| + // Using a synthesized baseline (content-box's bottom) if baselineChild has an orthogonal writing mode. |
| + // TODO (lajava) We still don't support orthogonal modes in grid. |
| + if (baselineChild->isHorizontalWritingMode() != isHorizontalWritingMode()) |
| + return -1; |
| + |
| + int baseline = baselineChild->firstLineBoxBaseline(); |
| + if (baseline == -1) { |
| + // TODO (lajava): We should pass |direction| into firstLineBoxBaseline and stop bailing out if we're a writing mode root. |
| + // This would also fix some cases where the grid is orthogonal to its container. |
| + LineDirectionMode direction = isHorizontalWritingMode() ? HorizontalLine : VerticalLine; |
| + return synthesizedBaselineFromContentBox(*baselineChild, direction) + baselineChild->logicalTop(); |
| + } |
| + |
| + return baseline + baselineChild->logicalTop(); |
| +} |
| + |
| +int LayoutGrid::inlineBlockBaseline(LineDirectionMode direction) const |
| +{ |
| + int baseline = firstLineBoxBaseline(); |
| + if (baseline != -1) |
| + return baseline; |
| + |
| + int marginHeight = direction == HorizontalLine ? marginTop() : marginRight(); |
| + return synthesizedBaselineFromContentBox(*this, direction) + marginHeight; |
| +} |
| + |
| GridAxisPosition LayoutGrid::columnAxisPositionForChild(const LayoutBox& child) const |
| { |
| bool hasOrthogonalWritingMode = child.isHorizontalWritingMode() != isHorizontalWritingMode(); |
| @@ -1747,8 +1931,6 @@ GridAxisPosition LayoutGrid::columnAxisPositionForChild(const LayoutBox& child) |
| 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: |
| break; |
| @@ -1790,8 +1972,6 @@ GridAxisPosition LayoutGrid::rowAxisPositionForChild(const LayoutBox& child) con |
| 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: |
| break; |