Global Metrics

path: .metrics.cognitive.average
old: null
new: 3.909090909090909

path: .metrics.cognitive.sum
old: 0.0
new: 43.0

path: .metrics.loc.blank
old: 5.0
new: 33.0

path: .metrics.loc.ploc
old: 24.0
new: 278.0

path: .metrics.loc.sloc
old: 37.0
new: 354.0

path: .metrics.loc.lloc
old: 0.0
new: 98.0

path: .metrics.loc.cloc
old: 8.0
new: 43.0

path: .metrics.cyclomatic.average
old: 1.0
new: 4.375

path: .metrics.cyclomatic.sum
old: 2.0
new: 70.0

path: .metrics.halstead.volume
old: 330.3427076686749
new: 12101.92343184597

path: .metrics.halstead.difficulty
old: 4.173913043478261
new: 66.53954802259886

path: .metrics.halstead.vocabulary
old: 29.0
new: 212.0

path: .metrics.halstead.length
old: 68.0
new: 1566.0

path: .metrics.halstead.time
old: 76.6012075753449
new: 44736.47307550718

path: .metrics.halstead.n2
old: 23.0
new: 177.0

path: .metrics.halstead.purity_ratio
old: 1.7581132352005624
new: 0.9586788556562992

path: .metrics.halstead.bugs
old: 0.04129373433627555
new: 2.885148979180852

path: .metrics.halstead.N2
old: 32.0
new: 673.0

path: .metrics.halstead.level
old: 0.23958333333333337
new: 0.015028656336234346

path: .metrics.halstead.N1
old: 36.0
new: 893.0

path: .metrics.halstead.estimated_program_length
old: 119.55169999363824
new: 1501.2910879577644

path: .metrics.halstead.effort
old: 1378.8217363562082
new: 805256.5153591293

path: .metrics.halstead.n1
old: 6.0
new: 35.0

path: .metrics.nexits.average
old: null
new: 2.0

path: .metrics.nexits.sum
old: 0.0
new: 22.0

path: .metrics.nargs.average
old: null
new: 2.1818181818181817

path: .metrics.nargs.sum
old: 0.0
new: 24.0

path: .metrics.mi.mi_sei
old: 75.61696722259691
new: -27.098705364640544

path: .metrics.mi.mi_visual_studio
old: 47.88447401875708
new: 6.392729631505006

path: .metrics.mi.mi_original
old: 81.88245057207462
new: 10.93156766987356

path: .metrics.nom.functions
old: 0.0
new: 10.0

path: .metrics.nom.closures
old: 0.0
new: 1.0

path: .metrics.nom.total
old: 0.0
new: 11.0

Spaces Data

Minimal test - lines (16, 354)

path: .spaces[0].metrics.mi.mi_original
old: null
new: 11.92708504330217

path: .spaces[0].metrics.mi.mi_visual_studio
old: null
new: 6.974903534094836

path: .spaces[0].metrics.mi.mi_sei
old: null
new: -26.98043914866469

path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 69.0

path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 4.6

path: .spaces[0].metrics.halstead.purity_ratio
old: null
new: 0.9180189539953548

path: .spaces[0].metrics.halstead.estimated_program_length
old: null
new: 1430.273530324763

path: .spaces[0].metrics.halstead.difficulty
old: 0.0
new: 68.86094674556213

path: .spaces[0].metrics.halstead.n2
old: 1.0
new: 169.0

path: .spaces[0].metrics.halstead.effort
old: 0.0
new: 823138.8767514032

path: .spaces[0].metrics.halstead.volume
old: 0.0
new: 11953.63868279159

path: .spaces[0].metrics.halstead.length
old: 1.0
new: 1558.0

path: .spaces[0].metrics.halstead.level
old: null
new: 0.014522019334049409

path: .spaces[0].metrics.halstead.N2
old: 1.0
new: 665.0

path: .spaces[0].metrics.halstead.N1
old: 0.0
new: 893.0

path: .spaces[0].metrics.halstead.bugs
old: 0.0
new: 2.9277061691230233

path: .spaces[0].metrics.halstead.vocabulary
old: 1.0
new: 204.0

path: .spaces[0].metrics.halstead.n1
old: 0.0
new: 35.0

path: .spaces[0].metrics.halstead.time
old: 0.0
new: 45729.93759730018

path: .spaces[0].metrics.nexits.average
old: null
new: 2.0

path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 22.0

path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 24.0

path: .spaces[0].metrics.nargs.average
old: null
new: 2.1818181818181817

path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 43.0

path: .spaces[0].metrics.cognitive.average
old: null
new: 3.909090909090909

path: .spaces[0].metrics.loc.blank
old: 0.0
new: 32.0

path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 98.0

path: .spaces[0].metrics.loc.sloc
old: 1.0
new: 339.0

path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 37.0

path: .spaces[0].metrics.loc.ploc
old: 1.0
new: 270.0

path: .spaces[0].metrics.nom.closures
old: 0.0
new: 1.0

path: .spaces[0].metrics.nom.functions
old: 0.0
new: 10.0

path: .spaces[0].metrics.nom.total
old: 0.0
new: 11.0

Code

namespace mozilla {

using namespace gfx;

namespace widget {

static nsIFrame* GetParentScrollbarFrame(nsIFrame* aFrame) {
  // Walk our parents to find a scrollbar frame
  nsIFrame* scrollbarFrame = aFrame;
  do {
    if (scrollbarFrame->IsScrollbarFrame()) {
      break;
    }
  } while ((scrollbarFrame = scrollbarFrame->GetParent()));

  // We return null if we can't find a parent scrollbar frame
  return scrollbarFrame;
}

static bool IsParentScrollbarRolledOver(nsIFrame* aFrame) {
  nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame);
  return nsLookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars) != 0
             ? nsNativeTheme::CheckBooleanAttr(scrollbarFrame, nsGkAtoms::hover)
             : nsNativeTheme::GetContentState(scrollbarFrame,
                                              StyleAppearance::None)
                   .HasState(NS_EVENT_STATE_HOVER);
}

CSSIntCoord ScrollbarDrawingMac::GetScrollbarSize(StyleScrollbarWidth aWidth,
                                                  bool aOverlay) {
  bool isSmall = aWidth == StyleScrollbarWidth::Thin;
  if (aOverlay) {
    return isSmall ? 14 : 16;
  }
  return isSmall ? 11 : 15;
}

LayoutDeviceIntCoord ScrollbarDrawingMac::GetScrollbarSize(
    StyleScrollbarWidth aWidth, bool aOverlay, float aDpiRatio) {
  CSSIntCoord size = GetScrollbarSize(aWidth, aOverlay);
  if (aDpiRatio >= 2.0f) {
    return int32_t(size) * 2;
  }
  return int32_t(size);
}

LayoutDeviceIntSize ScrollbarDrawingMac::GetMinimumWidgetSize(
    StyleAppearance aAppearance, nsIFrame* aFrame, float aDpiRatio) {
  auto minSize = [&] {
    switch (aAppearance) {
      case StyleAppearance::ScrollbarthumbHorizontal:
        return IntSize{26, 0};
      case StyleAppearance::ScrollbarthumbVertical:
        return IntSize{0, 26};
      case StyleAppearance::ScrollbarVertical:
      case StyleAppearance::ScrollbarHorizontal:
      case StyleAppearance::ScrollbartrackVertical:
      case StyleAppearance::ScrollbartrackHorizontal: {
        ComputedStyle* style = nsLayoutUtils::StyleForScrollbar(aFrame);
        auto scrollbarWidth = style->StyleUIReset()->mScrollbarWidth;
        auto size = GetScrollbarSize(
            scrollbarWidth,
            LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars));
        return IntSize{size, size};
      }
      case StyleAppearance::MozMenulistArrowButton: {
        auto size =
            GetScrollbarSize(StyleScrollbarWidth::Auto, /* aOverlay = */ false);
        return IntSize{size, size};
      }
      case StyleAppearance::ScrollbarbuttonUp:
      case StyleAppearance::ScrollbarbuttonDown:
        return IntSize{15, 16};
      case StyleAppearance::ScrollbarbuttonLeft:
      case StyleAppearance::ScrollbarbuttonRight:
        return IntSize{16, 15};
      default:
        return IntSize{};
    }
  }();

  if (aDpiRatio >= 2.0f) {
    return LayoutDeviceIntSize{minSize.width * 2, minSize.height * 2};
  }
  return LayoutDeviceIntSize{minSize.width, minSize.height};
}

ScrollbarParams ScrollbarDrawingMac::ComputeScrollbarParams(
    nsIFrame* aFrame, const ComputedStyle& aStyle, bool aIsHorizontal) {
  ScrollbarParams params;
  params.overlay =
      nsLookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars) != 0;
  params.rolledOver = IsParentScrollbarRolledOver(aFrame);
  params.small =
      aStyle.StyleUIReset()->mScrollbarWidth == StyleScrollbarWidth::Thin;
  params.rtl = nsNativeTheme::IsFrameRTL(aFrame);
  params.horizontal = aIsHorizontal;
  params.onDarkBackground = nsNativeTheme::IsDarkBackground(aFrame);
  // Don't use custom scrollbars for overlay scrollbars since they are
  // generally good enough for use cases of custom scrollbars.
  if (!params.overlay) {
    const nsStyleUI* ui = aStyle.StyleUI();
    if (ui->HasCustomScrollbars()) {
      const auto& colors = ui->mScrollbarColor.AsColors();
      params.custom = true;
      params.trackColor = colors.track.CalcColor(aStyle);
      params.faceColor = colors.thumb.CalcColor(aStyle);
    }
  }

  return params;
}

auto ScrollbarDrawingMac::GetThumbRect(const Rect& aRect,
                                       const ScrollbarParams& aParams,
                                       float aScale) -> ThumbRect {
  // This matches the sizing checks in GetMinimumWidgetSize etc.
  aScale = aScale >= 2.0f ? 2.0f : 1.0f;

  // Compute the thumb thickness. This varies based on aParams.small,
  // aParams.overlay and aParams.rolledOver. non-overlay: 6 / 8, overlay
  // non-hovered: 5 / 7, overlay hovered: 9 / 11
  float thickness = aParams.small ? 6.0f : 8.0f;
  if (aParams.overlay) {
    thickness -= 1.0f;
    if (aParams.rolledOver) {
      thickness += 4.0f;
    }
  }
  thickness *= aScale;

  // Compute the thumb rect.
  const float outerSpacing =
      ((aParams.overlay || aParams.small) ? 1.0f : 2.0f) * aScale;
  Rect thumbRect = aRect;
  thumbRect.Deflate(1.0f * aScale);
  if (aParams.horizontal) {
    float bottomEdge = thumbRect.YMost() - outerSpacing;
    thumbRect.SetBoxY(bottomEdge - thickness, bottomEdge);
  } else {
    if (aParams.rtl) {
      float leftEdge = thumbRect.X() + outerSpacing;
      thumbRect.SetBoxX(leftEdge, leftEdge + thickness);
    } else {
      float rightEdge = thumbRect.XMost() - outerSpacing;
      thumbRect.SetBoxX(rightEdge - thickness, rightEdge);
    }
  }

  // Compute the thumb fill color.
  nscolor faceColor;
  if (aParams.custom) {
    faceColor = aParams.faceColor;
  } else {
    if (aParams.overlay) {
      faceColor = aParams.onDarkBackground ? NS_RGBA(255, 255, 255, 128)
                                           : NS_RGBA(0, 0, 0, 128);
    } else {
      faceColor = aParams.rolledOver ? NS_RGBA(125, 125, 125, 255)
                                     : NS_RGBA(194, 194, 194, 255);
    }
  }

  nscolor strokeColor = 0;
  float strokeOutset = 0.0f;
  float strokeWidth = 0.0f;

  // Overlay scrollbars have an additional stroke around the fill.
  if (aParams.overlay) {
    strokeOutset = (aParams.onDarkBackground ? 0.3f : 0.5f) * aScale;
    strokeWidth = (aParams.onDarkBackground ? 0.6f : 0.8f) * aScale;

    strokeColor = aParams.onDarkBackground ? NS_RGBA(0, 0, 0, 48)
                                           : NS_RGBA(255, 255, 255, 48);
  }

  return {thumbRect, faceColor, strokeColor, strokeWidth, strokeOutset};
}

struct ScrollbarTrackDecorationColors {
  nscolor mInnerColor = 0;
  nscolor mShadowColor = 0;
  nscolor mOuterColor = 0;
};

static ScrollbarTrackDecorationColors ComputeScrollbarTrackDecorationColors(
    nscolor aTrackColor) {
  ScrollbarTrackDecorationColors result;
  float luminance = RelativeLuminanceUtils::Compute(aTrackColor);
  if (luminance >= 0.5f) {
    result.mInnerColor =
        RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 0.836f);
    result.mShadowColor =
        RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 0.982f);
    result.mOuterColor =
        RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 0.886f);
  } else {
    result.mInnerColor =
        RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 1.196f);
    result.mShadowColor =
        RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 1.018f);
    result.mOuterColor =
        RelativeLuminanceUtils::Adjust(aTrackColor, luminance * 1.129f);
  }
  return result;
}

bool ScrollbarDrawingMac::GetScrollbarTrackRects(const Rect& aRect,
                                                 const ScrollbarParams& aParams,
                                                 float aScale,
                                                 ScrollbarTrackRects& aRects) {
  if (aParams.overlay && !aParams.rolledOver) {
    // Non-hovered overlay scrollbars don't have a track. Draw nothing.
    return false;
  }

  // This matches the sizing checks in GetMinimumWidgetSize etc.
  aScale = aScale >= 2.0f ? 2.0f : 1.0f;

  nscolor trackColor;
  if (aParams.custom) {
    trackColor = aParams.trackColor;
  } else {
    if (aParams.overlay) {
      trackColor = aParams.onDarkBackground ? NS_RGBA(201, 201, 201, 38)
                                            : NS_RGBA(250, 250, 250, 191);
    } else {
      trackColor = NS_RGBA(250, 250, 250, 255);
    }
  }

  float thickness = aParams.horizontal ? aRect.height : aRect.width;

  // The scrollbar track is drawn as multiple non-overlapping segments, which
  // make up lines of different widths and with slightly different shading.
  ScrollbarTrackDecorationColors colors =
      ComputeScrollbarTrackDecorationColors(trackColor);
  struct {
    nscolor color;
    float thickness;
  } segments[] = {
      {colors.mInnerColor, 1.0f * aScale},
      {colors.mShadowColor, 1.0f * aScale},
      {trackColor, thickness - 3.0f * aScale},
      {colors.mOuterColor, 1.0f * aScale},
  };

  // Iterate over the segments "from inside to outside" and fill each segment.
  // For horizontal scrollbars, iterate top to bottom.
  // For vertical scrollbars, iterate left to right or right to left based on
  // aParams.rtl.
  auto current = aRects.begin();
  float accumulatedThickness = 0.0f;
  for (const auto& segment : segments) {
    Rect segmentRect = aRect;
    float startThickness = accumulatedThickness;
    float endThickness = startThickness + segment.thickness;
    if (aParams.horizontal) {
      segmentRect.SetBoxY(aRect.Y() + startThickness, aRect.Y() + endThickness);
    } else {
      if (aParams.rtl) {
        segmentRect.SetBoxX(aRect.XMost() - endThickness,
                            aRect.XMost() - startThickness);
      } else {
        segmentRect.SetBoxX(aRect.X() + startThickness,
                            aRect.X() + endThickness);
      }
    }
    accumulatedThickness = endThickness;
    *current++ = {segmentRect, segment.color};
  }

  return true;
}

bool ScrollbarDrawingMac::GetScrollCornerRects(const Rect& aRect,
                                               const ScrollbarParams& aParams,
                                               float aScale,
                                               ScrollCornerRects& aRects) {
  if (aParams.overlay && !aParams.rolledOver) {
    // Non-hovered overlay scrollbars don't have a corner. Draw nothing.
    return false;
  }

  // This matches the sizing checks in GetMinimumWidgetSize etc.
  aScale = aScale >= 2.0f ? 2.0f : 1.0f;

  // Draw the following scroll corner.
  //
  //        Output:                      Rectangles:
  // +---+---+----------+---+     +---+---+----------+---+
  // | I | S | T ...  T | O |     | I | S | T ...  T | O |
  // +---+   |          |   |     +---+---+          |   |
  // | S   S | T ...  T |   |     | S   S | T ...  T | . |
  // +-------+          | . |     +-------+----------+ . |
  // | T      ...     T | . |     | T      ...     T | . |
  // | .              . | . |     | .              . |   |
  // | T      ...     T |   |     | T      ...     T | O |
  // +------------------+   |     +------------------+---+
  // | O       ...        O |     | O       ...        O |
  // +----------------------+     +----------------------+

  float width = aRect.width;
  float height = aRect.height;
  nscolor trackColor =
      aParams.custom ? aParams.trackColor : NS_RGBA(250, 250, 250, 255);
  ScrollbarTrackDecorationColors colors =
      ComputeScrollbarTrackDecorationColors(trackColor);
  struct {
    nscolor color;
    Rect relativeRect;
  } pieces[] = {
      {colors.mInnerColor, {0.0f, 0.0f, 1.0f * aScale, 1.0f * aScale}},
      {colors.mShadowColor,
       {1.0f * aScale, 0.0f, 1.0f * aScale, 1.0f * aScale}},
      {colors.mShadowColor,
       {0.0f, 1.0f * aScale, 2.0f * aScale, 1.0f * aScale}},
      {trackColor, {2.0f * aScale, 0.0f, width - 3.0f * aScale, 2.0f * aScale}},
      {trackColor,
       {0.0f, 2.0f * aScale, width - 1.0f * aScale, height - 3.0f * aScale}},
      {colors.mOuterColor,
       {width - 1.0f * aScale, 0.0f, 1.0f * aScale, height - 1.0f * aScale}},
      {colors.mOuterColor,
       {0.0f, height - 1.0f * aScale, width, 1.0f * aScale}},
  };

  auto current = aRects.begin();
  for (const auto& piece : pieces) {
    Rect pieceRect = piece.relativeRect + aRect.TopLeft();
    if (aParams.rtl) {
      pieceRect.x = aRect.XMost() - piece.relativeRect.XMost();
    }
    *current++ = {pieceRect, piece.color};
  }
  return true;
}

}  // namespace widget
}  // namespace mozilla