tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CSSAlignUtils.cpp (8573B)


      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 
     11 #include "ReflowInput.h"
     12 #include "nsIFrame.h"
     13 #include "nsIFrameInlines.h"
     14 
     15 namespace mozilla {
     16 
     17 StyleAlignFlags CSSAlignUtils::UsedAlignmentForAbsPos(nsIFrame* aFrame,
     18                                                      StyleAlignFlags aFlags,
     19                                                      LogicalAxis aLogicalAxis,
     20                                                      WritingMode aCBWM) {
     21  MOZ_ASSERT(aFrame->IsAbsolutelyPositioned());
     22 
     23  // Extract and strip the flag bits
     24  StyleAlignFlags alignmentFlags = aFlags & StyleAlignFlags::FLAG_BITS;
     25  aFlags &= ~StyleAlignFlags::FLAG_BITS;
     26 
     27  if (aFlags == StyleAlignFlags::NORMAL) {
     28    // "the 'normal' keyword behaves as 'start' on replaced
     29    // absolutely-positioned boxes, and behaves as 'stretch' on all other
     30    // absolutely-positioned boxes."
     31    // https://drafts.csswg.org/css-align/#align-abspos
     32    // https://drafts.csswg.org/css-align/#justify-abspos
     33    aFlags = aFrame->HasReplacedSizing() ? StyleAlignFlags::START
     34                                         : StyleAlignFlags::STRETCH;
     35  } else if (aFlags == StyleAlignFlags::FLEX_START) {
     36    aFlags = StyleAlignFlags::START;
     37  } else if (aFlags == StyleAlignFlags::FLEX_END) {
     38    aFlags = StyleAlignFlags::END;
     39  } else if (aFlags == StyleAlignFlags::LEFT ||
     40             aFlags == StyleAlignFlags::RIGHT) {
     41    if (aLogicalAxis == LogicalAxis::Inline) {
     42      const bool isLeft = (aFlags == StyleAlignFlags::LEFT);
     43      aFlags = (isLeft == aCBWM.IsBidiLTR()) ? StyleAlignFlags::START
     44                                             : StyleAlignFlags::END;
     45    } else {
     46      aFlags = StyleAlignFlags::START;
     47    }
     48  } else if (aFlags == StyleAlignFlags::BASELINE) {
     49    aFlags = StyleAlignFlags::START;
     50  } else if (aFlags == StyleAlignFlags::LAST_BASELINE) {
     51    aFlags = StyleAlignFlags::END;
     52  }
     53 
     54  return (aFlags | alignmentFlags);
     55 }
     56 
     57 static nscoord SpaceToFill(WritingMode aWM, const LogicalSize& aSize,
     58                           nscoord aMargin, LogicalAxis aAxis,
     59                           nscoord aCBSize) {
     60  nscoord size = aSize.Size(aAxis, aWM);
     61  return aCBSize - (size + aMargin);
     62 }
     63 
     64 nscoord CSSAlignUtils::AlignJustifySelf(
     65    const StyleAlignFlags& aAlignment, LogicalAxis aAxis,
     66    AlignJustifyFlags aFlags, nscoord aBaselineAdjust, nscoord aCBSize,
     67    const ReflowInput& aRI, const LogicalSize& aChildSize,
     68    const Maybe<AnchorAlignInfo>& aAnchorInfo) {
     69  MOZ_ASSERT(aAlignment != StyleAlignFlags::AUTO,
     70             "auto values should have resolved already");
     71  MOZ_ASSERT(aAlignment != StyleAlignFlags::LEFT &&
     72                 aAlignment != StyleAlignFlags::RIGHT,
     73             "caller should map that to the corresponding START/END");
     74 
     75  const bool isSameSide = aFlags.contains(AlignJustifyFlag::SameSide);
     76 
     77  StyleAlignFlags alignment = aAlignment;
     78  // Map some alignment values to 'start' / 'end'.
     79  if (alignment == StyleAlignFlags::SELF_START) {
     80    // align/justify-self: self-start
     81    alignment =
     82        MOZ_LIKELY(isSameSide) ? StyleAlignFlags::START : StyleAlignFlags::END;
     83  } else if (alignment == StyleAlignFlags::SELF_END) {
     84    alignment =
     85        MOZ_LIKELY(isSameSide) ? StyleAlignFlags::END : StyleAlignFlags::START;
     86    // flex-start/flex-end are the same as start/end, in most contexts.
     87    // (They have special behavior in flex containers, so flex containers
     88    // should map them to some other value before calling this method.)
     89  } else if (alignment == StyleAlignFlags::FLEX_START) {
     90    alignment = StyleAlignFlags::START;
     91  } else if (alignment == StyleAlignFlags::FLEX_END) {
     92    alignment = StyleAlignFlags::END;
     93  }
     94 
     95  // Get the item's margin corresponding to the container's start/end side.
     96  WritingMode wm = aRI.GetWritingMode();
     97  // If we're handling the margin box, it's already included in the incoming
     98  // size.
     99  const LogicalMargin margin =
    100      aFlags.contains(AlignJustifyFlag::AligningMarginBox)
    101          ? LogicalMargin(wm)
    102          : aRI.ComputedLogicalMargin(wm);
    103  const auto startSide = MakeLogicalSide(
    104      aAxis, MOZ_LIKELY(isSameSide) ? LogicalEdge::Start : LogicalEdge::End);
    105  const nscoord marginStart = margin.Side(startSide, wm);
    106  const auto endSide = GetOppositeSide(startSide);
    107  const nscoord marginEnd = margin.Side(endSide, wm);
    108 
    109  bool hasAutoMarginStart;
    110  bool hasAutoMarginEnd;
    111  const auto* styleMargin = aRI.mStyleMargin;
    112  const auto anchorResolutionParams = AnchorPosResolutionParams::From(&aRI);
    113  if (aFlags.contains(AlignJustifyFlag::IgnoreAutoMargins) ||
    114      aFlags.contains(AlignJustifyFlag::AligningMarginBox)) {
    115    // (Note: ReflowInput will have treated "auto" margins as 0, so we
    116    // don't need to do anything special to avoid expanding them.)
    117    hasAutoMarginStart = hasAutoMarginEnd = false;
    118  } else if (aAxis == LogicalAxis::Block) {
    119    hasAutoMarginStart =
    120        styleMargin->GetMargin(LogicalSide::BStart, wm, anchorResolutionParams)
    121            ->IsAuto();
    122    hasAutoMarginEnd =
    123        styleMargin->GetMargin(LogicalSide::BEnd, wm, anchorResolutionParams)
    124            ->IsAuto();
    125  } else { /* aAxis == LogicalAxis::Inline */
    126    hasAutoMarginStart =
    127        styleMargin->GetMargin(LogicalSide::IStart, wm, anchorResolutionParams)
    128            ->IsAuto();
    129    hasAutoMarginEnd =
    130        styleMargin->GetMargin(LogicalSide::IEnd, wm, anchorResolutionParams)
    131            ->IsAuto();
    132  }
    133 
    134  // https://drafts.csswg.org/css-align-3/#overflow-values
    135  // This implements <overflow-position> = 'safe'.
    136  // And auto-margins: https://drafts.csswg.org/css-grid/#auto-margins
    137  if ((MOZ_UNLIKELY(aFlags.contains(AlignJustifyFlag::OverflowSafe)) &&
    138       alignment != StyleAlignFlags::START) ||
    139      hasAutoMarginStart || hasAutoMarginEnd) {
    140    nscoord space =
    141        SpaceToFill(wm, aChildSize, marginStart + marginEnd, aAxis, aCBSize);
    142    // XXX we might want to include == 0 here as an optimization -
    143    // I need to see what the baseline/last baseline code looks like first.
    144    if (space < 0) {
    145      // "Overflowing elements ignore their auto margins and overflow
    146      // in the end directions"
    147      alignment = StyleAlignFlags::START;
    148    } else if (hasAutoMarginEnd) {
    149      alignment = hasAutoMarginStart ? StyleAlignFlags::CENTER
    150                                     : (isSameSide ? StyleAlignFlags::START
    151                                                   : StyleAlignFlags::END);
    152    } else if (hasAutoMarginStart) {
    153      alignment = isSameSide ? StyleAlignFlags::END : StyleAlignFlags::START;
    154    }
    155  }
    156 
    157  // Determine the offset for the child frame (its border-box) which will
    158  // achieve the requested alignment.
    159  nscoord offset = 0;
    160  if (alignment == StyleAlignFlags::BASELINE ||
    161      alignment == StyleAlignFlags::LAST_BASELINE) {
    162    const bool isFirstBaselineSharingGroup =
    163        !aFlags.contains(AlignJustifyFlag::LastBaselineSharingGroup);
    164    if (MOZ_LIKELY(isFirstBaselineSharingGroup)) {
    165      offset = marginStart + aBaselineAdjust;
    166    } else {
    167      nscoord size = aChildSize.Size(aAxis, wm);
    168      offset = aCBSize - (size + marginEnd) - aBaselineAdjust;
    169    }
    170  } else if (alignment == StyleAlignFlags::STRETCH ||
    171             alignment == StyleAlignFlags::START) {
    172    // ComputeSize() deals with stretch
    173    offset = marginStart;
    174  } else if (alignment == StyleAlignFlags::END) {
    175    nscoord size = aChildSize.Size(aAxis, wm);
    176    offset = aCBSize - (size + marginEnd);
    177  } else if (alignment == StyleAlignFlags::ANCHOR_CENTER && aAnchorInfo) {
    178    const nscoord anchorSize = aAnchorInfo->mAnchorSize;
    179    const nscoord anchorStart = aAnchorInfo->mAnchorStart;
    180    const nscoord size = aChildSize.Size(aAxis, wm);
    181 
    182    // Offset relative to the anchors center, accounting for margins
    183    offset = anchorStart + (anchorSize - size + marginStart - marginEnd) / 2;
    184  } else {
    185    // ANCHOR_CENTER with no Anchor is treated like CENTER.
    186    MOZ_ASSERT(alignment == StyleAlignFlags::CENTER ||
    187                   alignment == StyleAlignFlags::ANCHOR_CENTER,
    188               "unknown align-/justify-self value");
    189    nscoord size = aChildSize.Size(aAxis, wm);
    190    offset = (aCBSize - size + marginStart - marginEnd) / 2;
    191  }
    192 
    193  return offset;
    194 }
    195 
    196 }  // namespace mozilla