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   // XXX try to condense this code a bit by adding the necessary convenience
56   // methods? (bug 1209710)
57 
58   // Get the item's margin corresponding to the container's start/end side.
59   const LogicalMargin margin = aRI.ComputedLogicalMargin();
60   WritingMode wm = aRI.GetWritingMode();
61   nscoord marginStart, marginEnd;
62   if (aAxis == eLogicalAxisBlock) {
63     if (MOZ_LIKELY(isSameSide)) {
64       marginStart = margin.BStart(wm);
65       marginEnd = margin.BEnd(wm);
66     } else {
67       marginStart = margin.BEnd(wm);
68       marginEnd = margin.BStart(wm);
69     }
70   } else {
71     if (MOZ_LIKELY(isSameSide)) {
72       marginStart = margin.IStart(wm);
73       marginEnd = margin.IEnd(wm);
74     } else {
75       marginStart = margin.IEnd(wm);
76       marginEnd = margin.IStart(wm);
77     }
78   }
79 
80   const auto& styleMargin = aRI.mStyleMargin->mMargin;
81   bool hasAutoMarginStart;
82   bool hasAutoMarginEnd;
83   if (aFlags & AlignJustifyFlags::IgnoreAutoMargins) {
84     // (Note: ReflowInput will have treated "auto" margins as 0, so we
85     // don't need to do anything special to avoid expanding them.)
86     hasAutoMarginStart = hasAutoMarginEnd = false;
87   } else if (aAxis == eLogicalAxisBlock) {
88     hasAutoMarginStart = styleMargin.GetBStart(wm).IsAuto();
89     hasAutoMarginEnd = styleMargin.GetBEnd(wm).IsAuto();
90   } else { /* aAxis == eLogicalAxisInline */
91     hasAutoMarginStart = styleMargin.GetIStart(wm).IsAuto();
92     hasAutoMarginEnd = styleMargin.GetIEnd(wm).IsAuto();
93   }
94 
95   // https://drafts.csswg.org/css-align-3/#overflow-values
96   // This implements <overflow-position> = 'safe'.
97   // And auto-margins: https://drafts.csswg.org/css-grid/#auto-margins
98   if ((MOZ_UNLIKELY(isOverflowSafe) && alignment != StyleAlignFlags::START) ||
99       hasAutoMarginStart || hasAutoMarginEnd) {
100     nscoord space =
101         SpaceToFill(wm, aChildSize, marginStart + marginEnd, aAxis, aCBSize);
102     // XXX we might want to include == 0 here as an optimization -
103     // I need to see what the baseline/last baseline code looks like first.
104     if (space < 0) {
105       // "Overflowing elements ignore their auto margins and overflow
106       // in the end directions"
107       alignment = StyleAlignFlags::START;
108     } else if (hasAutoMarginEnd) {
109       alignment = hasAutoMarginStart ? StyleAlignFlags::CENTER
110                                      : (isSameSide ? StyleAlignFlags::START
111                                                    : StyleAlignFlags::END);
112     } else if (hasAutoMarginStart) {
113       alignment = isSameSide ? StyleAlignFlags::END : StyleAlignFlags::START;
114     }
115   }
116 
117   // Determine the offset for the child frame (its border-box) which will
118   // achieve the requested alignment.
119   nscoord offset = 0;
120   if (alignment == StyleAlignFlags::BASELINE ||
121       alignment == StyleAlignFlags::LAST_BASELINE) {
122     if (MOZ_LIKELY(isSameSide == (alignment == StyleAlignFlags::BASELINE))) {
123       offset = marginStart + aBaselineAdjust;
124     } else {
125       nscoord size = aChildSize.Size(aAxis, wm);
126       offset = aCBSize - (size + marginEnd) - aBaselineAdjust;
127     }
128   } else if (alignment == StyleAlignFlags::STRETCH ||
129              alignment == StyleAlignFlags::START) {
130     // ComputeSize() deals with stretch
131     offset = marginStart;
132   } else if (alignment == StyleAlignFlags::END) {
133     nscoord size = aChildSize.Size(aAxis, wm);
134     offset = aCBSize - (size + marginEnd);
135   } else if (alignment == StyleAlignFlags::CENTER) {
136     nscoord size = aChildSize.Size(aAxis, wm);
137     offset = (aCBSize - size + marginStart - marginEnd) / 2;
138   } else {
139     MOZ_ASSERT_UNREACHABLE("unknown align-/justify-self value");
140   }
141 
142   return offset;
143 }
144 
145 }  // namespace mozilla
146