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