1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /* Utility code for performing CSS Box Alignment */
8
9 #include "CSSAlignUtils.h"
10 #include "ReflowInput.h"
11
12 namespace mozilla {
13
SpaceToFill(WritingMode aWM,const LogicalSize & aSize,nscoord aMargin,LogicalAxis aAxis,nscoord aCBSize)14 static nscoord SpaceToFill(WritingMode aWM, const LogicalSize& aSize,
15 nscoord aMargin, LogicalAxis aAxis,
16 nscoord aCBSize) {
17 nscoord size = aSize.Size(aAxis, aWM);
18 return aCBSize - (size + aMargin);
19 }
20
AlignJustifySelf(const StyleAlignFlags & aAlignment,LogicalAxis aAxis,AlignJustifyFlags aFlags,nscoord aBaselineAdjust,nscoord aCBSize,const ReflowInput & aRI,const LogicalSize & aChildSize)21 nscoord CSSAlignUtils::AlignJustifySelf(const StyleAlignFlags& aAlignment,
22 LogicalAxis aAxis,
23 AlignJustifyFlags aFlags,
24 nscoord aBaselineAdjust,
25 nscoord aCBSize, const ReflowInput& aRI,
26 const LogicalSize& aChildSize) {
27 MOZ_ASSERT(aAlignment != StyleAlignFlags::AUTO,
28 "auto values should have resolved already");
29 MOZ_ASSERT(aAlignment != StyleAlignFlags::LEFT &&
30 aAlignment != StyleAlignFlags::RIGHT,
31 "caller should map that to the corresponding START/END");
32
33 // Promote aFlags to convenience bools:
34 const bool isOverflowSafe = !!(aFlags & AlignJustifyFlags::OverflowSafe);
35 const bool isSameSide = !!(aFlags & AlignJustifyFlags::SameSide);
36
37 StyleAlignFlags alignment = aAlignment;
38 // Map some alignment values to 'start' / 'end'.
39 if (alignment == StyleAlignFlags::SELF_START) {
40 // align/justify-self: self-start
41 alignment =
42 MOZ_LIKELY(isSameSide) ? StyleAlignFlags::START : StyleAlignFlags::END;
43 } else if (alignment == StyleAlignFlags::SELF_END) {
44 alignment =
45 MOZ_LIKELY(isSameSide) ? StyleAlignFlags::END : StyleAlignFlags::START;
46 // flex-start/flex-end are the same as start/end, in most contexts.
47 // (They have special behavior in flex containers, so flex containers
48 // should map them to some other value before calling this method.)
49 } else if (alignment == StyleAlignFlags::FLEX_START) {
50 alignment = StyleAlignFlags::START;
51 } else if (alignment == StyleAlignFlags::FLEX_END) {
52 alignment = StyleAlignFlags::END;
53 }
54
55 // Get the item's margin corresponding to the container's start/end side.
56 WritingMode wm = aRI.GetWritingMode();
57 const LogicalMargin margin = aRI.ComputedLogicalMargin(wm);
58 const auto startSide = MakeLogicalSide(
59 aAxis, MOZ_LIKELY(isSameSide) ? eLogicalEdgeStart : eLogicalEdgeEnd);
60 const nscoord marginStart = margin.Side(startSide, wm);
61 const auto endSide = GetOppositeSide(startSide);
62 const nscoord marginEnd = margin.Side(endSide, wm);
63
64 const auto& styleMargin = aRI.mStyleMargin->mMargin;
65 bool hasAutoMarginStart;
66 bool hasAutoMarginEnd;
67 if (aFlags & AlignJustifyFlags::IgnoreAutoMargins) {
68 // (Note: ReflowInput will have treated "auto" margins as 0, so we
69 // don't need to do anything special to avoid expanding them.)
70 hasAutoMarginStart = hasAutoMarginEnd = false;
71 } else if (aAxis == eLogicalAxisBlock) {
72 hasAutoMarginStart = styleMargin.GetBStart(wm).IsAuto();
73 hasAutoMarginEnd = styleMargin.GetBEnd(wm).IsAuto();
74 } else { /* aAxis == eLogicalAxisInline */
75 hasAutoMarginStart = styleMargin.GetIStart(wm).IsAuto();
76 hasAutoMarginEnd = styleMargin.GetIEnd(wm).IsAuto();
77 }
78
79 // https://drafts.csswg.org/css-align-3/#overflow-values
80 // This implements <overflow-position> = 'safe'.
81 // And auto-margins: https://drafts.csswg.org/css-grid/#auto-margins
82 if ((MOZ_UNLIKELY(isOverflowSafe) && alignment != StyleAlignFlags::START) ||
83 hasAutoMarginStart || hasAutoMarginEnd) {
84 nscoord space =
85 SpaceToFill(wm, aChildSize, marginStart + marginEnd, aAxis, aCBSize);
86 // XXX we might want to include == 0 here as an optimization -
87 // I need to see what the baseline/last baseline code looks like first.
88 if (space < 0) {
89 // "Overflowing elements ignore their auto margins and overflow
90 // in the end directions"
91 alignment = StyleAlignFlags::START;
92 } else if (hasAutoMarginEnd) {
93 alignment = hasAutoMarginStart ? StyleAlignFlags::CENTER
94 : (isSameSide ? StyleAlignFlags::START
95 : StyleAlignFlags::END);
96 } else if (hasAutoMarginStart) {
97 alignment = isSameSide ? StyleAlignFlags::END : StyleAlignFlags::START;
98 }
99 }
100
101 // Determine the offset for the child frame (its border-box) which will
102 // achieve the requested alignment.
103 nscoord offset = 0;
104 if (alignment == StyleAlignFlags::BASELINE ||
105 alignment == StyleAlignFlags::LAST_BASELINE) {
106 if (MOZ_LIKELY(isSameSide == (alignment == StyleAlignFlags::BASELINE))) {
107 offset = marginStart + aBaselineAdjust;
108 } else {
109 nscoord size = aChildSize.Size(aAxis, wm);
110 offset = aCBSize - (size + marginEnd) - aBaselineAdjust;
111 }
112 } else if (alignment == StyleAlignFlags::STRETCH ||
113 alignment == StyleAlignFlags::START) {
114 // ComputeSize() deals with stretch
115 offset = marginStart;
116 } else if (alignment == StyleAlignFlags::END) {
117 nscoord size = aChildSize.Size(aAxis, wm);
118 offset = aCBSize - (size + marginEnd);
119 } else if (alignment == StyleAlignFlags::CENTER) {
120 nscoord size = aChildSize.Size(aAxis, wm);
121 offset = (aCBSize - size + marginStart - marginEnd) / 2;
122 } else {
123 MOZ_ASSERT_UNREACHABLE("unknown align-/justify-self value");
124 }
125
126 return offset;
127 }
128
129 } // namespace mozilla
130