Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(208)

Unified Diff: third_party/WebKit/Source/core/layout/LayoutGrid.cpp

Issue 1407633003: [css-grid] Implementation of Baseline Self-Alignment (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Patch rebased and applied several refactoring and code clean up. Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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();

Powered by Google App Engine
This is Rietveld 408576698