CSSAlignUtils.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /* Utility code for performing CSS Box Alignment */
  6. #include "CSSAlignUtils.h"
  7. namespace mozilla {
  8. static nscoord
  9. SpaceToFill(WritingMode aWM, const LogicalSize& aSize, nscoord aMargin,
  10. LogicalAxis aAxis, nscoord aCBSize)
  11. {
  12. nscoord size = aAxis == eLogicalAxisBlock ? aSize.BSize(aWM)
  13. : aSize.ISize(aWM);
  14. return aCBSize - (size + aMargin);
  15. }
  16. nscoord
  17. CSSAlignUtils::AlignJustifySelf(uint8_t aAlignment, LogicalAxis aAxis,
  18. AlignJustifyFlags aFlags,
  19. nscoord aBaselineAdjust, nscoord aCBSize,
  20. const ReflowInput& aRI,
  21. const LogicalSize& aChildSize)
  22. {
  23. MOZ_ASSERT(aAlignment != NS_STYLE_ALIGN_AUTO,
  24. "auto values should have resolved already");
  25. MOZ_ASSERT(aAlignment != NS_STYLE_ALIGN_LEFT &&
  26. aAlignment != NS_STYLE_ALIGN_RIGHT,
  27. "caller should map that to the corresponding START/END");
  28. // Promote aFlags to convenience bools:
  29. const bool isOverflowSafe = !!(aFlags & AlignJustifyFlags::eOverflowSafe);
  30. const bool isSameSide = !!(aFlags & AlignJustifyFlags::eSameSide);
  31. // Map some alignment values to 'start' / 'end'.
  32. switch (aAlignment) {
  33. case NS_STYLE_ALIGN_SELF_START: // align/justify-self: self-start
  34. aAlignment = MOZ_LIKELY(isSameSide) ? NS_STYLE_ALIGN_START
  35. : NS_STYLE_ALIGN_END;
  36. break;
  37. case NS_STYLE_ALIGN_SELF_END: // align/justify-self: self-end
  38. aAlignment = MOZ_LIKELY(isSameSide) ? NS_STYLE_ALIGN_END
  39. : NS_STYLE_ALIGN_START;
  40. break;
  41. // flex-start/flex-end are the same as start/end, in most contexts.
  42. // (They have special behavior in flex containers, so flex containers
  43. // should map them to some other value before calling this method.)
  44. case NS_STYLE_ALIGN_FLEX_START:
  45. aAlignment = NS_STYLE_ALIGN_START;
  46. break;
  47. case NS_STYLE_ALIGN_FLEX_END:
  48. aAlignment = NS_STYLE_ALIGN_END;
  49. break;
  50. }
  51. // XXX try to condense this code a bit by adding the necessary convenience
  52. // methods? (bug 1209710)
  53. // Get the item's margin corresponding to the container's start/end side.
  54. const LogicalMargin margin = aRI.ComputedLogicalMargin();
  55. WritingMode wm = aRI.GetWritingMode();
  56. nscoord marginStart, marginEnd;
  57. if (aAxis == eLogicalAxisBlock) {
  58. if (MOZ_LIKELY(isSameSide)) {
  59. marginStart = margin.BStart(wm);
  60. marginEnd = margin.BEnd(wm);
  61. } else {
  62. marginStart = margin.BEnd(wm);
  63. marginEnd = margin.BStart(wm);
  64. }
  65. } else {
  66. if (MOZ_LIKELY(isSameSide)) {
  67. marginStart = margin.IStart(wm);
  68. marginEnd = margin.IEnd(wm);
  69. } else {
  70. marginStart = margin.IEnd(wm);
  71. marginEnd = margin.IStart(wm);
  72. }
  73. }
  74. const auto& styleMargin = aRI.mStyleMargin->mMargin;
  75. bool hasAutoMarginStart;
  76. bool hasAutoMarginEnd;
  77. if (aFlags & AlignJustifyFlags::eIgnoreAutoMargins) {
  78. // (Note: ReflowInput will have treated "auto" margins as 0, so we
  79. // don't need to do anything special to avoid expanding them.)
  80. hasAutoMarginStart = hasAutoMarginEnd = false;
  81. } else if (aAxis == eLogicalAxisBlock) {
  82. hasAutoMarginStart = styleMargin.GetBStartUnit(wm) == eStyleUnit_Auto;
  83. hasAutoMarginEnd = styleMargin.GetBEndUnit(wm) == eStyleUnit_Auto;
  84. } else { /* aAxis == eLogicalAxisInline */
  85. hasAutoMarginStart = styleMargin.GetIStartUnit(wm) == eStyleUnit_Auto;
  86. hasAutoMarginEnd = styleMargin.GetIEndUnit(wm) == eStyleUnit_Auto;
  87. }
  88. // https://drafts.csswg.org/css-align-3/#overflow-values
  89. // This implements <overflow-position> = 'safe'.
  90. // And auto-margins: https://drafts.csswg.org/css-grid/#auto-margins
  91. if ((MOZ_UNLIKELY(isOverflowSafe) && aAlignment != NS_STYLE_ALIGN_START) ||
  92. hasAutoMarginStart || hasAutoMarginEnd) {
  93. nscoord space = SpaceToFill(wm, aChildSize, marginStart + marginEnd,
  94. aAxis, aCBSize);
  95. // XXX we might want to include == 0 here as an optimization -
  96. // I need to see what the baseline/last baseline code looks like first.
  97. if (space < 0) {
  98. // "Overflowing elements ignore their auto margins and overflow
  99. // in the end directions"
  100. aAlignment = NS_STYLE_ALIGN_START;
  101. } else if (hasAutoMarginEnd) {
  102. aAlignment = hasAutoMarginStart ? NS_STYLE_ALIGN_CENTER
  103. : (isSameSide ? NS_STYLE_ALIGN_START
  104. : NS_STYLE_ALIGN_END);
  105. } else if (hasAutoMarginStart) {
  106. aAlignment = isSameSide ? NS_STYLE_ALIGN_END : NS_STYLE_ALIGN_START;
  107. }
  108. }
  109. // Determine the offset for the child frame (its border-box) which will
  110. // achieve the requested alignment.
  111. nscoord offset = 0;
  112. switch (aAlignment) {
  113. case NS_STYLE_ALIGN_BASELINE:
  114. case NS_STYLE_ALIGN_LAST_BASELINE:
  115. if (MOZ_LIKELY(isSameSide == (aAlignment == NS_STYLE_ALIGN_BASELINE))) {
  116. offset = marginStart + aBaselineAdjust;
  117. } else {
  118. nscoord size = aAxis == eLogicalAxisBlock ? aChildSize.BSize(wm)
  119. : aChildSize.ISize(wm);
  120. offset = aCBSize - (size + marginEnd) - aBaselineAdjust;
  121. }
  122. break;
  123. case NS_STYLE_ALIGN_STRETCH:
  124. MOZ_FALLTHROUGH; // ComputeSize() deals with it
  125. case NS_STYLE_ALIGN_START:
  126. offset = marginStart;
  127. break;
  128. case NS_STYLE_ALIGN_END: {
  129. nscoord size = aAxis == eLogicalAxisBlock ? aChildSize.BSize(wm)
  130. : aChildSize.ISize(wm);
  131. offset = aCBSize - (size + marginEnd);
  132. break;
  133. }
  134. case NS_STYLE_ALIGN_CENTER: {
  135. nscoord size = aAxis == eLogicalAxisBlock ? aChildSize.BSize(wm)
  136. : aChildSize.ISize(wm);
  137. offset = (aCBSize - size + marginStart - marginEnd) / 2;
  138. break;
  139. }
  140. default:
  141. MOZ_ASSERT_UNREACHABLE("unknown align-/justify-self value");
  142. }
  143. return offset;
  144. }
  145. } // namespace mozilla