tor-browser

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

nsCSSRendering.cpp (196431B)


      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 functions for drawing borders and backgrounds */
      8 
      9 #include "nsCSSRendering.h"
     10 
     11 #include <algorithm>
     12 #include <ctime>
     13 
     14 #include "BorderConsts.h"
     15 #include "ImageContainer.h"
     16 #include "ImageOps.h"
     17 #include "ScaledFontBase.h"
     18 #include "TextDrawTarget.h"
     19 #include "gfx2DGlue.h"
     20 #include "gfxContext.h"
     21 #include "gfxDrawable.h"
     22 #include "gfxFont.h"
     23 #include "gfxGradientCache.h"
     24 #include "gfxUtils.h"
     25 #include "imgIContainer.h"
     26 #include "mozilla/ComputedStyle.h"
     27 #include "mozilla/PresShell.h"
     28 #include "mozilla/ProfilerLabels.h"
     29 #include "mozilla/SVGImageContext.h"
     30 #include "mozilla/ScrollContainerFrame.h"
     31 #include "mozilla/StaticPtr.h"
     32 #include "mozilla/css/ImageLoader.h"
     33 #include "mozilla/dom/DocumentInlines.h"
     34 #include "mozilla/gfx/2D.h"
     35 #include "mozilla/gfx/Helpers.h"
     36 #include "mozilla/gfx/Logging.h"
     37 #include "mozilla/gfx/PathHelpers.h"
     38 #include "nsBlockFrame.h"
     39 #include "nsCSSAnonBoxes.h"
     40 #include "nsCSSColorUtils.h"
     41 #include "nsCSSFrameConstructor.h"
     42 #include "nsCSSProps.h"
     43 #include "nsCSSRenderingBorders.h"
     44 #include "nsCanvasFrame.h"
     45 #include "nsContentUtils.h"
     46 #include "nsFrameManager.h"
     47 #include "nsGkAtoms.h"
     48 #include "nsIContent.h"
     49 #include "nsIFrame.h"
     50 #include "nsIFrameInlines.h"
     51 #include "nsITheme.h"
     52 #include "nsInlineFrame.h"
     53 #include "nsLayoutUtils.h"
     54 #include "nsPageSequenceFrame.h"
     55 #include "nsPoint.h"
     56 #include "nsPresContext.h"
     57 #include "nsRect.h"
     58 #include "nsRubyTextContainerFrame.h"
     59 #include "nsStyleConsts.h"
     60 #include "nsStyleStructInlines.h"
     61 #include "skia/include/core/SkTextBlob.h"
     62 
     63 using namespace mozilla;
     64 using namespace mozilla::css;
     65 using namespace mozilla::gfx;
     66 using namespace mozilla::image;
     67 using mozilla::CSSSizeOrRatio;
     68 using mozilla::dom::Document;
     69 
     70 static int gFrameTreeLockCount = 0;
     71 
     72 // To avoid storing this data on nsInlineFrame (bloat) and to avoid
     73 // recalculating this for each frame in a continuation (perf), hold
     74 // a cache of various coordinate information that we need in order
     75 // to paint inline backgrounds.
     76 struct InlineBackgroundData {
     77  InlineBackgroundData()
     78      : mFrame(nullptr),
     79        mLineContainer(nullptr),
     80        mContinuationPoint(0),
     81        mUnbrokenMeasure(0),
     82        mLineContinuationPoint(0),
     83        mPIStartBorderData{},
     84        mBidiEnabled(false),
     85        mVertical(false) {}
     86 
     87  ~InlineBackgroundData() = default;
     88 
     89  void Reset() {
     90    mBoundingBox.SetRect(0, 0, 0, 0);
     91    mContinuationPoint = mLineContinuationPoint = mUnbrokenMeasure = 0;
     92    mFrame = mLineContainer = nullptr;
     93    mPIStartBorderData.Reset();
     94  }
     95 
     96  /**
     97   * Return a continuous rect for (an inline) aFrame relative to the
     98   * continuation that draws the left-most part of the background.
     99   * This is used when painting backgrounds.
    100   */
    101  nsRect GetContinuousRect(nsIFrame* aFrame) {
    102    MOZ_ASSERT(static_cast<nsInlineFrame*>(do_QueryFrame(aFrame)));
    103 
    104    SetFrame(aFrame);
    105 
    106    nscoord pos;  // an x coordinate if writing-mode is horizontal;
    107                  // y coordinate if vertical
    108    if (mBidiEnabled) {
    109      pos = mLineContinuationPoint;
    110 
    111      // Scan continuations on the same line as aFrame and accumulate the widths
    112      // of frames that are to the left (if this is an LTR block) or right
    113      // (if it's RTL) of the current one.
    114      bool isRtlBlock = (mLineContainer->StyleVisibility()->mDirection ==
    115                         StyleDirection::Rtl);
    116      nscoord curOffset = mVertical ? aFrame->GetOffsetTo(mLineContainer).y
    117                                    : aFrame->GetOffsetTo(mLineContainer).x;
    118 
    119      // If the continuation is fluid we know inlineFrame is not on the same
    120      // line. If it's not fluid, we need to test further to be sure.
    121      nsIFrame* inlineFrame = aFrame->GetPrevContinuation();
    122      while (inlineFrame && !inlineFrame->GetNextInFlow() &&
    123             AreOnSameLine(aFrame, inlineFrame)) {
    124        nscoord frameOffset = mVertical
    125                                  ? inlineFrame->GetOffsetTo(mLineContainer).y
    126                                  : inlineFrame->GetOffsetTo(mLineContainer).x;
    127        if (isRtlBlock == (frameOffset >= curOffset)) {
    128          pos += mVertical ? inlineFrame->GetSize().height
    129                           : inlineFrame->GetSize().width;
    130        }
    131        inlineFrame = inlineFrame->GetPrevContinuation();
    132      }
    133 
    134      inlineFrame = aFrame->GetNextContinuation();
    135      while (inlineFrame && !inlineFrame->GetPrevInFlow() &&
    136             AreOnSameLine(aFrame, inlineFrame)) {
    137        nscoord frameOffset = mVertical
    138                                  ? inlineFrame->GetOffsetTo(mLineContainer).y
    139                                  : inlineFrame->GetOffsetTo(mLineContainer).x;
    140        if (isRtlBlock == (frameOffset >= curOffset)) {
    141          pos += mVertical ? inlineFrame->GetSize().height
    142                           : inlineFrame->GetSize().width;
    143        }
    144        inlineFrame = inlineFrame->GetNextContinuation();
    145      }
    146      if (isRtlBlock) {
    147        // aFrame itself is also to the right of its left edge, so add its
    148        // width.
    149        pos += mVertical ? aFrame->GetSize().height : aFrame->GetSize().width;
    150        // pos is now the distance from the left [top] edge of aFrame to the
    151        // right [bottom] edge of the unbroken content. Change it to indicate
    152        // the distance from the left [top] edge of the unbroken content to the
    153        // left [top] edge of aFrame.
    154        pos = mUnbrokenMeasure - pos;
    155      }
    156    } else {
    157      pos = mContinuationPoint;
    158    }
    159 
    160    // Assume background-origin: border and return a rect with offsets
    161    // relative to (0,0).  If we have a different background-origin,
    162    // then our rect should be deflated appropriately by our caller.
    163    return mVertical
    164               ? nsRect(0, -pos, mFrame->GetSize().width, mUnbrokenMeasure)
    165               : nsRect(-pos, 0, mUnbrokenMeasure, mFrame->GetSize().height);
    166  }
    167 
    168  /**
    169   * Return a continuous rect for (an inline) aFrame relative to the
    170   * continuation that should draw the left[top]-border.  This is used when
    171   * painting borders and clipping backgrounds.  This may NOT be the same
    172   * continuous rect as for drawing backgrounds; the continuation with the
    173   * left[top]-border might be somewhere in the middle of that rect (e.g. BIDI),
    174   * in those cases we need the reverse background order starting at the
    175   * left[top]-border continuation.
    176   */
    177  nsRect GetBorderContinuousRect(nsIFrame* aFrame, nsRect aBorderArea) {
    178    // Calling GetContinuousRect(aFrame) here may lead to Reset/Init which
    179    // resets our mPIStartBorderData so we save it ...
    180    PhysicalInlineStartBorderData saved(mPIStartBorderData);
    181    nsRect joinedBorderArea = GetContinuousRect(aFrame);
    182    if (!saved.mIsValid || saved.mFrame != mPIStartBorderData.mFrame) {
    183      if (aFrame == mPIStartBorderData.mFrame) {
    184        if (mVertical) {
    185          mPIStartBorderData.SetCoord(joinedBorderArea.y);
    186        } else {
    187          mPIStartBorderData.SetCoord(joinedBorderArea.x);
    188        }
    189      } else if (mPIStartBorderData.mFrame) {
    190        // Copy data to a temporary object so that computing the
    191        // continous rect here doesn't clobber our normal state.
    192        InlineBackgroundData temp = *this;
    193        if (mVertical) {
    194          mPIStartBorderData.SetCoord(
    195              temp.GetContinuousRect(mPIStartBorderData.mFrame).y);
    196        } else {
    197          mPIStartBorderData.SetCoord(
    198              temp.GetContinuousRect(mPIStartBorderData.mFrame).x);
    199        }
    200      }
    201    } else {
    202      // ... and restore it when possible.
    203      mPIStartBorderData.SetCoord(saved.mCoord);
    204    }
    205    if (mVertical) {
    206      if (joinedBorderArea.y > mPIStartBorderData.mCoord) {
    207        joinedBorderArea.y =
    208            -(mUnbrokenMeasure + joinedBorderArea.y - aBorderArea.height);
    209      } else {
    210        joinedBorderArea.y -= mPIStartBorderData.mCoord;
    211      }
    212    } else {
    213      if (joinedBorderArea.x > mPIStartBorderData.mCoord) {
    214        joinedBorderArea.x =
    215            -(mUnbrokenMeasure + joinedBorderArea.x - aBorderArea.width);
    216      } else {
    217        joinedBorderArea.x -= mPIStartBorderData.mCoord;
    218      }
    219    }
    220    return joinedBorderArea;
    221  }
    222 
    223  nsRect GetBoundingRect(nsIFrame* aFrame) {
    224    SetFrame(aFrame);
    225 
    226    // Move the offsets relative to (0,0) which puts the bounding box into
    227    // our coordinate system rather than our parent's.  We do this by
    228    // moving it the back distance from us to the bounding box.
    229    // This also assumes background-origin: border, so our caller will
    230    // need to deflate us if needed.
    231    nsRect boundingBox(mBoundingBox);
    232    nsPoint point = mFrame->GetPosition();
    233    boundingBox.MoveBy(-point.x, -point.y);
    234 
    235    return boundingBox;
    236  }
    237 
    238 protected:
    239  // This is a coordinate on the inline axis, but is not a true logical inline-
    240  // coord because it is always measured from left to right (if horizontal) or
    241  // from top to bottom (if vertical), ignoring any bidi RTL directionality.
    242  // We'll call this "physical inline start", or PIStart for short.
    243  struct PhysicalInlineStartBorderData {
    244    nsIFrame* mFrame;  // the continuation that may have a left-border
    245    nscoord mCoord;    // cached GetContinuousRect(mFrame).x or .y
    246    bool mIsValid;     // true if mCoord is valid
    247    void Reset() {
    248      mFrame = nullptr;
    249      mIsValid = false;
    250    }
    251    void SetCoord(nscoord aCoord) {
    252      mCoord = aCoord;
    253      mIsValid = true;
    254    }
    255  };
    256 
    257  nsIFrame* mFrame;
    258  nsIFrame* mLineContainer;
    259  nsRect mBoundingBox;
    260  nscoord mContinuationPoint;
    261  nscoord mUnbrokenMeasure;
    262  nscoord mLineContinuationPoint;
    263  PhysicalInlineStartBorderData mPIStartBorderData;
    264  bool mBidiEnabled;
    265  bool mVertical;
    266 
    267  void SetFrame(nsIFrame* aFrame) {
    268    MOZ_ASSERT(aFrame, "Need a frame");
    269    NS_ASSERTION(gFrameTreeLockCount > 0,
    270                 "Can't call this when frame tree is not locked");
    271 
    272    if (aFrame == mFrame) {
    273      return;
    274    }
    275 
    276    nsIFrame* prevContinuation = GetPrevContinuation(aFrame);
    277 
    278    if (!prevContinuation || mFrame != prevContinuation) {
    279      // Ok, we've got the wrong frame.  We have to start from scratch.
    280      Reset();
    281      Init(aFrame);
    282      return;
    283    }
    284 
    285    // Get our last frame's size and add its width to our continuation
    286    // point before we cache the new frame.
    287    mContinuationPoint +=
    288        mVertical ? mFrame->GetSize().height : mFrame->GetSize().width;
    289 
    290    // If this a new line, update mLineContinuationPoint.
    291    if (mBidiEnabled &&
    292        (aFrame->GetPrevInFlow() || !AreOnSameLine(mFrame, aFrame))) {
    293      mLineContinuationPoint = mContinuationPoint;
    294    }
    295 
    296    mFrame = aFrame;
    297  }
    298 
    299  nsIFrame* GetPrevContinuation(nsIFrame* aFrame) {
    300    nsIFrame* prevCont = aFrame->GetPrevContinuation();
    301    if (!prevCont && aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
    302      nsIFrame* block = aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
    303      if (block) {
    304        // The {ib} properties are only stored on first continuations
    305        NS_ASSERTION(!block->GetPrevContinuation(),
    306                     "Incorrect value for IBSplitPrevSibling");
    307        prevCont = block->GetProperty(nsIFrame::IBSplitPrevSibling());
    308        NS_ASSERTION(prevCont, "How did that happen?");
    309      }
    310    }
    311    return prevCont;
    312  }
    313 
    314  nsIFrame* GetNextContinuation(nsIFrame* aFrame) {
    315    nsIFrame* nextCont = aFrame->GetNextContinuation();
    316    if (!nextCont && aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
    317      // The {ib} properties are only stored on first continuations
    318      aFrame = aFrame->FirstContinuation();
    319      nsIFrame* block = aFrame->GetProperty(nsIFrame::IBSplitSibling());
    320      if (block) {
    321        nextCont = block->GetProperty(nsIFrame::IBSplitSibling());
    322        NS_ASSERTION(nextCont, "How did that happen?");
    323      }
    324    }
    325    return nextCont;
    326  }
    327 
    328  void Init(nsIFrame* aFrame) {
    329    mPIStartBorderData.Reset();
    330    mBidiEnabled = aFrame->PresContext()->BidiEnabled();
    331    if (mBidiEnabled) {
    332      // Find the line container frame
    333      mLineContainer = aFrame;
    334      while (mLineContainer && mLineContainer->IsLineParticipant()) {
    335        mLineContainer = mLineContainer->GetParent();
    336      }
    337 
    338      MOZ_ASSERT(mLineContainer, "Cannot find line containing frame.");
    339      MOZ_ASSERT(mLineContainer != aFrame,
    340                 "line container frame "
    341                 "should be an ancestor of the target frame.");
    342    }
    343 
    344    mVertical = aFrame->GetWritingMode().IsVertical();
    345 
    346    // Start with the previous flow frame as our continuation point
    347    // is the total of the widths of the previous frames.
    348    nsIFrame* inlineFrame = GetPrevContinuation(aFrame);
    349    bool changedLines = false;
    350    while (inlineFrame) {
    351      if (!mPIStartBorderData.mFrame &&
    352          !(mVertical ? inlineFrame->GetSkipSides().Top()
    353                      : inlineFrame->GetSkipSides().Left())) {
    354        mPIStartBorderData.mFrame = inlineFrame;
    355      }
    356      nsRect rect = inlineFrame->GetRect();
    357      mContinuationPoint += mVertical ? rect.height : rect.width;
    358      if (mBidiEnabled &&
    359          (changedLines || !AreOnSameLine(aFrame, inlineFrame))) {
    360        mLineContinuationPoint += mVertical ? rect.height : rect.width;
    361        changedLines = true;
    362      }
    363      mUnbrokenMeasure += mVertical ? rect.height : rect.width;
    364      mBoundingBox.UnionRect(mBoundingBox, rect);
    365      inlineFrame = GetPrevContinuation(inlineFrame);
    366    }
    367 
    368    // Next add this frame and subsequent frames to the bounding box and
    369    // unbroken width.
    370    inlineFrame = aFrame;
    371    while (inlineFrame) {
    372      if (!mPIStartBorderData.mFrame &&
    373          !(mVertical ? inlineFrame->GetSkipSides().Top()
    374                      : inlineFrame->GetSkipSides().Left())) {
    375        mPIStartBorderData.mFrame = inlineFrame;
    376      }
    377      nsRect rect = inlineFrame->GetRect();
    378      mUnbrokenMeasure += mVertical ? rect.height : rect.width;
    379      mBoundingBox.UnionRect(mBoundingBox, rect);
    380      inlineFrame = GetNextContinuation(inlineFrame);
    381    }
    382 
    383    mFrame = aFrame;
    384  }
    385 
    386  bool AreOnSameLine(nsIFrame* aFrame1, nsIFrame* aFrame2) {
    387    if (nsBlockFrame* blockFrame = do_QueryFrame(mLineContainer)) {
    388      bool isValid1, isValid2;
    389      nsBlockInFlowLineIterator it1(blockFrame, aFrame1, &isValid1);
    390      nsBlockInFlowLineIterator it2(blockFrame, aFrame2, &isValid2);
    391      return isValid1 && isValid2 &&
    392             // Make sure aFrame1 and aFrame2 are in the same continuation of
    393             // blockFrame.
    394             it1.GetContainer() == it2.GetContainer() &&
    395             // And on the same line in it
    396             it1.GetLine().get() == it2.GetLine().get();
    397    }
    398    if (nsRubyTextContainerFrame* rtcFrame = do_QueryFrame(mLineContainer)) {
    399      nsBlockFrame* block = nsLayoutUtils::FindNearestBlockAncestor(rtcFrame);
    400      // Ruby text container can only hold one line of text, so if they
    401      // are in the same continuation, they are in the same line. Since
    402      // ruby text containers are bidi isolate, they are never split for
    403      // bidi reordering, which means being in different continuation
    404      // indicates being in different lines.
    405      for (nsIFrame* frame = rtcFrame->FirstContinuation(); frame;
    406           frame = frame->GetNextContinuation()) {
    407        bool isDescendant1 =
    408            nsLayoutUtils::IsProperAncestorFrame(frame, aFrame1, block);
    409        bool isDescendant2 =
    410            nsLayoutUtils::IsProperAncestorFrame(frame, aFrame2, block);
    411        if (isDescendant1 && isDescendant2) {
    412          return true;
    413        }
    414        if (isDescendant1 || isDescendant2) {
    415          return false;
    416        }
    417      }
    418      MOZ_ASSERT_UNREACHABLE("None of the frames is a descendant of this rtc?");
    419    }
    420    MOZ_ASSERT_UNREACHABLE("Do we have any other type of line container?");
    421    return false;
    422  }
    423 };
    424 
    425 static StaticAutoPtr<InlineBackgroundData> gInlineBGData;
    426 
    427 // Initialize any static variables used by nsCSSRendering.
    428 void nsCSSRendering::Init() {
    429  NS_ASSERTION(!gInlineBGData, "Init called twice");
    430  gInlineBGData = new InlineBackgroundData();
    431 }
    432 
    433 // Clean up any global variables used by nsCSSRendering.
    434 void nsCSSRendering::Shutdown() { gInlineBGData = nullptr; }
    435 
    436 /**
    437 * Make a bevel color
    438 */
    439 static nscolor MakeBevelColor(mozilla::Side whichSide, StyleBorderStyle style,
    440                              nscolor aBorderColor) {
    441  nscolor colors[2];
    442  nscolor theColor;
    443 
    444  // Given a background color and a border color
    445  // calculate the color used for the shading
    446  NS_GetSpecial3DColors(colors, aBorderColor);
    447 
    448  if ((style == StyleBorderStyle::Outset) ||
    449      (style == StyleBorderStyle::Ridge)) {
    450    // Flip colors for these two border styles
    451    switch (whichSide) {
    452      case eSideBottom:
    453        whichSide = eSideTop;
    454        break;
    455      case eSideRight:
    456        whichSide = eSideLeft;
    457        break;
    458      case eSideTop:
    459        whichSide = eSideBottom;
    460        break;
    461      case eSideLeft:
    462        whichSide = eSideRight;
    463        break;
    464    }
    465  }
    466 
    467  switch (whichSide) {
    468    case eSideBottom:
    469      theColor = colors[1];
    470      break;
    471    case eSideRight:
    472      theColor = colors[1];
    473      break;
    474    case eSideTop:
    475      theColor = colors[0];
    476      break;
    477    case eSideLeft:
    478    default:
    479      theColor = colors[0];
    480      break;
    481  }
    482  return theColor;
    483 }
    484 
    485 static bool GetRadii(nsIFrame* aForFrame, const nsStyleBorder& aBorder,
    486                     const nsRect& aOrigBorderArea, const nsRect& aBorderArea,
    487                     nsRectCornerRadii& aRadii) {
    488  bool haveRoundedCorners;
    489  nsSize sz = aBorderArea.Size();
    490  nsSize frameSize = aForFrame->GetSize();
    491  if (&aBorder == aForFrame->StyleBorder() &&
    492      frameSize == aOrigBorderArea.Size()) {
    493    haveRoundedCorners = aForFrame->GetBorderRadii(sz, sz, Sides(), aRadii);
    494  } else {
    495    haveRoundedCorners = nsIFrame::ComputeBorderRadii(
    496        aBorder.mBorderRadius, frameSize, sz, Sides(), aRadii);
    497  }
    498 
    499  return haveRoundedCorners;
    500 }
    501 
    502 static bool GetRadii(nsIFrame* aForFrame, const nsStyleBorder& aBorder,
    503                     const nsRect& aOrigBorderArea, const nsRect& aBorderArea,
    504                     RectCornerRadii* aBgRadii) {
    505  nsRectCornerRadii radii;
    506  bool haveRoundedCorners =
    507      GetRadii(aForFrame, aBorder, aOrigBorderArea, aBorderArea, radii);
    508 
    509  if (haveRoundedCorners) {
    510    auto d2a = aForFrame->PresContext()->AppUnitsPerDevPixel();
    511    nsCSSRendering::ComputePixelRadii(radii, d2a, aBgRadii);
    512  }
    513  return haveRoundedCorners;
    514 }
    515 
    516 static nsRect JoinBoxesForBlockAxisSlice(nsIFrame* aFrame,
    517                                         const nsRect& aBorderArea) {
    518  // Inflate the block-axis size as if our continuations were laid out
    519  // adjacent in that axis.  Note that we don't touch the inline size.
    520  const auto wm = aFrame->GetWritingMode();
    521  const nsSize dummyContainerSize;
    522  LogicalRect borderArea(wm, aBorderArea, dummyContainerSize);
    523  nscoord bSize = 0;
    524  nsIFrame* f = aFrame->GetNextContinuation();
    525  for (; f; f = f->GetNextContinuation()) {
    526    bSize += f->BSize(wm);
    527  }
    528  borderArea.BSize(wm) += bSize;
    529  bSize = 0;
    530  f = aFrame->GetPrevContinuation();
    531  for (; f; f = f->GetPrevContinuation()) {
    532    bSize += f->BSize(wm);
    533  }
    534  borderArea.BStart(wm) -= bSize;
    535  borderArea.BSize(wm) += bSize;
    536  return borderArea.GetPhysicalRect(wm, dummyContainerSize);
    537 }
    538 
    539 /**
    540 * Inflate aBorderArea which is relative to aFrame's origin to calculate
    541 * a hypothetical non-split frame area for all the continuations.
    542 * See "Joining Boxes for 'slice'" in
    543 * http://dev.w3.org/csswg/css-break/#break-decoration
    544 */
    545 enum InlineBoxOrder { eForBorder, eForBackground };
    546 static nsRect JoinBoxesForSlice(nsIFrame* aFrame, const nsRect& aBorderArea,
    547                                InlineBoxOrder aOrder) {
    548  if (static_cast<nsInlineFrame*>(do_QueryFrame(aFrame))) {
    549    return (aOrder == eForBorder
    550                ? gInlineBGData->GetBorderContinuousRect(aFrame, aBorderArea)
    551                : gInlineBGData->GetContinuousRect(aFrame)) +
    552           aBorderArea.TopLeft();
    553  }
    554  return JoinBoxesForBlockAxisSlice(aFrame, aBorderArea);
    555 }
    556 
    557 /* static */
    558 bool nsCSSRendering::IsBoxDecorationSlice(const nsStyleBorder& aStyleBorder) {
    559  return aStyleBorder.mBoxDecorationBreak == StyleBoxDecorationBreak::Slice;
    560 }
    561 
    562 /* static */
    563 nsRect nsCSSRendering::BoxDecorationRectForBorder(
    564    nsIFrame* aFrame, const nsRect& aBorderArea, Sides aSkipSides,
    565    const nsStyleBorder* aStyleBorder) {
    566  if (!aStyleBorder) {
    567    aStyleBorder = aFrame->StyleBorder();
    568  }
    569  // If aSkipSides.IsEmpty() then there are no continuations, or it's
    570  // a ::first-letter that wants all border sides on the first continuation.
    571  return IsBoxDecorationSlice(*aStyleBorder) && !aSkipSides.IsEmpty()
    572             ? ::JoinBoxesForSlice(aFrame, aBorderArea, eForBorder)
    573             : aBorderArea;
    574 }
    575 
    576 /* static */
    577 nsRect nsCSSRendering::BoxDecorationRectForBackground(
    578    nsIFrame* aFrame, const nsRect& aBorderArea, Sides aSkipSides,
    579    const nsStyleBorder* aStyleBorder) {
    580  if (!aStyleBorder) {
    581    aStyleBorder = aFrame->StyleBorder();
    582  }
    583  // If aSkipSides.IsEmpty() then there are no continuations, or it's
    584  // a ::first-letter that wants all border sides on the first continuation.
    585  return IsBoxDecorationSlice(*aStyleBorder) && !aSkipSides.IsEmpty()
    586             ? ::JoinBoxesForSlice(aFrame, aBorderArea, eForBackground)
    587             : aBorderArea;
    588 }
    589 
    590 //----------------------------------------------------------------------
    591 // Thebes Border Rendering Code Start
    592 
    593 /*
    594 * Compute the float-pixel radii that should be used for drawing
    595 * this border/outline, given the various input bits.
    596 */
    597 /* static */
    598 void nsCSSRendering::ComputePixelRadii(const nsRectCornerRadii& aRadii,
    599                                       nscoord aAppUnitsPerPixel,
    600                                       RectCornerRadii* oBorderRadii) {
    601  for (const auto corner : mozilla::AllPhysicalCorners()) {
    602    (*oBorderRadii)[corner] =
    603        LayoutDeviceSize::FromAppUnits(aRadii[corner], aAppUnitsPerPixel)
    604            .ToUnknownSize();
    605  }
    606 }
    607 
    608 static Maybe<nsStyleBorder> GetBorderIfVisited(const ComputedStyle& aStyle) {
    609  Maybe<nsStyleBorder> result;
    610  // Don't check RelevantLinkVisited here, since we want to take the
    611  // same amount of time whether or not it's true.
    612  const ComputedStyle* styleIfVisited = aStyle.GetStyleIfVisited();
    613  if (MOZ_LIKELY(!styleIfVisited)) {
    614    return result;
    615  }
    616 
    617  result.emplace(*aStyle.StyleBorder());
    618  auto& newBorder = result.ref();
    619  for (const auto side : mozilla::AllPhysicalSides()) {
    620    nscolor color = aStyle.GetVisitedDependentColor(
    621        nsStyleBorder::BorderColorFieldFor(side));
    622    newBorder.BorderColorFor(side) = StyleColor::FromColor(color);
    623  }
    624 
    625  return result;
    626 }
    627 
    628 ImgDrawResult nsCSSRendering::PaintBorder(
    629    nsPresContext* aPresContext, gfxContext& aRenderingContext,
    630    nsIFrame* aForFrame, const nsRect& aDirtyRect, const nsRect& aBorderArea,
    631    ComputedStyle* aStyle, PaintBorderFlags aFlags, Sides aSkipSides) {
    632  AUTO_PROFILER_LABEL("nsCSSRendering::PaintBorder", GRAPHICS);
    633 
    634  Maybe<nsStyleBorder> visitedBorder = GetBorderIfVisited(*aStyle);
    635  return PaintBorderWithStyleBorder(
    636      aPresContext, aRenderingContext, aForFrame, aDirtyRect, aBorderArea,
    637      visitedBorder.refOr(*aStyle->StyleBorder()), aStyle, aFlags, aSkipSides);
    638 }
    639 
    640 Maybe<nsCSSBorderRenderer> nsCSSRendering::CreateBorderRenderer(
    641    nsPresContext* aPresContext, DrawTarget* aDrawTarget, nsIFrame* aForFrame,
    642    const nsRect& aDirtyRect, const nsRect& aBorderArea, ComputedStyle* aStyle,
    643    bool* aOutBorderIsEmpty, Sides aSkipSides) {
    644  Maybe<nsStyleBorder> visitedBorder = GetBorderIfVisited(*aStyle);
    645  return CreateBorderRendererWithStyleBorder(
    646      aPresContext, aDrawTarget, aForFrame, aDirtyRect, aBorderArea,
    647      visitedBorder.refOr(*aStyle->StyleBorder()), aStyle, aOutBorderIsEmpty,
    648      aSkipSides);
    649 }
    650 
    651 ImgDrawResult nsCSSRendering::CreateWebRenderCommandsForBorder(
    652    nsDisplayItem* aItem, nsIFrame* aForFrame, const nsRect& aBorderArea,
    653    mozilla::wr::DisplayListBuilder& aBuilder,
    654    mozilla::wr::IpcResourceUpdateQueue& aResources,
    655    const mozilla::layers::StackingContextHelper& aSc,
    656    mozilla::layers::RenderRootStateManager* aManager,
    657    nsDisplayListBuilder* aDisplayListBuilder) {
    658  const auto* style = aForFrame->Style();
    659  Maybe<nsStyleBorder> visitedBorder = GetBorderIfVisited(*style);
    660  return nsCSSRendering::CreateWebRenderCommandsForBorderWithStyleBorder(
    661      aItem, aForFrame, aBorderArea, aBuilder, aResources, aSc, aManager,
    662      aDisplayListBuilder, visitedBorder.refOr(*style->StyleBorder()));
    663 }
    664 
    665 void nsCSSRendering::CreateWebRenderCommandsForNullBorder(
    666    nsDisplayItem* aItem, nsIFrame* aForFrame, const nsRect& aBorderArea,
    667    mozilla::wr::DisplayListBuilder& aBuilder,
    668    mozilla::wr::IpcResourceUpdateQueue& aResources,
    669    const mozilla::layers::StackingContextHelper& aSc,
    670    const nsStyleBorder& aStyleBorder) {
    671  bool borderIsEmpty = false;
    672  Maybe<nsCSSBorderRenderer> br =
    673      nsCSSRendering::CreateNullBorderRendererWithStyleBorder(
    674          aForFrame->PresContext(), nullptr, aForFrame, nsRect(), aBorderArea,
    675          aStyleBorder, aForFrame->Style(), &borderIsEmpty,
    676          aForFrame->GetSkipSides());
    677  if (!borderIsEmpty && br) {
    678    br->CreateWebRenderCommands(aItem, aBuilder, aResources, aSc);
    679  }
    680 }
    681 
    682 ImgDrawResult nsCSSRendering::CreateWebRenderCommandsForBorderWithStyleBorder(
    683    nsDisplayItem* aItem, nsIFrame* aForFrame, const nsRect& aBorderArea,
    684    mozilla::wr::DisplayListBuilder& aBuilder,
    685    mozilla::wr::IpcResourceUpdateQueue& aResources,
    686    const mozilla::layers::StackingContextHelper& aSc,
    687    mozilla::layers::RenderRootStateManager* aManager,
    688    nsDisplayListBuilder* aDisplayListBuilder,
    689    const nsStyleBorder& aStyleBorder) {
    690  auto& borderImage = aStyleBorder.mBorderImageSource;
    691  // First try to create commands for simple borders.
    692  if (borderImage.IsNone()) {
    693    CreateWebRenderCommandsForNullBorder(
    694        aItem, aForFrame, aBorderArea, aBuilder, aResources, aSc, aStyleBorder);
    695    return ImgDrawResult::SUCCESS;
    696  }
    697 
    698  // Next we try image and gradient borders. Gradients are not supported at
    699  // this very moment.
    700  if (!borderImage.IsImageRequestType()) {
    701    return ImgDrawResult::NOT_SUPPORTED;
    702  }
    703 
    704  if (aStyleBorder.mBorderImageRepeat._0 ==
    705          StyleBorderImageRepeatKeyword::Space ||
    706      aStyleBorder.mBorderImageRepeat._1 ==
    707          StyleBorderImageRepeatKeyword::Space) {
    708    return ImgDrawResult::NOT_SUPPORTED;
    709  }
    710 
    711  uint32_t flags = 0;
    712  if (aDisplayListBuilder->IsPaintingToWindow()) {
    713    flags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
    714  }
    715  if (aDisplayListBuilder->ShouldSyncDecodeImages()) {
    716    flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
    717  }
    718 
    719  bool dummy;
    720  image::ImgDrawResult result;
    721  Maybe<nsCSSBorderImageRenderer> bir =
    722      nsCSSBorderImageRenderer::CreateBorderImageRenderer(
    723          aForFrame->PresContext(), aForFrame, aBorderArea, aStyleBorder,
    724          aItem->GetBounds(aDisplayListBuilder, &dummy),
    725          aForFrame->GetSkipSides(), flags, &result);
    726 
    727  if (!bir) {
    728    // We aren't ready. Try to fallback to the null border image if present but
    729    // return the draw result for the border image renderer.
    730    CreateWebRenderCommandsForNullBorder(
    731        aItem, aForFrame, aBorderArea, aBuilder, aResources, aSc, aStyleBorder);
    732    return result;
    733  }
    734 
    735  return bir->CreateWebRenderCommands(aItem, aForFrame, aBuilder, aResources,
    736                                      aSc, aManager, aDisplayListBuilder);
    737 }
    738 
    739 static nsCSSBorderRenderer ConstructBorderRenderer(
    740    nsPresContext* aPresContext, ComputedStyle* aStyle, DrawTarget* aDrawTarget,
    741    nsIFrame* aForFrame, const nsRect& aDirtyRect, const nsRect& aBorderArea,
    742    const nsStyleBorder& aStyleBorder, Sides aSkipSides, bool* aNeedsClip) {
    743  nsMargin border = aStyleBorder.GetComputedBorder();
    744 
    745  // Compute the outermost boundary of the area that might be painted.
    746  // Same coordinate space as aBorderArea & aBGClipRect.
    747  nsRect joinedBorderArea = nsCSSRendering::BoxDecorationRectForBorder(
    748      aForFrame, aBorderArea, aSkipSides, &aStyleBorder);
    749  RectCornerRadii bgRadii;
    750  ::GetRadii(aForFrame, aStyleBorder, aBorderArea, joinedBorderArea, &bgRadii);
    751 
    752  PrintAsFormatString(" joinedBorderArea: %d %d %d %d\n", joinedBorderArea.x,
    753                      joinedBorderArea.y, joinedBorderArea.width,
    754                      joinedBorderArea.height);
    755 
    756  // start drawing
    757  if (nsCSSRendering::IsBoxDecorationSlice(aStyleBorder)) {
    758    if (joinedBorderArea.IsEqualEdges(aBorderArea)) {
    759      // No need for a clip, just skip the sides we don't want.
    760      border.ApplySkipSides(aSkipSides);
    761    } else {
    762      // We're drawing borders around the joined continuation boxes so we need
    763      // to clip that to the slice that we want for this frame.
    764      *aNeedsClip = true;
    765    }
    766  } else {
    767    MOZ_ASSERT(joinedBorderArea.IsEqualEdges(aBorderArea),
    768               "Should use aBorderArea for box-decoration-break:clone");
    769    MOZ_ASSERT(
    770        aForFrame->GetSkipSides().IsEmpty() ||
    771            aForFrame->IsTrueOverflowContainer() ||
    772            aForFrame->IsColumnSetFrame(),  // a little broader than column-rule
    773        "Should not skip sides for box-decoration-break:clone except "
    774        "::first-letter/line continuations or other frame types that "
    775        "don't have borders but those shouldn't reach this point. "
    776        "Overflow containers do reach this point though, as does "
    777        "column-rule drawing (which always involves a columnset).");
    778    border.ApplySkipSides(aSkipSides);
    779  }
    780 
    781  // Convert to dev pixels.
    782  nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1);
    783  Rect joinedBorderAreaPx = NSRectToRect(joinedBorderArea, oneDevPixel);
    784  Margin borderWidths(
    785      Float(border.top) / oneDevPixel, Float(border.right) / oneDevPixel,
    786      Float(border.bottom) / oneDevPixel, Float(border.left) / oneDevPixel);
    787  Rect dirtyRect = NSRectToRect(aDirtyRect, oneDevPixel);
    788 
    789  StyleBorderStyle borderStyles[4];
    790  nscolor borderColors[4];
    791 
    792  // pull out styles, colors
    793  for (const auto i : mozilla::AllPhysicalSides()) {
    794    borderStyles[i] = aStyleBorder.GetBorderStyle(i);
    795    borderColors[i] = aStyleBorder.BorderColorFor(i).CalcColor(*aStyle);
    796  }
    797 
    798  PrintAsFormatString(
    799      " borderStyles: %d %d %d %d\n", static_cast<int>(borderStyles[0]),
    800      static_cast<int>(borderStyles[1]), static_cast<int>(borderStyles[2]),
    801      static_cast<int>(borderStyles[3]));
    802 
    803  return nsCSSBorderRenderer(
    804      aPresContext, aDrawTarget, dirtyRect, joinedBorderAreaPx, borderStyles,
    805      borderWidths, bgRadii, borderColors, !aForFrame->BackfaceIsHidden(),
    806      *aNeedsClip ? Some(NSRectToRect(aBorderArea, oneDevPixel)) : Nothing());
    807 }
    808 
    809 ImgDrawResult nsCSSRendering::PaintBorderWithStyleBorder(
    810    nsPresContext* aPresContext, gfxContext& aRenderingContext,
    811    nsIFrame* aForFrame, const nsRect& aDirtyRect, const nsRect& aBorderArea,
    812    const nsStyleBorder& aStyleBorder, ComputedStyle* aStyle,
    813    PaintBorderFlags aFlags, Sides aSkipSides) {
    814  DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget();
    815 
    816  PrintAsStringNewline("++ PaintBorder");
    817 
    818  // Check to see if we have an appearance defined.  If so, we let the theme
    819  // renderer draw the border.  DO not get the data from aForFrame, since the
    820  // passed in ComputedStyle may be different!  Always use |aStyle|!
    821  StyleAppearance appearance = aStyle->StyleDisplay()->EffectiveAppearance();
    822  if (appearance != StyleAppearance::None) {
    823    nsITheme* theme = aPresContext->Theme();
    824    if (theme->ThemeSupportsWidget(aPresContext, aForFrame, appearance)) {
    825      return ImgDrawResult::SUCCESS;  // Let the theme handle it.
    826    }
    827  }
    828 
    829  if (!aStyleBorder.mBorderImageSource.IsNone()) {
    830    ImgDrawResult result = ImgDrawResult::SUCCESS;
    831 
    832    uint32_t irFlags = 0;
    833    if (aFlags & PaintBorderFlags::SyncDecodeImages) {
    834      irFlags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
    835    }
    836 
    837    // Creating the border image renderer will request a decode, and we rely on
    838    // that happening.
    839    Maybe<nsCSSBorderImageRenderer> renderer =
    840        nsCSSBorderImageRenderer::CreateBorderImageRenderer(
    841            aPresContext, aForFrame, aBorderArea, aStyleBorder, aDirtyRect,
    842            aSkipSides, irFlags, &result);
    843    // renderer was created successfully, which means border image is ready to
    844    // be used.
    845    if (renderer) {
    846      MOZ_ASSERT(result == ImgDrawResult::SUCCESS);
    847      return renderer->DrawBorderImage(aPresContext, aRenderingContext,
    848                                       aForFrame, aDirtyRect);
    849    }
    850  }
    851 
    852  ImgDrawResult result = ImgDrawResult::SUCCESS;
    853 
    854  // If we had a border-image, but it wasn't loaded, then we should return
    855  // ImgDrawResult::NOT_READY; we'll want to try again if we do a paint with
    856  // sync decoding enabled.
    857  if (!aStyleBorder.mBorderImageSource.IsNone()) {
    858    result = ImgDrawResult::NOT_READY;
    859  }
    860 
    861  nsMargin border = aStyleBorder.GetComputedBorder();
    862  if (0 == border.left && 0 == border.right && 0 == border.top &&
    863      0 == border.bottom) {
    864    // Empty border area
    865    return result;
    866  }
    867 
    868  bool needsClip = false;
    869  nsCSSBorderRenderer br = ConstructBorderRenderer(
    870      aPresContext, aStyle, &aDrawTarget, aForFrame, aDirtyRect, aBorderArea,
    871      aStyleBorder, aSkipSides, &needsClip);
    872  if (needsClip) {
    873    aDrawTarget.PushClipRect(NSRectToSnappedRect(
    874        aBorderArea, aForFrame->PresContext()->AppUnitsPerDevPixel(),
    875        aDrawTarget));
    876  }
    877 
    878  br.DrawBorders();
    879 
    880  if (needsClip) {
    881    aDrawTarget.PopClip();
    882  }
    883 
    884  PrintAsStringNewline();
    885 
    886  return result;
    887 }
    888 
    889 Maybe<nsCSSBorderRenderer> nsCSSRendering::CreateBorderRendererWithStyleBorder(
    890    nsPresContext* aPresContext, DrawTarget* aDrawTarget, nsIFrame* aForFrame,
    891    const nsRect& aDirtyRect, const nsRect& aBorderArea,
    892    const nsStyleBorder& aStyleBorder, ComputedStyle* aStyle,
    893    bool* aOutBorderIsEmpty, Sides aSkipSides) {
    894  if (!aStyleBorder.mBorderImageSource.IsNone()) {
    895    return Nothing();
    896  }
    897  return CreateNullBorderRendererWithStyleBorder(
    898      aPresContext, aDrawTarget, aForFrame, aDirtyRect, aBorderArea,
    899      aStyleBorder, aStyle, aOutBorderIsEmpty, aSkipSides);
    900 }
    901 
    902 Maybe<nsCSSBorderRenderer>
    903 nsCSSRendering::CreateNullBorderRendererWithStyleBorder(
    904    nsPresContext* aPresContext, DrawTarget* aDrawTarget, nsIFrame* aForFrame,
    905    const nsRect& aDirtyRect, const nsRect& aBorderArea,
    906    const nsStyleBorder& aStyleBorder, ComputedStyle* aStyle,
    907    bool* aOutBorderIsEmpty, Sides aSkipSides) {
    908  StyleAppearance appearance = aStyle->StyleDisplay()->EffectiveAppearance();
    909  if (appearance != StyleAppearance::None) {
    910    nsITheme* theme = aPresContext->Theme();
    911    if (theme->ThemeSupportsWidget(aPresContext, aForFrame, appearance)) {
    912      // The border will be draw as part of the themed background item created
    913      // for this same frame. If no themed background item was created then not
    914      // drawing also matches that we do without webrender and what
    915      // nsDisplayBorder does for themed borders.
    916      if (aOutBorderIsEmpty) {
    917        *aOutBorderIsEmpty = true;
    918      }
    919      return Nothing();
    920    }
    921  }
    922 
    923  nsMargin border = aStyleBorder.GetComputedBorder();
    924  if (0 == border.left && 0 == border.right && 0 == border.top &&
    925      0 == border.bottom) {
    926    // Empty border area
    927    if (aOutBorderIsEmpty) {
    928      *aOutBorderIsEmpty = true;
    929    }
    930    return Nothing();
    931  }
    932 
    933  bool needsClip = false;
    934  nsCSSBorderRenderer br = ConstructBorderRenderer(
    935      aPresContext, aStyle, aDrawTarget, aForFrame, aDirtyRect, aBorderArea,
    936      aStyleBorder, aSkipSides, &needsClip);
    937  return Some(br);
    938 }
    939 
    940 Maybe<nsCSSBorderRenderer>
    941 nsCSSRendering::CreateBorderRendererForNonThemedOutline(
    942    nsPresContext* aPresContext, DrawTarget* aDrawTarget, nsIFrame* aForFrame,
    943    const nsRect& aDirtyRect, const nsRect& aInnerRect, ComputedStyle* aStyle) {
    944  // Get our ComputedStyle's color struct.
    945  const nsStyleOutline* ourOutline = aStyle->StyleOutline();
    946  if (!ourOutline->ShouldPaintOutline()) {
    947    // Empty outline
    948    return Nothing();
    949  }
    950 
    951  nsRect innerRect = aInnerRect;
    952 
    953  const nsSize effectiveOffset = ourOutline->EffectiveOffsetFor(innerRect);
    954  innerRect.Inflate(effectiveOffset);
    955 
    956  // If the dirty rect is completely inside the border area (e.g., only the
    957  // content is being painted), then we can skip out now
    958  // XXX this isn't exactly true for rounded borders, where the inside curves
    959  // may encroach into the content area.  A safer calculation would be to
    960  // shorten insideRect by the radius one each side before performing this test.
    961  if (innerRect.Contains(aDirtyRect)) {
    962    return Nothing();
    963  }
    964 
    965  const nscoord width = ourOutline->mOutlineWidth;
    966 
    967  StyleBorderStyle outlineStyle;
    968  // Themed outlines are handled by our callers, if supported.
    969  if (ourOutline->mOutlineStyle.IsAuto()) {
    970    if (width == 0) {
    971      return Nothing();  // empty outline
    972    }
    973    // http://dev.w3.org/csswg/css-ui/#outline
    974    // "User agents may treat 'auto' as 'solid'."
    975    outlineStyle = StyleBorderStyle::Solid;
    976  } else {
    977    outlineStyle = ourOutline->mOutlineStyle.AsBorderStyle();
    978  }
    979 
    980  RectCornerRadii outlineRadii;
    981  nsRect outerRect = innerRect;
    982  outerRect.Inflate(width);
    983 
    984  const nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
    985  Rect oRect(NSRectToRect(outerRect, oneDevPixel));
    986 
    987  const Margin outlineWidths(
    988      Float(width) / oneDevPixel, Float(width) / oneDevPixel,
    989      Float(width) / oneDevPixel, Float(width) / oneDevPixel);
    990 
    991  // convert the radii
    992  nsRectCornerRadii twipsRadii;
    993 
    994  // get the radius for our outline
    995  if (aForFrame->GetBorderRadii(twipsRadii)) {
    996    RectCornerRadii innerRadii;
    997    ComputePixelRadii(twipsRadii, oneDevPixel, &innerRadii);
    998 
    999    const auto devPxOffset = LayoutDeviceSize::FromAppUnits(
   1000        effectiveOffset, aPresContext->AppUnitsPerDevPixel());
   1001 
   1002    const Margin widths(outlineWidths.top + devPxOffset.Height(),
   1003                        outlineWidths.right + devPxOffset.Width(),
   1004                        outlineWidths.bottom + devPxOffset.Height(),
   1005                        outlineWidths.left + devPxOffset.Width());
   1006    nsCSSBorderRenderer::ComputeOuterRadii(innerRadii, widths, &outlineRadii);
   1007  }
   1008 
   1009  StyleBorderStyle outlineStyles[4] = {outlineStyle, outlineStyle, outlineStyle,
   1010                                       outlineStyle};
   1011 
   1012  // This handles treating the initial color as 'currentColor'; if we
   1013  // ever want 'invert' back we'll need to do a bit of work here too.
   1014  nscolor outlineColor =
   1015      aStyle->GetVisitedDependentColor(&nsStyleOutline::mOutlineColor);
   1016  nscolor outlineColors[4] = {outlineColor, outlineColor, outlineColor,
   1017                              outlineColor};
   1018 
   1019  Rect dirtyRect = NSRectToRect(aDirtyRect, oneDevPixel);
   1020 
   1021  return Some(nsCSSBorderRenderer(
   1022      aPresContext, aDrawTarget, dirtyRect, oRect, outlineStyles, outlineWidths,
   1023      outlineRadii, outlineColors, !aForFrame->BackfaceIsHidden(), Nothing()));
   1024 }
   1025 
   1026 void nsCSSRendering::PaintNonThemedOutline(nsPresContext* aPresContext,
   1027                                           gfxContext& aRenderingContext,
   1028                                           nsIFrame* aForFrame,
   1029                                           const nsRect& aDirtyRect,
   1030                                           const nsRect& aInnerRect,
   1031                                           ComputedStyle* aStyle) {
   1032  Maybe<nsCSSBorderRenderer> br = CreateBorderRendererForNonThemedOutline(
   1033      aPresContext, aRenderingContext.GetDrawTarget(), aForFrame, aDirtyRect,
   1034      aInnerRect, aStyle);
   1035  if (!br) {
   1036    return;
   1037  }
   1038 
   1039  // start drawing
   1040  br->DrawBorders();
   1041 
   1042  PrintAsStringNewline();
   1043 }
   1044 
   1045 nsCSSBorderRenderer nsCSSRendering::GetBorderRendererForFocus(
   1046    nsIFrame* aForFrame, DrawTarget* aDrawTarget, const nsRect& aFocusRect,
   1047    nscolor aColor) {
   1048  auto* pc = aForFrame->PresContext();
   1049  nscoord oneCSSPixel = nsPresContext::CSSPixelsToAppUnits(1);
   1050  nscoord oneDevPixel = pc->DevPixelsToAppUnits(1);
   1051 
   1052  Rect focusRect(NSRectToRect(aFocusRect, oneDevPixel));
   1053 
   1054  RectCornerRadii focusRadii;
   1055  Margin focusWidths(
   1056      Float(oneCSSPixel) / oneDevPixel, Float(oneCSSPixel) / oneDevPixel,
   1057      Float(oneCSSPixel) / oneDevPixel, Float(oneCSSPixel) / oneDevPixel);
   1058 
   1059  StyleBorderStyle focusStyles[4] = {
   1060      StyleBorderStyle::Dotted, StyleBorderStyle::Dotted,
   1061      StyleBorderStyle::Dotted, StyleBorderStyle::Dotted};
   1062  nscolor focusColors[4] = {aColor, aColor, aColor, aColor};
   1063 
   1064  // Because this renders a dotted border, the background color
   1065  // should not be used.  Therefore, we provide a value that will
   1066  // be blatantly wrong if it ever does get used.  (If this becomes
   1067  // something that CSS can style, this function will then have access
   1068  // to a ComputedStyle and can use the same logic that PaintBorder
   1069  // and PaintOutline do.)
   1070  return nsCSSBorderRenderer(pc, aDrawTarget, focusRect, focusRect, focusStyles,
   1071                             focusWidths, focusRadii, focusColors,
   1072                             !aForFrame->BackfaceIsHidden(), Nothing());
   1073 }
   1074 
   1075 // Thebes Border Rendering Code End
   1076 //----------------------------------------------------------------------
   1077 
   1078 //----------------------------------------------------------------------
   1079 
   1080 /**
   1081 * Helper for ComputeObjectAnchorPoint; parameters are the same as for
   1082 * that function, except they're for a single coordinate / a single size
   1083 * dimension. (so, x/width vs. y/height)
   1084 */
   1085 static void ComputeObjectAnchorCoord(const LengthPercentage& aCoord,
   1086                                     const nscoord aOriginBounds,
   1087                                     const nscoord aImageSize,
   1088                                     nscoord* aTopLeftCoord,
   1089                                     nscoord* aAnchorPointCoord) {
   1090  nscoord extraSpace = aOriginBounds - aImageSize;
   1091 
   1092  // The anchor-point doesn't care about our image's size; just the size
   1093  // of the region we're rendering into.
   1094  *aAnchorPointCoord = aCoord.Resolve(
   1095      aOriginBounds, static_cast<nscoord (*)(float)>(NSToCoordRoundWithClamp));
   1096  // Adjust aTopLeftCoord by the specified % of the extra space.
   1097  *aTopLeftCoord = aCoord.Resolve(
   1098      extraSpace, static_cast<nscoord (*)(float)>(NSToCoordRoundWithClamp));
   1099 }
   1100 
   1101 void nsImageRenderer::ComputeObjectAnchorPoint(const Position& aPos,
   1102                                               const nsSize& aOriginBounds,
   1103                                               const nsSize& aImageSize,
   1104                                               nsPoint* aTopLeft,
   1105                                               nsPoint* aAnchorPoint) {
   1106  ComputeObjectAnchorCoord(aPos.horizontal, aOriginBounds.width,
   1107                           aImageSize.width, &aTopLeft->x, &aAnchorPoint->x);
   1108 
   1109  ComputeObjectAnchorCoord(aPos.vertical, aOriginBounds.height,
   1110                           aImageSize.height, &aTopLeft->y, &aAnchorPoint->y);
   1111 }
   1112 
   1113 // In print / print preview we have multiple canvas frames (one for each page,
   1114 // and one for the document as a whole). For the topmost one, we really want the
   1115 // page sequence page background, not the root or body's background.
   1116 static nsIFrame* GetPageSequenceForCanvas(const nsIFrame* aCanvasFrame) {
   1117  MOZ_ASSERT(aCanvasFrame->IsCanvasFrame(), "not a canvas frame");
   1118  nsPresContext* pc = aCanvasFrame->PresContext();
   1119  if (!pc->IsRootPaginatedDocument()) {
   1120    return nullptr;
   1121  }
   1122  auto* ps = pc->PresShell()->GetPageSequenceFrame();
   1123  if (NS_WARN_IF(!ps)) {
   1124    return nullptr;
   1125  }
   1126  if (ps->GetParent() != aCanvasFrame) {
   1127    return nullptr;
   1128  }
   1129  return ps;
   1130 }
   1131 
   1132 auto nsCSSRendering::FindEffectiveBackgroundColor(nsIFrame* aFrame,
   1133                                                  bool aStopAtThemed,
   1134                                                  bool aPreferBodyToCanvas)
   1135    -> EffectiveBackgroundColor {
   1136  MOZ_ASSERT(aFrame);
   1137  nsPresContext* pc = aFrame->PresContext();
   1138  auto BgColorIfNotTransparent = [&](nsIFrame* aFrame) -> Maybe<nscolor> {
   1139    nscolor c =
   1140        aFrame->GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
   1141    if (NS_GET_A(c) == 255) {
   1142      return Some(c);
   1143    }
   1144    if (NS_GET_A(c)) {
   1145      // TODO(emilio): We should maybe just blend with ancestor bg colors and
   1146      // such, but this is probably good enough for now, matches pre-existing
   1147      // behavior.
   1148      const nscolor defaultBg = pc->DefaultBackgroundColor();
   1149      MOZ_ASSERT(NS_GET_A(defaultBg) == 255, "PreferenceSheet guarantees this");
   1150      return Some(NS_ComposeColors(defaultBg, c));
   1151    }
   1152    return Nothing();
   1153  };
   1154 
   1155  for (nsIFrame* frame = aFrame; frame;
   1156       frame = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(frame)) {
   1157    if (auto bg = BgColorIfNotTransparent(frame)) {
   1158      return {*bg};
   1159    }
   1160 
   1161    if (aStopAtThemed && frame->IsThemed()) {
   1162      return {NS_TRANSPARENT, true};
   1163    }
   1164 
   1165    if (frame->IsCanvasFrame()) {
   1166      if (aPreferBodyToCanvas && !GetPageSequenceForCanvas(frame)) {
   1167        if (auto* body = pc->Document()->GetBodyElement()) {
   1168          if (nsIFrame* f = body->GetPrimaryFrame()) {
   1169            if (auto bg = BgColorIfNotTransparent(f)) {
   1170              return {*bg};
   1171            }
   1172          }
   1173        }
   1174      }
   1175      if (nsIFrame* bgFrame = FindBackgroundFrame(frame)) {
   1176        if (auto bg = BgColorIfNotTransparent(bgFrame)) {
   1177          return {*bg};
   1178        }
   1179      }
   1180    }
   1181  }
   1182 
   1183  return {pc->DefaultBackgroundColor()};
   1184 }
   1185 
   1186 nsIFrame* nsCSSRendering::FindBackgroundStyleFrame(nsIFrame* aForFrame) {
   1187  const nsStyleBackground* result = aForFrame->StyleBackground();
   1188 
   1189  // Check if we need to do propagation from BODY rather than HTML.
   1190  if (!result->IsTransparent(aForFrame)) {
   1191    return aForFrame;
   1192  }
   1193 
   1194  nsIContent* content = aForFrame->GetContent();
   1195  // The root element content can't be null. We wouldn't know what
   1196  // frame to create for aFrame.
   1197  // Use |OwnerDoc| so it works during destruction.
   1198  if (!content) {
   1199    return aForFrame;
   1200  }
   1201 
   1202  Document* document = content->OwnerDoc();
   1203 
   1204  dom::Element* bodyContent = document->GetBodyElement();
   1205  // We need to null check the body node (bug 118829) since
   1206  // there are cases, thanks to the fix for bug 5569, where we
   1207  // will reflow a document with no body.  In particular, if a
   1208  // SCRIPT element in the head blocks the parser and then has a
   1209  // SCRIPT that does "document.location.href = 'foo'", then
   1210  // nsParser::Terminate will call |DidBuildModel| methods
   1211  // through to the content sink, which will call |StartLayout|
   1212  // and thus |Initialize| on the pres shell.  See bug 119351
   1213  // for the ugly details.
   1214  if (!bodyContent || aForFrame->StyleDisplay()->IsContainAny()) {
   1215    return aForFrame;
   1216  }
   1217 
   1218  nsIFrame* bodyFrame = bodyContent->GetPrimaryFrame();
   1219  if (!bodyFrame || bodyFrame->StyleDisplay()->IsContainAny()) {
   1220    return aForFrame;
   1221  }
   1222 
   1223  return nsLayoutUtils::GetStyleFrame(bodyFrame);
   1224 }
   1225 
   1226 /**
   1227 * |FindBackground| finds the correct style data to use to paint the
   1228 * background.  It is responsible for handling the following two
   1229 * statements in section 14.2 of CSS2:
   1230 *
   1231 *   The background of the box generated by the root element covers the
   1232 *   entire canvas.
   1233 *
   1234 *   For HTML documents, however, we recommend that authors specify the
   1235 *   background for the BODY element rather than the HTML element. User
   1236 *   agents should observe the following precedence rules to fill in the
   1237 *   background: if the value of the 'background' property for the HTML
   1238 *   element is different from 'transparent' then use it, else use the
   1239 *   value of the 'background' property for the BODY element. If the
   1240 *   resulting value is 'transparent', the rendering is undefined.
   1241 *
   1242 * Thus, in our implementation, it is responsible for ensuring that:
   1243 *  + we paint the correct background on the |nsCanvasFrame| or |nsPageFrame|,
   1244 *  + we don't paint the background on the root element, and
   1245 *  + we don't paint the background on the BODY element in *some* cases,
   1246 *    and for SGML-based HTML documents only.
   1247 *
   1248 * |FindBackground| checks whether a background should be painted. If yes, it
   1249 * returns the resulting ComputedStyle to use for the background information;
   1250 * Otherwise, it returns nullptr.
   1251 */
   1252 ComputedStyle* nsCSSRendering::FindRootFrameBackground(nsIFrame* aForFrame) {
   1253  return FindBackgroundStyleFrame(aForFrame)->Style();
   1254 }
   1255 
   1256 static nsIFrame* FindCanvasBackgroundFrame(const nsIFrame* aForFrame,
   1257                                           nsIFrame* aRootElementFrame) {
   1258  MOZ_ASSERT(aForFrame->IsCanvasFrame(), "not a canvas frame");
   1259  if (auto* ps = GetPageSequenceForCanvas(aForFrame)) {
   1260    return ps;
   1261  }
   1262  if (aRootElementFrame) {
   1263    return nsCSSRendering::FindBackgroundStyleFrame(aRootElementFrame);
   1264  }
   1265  // This should always give transparent, so we'll fill it in with the default
   1266  // color if needed.  This seems to happen a bit while a page is being loaded.
   1267  return const_cast<nsIFrame*>(aForFrame);
   1268 }
   1269 
   1270 // Helper for FindBackgroundFrame. Returns true if aForFrame has a meaningful
   1271 // background that it should draw (i.e. that it hasn't propagated to another
   1272 // frame).  See documentation for FindBackground.
   1273 inline bool FrameHasMeaningfulBackground(const nsIFrame* aForFrame,
   1274                                         nsIFrame* aRootElementFrame) {
   1275  MOZ_ASSERT(!aForFrame->IsCanvasFrame(),
   1276             "FindBackgroundFrame handles canvas frames before calling us, "
   1277             "so we don't need to consider them here");
   1278 
   1279  if (aForFrame == aRootElementFrame) {
   1280    // We must have propagated our background to the viewport or canvas. Abort.
   1281    return false;
   1282  }
   1283 
   1284  // Return true unless the frame is for a BODY element whose background
   1285  // was propagated to the viewport.
   1286 
   1287  nsIContent* content = aForFrame->GetContent();
   1288  if (!content || content->NodeInfo()->NameAtom() != nsGkAtoms::body) {
   1289    return true;  // not frame for a "body" element
   1290  }
   1291  // It could be a non-HTML "body" element but that's OK, we'd fail the
   1292  // bodyContent check below
   1293 
   1294  if (aForFrame->Style()->GetPseudoType() != PseudoStyleType::NotPseudo ||
   1295      aForFrame->StyleDisplay()->IsContainAny()) {
   1296    return true;  // A pseudo-element frame, or contained.
   1297  }
   1298 
   1299  // We should only look at the <html> background if we're in an HTML document
   1300  Document* document = content->OwnerDoc();
   1301 
   1302  dom::Element* bodyContent = document->GetBodyElement();
   1303  if (bodyContent != content) {
   1304    return true;  // this wasn't the background that was propagated
   1305  }
   1306 
   1307  // This can be called even when there's no root element yet, during frame
   1308  // construction, via nsLayoutUtils::FrameHasTransparency and
   1309  // nsContainerFrame::SyncFrameViewProperties.
   1310  if (!aRootElementFrame || aRootElementFrame->StyleDisplay()->IsContainAny()) {
   1311    return true;
   1312  }
   1313 
   1314  const nsStyleBackground* htmlBG = aRootElementFrame->StyleBackground();
   1315  return !htmlBG->IsTransparent(aRootElementFrame);
   1316 }
   1317 
   1318 nsIFrame* nsCSSRendering::FindBackgroundFrame(const nsIFrame* aForFrame) {
   1319  nsIFrame* rootElementFrame =
   1320      aForFrame->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
   1321  if (aForFrame->IsCanvasFrame()) {
   1322    return FindCanvasBackgroundFrame(aForFrame, rootElementFrame);
   1323  }
   1324 
   1325  if (FrameHasMeaningfulBackground(aForFrame, rootElementFrame)) {
   1326    return const_cast<nsIFrame*>(aForFrame);
   1327  }
   1328 
   1329  return nullptr;
   1330 }
   1331 
   1332 ComputedStyle* nsCSSRendering::FindBackground(const nsIFrame* aForFrame) {
   1333  if (auto* backgroundFrame = FindBackgroundFrame(aForFrame)) {
   1334    return backgroundFrame->Style();
   1335  }
   1336  return nullptr;
   1337 }
   1338 
   1339 void nsCSSRendering::BeginFrameTreesLocked() { ++gFrameTreeLockCount; }
   1340 
   1341 void nsCSSRendering::EndFrameTreesLocked() {
   1342  NS_ASSERTION(gFrameTreeLockCount > 0, "Unbalanced EndFrameTreeLocked");
   1343  --gFrameTreeLockCount;
   1344  if (gFrameTreeLockCount == 0) {
   1345    gInlineBGData->Reset();
   1346  }
   1347 }
   1348 
   1349 bool nsCSSRendering::HasBoxShadowNativeTheme(nsIFrame* aFrame,
   1350                                             bool& aMaybeHasBorderRadius) {
   1351  const nsStyleDisplay* styleDisplay = aFrame->StyleDisplay();
   1352  nsITheme::Transparency transparency;
   1353  if (aFrame->IsThemed(styleDisplay, &transparency)) {
   1354    aMaybeHasBorderRadius = false;
   1355    // For opaque (rectangular) theme widgets we can take the generic
   1356    // border-box path with border-radius disabled.
   1357    return transparency != nsITheme::eOpaque;
   1358  }
   1359 
   1360  aMaybeHasBorderRadius = true;
   1361  return false;
   1362 }
   1363 
   1364 gfx::sRGBColor nsCSSRendering::GetShadowColor(const StyleSimpleShadow& aShadow,
   1365                                              nsIFrame* aFrame,
   1366                                              float aOpacity) {
   1367  // Get the shadow color; if not specified, use the foreground color
   1368  nscolor shadowColor = aShadow.color.CalcColor(aFrame);
   1369  sRGBColor color = sRGBColor::FromABGR(shadowColor);
   1370  color.a *= aOpacity;
   1371  return color;
   1372 }
   1373 
   1374 nsRect nsCSSRendering::GetShadowRect(const nsRect& aFrameArea,
   1375                                     bool aNativeTheme, nsIFrame* aForFrame) {
   1376  nsRect frameRect = aNativeTheme ? aForFrame->InkOverflowRectRelativeToSelf() +
   1377                                        aFrameArea.TopLeft()
   1378                                  : aFrameArea;
   1379  Sides skipSides = aForFrame->GetSkipSides();
   1380  frameRect = BoxDecorationRectForBorder(aForFrame, frameRect, skipSides);
   1381 
   1382  // Explicitly do not need to account for the spread radius here
   1383  // Webrender does it for us or PaintBoxShadow will for non-WR
   1384  return frameRect;
   1385 }
   1386 
   1387 bool nsCSSRendering::GetBorderRadii(const nsRect& aFrameRect,
   1388                                    const nsRect& aBorderRect, nsIFrame* aFrame,
   1389                                    RectCornerRadii& aOutRadii) {
   1390  const nscoord oneDevPixel = aFrame->PresContext()->DevPixelsToAppUnits(1);
   1391  nsRectCornerRadii twipsRadii;
   1392  NS_ASSERTION(
   1393      aBorderRect.Size() == aFrame->VisualBorderRectRelativeToSelf().Size(),
   1394      "unexpected size");
   1395  nsSize sz = aFrameRect.Size();
   1396  bool hasBorderRadius = aFrame->GetBorderRadii(sz, sz, Sides(), twipsRadii);
   1397  if (hasBorderRadius) {
   1398    ComputePixelRadii(twipsRadii, oneDevPixel, &aOutRadii);
   1399  }
   1400 
   1401  return hasBorderRadius;
   1402 }
   1403 
   1404 void nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
   1405                                         gfxContext& aRenderingContext,
   1406                                         nsIFrame* aForFrame,
   1407                                         const nsRect& aFrameArea,
   1408                                         const nsRect& aDirtyRect,
   1409                                         float aOpacity) {
   1410  DrawTarget& aDrawTarget = *aRenderingContext.GetDrawTarget();
   1411  auto shadows = aForFrame->StyleEffects()->mBoxShadow.AsSpan();
   1412  if (shadows.IsEmpty()) {
   1413    return;
   1414  }
   1415 
   1416  bool hasBorderRadius;
   1417  // mutually exclusive with hasBorderRadius
   1418  bool nativeTheme = HasBoxShadowNativeTheme(aForFrame, hasBorderRadius);
   1419  const nsStyleDisplay* styleDisplay = aForFrame->StyleDisplay();
   1420 
   1421  nsRect frameRect = GetShadowRect(aFrameArea, nativeTheme, aForFrame);
   1422 
   1423  // Get any border radius, since box-shadow must also have rounded corners if
   1424  // the frame does.
   1425  RectCornerRadii borderRadii;
   1426  const nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1);
   1427  if (hasBorderRadius) {
   1428    nsRectCornerRadii twipsRadii;
   1429    NS_ASSERTION(
   1430        aFrameArea.Size() == aForFrame->VisualBorderRectRelativeToSelf().Size(),
   1431        "unexpected size");
   1432    nsSize sz = frameRect.Size();
   1433    hasBorderRadius = aForFrame->GetBorderRadii(sz, sz, Sides(), twipsRadii);
   1434    if (hasBorderRadius) {
   1435      ComputePixelRadii(twipsRadii, oneDevPixel, &borderRadii);
   1436    }
   1437  }
   1438 
   1439  // We don't show anything that intersects with the frame we're blurring on. So
   1440  // tell the blurrer not to do unnecessary work there.
   1441  gfxRect skipGfxRect = ThebesRect(NSRectToRect(frameRect, oneDevPixel));
   1442  skipGfxRect.Round();
   1443  bool useSkipGfxRect = true;
   1444  if (nativeTheme) {
   1445    // Optimize non-leaf native-themed frames by skipping computing pixels
   1446    // in the padding-box. We assume the padding-box is going to be painted
   1447    // opaquely for non-leaf frames.
   1448    // XXX this may not be a safe assumption; we should make this go away
   1449    // by optimizing box-shadow drawing more for the cases where we don't have a
   1450    // skip-rect.
   1451    useSkipGfxRect = !aForFrame->IsLeaf();
   1452    nsRect paddingRect =
   1453        aForFrame->GetPaddingRectRelativeToSelf() + aFrameArea.TopLeft();
   1454    skipGfxRect = nsLayoutUtils::RectToGfxRect(paddingRect, oneDevPixel);
   1455  } else if (hasBorderRadius) {
   1456    skipGfxRect.Deflate(gfxMargin(
   1457        std::max(borderRadii[C_TL].height, borderRadii[C_TR].height), 0,
   1458        std::max(borderRadii[C_BL].height, borderRadii[C_BR].height), 0));
   1459  }
   1460 
   1461  for (const StyleBoxShadow& shadow : Reversed(shadows)) {
   1462    if (shadow.inset) {
   1463      continue;
   1464    }
   1465 
   1466    nsRect shadowRect = frameRect;
   1467    nsPoint shadowOffset(shadow.base.horizontal.ToAppUnits(),
   1468                         shadow.base.vertical.ToAppUnits());
   1469    shadowRect.MoveBy(shadowOffset);
   1470    nscoord shadowSpread = shadow.spread.ToAppUnits();
   1471    if (!nativeTheme) {
   1472      shadowRect.Inflate(shadowSpread);
   1473    }
   1474 
   1475    // shadowRect won't include the blur, so make an extra rect here that
   1476    // includes the blur for use in the even-odd rule below.
   1477    nsRect shadowRectPlusBlur = shadowRect;
   1478    nscoord blurRadius = shadow.base.blur.ToAppUnits();
   1479    shadowRectPlusBlur.Inflate(
   1480        nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, oneDevPixel));
   1481 
   1482    Rect shadowGfxRectPlusBlur = NSRectToRect(shadowRectPlusBlur, oneDevPixel);
   1483    shadowGfxRectPlusBlur.RoundOut();
   1484    MaybeSnapToDevicePixels(shadowGfxRectPlusBlur, aDrawTarget, true);
   1485 
   1486    sRGBColor gfxShadowColor = GetShadowColor(shadow.base, aForFrame, aOpacity);
   1487 
   1488    if (nativeTheme) {
   1489      nsContextBoxBlur blurringArea;
   1490 
   1491      // When getting the widget shape from the native theme, we're going
   1492      // to draw the widget into the shadow surface to create a mask.
   1493      // We need to ensure that there actually *is* a shadow surface
   1494      // and that we're not going to draw directly into aRenderingContext.
   1495      gfxContext* shadowContext = blurringArea.Init(
   1496          shadowRect, shadowSpread, blurRadius, oneDevPixel, &aRenderingContext,
   1497          aDirtyRect, useSkipGfxRect ? &skipGfxRect : nullptr,
   1498          nsContextBoxBlur::FORCE_MASK);
   1499      if (!shadowContext) {
   1500        continue;
   1501      }
   1502 
   1503      MOZ_ASSERT(shadowContext == blurringArea.GetContext());
   1504 
   1505      aRenderingContext.Save();
   1506      aRenderingContext.SetColor(gfxShadowColor);
   1507 
   1508      // Draw the shape of the frame so it can be blurred. Recall how
   1509      // nsContextBoxBlur doesn't make any temporary surfaces if blur is 0 and
   1510      // it just returns the original surface? If we have no blur, we're
   1511      // painting this fill on the actual content surface (aRenderingContext ==
   1512      // shadowContext) which is why we set up the color and clip before doing
   1513      // this.
   1514 
   1515      // We don't clip the border-box from the shadow, nor any other box.
   1516      // We assume that the native theme is going to paint over the shadow.
   1517 
   1518      // Draw the widget shape
   1519      gfxContextMatrixAutoSaveRestore save(shadowContext);
   1520      gfxPoint devPixelOffset = nsLayoutUtils::PointToGfxPoint(
   1521          shadowOffset, aPresContext->AppUnitsPerDevPixel());
   1522      shadowContext->SetMatrixDouble(
   1523          shadowContext->CurrentMatrixDouble().PreTranslate(devPixelOffset));
   1524 
   1525      nsRect nativeRect = aDirtyRect;
   1526      nativeRect.MoveBy(-shadowOffset);
   1527      nativeRect.IntersectRect(frameRect, nativeRect);
   1528      aPresContext->Theme()->DrawWidgetBackground(
   1529          shadowContext, aForFrame, styleDisplay->EffectiveAppearance(),
   1530          aFrameArea, nativeRect, nsITheme::DrawOverflow::No);
   1531 
   1532      blurringArea.DoPaint();
   1533      aRenderingContext.Restore();
   1534    } else {
   1535      aRenderingContext.Save();
   1536 
   1537      {
   1538        Rect innerClipRect = NSRectToRect(frameRect, oneDevPixel);
   1539        if (!MaybeSnapToDevicePixels(innerClipRect, aDrawTarget, true)) {
   1540          innerClipRect.Round();
   1541        }
   1542 
   1543        // Clip out the interior of the frame's border edge so that the shadow
   1544        // is only painted outside that area.
   1545        RefPtr<PathBuilder> builder =
   1546            aDrawTarget.CreatePathBuilder(FillRule::FILL_EVEN_ODD);
   1547        AppendRectToPath(builder, shadowGfxRectPlusBlur);
   1548        if (hasBorderRadius) {
   1549          AppendRoundedRectToPath(builder, innerClipRect, borderRadii);
   1550        } else {
   1551          AppendRectToPath(builder, innerClipRect);
   1552        }
   1553        RefPtr<Path> path = builder->Finish();
   1554        aRenderingContext.Clip(path);
   1555      }
   1556 
   1557      // Clip the shadow so that we only get the part that applies to aForFrame.
   1558      nsRect fragmentClip = shadowRectPlusBlur;
   1559      Sides skipSides = aForFrame->GetSkipSides();
   1560      if (!skipSides.IsEmpty()) {
   1561        if (skipSides.Left()) {
   1562          nscoord xmost = fragmentClip.XMost();
   1563          fragmentClip.x = aFrameArea.x;
   1564          fragmentClip.width = xmost - fragmentClip.x;
   1565        }
   1566        if (skipSides.Right()) {
   1567          nscoord xmost = fragmentClip.XMost();
   1568          nscoord overflow = xmost - aFrameArea.XMost();
   1569          if (overflow > 0) {
   1570            fragmentClip.width -= overflow;
   1571          }
   1572        }
   1573        if (skipSides.Top()) {
   1574          nscoord ymost = fragmentClip.YMost();
   1575          fragmentClip.y = aFrameArea.y;
   1576          fragmentClip.height = ymost - fragmentClip.y;
   1577        }
   1578        if (skipSides.Bottom()) {
   1579          nscoord ymost = fragmentClip.YMost();
   1580          nscoord overflow = ymost - aFrameArea.YMost();
   1581          if (overflow > 0) {
   1582            fragmentClip.height -= overflow;
   1583          }
   1584        }
   1585      }
   1586      fragmentClip = fragmentClip.Intersect(aDirtyRect);
   1587      aRenderingContext.Clip(NSRectToSnappedRect(
   1588          fragmentClip, aForFrame->PresContext()->AppUnitsPerDevPixel(),
   1589          aDrawTarget));
   1590 
   1591      RectCornerRadii clipRectRadii;
   1592      if (hasBorderRadius) {
   1593        Float spreadDistance = Float(shadowSpread / oneDevPixel);
   1594        Margin borderSizes(spreadDistance, spreadDistance, spreadDistance,
   1595                           spreadDistance);
   1596        nsCSSBorderRenderer::ComputeOuterRadii(borderRadii, borderSizes,
   1597                                               &clipRectRadii);
   1598      }
   1599      nsContextBoxBlur::BlurRectangle(
   1600          &aRenderingContext, shadowRect, oneDevPixel,
   1601          hasBorderRadius ? &clipRectRadii : nullptr, blurRadius,
   1602          gfxShadowColor, aDirtyRect, skipGfxRect);
   1603      aRenderingContext.Restore();
   1604    }
   1605  }
   1606 }
   1607 
   1608 nsRect nsCSSRendering::GetBoxShadowInnerPaddingRect(nsIFrame* aFrame,
   1609                                                    const nsRect& aFrameArea) {
   1610  Sides skipSides = aFrame->GetSkipSides();
   1611  nsRect frameRect = BoxDecorationRectForBorder(aFrame, aFrameArea, skipSides);
   1612 
   1613  nsRect paddingRect = frameRect;
   1614  nsMargin border = aFrame->GetUsedBorder();
   1615  paddingRect.Deflate(border);
   1616  return paddingRect;
   1617 }
   1618 
   1619 bool nsCSSRendering::ShouldPaintBoxShadowInner(nsIFrame* aFrame) {
   1620  const Span<const StyleBoxShadow> shadows =
   1621      aFrame->StyleEffects()->mBoxShadow.AsSpan();
   1622  if (shadows.IsEmpty()) {
   1623    return false;
   1624  }
   1625 
   1626  if (aFrame->IsThemed() && aFrame->GetContent() &&
   1627      !nsContentUtils::IsChromeDoc(aFrame->GetContent()->GetComposedDoc())) {
   1628    // There's no way of getting hold of a shape corresponding to a
   1629    // "padding-box" for native-themed widgets, so just don't draw
   1630    // inner box-shadows for them. But we allow chrome to paint inner
   1631    // box shadows since chrome can be aware of the platform theme.
   1632    return false;
   1633  }
   1634 
   1635  return true;
   1636 }
   1637 
   1638 bool nsCSSRendering::GetShadowInnerRadii(nsIFrame* aFrame,
   1639                                         const nsRect& aFrameArea,
   1640                                         RectCornerRadii& aOutInnerRadii) {
   1641  // Get any border radius, since box-shadow must also have rounded corners
   1642  // if the frame does.
   1643  nsRectCornerRadii twipsRadii;
   1644  nsRect frameRect =
   1645      BoxDecorationRectForBorder(aFrame, aFrameArea, aFrame->GetSkipSides());
   1646  nsSize sz = frameRect.Size();
   1647  nsMargin border = aFrame->GetUsedBorder();
   1648  aFrame->GetBorderRadii(sz, sz, Sides(), twipsRadii);
   1649  const nscoord oneDevPixel = aFrame->PresContext()->DevPixelsToAppUnits(1);
   1650 
   1651  RectCornerRadii borderRadii;
   1652 
   1653  const bool hasBorderRadius =
   1654      GetBorderRadii(frameRect, aFrameArea, aFrame, borderRadii);
   1655 
   1656  if (hasBorderRadius) {
   1657    ComputePixelRadii(twipsRadii, oneDevPixel, &borderRadii);
   1658 
   1659    Margin borderSizes(
   1660        Float(border.top) / oneDevPixel, Float(border.right) / oneDevPixel,
   1661        Float(border.bottom) / oneDevPixel, Float(border.left) / oneDevPixel);
   1662    nsCSSBorderRenderer::ComputeInnerRadii(borderRadii, borderSizes,
   1663                                           &aOutInnerRadii);
   1664  }
   1665 
   1666  return hasBorderRadius;
   1667 }
   1668 
   1669 void nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext,
   1670                                         gfxContext& aRenderingContext,
   1671                                         nsIFrame* aForFrame,
   1672                                         const nsRect& aFrameArea) {
   1673  if (!ShouldPaintBoxShadowInner(aForFrame)) {
   1674    return;
   1675  }
   1676 
   1677  const Span<const StyleBoxShadow> shadows =
   1678      aForFrame->StyleEffects()->mBoxShadow.AsSpan();
   1679  NS_ASSERTION(
   1680      aForFrame->IsFieldSetFrame() || aFrameArea.Size() == aForFrame->GetSize(),
   1681      "unexpected size");
   1682 
   1683  nsRect paddingRect = GetBoxShadowInnerPaddingRect(aForFrame, aFrameArea);
   1684 
   1685  RectCornerRadii innerRadii;
   1686  bool hasBorderRadius = GetShadowInnerRadii(aForFrame, aFrameArea, innerRadii);
   1687 
   1688  const nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1);
   1689 
   1690  for (const StyleBoxShadow& shadow : Reversed(shadows)) {
   1691    if (!shadow.inset) {
   1692      continue;
   1693    }
   1694 
   1695    // shadowPaintRect: the area to paint on the temp surface
   1696    // shadowClipRect: the area on the temporary surface within shadowPaintRect
   1697    //                 that we will NOT paint in
   1698    nscoord blurRadius = shadow.base.blur.ToAppUnits();
   1699    nsMargin blurMargin =
   1700        nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, oneDevPixel);
   1701    nsRect shadowPaintRect = paddingRect;
   1702    shadowPaintRect.Inflate(blurMargin);
   1703 
   1704    // Round the spread radius to device pixels (by truncation).
   1705    // This mostly matches what we do for borders, except that we don't round
   1706    // up values between zero and one device pixels to one device pixel.
   1707    // This way of rounding is symmetric around zero, which makes sense for
   1708    // the spread radius.
   1709    int32_t spreadDistance = shadow.spread.ToAppUnits() / oneDevPixel;
   1710    nscoord spreadDistanceAppUnits =
   1711        aPresContext->DevPixelsToAppUnits(spreadDistance);
   1712 
   1713    nsRect shadowClipRect = paddingRect;
   1714    shadowClipRect.MoveBy(shadow.base.horizontal.ToAppUnits(),
   1715                          shadow.base.vertical.ToAppUnits());
   1716    shadowClipRect.Deflate(spreadDistanceAppUnits, spreadDistanceAppUnits);
   1717 
   1718    Rect shadowClipGfxRect = NSRectToRect(shadowClipRect, oneDevPixel);
   1719    shadowClipGfxRect.Round();
   1720 
   1721    RectCornerRadii clipRectRadii;
   1722    if (hasBorderRadius) {
   1723      // Calculate the radii the inner clipping rect will have
   1724      Margin borderSizes;
   1725 
   1726      // See PaintBoxShadowOuter and bug 514670
   1727      if (innerRadii[C_TL].width > 0 || innerRadii[C_BL].width > 0) {
   1728        borderSizes.left = spreadDistance;
   1729      }
   1730 
   1731      if (innerRadii[C_TL].height > 0 || innerRadii[C_TR].height > 0) {
   1732        borderSizes.top = spreadDistance;
   1733      }
   1734 
   1735      if (innerRadii[C_TR].width > 0 || innerRadii[C_BR].width > 0) {
   1736        borderSizes.right = spreadDistance;
   1737      }
   1738 
   1739      if (innerRadii[C_BL].height > 0 || innerRadii[C_BR].height > 0) {
   1740        borderSizes.bottom = spreadDistance;
   1741      }
   1742 
   1743      nsCSSBorderRenderer::ComputeInnerRadii(innerRadii, borderSizes,
   1744                                             &clipRectRadii);
   1745    }
   1746 
   1747    // Set the "skip rect" to the area within the frame that we don't paint in,
   1748    // including after blurring.
   1749    nsRect skipRect = shadowClipRect;
   1750    skipRect.Deflate(blurMargin);
   1751    gfxRect skipGfxRect = nsLayoutUtils::RectToGfxRect(skipRect, oneDevPixel);
   1752    if (hasBorderRadius) {
   1753      skipGfxRect.Deflate(gfxMargin(
   1754          std::max(clipRectRadii[C_TL].height, clipRectRadii[C_TR].height), 0,
   1755          std::max(clipRectRadii[C_BL].height, clipRectRadii[C_BR].height), 0));
   1756    }
   1757 
   1758    // When there's a blur radius, gfxGaussianBlur leaves the skiprect area
   1759    // unchanged. And by construction the gfxSkipRect is not touched by the
   1760    // rendered shadow (even after blurring), so those pixels must be completely
   1761    // transparent in the shadow, so drawing them changes nothing.
   1762    DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
   1763 
   1764    // Clip the context to the area of the frame's padding rect, so no part of
   1765    // the shadow is painted outside. Also cut out anything beyond where the
   1766    // inset shadow will be.
   1767    Rect shadowGfxRect = NSRectToRect(paddingRect, oneDevPixel);
   1768    shadowGfxRect.Round();
   1769 
   1770    sRGBColor shadowColor = GetShadowColor(shadow.base, aForFrame, 1.0);
   1771    aRenderingContext.Save();
   1772 
   1773    // This clips the outside border radius.
   1774    // clipRectRadii is the border radius inside the inset shadow.
   1775    if (hasBorderRadius) {
   1776      RefPtr<Path> roundedRect =
   1777          MakePathForRoundedRect(*drawTarget, shadowGfxRect, innerRadii);
   1778      aRenderingContext.Clip(roundedRect);
   1779    } else {
   1780      aRenderingContext.Clip(shadowGfxRect);
   1781    }
   1782 
   1783    nsContextBoxBlur insetBoxBlur;
   1784    gfxRect destRect =
   1785        nsLayoutUtils::RectToGfxRect(shadowPaintRect, oneDevPixel);
   1786    Point shadowOffset(shadow.base.horizontal.ToAppUnits() / oneDevPixel,
   1787                       shadow.base.vertical.ToAppUnits() / oneDevPixel);
   1788 
   1789    insetBoxBlur.InsetBoxBlur(
   1790        &aRenderingContext, ToRect(destRect), shadowClipGfxRect, shadowColor,
   1791        blurRadius, spreadDistanceAppUnits, oneDevPixel, hasBorderRadius,
   1792        clipRectRadii, ToRect(skipGfxRect), shadowOffset);
   1793    aRenderingContext.Restore();
   1794  }
   1795 }
   1796 
   1797 /* static */
   1798 nsCSSRendering::PaintBGParams nsCSSRendering::PaintBGParams::ForAllLayers(
   1799    nsPresContext& aPresCtx, const nsRect& aDirtyRect,
   1800    const nsRect& aBorderArea, nsIFrame* aFrame, uint32_t aPaintFlags,
   1801    float aOpacity) {
   1802  MOZ_ASSERT(aFrame);
   1803 
   1804  PaintBGParams result(aPresCtx, aDirtyRect, aBorderArea, aFrame, aPaintFlags,
   1805                       -1, CompositionOp::OP_OVER, aOpacity);
   1806 
   1807  return result;
   1808 }
   1809 
   1810 /* static */
   1811 nsCSSRendering::PaintBGParams nsCSSRendering::PaintBGParams::ForSingleLayer(
   1812    nsPresContext& aPresCtx, const nsRect& aDirtyRect,
   1813    const nsRect& aBorderArea, nsIFrame* aFrame, uint32_t aPaintFlags,
   1814    int32_t aLayer, CompositionOp aCompositionOp, float aOpacity) {
   1815  MOZ_ASSERT(aFrame && (aLayer != -1));
   1816 
   1817  PaintBGParams result(aPresCtx, aDirtyRect, aBorderArea, aFrame, aPaintFlags,
   1818                       aLayer, aCompositionOp, aOpacity);
   1819 
   1820  return result;
   1821 }
   1822 
   1823 ImgDrawResult nsCSSRendering::PaintStyleImageLayer(const PaintBGParams& aParams,
   1824                                                   gfxContext& aRenderingCtx) {
   1825  AUTO_PROFILER_LABEL("nsCSSRendering::PaintStyleImageLayer", GRAPHICS);
   1826 
   1827  MOZ_ASSERT(aParams.frame,
   1828             "Frame is expected to be provided to PaintStyleImageLayer");
   1829 
   1830  const ComputedStyle* sc = FindBackground(aParams.frame);
   1831  if (!sc) {
   1832    // We don't want to bail out if moz-appearance is set on a root
   1833    // node. If it has a parent content node, bail because it's not
   1834    // a root, otherwise keep going in order to let the theme stuff
   1835    // draw the background. The canvas really should be drawing the
   1836    // bg, but there's no way to hook that up via css.
   1837    if (!aParams.frame->StyleDisplay()->HasAppearance()) {
   1838      return ImgDrawResult::SUCCESS;
   1839    }
   1840 
   1841    nsIContent* content = aParams.frame->GetContent();
   1842    if (!content || content->GetParent()) {
   1843      return ImgDrawResult::SUCCESS;
   1844    }
   1845 
   1846    sc = aParams.frame->Style();
   1847  }
   1848 
   1849  return PaintStyleImageLayerWithSC(aParams, aRenderingCtx, sc,
   1850                                    *aParams.frame->StyleBorder());
   1851 }
   1852 
   1853 bool nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(
   1854    WebRenderLayerManager* aManager, nsPresContext& aPresCtx, nsIFrame* aFrame,
   1855    const nsStyleBackground* aBackgroundStyle, int32_t aLayer,
   1856    uint32_t aPaintFlags) {
   1857  if (!aBackgroundStyle) {
   1858    return false;
   1859  }
   1860 
   1861  MOZ_ASSERT(aFrame && aLayer >= 0 &&
   1862             (uint32_t)aLayer < aBackgroundStyle->mImage.mLayers.Length());
   1863 
   1864  // We cannot draw native themed backgrounds
   1865  StyleAppearance appearance = aFrame->StyleDisplay()->EffectiveAppearance();
   1866  if (appearance != StyleAppearance::None) {
   1867    nsITheme* theme = aPresCtx.Theme();
   1868    if (theme->ThemeSupportsWidget(&aPresCtx, aFrame, appearance)) {
   1869      return false;
   1870    }
   1871  }
   1872 
   1873  // We only support painting gradients and image for a single style image
   1874  // layer, and we don't support crop-rects.
   1875  const auto& styleImage =
   1876      aBackgroundStyle->mImage.mLayers[aLayer].mImage.FinalImage();
   1877  if (styleImage.IsImageRequestType()) {
   1878    imgRequestProxy* requestProxy = styleImage.GetImageRequest();
   1879    if (!requestProxy) {
   1880      return false;
   1881    }
   1882 
   1883    uint32_t imageFlags = imgIContainer::FLAG_NONE;
   1884    if (aPaintFlags & nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES) {
   1885      imageFlags |= imgIContainer::FLAG_SYNC_DECODE;
   1886    }
   1887 
   1888    nsCOMPtr<imgIContainer> srcImage;
   1889    requestProxy->GetImage(getter_AddRefs(srcImage));
   1890    if (!srcImage ||
   1891        !srcImage->IsImageContainerAvailable(aManager, imageFlags)) {
   1892      return false;
   1893    }
   1894 
   1895    return true;
   1896  }
   1897 
   1898  if (styleImage.IsGradient()) {
   1899    return true;
   1900  }
   1901 
   1902  return false;
   1903 }
   1904 
   1905 ImgDrawResult nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
   1906    const PaintBGParams& aParams, mozilla::wr::DisplayListBuilder& aBuilder,
   1907    mozilla::wr::IpcResourceUpdateQueue& aResources,
   1908    const mozilla::layers::StackingContextHelper& aSc,
   1909    mozilla::layers::RenderRootStateManager* aManager, nsDisplayItem* aItem) {
   1910  MOZ_ASSERT(aParams.frame,
   1911             "Frame is expected to be provided to "
   1912             "BuildWebRenderDisplayItemsForStyleImageLayer");
   1913 
   1914  ComputedStyle* sc = FindBackground(aParams.frame);
   1915  if (!sc) {
   1916    // We don't want to bail out if moz-appearance is set on a root
   1917    // node. If it has a parent content node, bail because it's not
   1918    // a root, otherwise keep going in order to let the theme stuff
   1919    // draw the background. The canvas really should be drawing the
   1920    // bg, but there's no way to hook that up via css.
   1921    if (!aParams.frame->StyleDisplay()->HasAppearance()) {
   1922      return ImgDrawResult::SUCCESS;
   1923    }
   1924 
   1925    nsIContent* content = aParams.frame->GetContent();
   1926    if (!content || content->GetParent()) {
   1927      return ImgDrawResult::SUCCESS;
   1928    }
   1929 
   1930    sc = aParams.frame->Style();
   1931  }
   1932  return BuildWebRenderDisplayItemsForStyleImageLayerWithSC(
   1933      aParams, aBuilder, aResources, aSc, aManager, aItem, sc,
   1934      *aParams.frame->StyleBorder());
   1935 }
   1936 
   1937 static bool IsOpaqueBorderEdge(const nsStyleBorder& aBorder,
   1938                               mozilla::Side aSide, const nsIFrame* aForFrame) {
   1939  if (aBorder.GetComputedBorder().Side(aSide) == 0) {
   1940    return true;
   1941  }
   1942  switch (aBorder.GetBorderStyle(aSide)) {
   1943    case StyleBorderStyle::Solid:
   1944    case StyleBorderStyle::Groove:
   1945    case StyleBorderStyle::Ridge:
   1946    case StyleBorderStyle::Inset:
   1947    case StyleBorderStyle::Outset:
   1948      break;
   1949    default:
   1950      return false;
   1951  }
   1952 
   1953  // If we're using a border image, assume it's not fully opaque,
   1954  // because we may not even have the image loaded at this point, and
   1955  // even if we did, checking whether the relevant tile is fully
   1956  // opaque would be too much work.
   1957  if (!aBorder.mBorderImageSource.IsNone()) {
   1958    return false;
   1959  }
   1960  return NS_GET_A(aBorder.BorderColorFor(aSide).CalcColor(aForFrame)) == 255;
   1961 }
   1962 
   1963 /**
   1964 * Returns true if all border edges are either missing or opaque.
   1965 */
   1966 static bool IsOpaqueBorder(const nsStyleBorder& aBorder,
   1967                           const nsIFrame* aForFrame) {
   1968  for (const auto i : mozilla::AllPhysicalSides()) {
   1969    if (!IsOpaqueBorderEdge(aBorder, i, aForFrame)) {
   1970      return false;
   1971    }
   1972  }
   1973  return true;
   1974 }
   1975 
   1976 static inline void SetupDirtyRects(const nsRect& aBGClipArea,
   1977                                   const nsRect& aCallerDirtyRect,
   1978                                   nscoord aAppUnitsPerPixel,
   1979                                   /* OUT: */
   1980                                   nsRect* aDirtyRect, gfxRect* aDirtyRectGfx) {
   1981  aDirtyRect->IntersectRect(aBGClipArea, aCallerDirtyRect);
   1982 
   1983  // Compute the Thebes equivalent of the dirtyRect.
   1984  *aDirtyRectGfx = nsLayoutUtils::RectToGfxRect(*aDirtyRect, aAppUnitsPerPixel);
   1985  NS_WARNING_ASSERTION(aDirtyRect->IsEmpty() || !aDirtyRectGfx->IsEmpty(),
   1986                       "converted dirty rect should not be empty");
   1987  MOZ_ASSERT(!aDirtyRect->IsEmpty() || aDirtyRectGfx->IsEmpty(),
   1988             "second should be empty if first is");
   1989 }
   1990 
   1991 static bool IsSVGStyleGeometryBox(StyleGeometryBox aBox) {
   1992  return (aBox == StyleGeometryBox::FillBox ||
   1993          aBox == StyleGeometryBox::StrokeBox ||
   1994          aBox == StyleGeometryBox::ViewBox);
   1995 }
   1996 
   1997 static bool IsHTMLStyleGeometryBox(StyleGeometryBox aBox) {
   1998  return (aBox == StyleGeometryBox::ContentBox ||
   1999          aBox == StyleGeometryBox::PaddingBox ||
   2000          aBox == StyleGeometryBox::BorderBox ||
   2001          aBox == StyleGeometryBox::MarginBox);
   2002 }
   2003 
   2004 static StyleGeometryBox ComputeBoxValueForOrigin(nsIFrame* aForFrame,
   2005                                                 StyleGeometryBox aBox) {
   2006  // The mapping for mask-origin is from
   2007  // https://drafts.fxtf.org/css-masking/#the-mask-origin
   2008  if (!aForFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
   2009    // For elements with associated CSS layout box, the values fill-box,
   2010    // stroke-box and view-box compute to the initial value of mask-origin.
   2011    if (IsSVGStyleGeometryBox(aBox)) {
   2012      return StyleGeometryBox::BorderBox;
   2013    }
   2014  } else {
   2015    // For SVG elements without associated CSS layout box, the values
   2016    // content-box, padding-box, border-box compute to fill-box.
   2017    if (IsHTMLStyleGeometryBox(aBox)) {
   2018      return StyleGeometryBox::FillBox;
   2019    }
   2020  }
   2021 
   2022  return aBox;
   2023 }
   2024 
   2025 static StyleGeometryBox ComputeBoxValueForClip(const nsIFrame* aForFrame,
   2026                                               StyleGeometryBox aBox) {
   2027  // The mapping for mask-clip is from
   2028  // https://drafts.fxtf.org/css-masking/#the-mask-clip
   2029  if (aForFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
   2030    // For SVG elements without associated CSS layout box, the used values for
   2031    // content-box and padding-box compute to fill-box and for border-box and
   2032    // margin-box compute to stroke-box.
   2033    switch (aBox) {
   2034      case StyleGeometryBox::ContentBox:
   2035      case StyleGeometryBox::PaddingBox:
   2036        return StyleGeometryBox::FillBox;
   2037      case StyleGeometryBox::BorderBox:
   2038      case StyleGeometryBox::MarginBox:
   2039        return StyleGeometryBox::StrokeBox;
   2040      default:
   2041        return aBox;
   2042    }
   2043  }
   2044 
   2045  // For elements with associated CSS layout box, the used values for fill-box
   2046  // compute to content-box and for stroke-box and view-box compute to
   2047  // border-box.
   2048  switch (aBox) {
   2049    case StyleGeometryBox::FillBox:
   2050      return StyleGeometryBox::ContentBox;
   2051    case StyleGeometryBox::StrokeBox:
   2052    case StyleGeometryBox::ViewBox:
   2053      return StyleGeometryBox::BorderBox;
   2054    default:
   2055      return aBox;
   2056  }
   2057 }
   2058 
   2059 bool nsCSSRendering::ImageLayerClipState::IsValid() const {
   2060  // mDirtyRectInDevPx comes from mDirtyRectInAppUnits. mDirtyRectInAppUnits
   2061  // can not be empty if mDirtyRectInDevPx is not.
   2062  if (!mDirtyRectInDevPx.IsEmpty() && mDirtyRectInAppUnits.IsEmpty()) {
   2063    return false;
   2064  }
   2065 
   2066  if (mHasRoundedCorners == mClippedRadii.IsEmpty()) {
   2067    return false;
   2068  }
   2069 
   2070  return true;
   2071 }
   2072 
   2073 /* static */
   2074 void nsCSSRendering::GetImageLayerClip(
   2075    const nsStyleImageLayers::Layer& aLayer, nsIFrame* aForFrame,
   2076    const nsStyleBorder& aBorder, const nsRect& aBorderArea,
   2077    const nsRect& aCallerDirtyRect, bool aWillPaintBorder,
   2078    nscoord aAppUnitsPerPixel,
   2079    /* out */ ImageLayerClipState* aClipState) {
   2080  StyleGeometryBox layerClip = ComputeBoxValueForClip(aForFrame, aLayer.mClip);
   2081  if (IsSVGStyleGeometryBox(layerClip)) {
   2082    MOZ_ASSERT(aForFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT));
   2083 
   2084    // The coordinate space of clipArea is svg user space.
   2085    nsRect clipArea =
   2086        nsLayoutUtils::ComputeSVGReferenceRect(aForFrame, layerClip);
   2087 
   2088    nsRect strokeBox = (layerClip == StyleGeometryBox::StrokeBox)
   2089                           ? clipArea
   2090                           : nsLayoutUtils::ComputeSVGReferenceRect(
   2091                                 aForFrame, StyleGeometryBox::StrokeBox);
   2092    nsRect clipAreaRelativeToStrokeBox = clipArea - strokeBox.TopLeft();
   2093 
   2094    // aBorderArea is the stroke-box area in a coordinate space defined by
   2095    // the caller. This coordinate space can be svg user space of aForFrame,
   2096    // the space of aForFrame's reference-frame, or anything else.
   2097    //
   2098    // Which coordinate space chosen for aBorderArea is not matter. What
   2099    // matter is to ensure returning aClipState->mBGClipArea in the consistent
   2100    // coordiante space with aBorderArea. So we evaluate the position of clip
   2101    // area base on the position of aBorderArea here.
   2102    aClipState->mBGClipArea =
   2103        clipAreaRelativeToStrokeBox + aBorderArea.TopLeft();
   2104 
   2105    SetupDirtyRects(aClipState->mBGClipArea, aCallerDirtyRect,
   2106                    aAppUnitsPerPixel, &aClipState->mDirtyRectInAppUnits,
   2107                    &aClipState->mDirtyRectInDevPx);
   2108    MOZ_ASSERT(aClipState->IsValid());
   2109    return;
   2110  }
   2111 
   2112  if (layerClip == StyleGeometryBox::NoClip) {
   2113    aClipState->mBGClipArea = aCallerDirtyRect;
   2114 
   2115    SetupDirtyRects(aClipState->mBGClipArea, aCallerDirtyRect,
   2116                    aAppUnitsPerPixel, &aClipState->mDirtyRectInAppUnits,
   2117                    &aClipState->mDirtyRectInDevPx);
   2118    MOZ_ASSERT(aClipState->IsValid());
   2119    return;
   2120  }
   2121 
   2122  MOZ_ASSERT(!aForFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT));
   2123 
   2124  // Compute the outermost boundary of the area that might be painted.
   2125  // Same coordinate space as aBorderArea.
   2126  Sides skipSides = aForFrame->GetSkipSides();
   2127  nsRect clipBorderArea =
   2128      BoxDecorationRectForBorder(aForFrame, aBorderArea, skipSides, &aBorder);
   2129 
   2130  bool haveRoundedCorners = false;
   2131  LayoutFrameType fType = aForFrame->Type();
   2132  if (fType != LayoutFrameType::TableColGroup &&
   2133      fType != LayoutFrameType::TableCol &&
   2134      fType != LayoutFrameType::TableRow &&
   2135      fType != LayoutFrameType::TableRowGroup) {
   2136    haveRoundedCorners = GetRadii(aForFrame, aBorder, aBorderArea,
   2137                                  clipBorderArea, aClipState->mRadii);
   2138  }
   2139  const bool isSolidBorder =
   2140      aWillPaintBorder && IsOpaqueBorder(aBorder, aForFrame);
   2141  if (isSolidBorder && layerClip == StyleGeometryBox::BorderBox) {
   2142    // If we have rounded corners, we need to inflate the background
   2143    // drawing area a bit to avoid seams between the border and
   2144    // background.
   2145    layerClip = haveRoundedCorners ? StyleGeometryBox::MozAlmostPadding
   2146                                   : StyleGeometryBox::PaddingBox;
   2147  }
   2148 
   2149  aClipState->mBGClipArea = clipBorderArea;
   2150 
   2151  if (aForFrame->IsScrollContainerFrame() &&
   2152      StyleImageLayerAttachment::Local == aLayer.mAttachment) {
   2153    // As of this writing, this is still in discussion in the CSS Working Group
   2154    // http://lists.w3.org/Archives/Public/www-style/2013Jul/0250.html
   2155 
   2156    // The rectangle for 'background-clip' scrolls with the content,
   2157    // but the background is also clipped at a non-scrolling 'padding-box'
   2158    // like the content. (See below.)
   2159    // Therefore, only 'content-box' makes a difference here.
   2160    if (layerClip == StyleGeometryBox::ContentBox) {
   2161      ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(aForFrame);
   2162      // Clip at a rectangle attached to the scrolled content.
   2163      aClipState->mHasAdditionalBGClipArea = true;
   2164      aClipState->mAdditionalBGClipArea =
   2165          nsRect(aClipState->mBGClipArea.TopLeft() +
   2166                     scrollContainerFrame->GetScrolledFrame()->GetPosition()
   2167                     // For the dir=rtl case:
   2168                     + scrollContainerFrame->GetScrollRange().TopLeft(),
   2169                 scrollContainerFrame->GetScrolledRect().Size());
   2170      nsMargin padding = aForFrame->GetUsedPadding();
   2171      // padding-bottom is ignored on scrollable frames:
   2172      // https://bugzilla.mozilla.org/show_bug.cgi?id=748518
   2173      padding.bottom = 0;
   2174      padding.ApplySkipSides(skipSides);
   2175      aClipState->mAdditionalBGClipArea.Deflate(padding);
   2176    }
   2177 
   2178    // Also clip at a non-scrolling, rounded-corner 'padding-box',
   2179    // same as the scrolled content because of the 'overflow' property.
   2180    layerClip = StyleGeometryBox::PaddingBox;
   2181  }
   2182 
   2183  // See the comment of StyleGeometryBox::Margin.
   2184  // Hitting this assertion means we decide to turn on margin-box support for
   2185  // positioned mask from CSS parser and style system. In this case, you
   2186  // should *inflate* mBGClipArea by the margin returning from
   2187  // aForFrame->GetUsedMargin() in the code chunk bellow.
   2188  MOZ_ASSERT(layerClip != StyleGeometryBox::MarginBox,
   2189             "StyleGeometryBox::MarginBox rendering is not supported yet.\n");
   2190 
   2191  if (layerClip != StyleGeometryBox::BorderBox &&
   2192      layerClip != StyleGeometryBox::Text) {
   2193    nsMargin border = aForFrame->GetUsedBorder();
   2194    if (layerClip == StyleGeometryBox::MozAlmostPadding) {
   2195      // Reduce |border| by 1px (device pixels) on all sides, if
   2196      // possible, so that we don't get antialiasing seams between the
   2197      // {background|mask} and border.
   2198      border.top = std::max(0, border.top - aAppUnitsPerPixel);
   2199      border.right = std::max(0, border.right - aAppUnitsPerPixel);
   2200      border.bottom = std::max(0, border.bottom - aAppUnitsPerPixel);
   2201      border.left = std::max(0, border.left - aAppUnitsPerPixel);
   2202    } else if (layerClip != StyleGeometryBox::PaddingBox) {
   2203      NS_ASSERTION(layerClip == StyleGeometryBox::ContentBox,
   2204                   "unexpected background-clip");
   2205      border += aForFrame->GetUsedPadding();
   2206    }
   2207    border.ApplySkipSides(skipSides);
   2208    aClipState->mBGClipArea.Deflate(border);
   2209 
   2210    if (haveRoundedCorners) {
   2211      aClipState->mRadii.AdjustInwards(border);
   2212    }
   2213  }
   2214 
   2215  if (haveRoundedCorners) {
   2216    auto d2a = aForFrame->PresContext()->AppUnitsPerDevPixel();
   2217    nsCSSRendering::ComputePixelRadii(aClipState->mRadii, d2a,
   2218                                      &aClipState->mClippedRadii);
   2219    aClipState->mHasRoundedCorners = !aClipState->mClippedRadii.IsEmpty();
   2220  }
   2221 
   2222  if (!haveRoundedCorners && aClipState->mHasAdditionalBGClipArea) {
   2223    // Do the intersection here to account for the fast path(?) below.
   2224    aClipState->mBGClipArea =
   2225        aClipState->mBGClipArea.Intersect(aClipState->mAdditionalBGClipArea);
   2226    aClipState->mHasAdditionalBGClipArea = false;
   2227  }
   2228 
   2229  SetupDirtyRects(aClipState->mBGClipArea, aCallerDirtyRect, aAppUnitsPerPixel,
   2230                  &aClipState->mDirtyRectInAppUnits,
   2231                  &aClipState->mDirtyRectInDevPx);
   2232 
   2233  MOZ_ASSERT(aClipState->IsValid());
   2234 }
   2235 
   2236 static void SetupImageLayerClip(nsCSSRendering::ImageLayerClipState& aClipState,
   2237                                gfxContext* aCtx, nscoord aAppUnitsPerPixel,
   2238                                gfxContextAutoSaveRestore* aAutoSR) {
   2239  if (aClipState.mDirtyRectInDevPx.IsEmpty()) {
   2240    // Our caller won't draw anything under this condition, so no need
   2241    // to set more up.
   2242    return;
   2243  }
   2244 
   2245  if (aClipState.mCustomClip) {
   2246    // We don't support custom clips and rounded corners, arguably a bug, but
   2247    // table painting seems to depend on it.
   2248    return;
   2249  }
   2250 
   2251  // If we have rounded corners, clip all subsequent drawing to the
   2252  // rounded rectangle defined by bgArea and bgRadii (we don't know
   2253  // whether the rounded corners intrude on the dirtyRect or not).
   2254  // Do not do this if we have a caller-provided clip rect --
   2255  // as above with bgArea, arguably a bug, but table painting seems
   2256  // to depend on it.
   2257 
   2258  if (aClipState.mHasAdditionalBGClipArea) {
   2259    gfxRect bgAreaGfx = nsLayoutUtils::RectToGfxRect(
   2260        aClipState.mAdditionalBGClipArea, aAppUnitsPerPixel);
   2261    bgAreaGfx.Round();
   2262    gfxUtils::ConditionRect(bgAreaGfx);
   2263 
   2264    aAutoSR->EnsureSaved(aCtx);
   2265    aCtx->SnappedClip(bgAreaGfx);
   2266  }
   2267 
   2268  if (aClipState.mHasRoundedCorners) {
   2269    Rect bgAreaGfx = NSRectToRect(aClipState.mBGClipArea, aAppUnitsPerPixel);
   2270    bgAreaGfx.Round();
   2271 
   2272    if (bgAreaGfx.IsEmpty()) {
   2273      // I think it's become possible to hit this since
   2274      // https://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.
   2275      NS_WARNING("converted background area should not be empty");
   2276      // Make our caller not do anything.
   2277      aClipState.mDirtyRectInDevPx.SizeTo(gfxSize(0.0, 0.0));
   2278      return;
   2279    }
   2280 
   2281    aAutoSR->EnsureSaved(aCtx);
   2282 
   2283    RefPtr<Path> roundedRect = MakePathForRoundedRect(
   2284        *aCtx->GetDrawTarget(), bgAreaGfx, aClipState.mClippedRadii);
   2285    aCtx->Clip(roundedRect);
   2286  }
   2287 }
   2288 
   2289 static void DrawBackgroundColor(nsCSSRendering::ImageLayerClipState& aClipState,
   2290                                gfxContext* aCtx, nscoord aAppUnitsPerPixel) {
   2291  if (aClipState.mDirtyRectInDevPx.IsEmpty()) {
   2292    // Our caller won't draw anything under this condition, so no need
   2293    // to set more up.
   2294    return;
   2295  }
   2296 
   2297  DrawTarget* drawTarget = aCtx->GetDrawTarget();
   2298 
   2299  // We don't support custom clips and rounded corners, arguably a bug, but
   2300  // table painting seems to depend on it.
   2301  if (!aClipState.mHasRoundedCorners || aClipState.mCustomClip) {
   2302    aCtx->NewPath();
   2303    aCtx->SnappedRectangle(aClipState.mDirtyRectInDevPx);
   2304    aCtx->Fill();
   2305    return;
   2306  }
   2307 
   2308  Rect bgAreaGfx = NSRectToRect(aClipState.mBGClipArea, aAppUnitsPerPixel);
   2309  bgAreaGfx.Round();
   2310 
   2311  if (bgAreaGfx.IsEmpty()) {
   2312    // I think it's become possible to hit this since
   2313    // https://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.
   2314    NS_WARNING("converted background area should not be empty");
   2315    // Make our caller not do anything.
   2316    aClipState.mDirtyRectInDevPx.SizeTo(gfxSize(0.0, 0.0));
   2317    return;
   2318  }
   2319 
   2320  aCtx->Save();
   2321  gfxRect dirty = ThebesRect(bgAreaGfx).Intersect(aClipState.mDirtyRectInDevPx);
   2322 
   2323  aCtx->SnappedClip(dirty);
   2324 
   2325  if (aClipState.mHasAdditionalBGClipArea) {
   2326    gfxRect bgAdditionalAreaGfx = nsLayoutUtils::RectToGfxRect(
   2327        aClipState.mAdditionalBGClipArea, aAppUnitsPerPixel);
   2328    bgAdditionalAreaGfx.Round();
   2329    gfxUtils::ConditionRect(bgAdditionalAreaGfx);
   2330    aCtx->SnappedClip(bgAdditionalAreaGfx);
   2331  }
   2332 
   2333  RefPtr<Path> roundedRect =
   2334      MakePathForRoundedRect(*drawTarget, bgAreaGfx, aClipState.mClippedRadii);
   2335  aCtx->SetPath(roundedRect);
   2336  aCtx->Fill();
   2337  aCtx->Restore();
   2338 }
   2339 
   2340 enum class ScrollbarColorKind {
   2341  Thumb,
   2342  Track,
   2343 };
   2344 
   2345 static Maybe<nscolor> CalcScrollbarColor(nsIFrame* aFrame,
   2346                                         ScrollbarColorKind aKind) {
   2347  ComputedStyle* scrollbarStyle = nsLayoutUtils::StyleForScrollbar(aFrame);
   2348  const auto& colors = scrollbarStyle->StyleUI()->mScrollbarColor;
   2349  if (colors.IsAuto()) {
   2350    return Nothing();
   2351  }
   2352  const auto& color = aKind == ScrollbarColorKind::Thumb
   2353                          ? colors.AsColors().thumb
   2354                          : colors.AsColors().track;
   2355  return Some(color.CalcColor(*scrollbarStyle));
   2356 }
   2357 
   2358 static nscolor GetBackgroundColor(nsIFrame* aFrame,
   2359                                  const ComputedStyle* aStyle) {
   2360  switch (aStyle->StyleDisplay()->EffectiveAppearance()) {
   2361    case StyleAppearance::ScrollbarthumbVertical:
   2362    case StyleAppearance::ScrollbarthumbHorizontal: {
   2363      if (Maybe<nscolor> overrideColor =
   2364              CalcScrollbarColor(aFrame, ScrollbarColorKind::Thumb)) {
   2365        return *overrideColor;
   2366      }
   2367      break;
   2368    }
   2369    case StyleAppearance::ScrollbarVertical:
   2370    case StyleAppearance::ScrollbarHorizontal:
   2371    case StyleAppearance::Scrollcorner: {
   2372      if (Maybe<nscolor> overrideColor =
   2373              CalcScrollbarColor(aFrame, ScrollbarColorKind::Track)) {
   2374        return *overrideColor;
   2375      }
   2376      break;
   2377    }
   2378    default:
   2379      break;
   2380  }
   2381  return aStyle->GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
   2382 }
   2383 
   2384 nscolor nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext,
   2385                                                 const ComputedStyle* aStyle,
   2386                                                 nsIFrame* aFrame,
   2387                                                 bool& aDrawBackgroundImage,
   2388                                                 bool& aDrawBackgroundColor) {
   2389  auto shouldPaint = aFrame->ComputeShouldPaintBackground();
   2390  aDrawBackgroundImage = shouldPaint.mImage;
   2391  aDrawBackgroundColor = shouldPaint.mColor;
   2392 
   2393  const nsStyleBackground* bg = aStyle->StyleBackground();
   2394  nscolor bgColor;
   2395  if (aDrawBackgroundColor) {
   2396    bgColor = GetBackgroundColor(aFrame, aStyle);
   2397    if (NS_GET_A(bgColor) == 0) {
   2398      aDrawBackgroundColor = false;
   2399    }
   2400  } else {
   2401    // If GetBackgroundColorDraw() is false, we are still expected to
   2402    // draw color in the background of any frame that's not completely
   2403    // transparent, but we are expected to use white instead of whatever
   2404    // color was specified.
   2405    bgColor = NS_RGB(255, 255, 255);
   2406    if (aDrawBackgroundImage || !bg->IsTransparent(aStyle)) {
   2407      aDrawBackgroundColor = true;
   2408    } else {
   2409      bgColor = NS_RGBA(0, 0, 0, 0);
   2410    }
   2411  }
   2412 
   2413  // We can skip painting the background color if a background image is opaque.
   2414  nsStyleImageLayers::Repeat repeat = bg->BottomLayer().mRepeat;
   2415  bool xFullRepeat = repeat.mXRepeat == StyleImageLayerRepeat::Repeat ||
   2416                     repeat.mXRepeat == StyleImageLayerRepeat::Round;
   2417  bool yFullRepeat = repeat.mYRepeat == StyleImageLayerRepeat::Repeat ||
   2418                     repeat.mYRepeat == StyleImageLayerRepeat::Round;
   2419  if (aDrawBackgroundColor && xFullRepeat && yFullRepeat &&
   2420      bg->BottomLayer().mImage.IsOpaque() &&
   2421      bg->BottomLayer().mBlendMode == StyleBlend::Normal) {
   2422    aDrawBackgroundColor = false;
   2423  }
   2424 
   2425  return bgColor;
   2426 }
   2427 
   2428 static CompositionOp DetermineCompositionOp(
   2429    const nsCSSRendering::PaintBGParams& aParams,
   2430    const nsStyleImageLayers& aLayers, uint32_t aLayerIndex) {
   2431  if (aParams.layer >= 0) {
   2432    // When drawing a single layer, use the specified composition op.
   2433    return aParams.compositionOp;
   2434  }
   2435 
   2436  const nsStyleImageLayers::Layer& layer = aLayers.mLayers[aLayerIndex];
   2437  // When drawing all layers, get the compositon op from each image layer.
   2438  if (aParams.paintFlags & nsCSSRendering::PAINTBG_MASK_IMAGE) {
   2439    // Always using OP_OVER mode while drawing the bottom mask layer.
   2440    if (aLayerIndex == (aLayers.mImageCount - 1)) {
   2441      return CompositionOp::OP_OVER;
   2442    }
   2443 
   2444    return nsCSSRendering::GetGFXCompositeMode(layer.mComposite);
   2445  }
   2446 
   2447  return nsCSSRendering::GetGFXBlendMode(layer.mBlendMode);
   2448 }
   2449 
   2450 ImgDrawResult nsCSSRendering::PaintStyleImageLayerWithSC(
   2451    const PaintBGParams& aParams, gfxContext& aRenderingCtx,
   2452    const ComputedStyle* aBackgroundSC, const nsStyleBorder& aBorder) {
   2453  MOZ_ASSERT(aParams.frame,
   2454             "Frame is expected to be provided to PaintStyleImageLayerWithSC");
   2455 
   2456  // If we're drawing all layers, aCompositonOp is ignored, so make sure that
   2457  // it was left at its default value.
   2458  MOZ_ASSERT(aParams.layer != -1 ||
   2459             aParams.compositionOp == CompositionOp::OP_OVER);
   2460 
   2461  // Check to see if we have an appearance defined.  If so, we let the theme
   2462  // renderer draw the background and bail out.
   2463  // XXXzw this ignores aParams.bgClipRect.
   2464  StyleAppearance appearance =
   2465      aParams.frame->StyleDisplay()->EffectiveAppearance();
   2466  if (appearance != StyleAppearance::None) {
   2467    nsITheme* theme = aParams.presCtx.Theme();
   2468    if (theme->ThemeSupportsWidget(&aParams.presCtx, aParams.frame,
   2469                                   appearance)) {
   2470      nsRect drawing(aParams.borderArea);
   2471      theme->GetWidgetOverflow(aParams.presCtx.DeviceContext(), aParams.frame,
   2472                               appearance, &drawing);
   2473      drawing.IntersectRect(drawing, aParams.dirtyRect);
   2474      theme->DrawWidgetBackground(&aRenderingCtx, aParams.frame, appearance,
   2475                                  aParams.borderArea, drawing);
   2476      return ImgDrawResult::SUCCESS;
   2477    }
   2478  }
   2479 
   2480  // For canvas frames (in the CSS sense) we draw the background color using
   2481  // a solid color item that gets added in nsLayoutUtils::PaintFrame, or
   2482  // nsSubDocumentFrame::BuildDisplayList (bug 488242).  Either way we don't
   2483  // need to paint the background color here.
   2484  bool isCanvasFrame = aParams.frame->IsCanvasFrame();
   2485  const bool paintMask = aParams.paintFlags & PAINTBG_MASK_IMAGE;
   2486 
   2487  // Determine whether we are drawing background images and/or
   2488  // background colors.
   2489  bool drawBackgroundImage = true;
   2490  bool drawBackgroundColor = !paintMask;
   2491  nscolor bgColor = NS_RGBA(0, 0, 0, 0);
   2492  if (!paintMask) {
   2493    bgColor =
   2494        DetermineBackgroundColor(&aParams.presCtx, aBackgroundSC, aParams.frame,
   2495                                 drawBackgroundImage, drawBackgroundColor);
   2496  }
   2497 
   2498  // Masks shouldn't be suppressed for print.
   2499  MOZ_ASSERT_IF(paintMask, drawBackgroundImage);
   2500 
   2501  const nsStyleImageLayers& layers =
   2502      paintMask ? aBackgroundSC->StyleSVGReset()->mMask
   2503                : aBackgroundSC->StyleBackground()->mImage;
   2504  // If we're drawing a specific layer, we don't want to draw the
   2505  // background color.
   2506  if (drawBackgroundColor && aParams.layer >= 0) {
   2507    drawBackgroundColor = false;
   2508  }
   2509 
   2510  // At this point, drawBackgroundImage and drawBackgroundColor are
   2511  // true if and only if we are actually supposed to paint an image or
   2512  // color into aDirtyRect, respectively.
   2513  if (!drawBackgroundImage && !drawBackgroundColor) {
   2514    return ImgDrawResult::SUCCESS;
   2515  }
   2516 
   2517  // The 'bgClipArea' (used only by the image tiling logic, far below)
   2518  // is the caller-provided aParams.bgClipRect if any, or else the area
   2519  // determined by the value of 'background-clip' in
   2520  // SetupCurrentBackgroundClip.  (Arguably it should be the
   2521  // intersection, but that breaks the table painter -- in particular,
   2522  // taking the intersection breaks reftests/bugs/403249-1[ab].)
   2523  nscoord appUnitsPerPixel = aParams.presCtx.AppUnitsPerDevPixel();
   2524  ImageLayerClipState clipState;
   2525  if (aParams.bgClipRect) {
   2526    clipState.mBGClipArea = *aParams.bgClipRect;
   2527    clipState.mCustomClip = true;
   2528    clipState.mHasRoundedCorners = false;
   2529    SetupDirtyRects(clipState.mBGClipArea, aParams.dirtyRect, appUnitsPerPixel,
   2530                    &clipState.mDirtyRectInAppUnits,
   2531                    &clipState.mDirtyRectInDevPx);
   2532  } else {
   2533    GetImageLayerClip(layers.BottomLayer(), aParams.frame, aBorder,
   2534                      aParams.borderArea, aParams.dirtyRect,
   2535                      (aParams.paintFlags & PAINTBG_WILL_PAINT_BORDER),
   2536                      appUnitsPerPixel, &clipState);
   2537  }
   2538 
   2539  // If we might be using a background color, go ahead and set it now.
   2540  if (drawBackgroundColor && !isCanvasFrame) {
   2541    aRenderingCtx.SetColor(sRGBColor::FromABGR(bgColor));
   2542  }
   2543 
   2544  // If there is no background image, draw a color.  (If there is
   2545  // neither a background image nor a color, we wouldn't have gotten
   2546  // this far.)
   2547  if (!drawBackgroundImage) {
   2548    if (!isCanvasFrame) {
   2549      DrawBackgroundColor(clipState, &aRenderingCtx, appUnitsPerPixel);
   2550    }
   2551    return ImgDrawResult::SUCCESS;
   2552  }
   2553 
   2554  if (layers.mImageCount < 1) {
   2555    // Return if there are no background layers, all work from this point
   2556    // onwards happens iteratively on these.
   2557    return ImgDrawResult::SUCCESS;
   2558  }
   2559 
   2560  MOZ_ASSERT((aParams.layer < 0) ||
   2561             (layers.mImageCount > uint32_t(aParams.layer)));
   2562 
   2563  // The background color is rendered over the entire dirty area,
   2564  // even if the image isn't.
   2565  if (drawBackgroundColor && !isCanvasFrame) {
   2566    DrawBackgroundColor(clipState, &aRenderingCtx, appUnitsPerPixel);
   2567  }
   2568 
   2569  // Compute the outermost boundary of the area that might be painted.
   2570  // Same coordinate space as aParams.borderArea & aParams.bgClipRect.
   2571  Sides skipSides = aParams.frame->GetSkipSides();
   2572  nsRect paintBorderArea = BoxDecorationRectForBackground(
   2573      aParams.frame, aParams.borderArea, skipSides, &aBorder);
   2574  nsRect clipBorderArea = BoxDecorationRectForBorder(
   2575      aParams.frame, aParams.borderArea, skipSides, &aBorder);
   2576 
   2577  ImgDrawResult result = ImgDrawResult::SUCCESS;
   2578  StyleGeometryBox currentBackgroundClip = StyleGeometryBox::BorderBox;
   2579  const bool drawAllLayers = (aParams.layer < 0);
   2580  uint32_t count = drawAllLayers
   2581                       ? layers.mImageCount  // iterate all image layers.
   2582                       : layers.mImageCount -
   2583                             aParams.layer;  // iterate from the bottom layer to
   2584                                             // the 'aParams.layer-th' layer.
   2585  NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT_WITH_RANGE(
   2586      i, layers, layers.mImageCount - 1, count) {
   2587    // NOTE: no Save() yet, we do that later by calling autoSR.EnsureSaved(ctx)
   2588    // in the cases we need it.
   2589    gfxContextAutoSaveRestore autoSR;
   2590    const nsStyleImageLayers::Layer& layer = layers.mLayers[i];
   2591 
   2592    ImageLayerClipState currentLayerClipState = clipState;
   2593    if (!aParams.bgClipRect) {
   2594      bool isBottomLayer = (i == layers.mImageCount - 1);
   2595      if (currentBackgroundClip != layer.mClip || isBottomLayer) {
   2596        currentBackgroundClip = layer.mClip;
   2597        if (!isBottomLayer) {
   2598          currentLayerClipState = {};
   2599          // For the bottom layer, we already called GetImageLayerClip above
   2600          // and it stored its results in clipState.
   2601          GetImageLayerClip(layer, aParams.frame, aBorder, aParams.borderArea,
   2602                            aParams.dirtyRect,
   2603                            (aParams.paintFlags & PAINTBG_WILL_PAINT_BORDER),
   2604                            appUnitsPerPixel, &currentLayerClipState);
   2605        }
   2606        SetupImageLayerClip(currentLayerClipState, &aRenderingCtx,
   2607                            appUnitsPerPixel, &autoSR);
   2608        if (!clipBorderArea.IsEqualEdges(aParams.borderArea)) {
   2609          // We're drawing the background for the joined continuation boxes
   2610          // so we need to clip that to the slice that we want for this
   2611          // frame.
   2612          gfxRect clip = nsLayoutUtils::RectToGfxRect(aParams.borderArea,
   2613                                                      appUnitsPerPixel);
   2614          autoSR.EnsureSaved(&aRenderingCtx);
   2615          aRenderingCtx.SnappedClip(clip);
   2616        }
   2617      }
   2618    }
   2619 
   2620    // Skip the following layer preparing and painting code if the current
   2621    // layer is not selected for drawing.
   2622    if (aParams.layer >= 0 && i != (uint32_t)aParams.layer) {
   2623      continue;
   2624    }
   2625    nsBackgroundLayerState state = PrepareImageLayer(
   2626        &aParams.presCtx, aParams.frame, aParams.paintFlags, paintBorderArea,
   2627        currentLayerClipState.mBGClipArea, layer, nullptr);
   2628    result &= state.mImageRenderer.PrepareResult();
   2629 
   2630    // Skip the layer painting code if we found the dirty region is empty.
   2631    if (currentLayerClipState.mDirtyRectInDevPx.IsEmpty()) {
   2632      continue;
   2633    }
   2634 
   2635    if (!state.mFillArea.IsEmpty()) {
   2636      CompositionOp co = DetermineCompositionOp(aParams, layers, i);
   2637      if (co != CompositionOp::OP_OVER) {
   2638        NS_ASSERTION(aRenderingCtx.CurrentOp() == CompositionOp::OP_OVER,
   2639                     "It is assumed the initial op is OP_OVER, when it is "
   2640                     "restored later");
   2641        aRenderingCtx.SetOp(co);
   2642      }
   2643 
   2644      result &= state.mImageRenderer.DrawLayer(
   2645          &aParams.presCtx, aRenderingCtx, state.mDestArea, state.mFillArea,
   2646          state.mAnchor + paintBorderArea.TopLeft(),
   2647          currentLayerClipState.mDirtyRectInAppUnits, state.mRepeatSize,
   2648          aParams.opacity);
   2649 
   2650      if (co != CompositionOp::OP_OVER) {
   2651        aRenderingCtx.SetOp(CompositionOp::OP_OVER);
   2652      }
   2653    }
   2654  }
   2655 
   2656  return result;
   2657 }
   2658 
   2659 ImgDrawResult
   2660 nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayerWithSC(
   2661    const PaintBGParams& aParams, mozilla::wr::DisplayListBuilder& aBuilder,
   2662    mozilla::wr::IpcResourceUpdateQueue& aResources,
   2663    const mozilla::layers::StackingContextHelper& aSc,
   2664    mozilla::layers::RenderRootStateManager* aManager, nsDisplayItem* aItem,
   2665    ComputedStyle* aBackgroundSC, const nsStyleBorder& aBorder) {
   2666  MOZ_ASSERT(!(aParams.paintFlags & PAINTBG_MASK_IMAGE));
   2667 
   2668  nscoord appUnitsPerPixel = aParams.presCtx.AppUnitsPerDevPixel();
   2669  ImageLayerClipState clipState;
   2670 
   2671  clipState.mBGClipArea = *aParams.bgClipRect;
   2672  clipState.mCustomClip = true;
   2673  clipState.mHasRoundedCorners = false;
   2674  SetupDirtyRects(clipState.mBGClipArea, aParams.dirtyRect, appUnitsPerPixel,
   2675                  &clipState.mDirtyRectInAppUnits,
   2676                  &clipState.mDirtyRectInDevPx);
   2677 
   2678  // Compute the outermost boundary of the area that might be painted.
   2679  // Same coordinate space as aParams.borderArea & aParams.bgClipRect.
   2680  Sides skipSides = aParams.frame->GetSkipSides();
   2681  nsRect paintBorderArea = BoxDecorationRectForBackground(
   2682      aParams.frame, aParams.borderArea, skipSides, &aBorder);
   2683 
   2684  const nsStyleImageLayers& layers = aBackgroundSC->StyleBackground()->mImage;
   2685  const nsStyleImageLayers::Layer& layer = layers.mLayers[aParams.layer];
   2686 
   2687  // Skip the following layer painting code if we found the dirty region is
   2688  // empty or the current layer is not selected for drawing.
   2689  if (clipState.mDirtyRectInDevPx.IsEmpty()) {
   2690    return ImgDrawResult::SUCCESS;
   2691  }
   2692 
   2693  ImgDrawResult result = ImgDrawResult::SUCCESS;
   2694  nsBackgroundLayerState state =
   2695      PrepareImageLayer(&aParams.presCtx, aParams.frame, aParams.paintFlags,
   2696                        paintBorderArea, clipState.mBGClipArea, layer, nullptr);
   2697  result &= state.mImageRenderer.PrepareResult();
   2698 
   2699  if (!state.mFillArea.IsEmpty()) {
   2700    result &= state.mImageRenderer.BuildWebRenderDisplayItemsForLayer(
   2701        &aParams.presCtx, aBuilder, aResources, aSc, aManager, aItem,
   2702        state.mDestArea, state.mFillArea,
   2703        state.mAnchor + paintBorderArea.TopLeft(),
   2704        clipState.mDirtyRectInAppUnits, state.mRepeatSize, aParams.opacity);
   2705  }
   2706 
   2707  return result;
   2708 }
   2709 
   2710 nsRect nsCSSRendering::ComputeImageLayerPositioningArea(
   2711    nsPresContext* aPresContext, nsIFrame* aForFrame, const nsRect& aBorderArea,
   2712    const nsStyleImageLayers::Layer& aLayer, nsIFrame** aAttachedToFrame,
   2713    bool* aOutIsTransformedFixed) {
   2714  // Compute {background|mask} origin area relative to aBorderArea now as we
   2715  // may need  it to compute the effective image size for a CSS gradient.
   2716  nsRect positionArea;
   2717 
   2718  StyleGeometryBox layerOrigin =
   2719      ComputeBoxValueForOrigin(aForFrame, aLayer.mOrigin);
   2720 
   2721  if (IsSVGStyleGeometryBox(layerOrigin)) {
   2722    MOZ_ASSERT(aForFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT));
   2723    *aAttachedToFrame = aForFrame;
   2724 
   2725    positionArea =
   2726        nsLayoutUtils::ComputeSVGReferenceRect(aForFrame, layerOrigin);
   2727 
   2728    nsPoint toStrokeBoxOffset = nsPoint(0, 0);
   2729    if (layerOrigin != StyleGeometryBox::StrokeBox) {
   2730      nsRect strokeBox = nsLayoutUtils::ComputeSVGReferenceRect(
   2731          aForFrame, StyleGeometryBox::StrokeBox);
   2732      toStrokeBoxOffset = positionArea.TopLeft() - strokeBox.TopLeft();
   2733    }
   2734 
   2735    // For SVG frames, the return value is relative to the stroke box
   2736    return nsRect(toStrokeBoxOffset, positionArea.Size());
   2737  }
   2738 
   2739  MOZ_ASSERT(!aForFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT));
   2740 
   2741  LayoutFrameType frameType = aForFrame->Type();
   2742  nsIFrame* geometryFrame = aForFrame;
   2743  if (MOZ_UNLIKELY(frameType == LayoutFrameType::ScrollContainer &&
   2744                   StyleImageLayerAttachment::Local == aLayer.mAttachment)) {
   2745    ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(aForFrame);
   2746    positionArea =
   2747        nsRect(scrollContainerFrame->GetScrolledFrame()->GetPosition()
   2748                   // For the dir=rtl case:
   2749                   + scrollContainerFrame->GetScrollRange().TopLeft(),
   2750               scrollContainerFrame->GetScrolledRect().Size());
   2751    // The ScrolledRect’s size does not include the borders or scrollbars,
   2752    // reverse the handling of background-origin
   2753    // compared to the common case below.
   2754    if (layerOrigin == StyleGeometryBox::BorderBox) {
   2755      nsMargin border = geometryFrame->GetUsedBorder();
   2756      border.ApplySkipSides(geometryFrame->GetSkipSides());
   2757      positionArea.Inflate(border);
   2758      positionArea.Inflate(scrollContainerFrame->GetActualScrollbarSizes());
   2759    } else if (layerOrigin != StyleGeometryBox::PaddingBox) {
   2760      nsMargin padding = geometryFrame->GetUsedPadding();
   2761      padding.ApplySkipSides(geometryFrame->GetSkipSides());
   2762      positionArea.Deflate(padding);
   2763      NS_ASSERTION(layerOrigin == StyleGeometryBox::ContentBox,
   2764                   "unknown background-origin value");
   2765    }
   2766    *aAttachedToFrame = aForFrame;
   2767    return positionArea;
   2768  }
   2769 
   2770  if (MOZ_UNLIKELY(frameType == LayoutFrameType::Canvas)) {
   2771    geometryFrame = aForFrame->PrincipalChildList().FirstChild();
   2772    // geometryFrame might be null if this canvas is a page created
   2773    // as an overflow container (e.g. the in-flow content has already
   2774    // finished and this page only displays the continuations of
   2775    // absolutely positioned content).
   2776    if (geometryFrame) {
   2777      positionArea =
   2778          nsPlaceholderFrame::GetRealFrameFor(geometryFrame)->GetRect();
   2779    }
   2780  } else {
   2781    positionArea = nsRect(nsPoint(0, 0), aBorderArea.Size());
   2782  }
   2783 
   2784  // See the comment of StyleGeometryBox::MarginBox.
   2785  // Hitting this assertion means we decide to turn on margin-box support for
   2786  // positioned mask from CSS parser and style system. In this case, you
   2787  // should *inflate* positionArea by the margin returning from
   2788  // geometryFrame->GetUsedMargin() in the code chunk bellow.
   2789  MOZ_ASSERT(aLayer.mOrigin != StyleGeometryBox::MarginBox,
   2790             "StyleGeometryBox::MarginBox rendering is not supported yet.\n");
   2791 
   2792  // {background|mask} images are tiled over the '{background|mask}-clip' area
   2793  // but the origin of the tiling is based on the '{background|mask}-origin'
   2794  // area.
   2795  if (layerOrigin != StyleGeometryBox::BorderBox && geometryFrame) {
   2796    nsMargin border = geometryFrame->GetUsedBorder();
   2797    if (layerOrigin != StyleGeometryBox::PaddingBox) {
   2798      border += geometryFrame->GetUsedPadding();
   2799      NS_ASSERTION(layerOrigin == StyleGeometryBox::ContentBox,
   2800                   "unknown background-origin value");
   2801    }
   2802    positionArea.Deflate(border);
   2803  }
   2804 
   2805  nsIFrame* attachedToFrame = aForFrame;
   2806  if (StyleImageLayerAttachment::Fixed == aLayer.mAttachment) {
   2807    // If it's a fixed background attachment, then the image is placed
   2808    // relative to the viewport, which is the area of the root frame
   2809    // in a screen context or the page content frame in a print context.
   2810    attachedToFrame = aPresContext->PresShell()->GetRootFrame();
   2811    NS_ASSERTION(attachedToFrame, "no root frame");
   2812    nsIFrame* pageContentFrame = nullptr;
   2813    if (aPresContext->IsPaginated()) {
   2814      pageContentFrame = nsLayoutUtils::GetClosestFrameOfType(
   2815          aForFrame, LayoutFrameType::PageContent);
   2816      if (pageContentFrame) {
   2817        attachedToFrame = pageContentFrame;
   2818      }
   2819      // else this is an embedded shell and its root frame is what we want
   2820    }
   2821 
   2822    // If the background is affected by a transform, treat is as if it
   2823    // wasn't fixed.
   2824    if (nsLayoutUtils::IsTransformed(aForFrame, attachedToFrame)) {
   2825      attachedToFrame = aForFrame;
   2826      *aOutIsTransformedFixed = true;
   2827    } else {
   2828      // Set the background positioning area to the viewport's area
   2829      // (relative to aForFrame)
   2830      positionArea = nsRect(-aForFrame->GetOffsetTo(attachedToFrame),
   2831                            attachedToFrame->GetSize());
   2832 
   2833      if (!pageContentFrame) {
   2834        // Subtract the size of scrollbars.
   2835        if (ScrollContainerFrame* sf =
   2836                aPresContext->PresShell()->GetRootScrollContainerFrame()) {
   2837          nsMargin scrollbars = sf->GetActualScrollbarSizes();
   2838          positionArea.Deflate(scrollbars);
   2839        }
   2840      }
   2841 
   2842      // If we have the dynamic toolbar, we need to expand the image area to
   2843      // include the region under the dynamic toolbar, otherwise we will see a
   2844      // blank space under the toolbar.
   2845      if (aPresContext->IsRootContentDocumentCrossProcess() &&
   2846          aPresContext->HasDynamicToolbar()) {
   2847        positionArea.SizeTo(nsLayoutUtils::ExpandHeightForDynamicToolbar(
   2848            aPresContext, positionArea.Size()));
   2849      }
   2850    }
   2851  }
   2852  *aAttachedToFrame = attachedToFrame;
   2853 
   2854  return positionArea;
   2855 }
   2856 
   2857 /* static */
   2858 nscoord nsCSSRendering::ComputeRoundedSize(nscoord aCurrentSize,
   2859                                           nscoord aPositioningSize) {
   2860  float repeatCount = NS_roundf(float(aPositioningSize) / float(aCurrentSize));
   2861  if (repeatCount < 1.0f) {
   2862    return aPositioningSize;
   2863  }
   2864  return nscoord(NS_lround(float(aPositioningSize) / repeatCount));
   2865 }
   2866 
   2867 // Apply the CSS image sizing algorithm as it applies to background images.
   2868 // See http://www.w3.org/TR/css3-background/#the-background-size .
   2869 // aIntrinsicSize is the size that the background image 'would like to be'.
   2870 // It can be found by calling nsImageRenderer::ComputeIntrinsicSize.
   2871 static nsSize ComputeDrawnSizeForBackground(
   2872    const CSSSizeOrRatio& aIntrinsicSize, const nsSize& aBgPositioningArea,
   2873    const StyleBackgroundSize& aLayerSize, StyleImageLayerRepeat aXRepeat,
   2874    StyleImageLayerRepeat aYRepeat) {
   2875  nsSize imageSize;
   2876 
   2877  // Size is dictated by cover or contain rules.
   2878  if (aLayerSize.IsContain() || aLayerSize.IsCover()) {
   2879    nsImageRenderer::FitType fitType = aLayerSize.IsCover()
   2880                                           ? nsImageRenderer::COVER
   2881                                           : nsImageRenderer::CONTAIN;
   2882    imageSize = nsImageRenderer::ComputeConstrainedSize(
   2883        aBgPositioningArea, aIntrinsicSize.mRatio, fitType);
   2884  } else {
   2885    MOZ_ASSERT(aLayerSize.IsExplicitSize());
   2886    const auto& width = aLayerSize.explicit_size.width;
   2887    const auto& height = aLayerSize.explicit_size.height;
   2888    // No cover/contain constraint, use default algorithm.
   2889    CSSSizeOrRatio specifiedSize;
   2890    if (width.IsLengthPercentage()) {
   2891      specifiedSize.SetWidth(
   2892          width.AsLengthPercentage().Resolve(aBgPositioningArea.width));
   2893    }
   2894    if (height.IsLengthPercentage()) {
   2895      specifiedSize.SetHeight(
   2896          height.AsLengthPercentage().Resolve(aBgPositioningArea.height));
   2897    }
   2898 
   2899    imageSize = nsImageRenderer::ComputeConcreteSize(
   2900        specifiedSize, aIntrinsicSize, aBgPositioningArea);
   2901  }
   2902 
   2903  // See https://www.w3.org/TR/css3-background/#background-size .
   2904  // "If 'background-repeat' is 'round' for one (or both) dimensions, there is a
   2905  // second
   2906  //  step. The UA must scale the image in that dimension (or both dimensions)
   2907  //  so that it fits a whole number of times in the background positioning
   2908  //  area."
   2909  // "If 'background-repeat' is 'round' for one dimension only and if
   2910  // 'background-size'
   2911  //  is 'auto' for the other dimension, then there is a third step: that other
   2912  //  dimension is scaled so that the original aspect ratio is restored."
   2913  bool isRepeatRoundInBothDimensions =
   2914      aXRepeat == StyleImageLayerRepeat::Round &&
   2915      aYRepeat == StyleImageLayerRepeat::Round;
   2916 
   2917  // Calculate the rounded size only if the background-size computation
   2918  // returned a correct size for the image.
   2919  if (imageSize.width && aXRepeat == StyleImageLayerRepeat::Round) {
   2920    imageSize.width = nsCSSRendering::ComputeRoundedSize(
   2921        imageSize.width, aBgPositioningArea.width);
   2922    if (!isRepeatRoundInBothDimensions && aLayerSize.IsExplicitSize() &&
   2923        aLayerSize.explicit_size.height.IsAuto()) {
   2924      // Restore intrinsic ratio
   2925      if (aIntrinsicSize.mRatio) {
   2926        imageSize.height =
   2927            aIntrinsicSize.mRatio.Inverted().ApplyTo(imageSize.width);
   2928      }
   2929    }
   2930  }
   2931 
   2932  // Calculate the rounded size only if the background-size computation
   2933  // returned a correct size for the image.
   2934  if (imageSize.height && aYRepeat == StyleImageLayerRepeat::Round) {
   2935    imageSize.height = nsCSSRendering::ComputeRoundedSize(
   2936        imageSize.height, aBgPositioningArea.height);
   2937    if (!isRepeatRoundInBothDimensions && aLayerSize.IsExplicitSize() &&
   2938        aLayerSize.explicit_size.width.IsAuto()) {
   2939      // Restore intrinsic ratio
   2940      if (aIntrinsicSize.mRatio) {
   2941        imageSize.width = aIntrinsicSize.mRatio.ApplyTo(imageSize.height);
   2942      }
   2943    }
   2944  }
   2945 
   2946  return imageSize;
   2947 }
   2948 
   2949 /* ComputeSpacedRepeatSize
   2950 * aImageDimension: the image width/height
   2951 * aAvailableSpace: the background positioning area width/height
   2952 * aRepeat: determine whether the image is repeated
   2953 * Returns the image size plus gap size of app units for use as spacing
   2954 */
   2955 static nscoord ComputeSpacedRepeatSize(nscoord aImageDimension,
   2956                                       nscoord aAvailableSpace, bool& aRepeat) {
   2957  float ratio = static_cast<float>(aAvailableSpace) / aImageDimension;
   2958 
   2959  if (ratio < 2.0f) {  // If you can't repeat at least twice, then don't repeat.
   2960    aRepeat = false;
   2961    return aImageDimension;
   2962  }
   2963 
   2964  aRepeat = true;
   2965  return (aAvailableSpace - aImageDimension) / (NSToIntFloor(ratio) - 1);
   2966 }
   2967 
   2968 /* static */
   2969 nscoord nsCSSRendering::ComputeBorderSpacedRepeatSize(nscoord aImageDimension,
   2970                                                      nscoord aAvailableSpace,
   2971                                                      nscoord& aSpace) {
   2972  int32_t count = aImageDimension ? (aAvailableSpace / aImageDimension) : 0;
   2973  aSpace = (aAvailableSpace - aImageDimension * count) / (count + 1);
   2974  return aSpace + aImageDimension;
   2975 }
   2976 
   2977 nsBackgroundLayerState nsCSSRendering::PrepareImageLayer(
   2978    nsPresContext* aPresContext, nsIFrame* aForFrame, uint32_t aFlags,
   2979    const nsRect& aBorderArea, const nsRect& aBGClipRect,
   2980    const nsStyleImageLayers::Layer& aLayer, bool* aOutIsTransformedFixed) {
   2981  /*
   2982   * The properties we need to keep in mind when drawing style image
   2983   * layers are:
   2984   *
   2985   *   background-image/ mask-image
   2986   *   background-repeat/ mask-repeat
   2987   *   background-attachment
   2988   *   background-position/ mask-position
   2989   *   background-clip/ mask-clip
   2990   *   background-origin/ mask-origin
   2991   *   background-size/ mask-size
   2992   *   background-blend-mode
   2993   *   box-decoration-break
   2994   *   mask-mode
   2995   *   mask-composite
   2996   *
   2997   * (background-color applies to the entire element and not to individual
   2998   * layers, so it is irrelevant to this method.)
   2999   *
   3000   * These properties have the following dependencies upon each other when
   3001   * determining rendering:
   3002   *
   3003   *   background-image/ mask-image
   3004   *     no dependencies
   3005   *   background-repeat/ mask-repeat
   3006   *     no dependencies
   3007   *   background-attachment
   3008   *     no dependencies
   3009   *   background-position/ mask-position
   3010   *     depends upon background-size/mask-size (for the image's scaled size)
   3011   *     and background-break (for the background positioning area)
   3012   *   background-clip/ mask-clip
   3013   *     no dependencies
   3014   *   background-origin/ mask-origin
   3015   *     depends upon background-attachment (only in the case where that value
   3016   *     is 'fixed')
   3017   *   background-size/ mask-size
   3018   *     depends upon box-decoration-break (for the background positioning area
   3019   *     for resolving percentages), background-image (for the image's intrinsic
   3020   *     size), background-repeat (if that value is 'round'), and
   3021   *     background-origin (for the background painting area, when
   3022   *     background-repeat is 'round')
   3023   *   background-blend-mode
   3024   *     no dependencies
   3025   *   mask-mode
   3026   *     no dependencies
   3027   *   mask-composite
   3028   *     no dependencies
   3029   *   box-decoration-break
   3030   *     no dependencies
   3031   *
   3032   * As a result of only-if dependencies we don't strictly do a topological
   3033   * sort of the above properties when processing, but it's pretty close to one:
   3034   *
   3035   *   background-clip/mask-clip (by caller)
   3036   *   background-image/ mask-image
   3037   *   box-decoration-break, background-origin/ mask origin
   3038   *   background-attachment (postfix for background-origin if 'fixed')
   3039   *   background-size/ mask-size
   3040   *   background-position/ mask-position
   3041   *   background-repeat/ mask-repeat
   3042   */
   3043 
   3044  uint32_t irFlags = 0;
   3045  if (aFlags & nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES) {
   3046    irFlags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
   3047  }
   3048  if (aFlags & nsCSSRendering::PAINTBG_TO_WINDOW) {
   3049    irFlags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
   3050  }
   3051  if (aFlags & nsCSSRendering::PAINTBG_HIGH_QUALITY_SCALING) {
   3052    irFlags |= nsImageRenderer::FLAG_HIGH_QUALITY_SCALING;
   3053  }
   3054  // Only do partial bg image drawing in content documents: non-content
   3055  // documents are viewed as UI of the browser and a partial draw of a bg image
   3056  // might look weird in that context.
   3057  if (XRE_IsContentProcess() && !aPresContext->IsChrome()) {
   3058    irFlags |= nsImageRenderer::FLAG_DRAW_PARTIAL_FRAMES;
   3059  }
   3060 
   3061  nsBackgroundLayerState state(aForFrame, &aLayer.mImage, irFlags);
   3062  if (!state.mImageRenderer.PrepareImage()) {
   3063    // There's no image or it's not ready to be painted.
   3064    if (aOutIsTransformedFixed &&
   3065        StyleImageLayerAttachment::Fixed == aLayer.mAttachment) {
   3066      nsIFrame* attachedToFrame = aPresContext->PresShell()->GetRootFrame();
   3067      NS_ASSERTION(attachedToFrame, "no root frame");
   3068      nsIFrame* pageContentFrame = nullptr;
   3069      if (aPresContext->IsPaginated()) {
   3070        pageContentFrame = nsLayoutUtils::GetClosestFrameOfType(
   3071            aForFrame, LayoutFrameType::PageContent);
   3072        if (pageContentFrame) {
   3073          attachedToFrame = pageContentFrame;
   3074        }
   3075        // else this is an embedded shell and its root frame is what we want
   3076      }
   3077 
   3078      *aOutIsTransformedFixed =
   3079          nsLayoutUtils::IsTransformed(aForFrame, attachedToFrame);
   3080    }
   3081    return state;
   3082  }
   3083 
   3084  // The frame to which the background is attached
   3085  nsIFrame* attachedToFrame = aForFrame;
   3086  // Is the background marked 'fixed', but affected by a transform?
   3087  bool transformedFixed = false;
   3088  // Compute background origin area relative to aBorderArea now as we may need
   3089  // it to compute the effective image size for a CSS gradient.
   3090  nsRect positionArea = ComputeImageLayerPositioningArea(
   3091      aPresContext, aForFrame, aBorderArea, aLayer, &attachedToFrame,
   3092      &transformedFixed);
   3093  if (aOutIsTransformedFixed) {
   3094    *aOutIsTransformedFixed = transformedFixed;
   3095  }
   3096 
   3097  // For background-attachment:fixed backgrounds, we'll override the area
   3098  // where the background can be drawn to the viewport.
   3099  nsRect bgClipRect = aBGClipRect;
   3100 
   3101  if (StyleImageLayerAttachment::Fixed == aLayer.mAttachment &&
   3102      !transformedFixed && (aFlags & nsCSSRendering::PAINTBG_TO_WINDOW)) {
   3103    bgClipRect = positionArea + aBorderArea.TopLeft();
   3104  }
   3105 
   3106  StyleImageLayerRepeat repeatX = aLayer.mRepeat.mXRepeat;
   3107  StyleImageLayerRepeat repeatY = aLayer.mRepeat.mYRepeat;
   3108 
   3109  // Scale the image as specified for background-size and background-repeat.
   3110  // Also as required for proper background positioning when background-position
   3111  // is defined with percentages.
   3112  CSSSizeOrRatio intrinsicSize = state.mImageRenderer.ComputeIntrinsicSize();
   3113  nsSize bgPositionSize = positionArea.Size();
   3114  nsSize imageSize = ComputeDrawnSizeForBackground(
   3115      intrinsicSize, bgPositionSize, aLayer.mSize, repeatX, repeatY);
   3116 
   3117  if (imageSize.width <= 0 || imageSize.height <= 0) {
   3118    return state;
   3119  }
   3120 
   3121  state.mImageRenderer.SetPreferredSize(intrinsicSize, imageSize);
   3122 
   3123  // Compute the anchor point.
   3124  //
   3125  // relative to aBorderArea.TopLeft() (which is where the top-left
   3126  // of aForFrame's border-box will be rendered)
   3127  nsPoint imageTopLeft;
   3128 
   3129  // Compute the position of the background now that the background's size is
   3130  // determined.
   3131  nsImageRenderer::ComputeObjectAnchorPoint(aLayer.mPosition, bgPositionSize,
   3132                                            imageSize, &imageTopLeft,
   3133                                            &state.mAnchor);
   3134  state.mRepeatSize = imageSize;
   3135  if (repeatX == StyleImageLayerRepeat::Space) {
   3136    bool isRepeat;
   3137    state.mRepeatSize.width = ComputeSpacedRepeatSize(
   3138        imageSize.width, bgPositionSize.width, isRepeat);
   3139    if (isRepeat) {
   3140      imageTopLeft.x = 0;
   3141      state.mAnchor.x = 0;
   3142    } else {
   3143      repeatX = StyleImageLayerRepeat::NoRepeat;
   3144    }
   3145  }
   3146 
   3147  if (repeatY == StyleImageLayerRepeat::Space) {
   3148    bool isRepeat;
   3149    state.mRepeatSize.height = ComputeSpacedRepeatSize(
   3150        imageSize.height, bgPositionSize.height, isRepeat);
   3151    if (isRepeat) {
   3152      imageTopLeft.y = 0;
   3153      state.mAnchor.y = 0;
   3154    } else {
   3155      repeatY = StyleImageLayerRepeat::NoRepeat;
   3156    }
   3157  }
   3158 
   3159  imageTopLeft += positionArea.TopLeft();
   3160  state.mAnchor += positionArea.TopLeft();
   3161  state.mDestArea = nsRect(imageTopLeft + aBorderArea.TopLeft(), imageSize);
   3162  state.mFillArea = state.mDestArea;
   3163 
   3164  ExtendMode repeatMode = ExtendMode::CLAMP;
   3165  if (repeatX == StyleImageLayerRepeat::Repeat ||
   3166      repeatX == StyleImageLayerRepeat::Round ||
   3167      repeatX == StyleImageLayerRepeat::Space) {
   3168    state.mFillArea.x = bgClipRect.x;
   3169    state.mFillArea.width = bgClipRect.width;
   3170    repeatMode = ExtendMode::REPEAT_X;
   3171  }
   3172  if (repeatY == StyleImageLayerRepeat::Repeat ||
   3173      repeatY == StyleImageLayerRepeat::Round ||
   3174      repeatY == StyleImageLayerRepeat::Space) {
   3175    state.mFillArea.y = bgClipRect.y;
   3176    state.mFillArea.height = bgClipRect.height;
   3177 
   3178    /***
   3179     * We're repeating on the X axis already,
   3180     * so if we have to repeat in the Y axis,
   3181     * we really need to repeat in both directions.
   3182     */
   3183    if (repeatMode == ExtendMode::REPEAT_X) {
   3184      repeatMode = ExtendMode::REPEAT;
   3185    } else {
   3186      repeatMode = ExtendMode::REPEAT_Y;
   3187    }
   3188  }
   3189  state.mImageRenderer.SetExtendMode(repeatMode);
   3190  state.mImageRenderer.SetMaskOp(aLayer.mMaskMode);
   3191 
   3192  state.mFillArea.IntersectRect(state.mFillArea, bgClipRect);
   3193 
   3194  return state;
   3195 }
   3196 
   3197 nsRect nsCSSRendering::GetBackgroundLayerRect(
   3198    nsPresContext* aPresContext, nsIFrame* aForFrame, const nsRect& aBorderArea,
   3199    const nsRect& aClipRect, const nsStyleImageLayers::Layer& aLayer,
   3200    uint32_t aFlags) {
   3201  Sides skipSides = aForFrame->GetSkipSides();
   3202  nsRect borderArea =
   3203      BoxDecorationRectForBackground(aForFrame, aBorderArea, skipSides);
   3204  nsBackgroundLayerState state = PrepareImageLayer(
   3205      aPresContext, aForFrame, aFlags, borderArea, aClipRect, aLayer);
   3206  return state.mFillArea;
   3207 }
   3208 
   3209 // Begin table border-collapsing section
   3210 // These functions were written to not disrupt the normal ones and yet satisfy
   3211 // some additional requirements At some point, all functions should be unified
   3212 // to include the additional functionality that these provide
   3213 
   3214 static nscoord RoundIntToPixel(nscoord aValue, nscoord aOneDevPixel,
   3215                               bool aRoundDown = false) {
   3216  if (aOneDevPixel <= 0) {
   3217    // We must be rendering to a device that has a resolution greater than
   3218    // one device pixel!
   3219    // In that case, aValue is as accurate as it's going to get.
   3220    return aValue;
   3221  }
   3222 
   3223  nscoord halfPixel = NSToCoordRound(aOneDevPixel / 2.0f);
   3224  nscoord extra = aValue % aOneDevPixel;
   3225  nscoord finalValue = (!aRoundDown && (extra >= halfPixel))
   3226                           ? aValue + (aOneDevPixel - extra)
   3227                           : aValue - extra;
   3228  return finalValue;
   3229 }
   3230 
   3231 static nscoord RoundFloatToPixel(float aValue, nscoord aOneDevPixel,
   3232                                 bool aRoundDown = false) {
   3233  return RoundIntToPixel(NSToCoordRound(aValue), aOneDevPixel, aRoundDown);
   3234 }
   3235 
   3236 static void SetPoly(const Rect& aRect, Point* poly) {
   3237  poly[0].x = aRect.x;
   3238  poly[0].y = aRect.y;
   3239  poly[1].x = aRect.x + aRect.width;
   3240  poly[1].y = aRect.y;
   3241  poly[2].x = aRect.x + aRect.width;
   3242  poly[2].y = aRect.y + aRect.height;
   3243  poly[3].x = aRect.x;
   3244  poly[3].y = aRect.y + aRect.height;
   3245 }
   3246 
   3247 static void DrawDashedSegment(DrawTarget& aDrawTarget, nsRect aRect,
   3248                              nscoord aDashLength, nscolor aColor,
   3249                              int32_t aAppUnitsPerDevPixel, bool aHorizontal) {
   3250  ColorPattern color(ToDeviceColor(aColor));
   3251  DrawOptions drawOptions(1.f, CompositionOp::OP_OVER, AntialiasMode::NONE);
   3252  StrokeOptions strokeOptions;
   3253 
   3254  Float dash[2];
   3255  dash[0] = Float(aDashLength) / aAppUnitsPerDevPixel;
   3256  dash[1] = dash[0];
   3257 
   3258  strokeOptions.mDashPattern = dash;
   3259  strokeOptions.mDashLength = std::size(dash);
   3260 
   3261  if (aHorizontal) {
   3262    nsPoint left = (aRect.TopLeft() + aRect.BottomLeft()) / 2;
   3263    nsPoint right = (aRect.TopRight() + aRect.BottomRight()) / 2;
   3264    strokeOptions.mLineWidth = Float(aRect.height) / aAppUnitsPerDevPixel;
   3265    StrokeLineWithSnapping(left, right, aAppUnitsPerDevPixel, aDrawTarget,
   3266                           color, strokeOptions, drawOptions);
   3267  } else {
   3268    nsPoint top = (aRect.TopLeft() + aRect.TopRight()) / 2;
   3269    nsPoint bottom = (aRect.BottomLeft() + aRect.BottomRight()) / 2;
   3270    strokeOptions.mLineWidth = Float(aRect.width) / aAppUnitsPerDevPixel;
   3271    StrokeLineWithSnapping(top, bottom, aAppUnitsPerDevPixel, aDrawTarget,
   3272                           color, strokeOptions, drawOptions);
   3273  }
   3274 }
   3275 
   3276 static void DrawSolidBorderSegment(
   3277    DrawTarget& aDrawTarget, nsRect aRect, nscolor aColor,
   3278    int32_t aAppUnitsPerDevPixel,
   3279    mozilla::Side aStartBevelSide = mozilla::eSideTop,
   3280    nscoord aStartBevelOffset = 0,
   3281    mozilla::Side aEndBevelSide = mozilla::eSideTop,
   3282    nscoord aEndBevelOffset = 0) {
   3283  ColorPattern color(ToDeviceColor(aColor));
   3284  DrawOptions drawOptions(1.f, CompositionOp::OP_OVER, AntialiasMode::NONE);
   3285 
   3286  nscoord oneDevPixel = NSIntPixelsToAppUnits(1, aAppUnitsPerDevPixel);
   3287  // We don't need to bevel single pixel borders
   3288  if ((aRect.width == oneDevPixel) || (aRect.height == oneDevPixel) ||
   3289      ((0 == aStartBevelOffset) && (0 == aEndBevelOffset))) {
   3290    // simple rectangle
   3291    aDrawTarget.FillRect(
   3292        NSRectToSnappedRect(aRect, aAppUnitsPerDevPixel, aDrawTarget), color,
   3293        drawOptions);
   3294  } else {
   3295    // polygon with beveling
   3296    Point poly[4];
   3297    SetPoly(NSRectToSnappedRect(aRect, aAppUnitsPerDevPixel, aDrawTarget),
   3298            poly);
   3299 
   3300    Float startBevelOffset =
   3301        NSAppUnitsToFloatPixels(aStartBevelOffset, aAppUnitsPerDevPixel);
   3302    switch (aStartBevelSide) {
   3303      case eSideTop:
   3304        poly[0].x += startBevelOffset;
   3305        break;
   3306      case eSideBottom:
   3307        poly[3].x += startBevelOffset;
   3308        break;
   3309      case eSideRight:
   3310        poly[1].y += startBevelOffset;
   3311        break;
   3312      case eSideLeft:
   3313        poly[0].y += startBevelOffset;
   3314    }
   3315 
   3316    Float endBevelOffset =
   3317        NSAppUnitsToFloatPixels(aEndBevelOffset, aAppUnitsPerDevPixel);
   3318    switch (aEndBevelSide) {
   3319      case eSideTop:
   3320        poly[1].x -= endBevelOffset;
   3321        break;
   3322      case eSideBottom:
   3323        poly[2].x -= endBevelOffset;
   3324        break;
   3325      case eSideRight:
   3326        poly[2].y -= endBevelOffset;
   3327        break;
   3328      case eSideLeft:
   3329        poly[3].y -= endBevelOffset;
   3330    }
   3331 
   3332    RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
   3333    builder->MoveTo(poly[0]);
   3334    builder->LineTo(poly[1]);
   3335    builder->LineTo(poly[2]);
   3336    builder->LineTo(poly[3]);
   3337    builder->Close();
   3338    RefPtr<Path> path = builder->Finish();
   3339    aDrawTarget.Fill(path, color, drawOptions);
   3340  }
   3341 }
   3342 
   3343 static void GetDashInfo(nscoord aBorderLength, nscoord aDashLength,
   3344                        nscoord aOneDevPixel, int32_t& aNumDashSpaces,
   3345                        nscoord& aStartDashLength, nscoord& aEndDashLength) {
   3346  aNumDashSpaces = 0;
   3347  if (aStartDashLength + aDashLength + aEndDashLength >= aBorderLength) {
   3348    aStartDashLength = aBorderLength;
   3349    aEndDashLength = 0;
   3350  } else {
   3351    aNumDashSpaces =
   3352        (aBorderLength - aDashLength) / (2 * aDashLength);  // round down
   3353    nscoord extra = aBorderLength - aStartDashLength - aEndDashLength -
   3354                    (((2 * aNumDashSpaces) - 1) * aDashLength);
   3355    if (extra > 0) {
   3356      nscoord half = RoundIntToPixel(extra / 2, aOneDevPixel);
   3357      aStartDashLength += half;
   3358      aEndDashLength += (extra - half);
   3359    }
   3360  }
   3361 }
   3362 
   3363 void nsCSSRendering::DrawTableBorderSegment(
   3364    DrawTarget& aDrawTarget, StyleBorderStyle aBorderStyle,
   3365    nscolor aBorderColor, const nsRect& aBorder, int32_t aAppUnitsPerDevPixel,
   3366    mozilla::Side aStartBevelSide, nscoord aStartBevelOffset,
   3367    mozilla::Side aEndBevelSide, nscoord aEndBevelOffset) {
   3368  bool horizontal =
   3369      ((eSideTop == aStartBevelSide) || (eSideBottom == aStartBevelSide));
   3370  nscoord oneDevPixel = NSIntPixelsToAppUnits(1, aAppUnitsPerDevPixel);
   3371 
   3372  if ((oneDevPixel >= aBorder.width) || (oneDevPixel >= aBorder.height) ||
   3373      (StyleBorderStyle::Dashed == aBorderStyle) ||
   3374      (StyleBorderStyle::Dotted == aBorderStyle)) {
   3375    // no beveling for 1 pixel border, dash or dot
   3376    aStartBevelOffset = 0;
   3377    aEndBevelOffset = 0;
   3378  }
   3379 
   3380  switch (aBorderStyle) {
   3381    case StyleBorderStyle::None:
   3382    case StyleBorderStyle::Hidden:
   3383      // NS_ASSERTION(false, "style of none or hidden");
   3384      break;
   3385    case StyleBorderStyle::Dotted:
   3386    case StyleBorderStyle::Dashed: {
   3387      nscoord dashLength =
   3388          (StyleBorderStyle::Dashed == aBorderStyle) ? DASH_LENGTH : DOT_LENGTH;
   3389      // make the dash length proportional to the border thickness
   3390      dashLength *= (horizontal) ? aBorder.height : aBorder.width;
   3391      // make the min dash length for the ends 1/2 the dash length
   3392      nscoord minDashLength =
   3393          (StyleBorderStyle::Dashed == aBorderStyle)
   3394              ? RoundFloatToPixel(((float)dashLength) / 2.0f,
   3395                                  aAppUnitsPerDevPixel)
   3396              : dashLength;
   3397      minDashLength = std::max(minDashLength, oneDevPixel);
   3398      nscoord numDashSpaces = 0;
   3399      nscoord startDashLength = minDashLength;
   3400      nscoord endDashLength = minDashLength;
   3401      if (horizontal) {
   3402        GetDashInfo(aBorder.width, dashLength, aAppUnitsPerDevPixel,
   3403                    numDashSpaces, startDashLength, endDashLength);
   3404        nsRect rect(aBorder.x, aBorder.y, startDashLength, aBorder.height);
   3405        DrawSolidBorderSegment(aDrawTarget, rect, aBorderColor,
   3406                               aAppUnitsPerDevPixel);
   3407 
   3408        rect.x += startDashLength + dashLength;
   3409        rect.width =
   3410            aBorder.width - (startDashLength + endDashLength + dashLength);
   3411        DrawDashedSegment(aDrawTarget, rect, dashLength, aBorderColor,
   3412                          aAppUnitsPerDevPixel, horizontal);
   3413 
   3414        rect.x += rect.width;
   3415        rect.width = endDashLength;
   3416        DrawSolidBorderSegment(aDrawTarget, rect, aBorderColor,
   3417                               aAppUnitsPerDevPixel);
   3418      } else {
   3419        GetDashInfo(aBorder.height, dashLength, aAppUnitsPerDevPixel,
   3420                    numDashSpaces, startDashLength, endDashLength);
   3421        nsRect rect(aBorder.x, aBorder.y, aBorder.width, startDashLength);
   3422        DrawSolidBorderSegment(aDrawTarget, rect, aBorderColor,
   3423                               aAppUnitsPerDevPixel);
   3424 
   3425        rect.y += rect.height + dashLength;
   3426        rect.height =
   3427            aBorder.height - (startDashLength + endDashLength + dashLength);
   3428        DrawDashedSegment(aDrawTarget, rect, dashLength, aBorderColor,
   3429                          aAppUnitsPerDevPixel, horizontal);
   3430 
   3431        rect.y += rect.height;
   3432        rect.height = endDashLength;
   3433        DrawSolidBorderSegment(aDrawTarget, rect, aBorderColor,
   3434                               aAppUnitsPerDevPixel);
   3435      }
   3436    } break;
   3437    default:
   3438      AutoTArray<SolidBeveledBorderSegment, 3> segments;
   3439      GetTableBorderSolidSegments(
   3440          segments, aBorderStyle, aBorderColor, aBorder, aAppUnitsPerDevPixel,
   3441          aStartBevelSide, aStartBevelOffset, aEndBevelSide, aEndBevelOffset);
   3442      for (const auto& segment : segments) {
   3443        DrawSolidBorderSegment(
   3444            aDrawTarget, segment.mRect, segment.mColor, aAppUnitsPerDevPixel,
   3445            segment.mStartBevel.mSide, segment.mStartBevel.mOffset,
   3446            segment.mEndBevel.mSide, segment.mEndBevel.mOffset);
   3447      }
   3448      break;
   3449  }
   3450 }
   3451 
   3452 void nsCSSRendering::GetTableBorderSolidSegments(
   3453    nsTArray<SolidBeveledBorderSegment>& aSegments,
   3454    StyleBorderStyle aBorderStyle, nscolor aBorderColor, const nsRect& aBorder,
   3455    int32_t aAppUnitsPerDevPixel, mozilla::Side aStartBevelSide,
   3456    nscoord aStartBevelOffset, mozilla::Side aEndBevelSide,
   3457    nscoord aEndBevelOffset) {
   3458  const bool horizontal =
   3459      eSideTop == aStartBevelSide || eSideBottom == aStartBevelSide;
   3460  const nscoord oneDevPixel = NSIntPixelsToAppUnits(1, aAppUnitsPerDevPixel);
   3461 
   3462  switch (aBorderStyle) {
   3463    case StyleBorderStyle::None:
   3464    case StyleBorderStyle::Hidden:
   3465      return;
   3466    case StyleBorderStyle::Dotted:
   3467    case StyleBorderStyle::Dashed:
   3468      MOZ_ASSERT_UNREACHABLE("Caller should have checked");
   3469      return;
   3470    case StyleBorderStyle::Groove:
   3471    case StyleBorderStyle::Ridge:
   3472      if ((horizontal && (oneDevPixel >= aBorder.height)) ||
   3473          (!horizontal && (oneDevPixel >= aBorder.width))) {
   3474        aSegments.AppendElement(
   3475            SolidBeveledBorderSegment{aBorder,
   3476                                      aBorderColor,
   3477                                      {aStartBevelSide, aStartBevelOffset},
   3478                                      {aEndBevelSide, aEndBevelOffset}});
   3479      } else {
   3480        nscoord startBevel =
   3481            (aStartBevelOffset > 0)
   3482                ? RoundFloatToPixel(0.5f * (float)aStartBevelOffset,
   3483                                    aAppUnitsPerDevPixel, true)
   3484                : 0;
   3485        nscoord endBevel =
   3486            (aEndBevelOffset > 0)
   3487                ? RoundFloatToPixel(0.5f * (float)aEndBevelOffset,
   3488                                    aAppUnitsPerDevPixel, true)
   3489                : 0;
   3490        mozilla::Side ridgeGrooveSide = (horizontal) ? eSideTop : eSideLeft;
   3491        // FIXME: In theory, this should use the visited-dependent
   3492        // background color, but I don't care.
   3493        nscolor bevelColor =
   3494            MakeBevelColor(ridgeGrooveSide, aBorderStyle, aBorderColor);
   3495        nsRect rect(aBorder);
   3496        nscoord half;
   3497        if (horizontal) {  // top, bottom
   3498          half = RoundFloatToPixel(0.5f * (float)aBorder.height,
   3499                                   aAppUnitsPerDevPixel);
   3500          rect.height = half;
   3501          if (eSideTop == aStartBevelSide) {
   3502            rect.x += startBevel;
   3503            rect.width -= startBevel;
   3504          }
   3505          if (eSideTop == aEndBevelSide) {
   3506            rect.width -= endBevel;
   3507          }
   3508          aSegments.AppendElement(
   3509              SolidBeveledBorderSegment{rect,
   3510                                        bevelColor,
   3511                                        {aStartBevelSide, startBevel},
   3512                                        {aEndBevelSide, endBevel}});
   3513        } else {  // left, right
   3514          half = RoundFloatToPixel(0.5f * (float)aBorder.width,
   3515                                   aAppUnitsPerDevPixel);
   3516          rect.width = half;
   3517          if (eSideLeft == aStartBevelSide) {
   3518            rect.y += startBevel;
   3519            rect.height -= startBevel;
   3520          }
   3521          if (eSideLeft == aEndBevelSide) {
   3522            rect.height -= endBevel;
   3523          }
   3524          aSegments.AppendElement(
   3525              SolidBeveledBorderSegment{rect,
   3526                                        bevelColor,
   3527                                        {aStartBevelSide, startBevel},
   3528                                        {aEndBevelSide, endBevel}});
   3529        }
   3530 
   3531        rect = aBorder;
   3532        ridgeGrooveSide =
   3533            (eSideTop == ridgeGrooveSide) ? eSideBottom : eSideRight;
   3534        // FIXME: In theory, this should use the visited-dependent
   3535        // background color, but I don't care.
   3536        bevelColor =
   3537            MakeBevelColor(ridgeGrooveSide, aBorderStyle, aBorderColor);
   3538        if (horizontal) {
   3539          rect.y = rect.y + half;
   3540          rect.height = aBorder.height - half;
   3541          if (eSideBottom == aStartBevelSide) {
   3542            rect.x += startBevel;
   3543            rect.width -= startBevel;
   3544          }
   3545          if (eSideBottom == aEndBevelSide) {
   3546            rect.width -= endBevel;
   3547          }
   3548          aSegments.AppendElement(
   3549              SolidBeveledBorderSegment{rect,
   3550                                        bevelColor,
   3551                                        {aStartBevelSide, startBevel},
   3552                                        {aEndBevelSide, endBevel}});
   3553        } else {
   3554          rect.x = rect.x + half;
   3555          rect.width = aBorder.width - half;
   3556          if (eSideRight == aStartBevelSide) {
   3557            rect.y += aStartBevelOffset - startBevel;
   3558            rect.height -= startBevel;
   3559          }
   3560          if (eSideRight == aEndBevelSide) {
   3561            rect.height -= endBevel;
   3562          }
   3563          aSegments.AppendElement(
   3564              SolidBeveledBorderSegment{rect,
   3565                                        bevelColor,
   3566                                        {aStartBevelSide, startBevel},
   3567                                        {aEndBevelSide, endBevel}});
   3568        }
   3569      }
   3570      break;
   3571    case StyleBorderStyle::Double:
   3572      // We can only do "double" borders if the thickness of the border
   3573      // is more than 2px.  Otherwise, we fall through to painting a
   3574      // solid border.
   3575      if ((aBorder.width > 2 * oneDevPixel || horizontal) &&
   3576          (aBorder.height > 2 * oneDevPixel || !horizontal)) {
   3577        nscoord startBevel =
   3578            (aStartBevelOffset > 0)
   3579                ? RoundFloatToPixel(0.333333f * (float)aStartBevelOffset,
   3580                                    aAppUnitsPerDevPixel)
   3581                : 0;
   3582        nscoord endBevel =
   3583            (aEndBevelOffset > 0)
   3584                ? RoundFloatToPixel(0.333333f * (float)aEndBevelOffset,
   3585                                    aAppUnitsPerDevPixel)
   3586                : 0;
   3587        if (horizontal) {  // top, bottom
   3588          nscoord thirdHeight = RoundFloatToPixel(
   3589              0.333333f * (float)aBorder.height, aAppUnitsPerDevPixel);
   3590 
   3591          // draw the top line or rect
   3592          nsRect topRect(aBorder.x, aBorder.y, aBorder.width, thirdHeight);
   3593          if (eSideTop == aStartBevelSide) {
   3594            topRect.x += aStartBevelOffset - startBevel;
   3595            topRect.width -= aStartBevelOffset - startBevel;
   3596          }
   3597          if (eSideTop == aEndBevelSide) {
   3598            topRect.width -= aEndBevelOffset - endBevel;
   3599          }
   3600 
   3601          aSegments.AppendElement(
   3602              SolidBeveledBorderSegment{topRect,
   3603                                        aBorderColor,
   3604                                        {aStartBevelSide, startBevel},
   3605                                        {aEndBevelSide, endBevel}});
   3606 
   3607          // draw the botom line or rect
   3608          nscoord heightOffset = aBorder.height - thirdHeight;
   3609          nsRect bottomRect(aBorder.x, aBorder.y + heightOffset, aBorder.width,
   3610                            aBorder.height - heightOffset);
   3611          if (eSideBottom == aStartBevelSide) {
   3612            bottomRect.x += aStartBevelOffset - startBevel;
   3613            bottomRect.width -= aStartBevelOffset - startBevel;
   3614          }
   3615          if (eSideBottom == aEndBevelSide) {
   3616            bottomRect.width -= aEndBevelOffset - endBevel;
   3617          }
   3618          aSegments.AppendElement(
   3619              SolidBeveledBorderSegment{bottomRect,
   3620                                        aBorderColor,
   3621                                        {aStartBevelSide, startBevel},
   3622                                        {aEndBevelSide, endBevel}});
   3623        } else {  // left, right
   3624          nscoord thirdWidth = RoundFloatToPixel(
   3625              0.333333f * (float)aBorder.width, aAppUnitsPerDevPixel);
   3626 
   3627          nsRect leftRect(aBorder.x, aBorder.y, thirdWidth, aBorder.height);
   3628          if (eSideLeft == aStartBevelSide) {
   3629            leftRect.y += aStartBevelOffset - startBevel;
   3630            leftRect.height -= aStartBevelOffset - startBevel;
   3631          }
   3632          if (eSideLeft == aEndBevelSide) {
   3633            leftRect.height -= aEndBevelOffset - endBevel;
   3634          }
   3635 
   3636          aSegments.AppendElement(
   3637              SolidBeveledBorderSegment{leftRect,
   3638                                        aBorderColor,
   3639                                        {aStartBevelSide, startBevel},
   3640                                        {aEndBevelSide, endBevel}});
   3641 
   3642          nscoord widthOffset = aBorder.width - thirdWidth;
   3643          nsRect rightRect(aBorder.x + widthOffset, aBorder.y,
   3644                           aBorder.width - widthOffset, aBorder.height);
   3645          if (eSideRight == aStartBevelSide) {
   3646            rightRect.y += aStartBevelOffset - startBevel;
   3647            rightRect.height -= aStartBevelOffset - startBevel;
   3648          }
   3649          if (eSideRight == aEndBevelSide) {
   3650            rightRect.height -= aEndBevelOffset - endBevel;
   3651          }
   3652          aSegments.AppendElement(
   3653              SolidBeveledBorderSegment{rightRect,
   3654                                        aBorderColor,
   3655                                        {aStartBevelSide, startBevel},
   3656                                        {aEndBevelSide, endBevel}});
   3657        }
   3658        break;
   3659      }
   3660      // else fall through to solid
   3661      [[fallthrough]];
   3662    case StyleBorderStyle::Solid:
   3663      aSegments.AppendElement(
   3664          SolidBeveledBorderSegment{aBorder,
   3665                                    aBorderColor,
   3666                                    {aStartBevelSide, aStartBevelOffset},
   3667                                    {aEndBevelSide, aEndBevelOffset}});
   3668      break;
   3669    case StyleBorderStyle::Outset:
   3670    case StyleBorderStyle::Inset:
   3671      MOZ_ASSERT_UNREACHABLE(
   3672          "inset, outset should have been converted to groove, ridge");
   3673      break;
   3674  }
   3675 }
   3676 
   3677 // End table border-collapsing section
   3678 
   3679 Rect nsCSSRendering::ExpandPaintingRectForDecorationLine(
   3680    nsIFrame* aFrame, const StyleTextDecorationStyle aStyle,
   3681    const Rect& aClippedRect, const Float aICoordInFrame,
   3682    const Float aCycleLength, bool aVertical) {
   3683  switch (aStyle) {
   3684    case StyleTextDecorationStyle::Dotted:
   3685    case StyleTextDecorationStyle::Dashed:
   3686    case StyleTextDecorationStyle::Wavy:
   3687      break;
   3688    default:
   3689      NS_ERROR("Invalid style was specified");
   3690      return aClippedRect;
   3691  }
   3692 
   3693  nsBlockFrame* block = nullptr;
   3694  // Note that when we paint the decoration lines in relative positioned
   3695  // box, we should paint them like all of the boxes are positioned as static.
   3696  nscoord framePosInBlockAppUnits = 0;
   3697  for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
   3698    block = do_QueryFrame(f);
   3699    if (block) {
   3700      break;
   3701    }
   3702    framePosInBlockAppUnits +=
   3703        aVertical ? f->GetNormalPosition().y : f->GetNormalPosition().x;
   3704  }
   3705 
   3706  NS_ENSURE_TRUE(block, aClippedRect);
   3707 
   3708  nsPresContext* pc = aFrame->PresContext();
   3709  Float framePosInBlock =
   3710      Float(pc->AppUnitsToGfxUnits(framePosInBlockAppUnits));
   3711  int32_t rectPosInBlock = int32_t(NS_round(framePosInBlock + aICoordInFrame));
   3712  int32_t extraStartEdge =
   3713      rectPosInBlock - (rectPosInBlock / int32_t(aCycleLength) * aCycleLength);
   3714  Rect rect(aClippedRect);
   3715  if (aVertical) {
   3716    rect.y -= extraStartEdge;
   3717    rect.height += extraStartEdge;
   3718  } else {
   3719    rect.x -= extraStartEdge;
   3720    rect.width += extraStartEdge;
   3721  }
   3722  return rect;
   3723 }
   3724 
   3725 // Converts a GfxFont to an SkFont
   3726 // Either returns true if it was successful, or false if something went wrong
   3727 static bool GetSkFontFromGfxFont(DrawTarget& aDrawTarget, gfxFont* aFont,
   3728                                 SkFont& aSkFont) {
   3729  RefPtr<ScaledFont> scaledFont = aFont->GetScaledFont(&aDrawTarget);
   3730  if (!scaledFont) {
   3731    return false;
   3732  }
   3733 
   3734  ScaledFontBase* fontBase = static_cast<ScaledFontBase*>(scaledFont.get());
   3735 
   3736  SkTypeface* typeface = fontBase->GetSkTypeface();
   3737  if (!typeface) {
   3738    return false;
   3739  }
   3740 
   3741  aSkFont = SkFont(sk_ref_sp(typeface), SkFloatToScalar(fontBase->GetSize()));
   3742  return true;
   3743 }
   3744 
   3745 // Computes data used to position the decoration line within a
   3746 // SkTextBlob, data is returned through aBounds
   3747 static void GetPositioning(
   3748    const nsCSSRendering::PaintDecorationLineParams& aParams, const Rect& aRect,
   3749    Float aOneCSSPixel, Float aCenterBaselineOffset, SkScalar aBounds[]) {
   3750  /**
   3751   * How Positioning in Skia Works
   3752   *  Take the letter "n" for example
   3753   *  We set textPos as 0, 0
   3754   *  This is represented in Skia like so (not to scale)
   3755   *        ^
   3756   *  -10px |  _ __
   3757   *        | | '_ \
   3758   *   -5px | | | | |
   3759   * y-axis | |_| |_|
   3760   *  (0,0) ----------------------->
   3761   *        |     5px        10px
   3762   *    5px |
   3763   *        |
   3764   *   10px |
   3765   *        v
   3766   *  0 on the x axis is a line that touches the bottom of the n
   3767   *  (0,0) is the bottom left-hand corner of the n character
   3768   *  Moving "up" from the n is going in a negative y direction
   3769   *  Moving "down" from the n is going in a positive y direction
   3770   *
   3771   *  The intercepts that are returned in this arrangement will be
   3772   *  offset by the original point it starts at. (This happens in
   3773   *  the SkipInk function below).
   3774   *
   3775   *  In Skia, text MUST be laid out such that the next character
   3776   *  in the RunBuffer is further along the x-axis than the previous
   3777   *  character, otherwise there is undefined/strange behavior.
   3778   */
   3779 
   3780  Float rectThickness = aParams.vertical ? aRect.Width() : aRect.Height();
   3781 
   3782  // the upper and lower lines/edges of the under or over line
   3783  SkScalar upperLine, lowerLine;
   3784  if (aParams.decoration == mozilla::StyleTextDecorationLine::OVERLINE) {
   3785    lowerLine =
   3786        -aParams.offset + aParams.defaultLineThickness - aCenterBaselineOffset;
   3787    upperLine = lowerLine - rectThickness;
   3788  } else {
   3789    // underlines in vertical text are offset from the center of
   3790    // the text, and not the baseline
   3791    // Skia sets the text at it's baseline so we have to offset it
   3792    // for text in vertical-* writing modes
   3793    upperLine = -aParams.offset - aCenterBaselineOffset;
   3794    lowerLine = upperLine + rectThickness;
   3795  }
   3796 
   3797  // set up the bounds, add in a little padding to the thickness of the line
   3798  // (unless the line is <= 1 CSS pixel thick)
   3799  Float lineThicknessPadding = aParams.lineSize.height > aOneCSSPixel
   3800                                   ? 0.25f * aParams.lineSize.height
   3801                                   : 0;
   3802  // don't allow padding greater than 0.75 CSS pixel
   3803  lineThicknessPadding = std::min(lineThicknessPadding, 0.75f * aOneCSSPixel);
   3804  aBounds[0] = upperLine - lineThicknessPadding;
   3805  aBounds[1] = lowerLine + lineThicknessPadding;
   3806 }
   3807 
   3808 // positions an individual glyph according to the given offset
   3809 static SkPoint GlyphPosition(const gfxTextRun::DetailedGlyph& aGlyph,
   3810                             const SkPoint& aTextPos,
   3811                             int32_t aAppUnitsPerDevPixel) {
   3812  SkPoint point = {aGlyph.mOffset.x, aGlyph.mOffset.y};
   3813 
   3814  // convert to device pixels
   3815  point.fX /= (float)aAppUnitsPerDevPixel;
   3816  point.fY /= (float)aAppUnitsPerDevPixel;
   3817 
   3818  // add offsets
   3819  point.fX += aTextPos.fX;
   3820  point.fY += aTextPos.fY;
   3821  return point;
   3822 }
   3823 
   3824 // returns a count of all the glyphs that will be rendered
   3825 // excludes ligature continuations, includes the number of individual
   3826 // glyph records. This includes the number of DetailedGlyphs that a single
   3827 // CompressedGlyph record points to. This function is necessary because Skia
   3828 // needs the total length of glyphs to add to it's run buffer before it creates
   3829 // the RunBuffer object, and this cannot be resized later.
   3830 static uint32_t CountAllGlyphs(
   3831    const gfxTextRun* aTextRun,
   3832    const gfxTextRun::CompressedGlyph* aCompressedGlyph, uint32_t aStringStart,
   3833    uint32_t aStringEnd) {
   3834  uint32_t totalGlyphCount = 0;
   3835 
   3836  for (const gfxTextRun::CompressedGlyph* cg = aCompressedGlyph + aStringStart;
   3837       cg < aCompressedGlyph + aStringEnd; ++cg) {
   3838    totalGlyphCount += cg->IsSimpleGlyph() ? 1 : cg->GetGlyphCount();
   3839  }
   3840 
   3841  return totalGlyphCount;
   3842 }
   3843 
   3844 static void AddDetailedGlyph(const SkTextBlobBuilder::RunBuffer& aRunBuffer,
   3845                             const gfxTextRun::DetailedGlyph& aGlyph,
   3846                             int aIndex, float aAppUnitsPerDevPixel,
   3847                             SkPoint& aTextPos) {
   3848  // add glyph ID to the run buffer at i
   3849  aRunBuffer.glyphs[aIndex] = aGlyph.mGlyphID;
   3850 
   3851  // position the glyph correctly using the detailed offsets
   3852  SkPoint position = GlyphPosition(aGlyph, aTextPos, aAppUnitsPerDevPixel);
   3853  aRunBuffer.pos[2 * aIndex] = position.fX;
   3854  aRunBuffer.pos[(2 * aIndex) + 1] = position.fY;
   3855 
   3856  // increase aTextPos.fx by the advance
   3857  aTextPos.fX += ((float)aGlyph.mAdvance / aAppUnitsPerDevPixel);
   3858 }
   3859 
   3860 static void AddSimpleGlyph(const SkTextBlobBuilder::RunBuffer& aRunBuffer,
   3861                           const gfxTextRun::CompressedGlyph& aGlyph,
   3862                           int aIndex, float aAppUnitsPerDevPixel,
   3863                           SkPoint& aTextPos) {
   3864  aRunBuffer.glyphs[aIndex] = aGlyph.GetSimpleGlyph();
   3865 
   3866  // simple glyphs are offset from 0, so we'll just use textPos
   3867  aRunBuffer.pos[2 * aIndex] = aTextPos.fX;
   3868  aRunBuffer.pos[(2 * aIndex) + 1] = aTextPos.fY;
   3869 
   3870  // increase aTextPos.fX by the advance
   3871  aTextPos.fX += ((float)aGlyph.GetSimpleAdvance() / aAppUnitsPerDevPixel);
   3872 }
   3873 
   3874 // Sets up a Skia TextBlob of the specified font, text position, and made up of
   3875 // the glyphs between aStringStart and aStringEnd. Handles RTL and LTR text
   3876 // and positions each glyph within the text blob
   3877 static sk_sp<const SkTextBlob> CreateTextBlob(
   3878    const gfxTextRun* aTextRun,
   3879    const gfxTextRun::CompressedGlyph* aCompressedGlyph, const SkFont& aFont,
   3880    const gfxTextRun::PropertyProvider::Spacing* aSpacing,
   3881    uint32_t aStringStart, uint32_t aStringEnd, float aAppUnitsPerDevPixel,
   3882    SkPoint& aTextPos, int32_t& aSpacingOffset) {
   3883  // allocate space for the run buffer, then fill it with the glyphs
   3884  uint32_t len =
   3885      CountAllGlyphs(aTextRun, aCompressedGlyph, aStringStart, aStringEnd);
   3886  if (len <= 0) {
   3887    return nullptr;
   3888  }
   3889 
   3890  SkTextBlobBuilder builder;
   3891  const SkTextBlobBuilder::RunBuffer& run = builder.allocRunPos(aFont, len);
   3892 
   3893  // RTL text should be read in by glyph starting at aStringEnd - 1 down until
   3894  // aStringStart.
   3895  bool isRTL = aTextRun->IsRightToLeft();
   3896  uint32_t currIndex = isRTL ? aStringEnd - 1 : aStringStart;  // textRun index
   3897  // currIndex will be advanced by |step| until it reaches |limit|, which is the
   3898  // final index to be handled (NOT one beyond the final index)
   3899  int step = isRTL ? -1 : 1;
   3900  uint32_t limit = isRTL ? aStringStart : aStringEnd - 1;
   3901 
   3902  uint32_t i = 0;  // index into the SkTextBlob we're building
   3903  while (true) {
   3904    // Loop exit test is below, just before we update currIndex.
   3905    aTextPos.fX +=
   3906        isRTL ? aSpacing[aSpacingOffset].mAfter / aAppUnitsPerDevPixel
   3907              : aSpacing[aSpacingOffset].mBefore / aAppUnitsPerDevPixel;
   3908 
   3909    if (aCompressedGlyph[currIndex].IsSimpleGlyph()) {
   3910      MOZ_ASSERT(i < len, "glyph count error!");
   3911      AddSimpleGlyph(run, aCompressedGlyph[currIndex], i, aAppUnitsPerDevPixel,
   3912                     aTextPos);
   3913      i++;
   3914    } else {
   3915      // if it's detailed, potentially add multiple into run.glyphs
   3916      uint32_t count = aCompressedGlyph[currIndex].GetGlyphCount();
   3917      if (count > 0) {
   3918        gfxTextRun::DetailedGlyph* detailGlyph =
   3919            aTextRun->GetDetailedGlyphs(currIndex);
   3920        for (uint32_t d = isRTL ? count - 1 : 0; count; count--, d += step) {
   3921          MOZ_ASSERT(i < len, "glyph count error!");
   3922          AddDetailedGlyph(run, detailGlyph[d], i, aAppUnitsPerDevPixel,
   3923                           aTextPos);
   3924          i++;
   3925        }
   3926      }
   3927    }
   3928    aTextPos.fX += isRTL
   3929                       ? aSpacing[aSpacingOffset].mBefore / aAppUnitsPerDevPixel
   3930                       : aSpacing[aSpacingOffset].mAfter / aAppUnitsPerDevPixel;
   3931    aSpacingOffset += step;
   3932 
   3933    if (currIndex == limit) {
   3934      break;
   3935    }
   3936    currIndex += step;
   3937  }
   3938 
   3939  MOZ_ASSERT(i == len, "glyph count error!");
   3940 
   3941  return builder.make();
   3942 }
   3943 
   3944 // Given a TextBlob, the bounding lines, and the set of current intercepts this
   3945 // function adds the intercepts for the current TextBlob into the given set of
   3946 // previoulsy calculated intercepts. This set is either of length 0, or a
   3947 // multiple of 2 (since every intersection with a piece of text results in two
   3948 // intercepts: entering/exiting)
   3949 static void GetTextIntercepts(const sk_sp<const SkTextBlob>& aBlob,
   3950                              const SkScalar aBounds[],
   3951                              nsTArray<SkScalar>& aIntercepts) {
   3952  // It's possible that we'll encounter a Windows exception deep inside
   3953  // Skia's DirectWrite code while trying to get the intercepts. To avoid
   3954  // crashing in this case, catch any such exception here and discard the
   3955  // newly-added (and incompletely filled in) elements.
   3956  int count = 0;
   3957  MOZ_SEH_TRY {
   3958    // https://skia.org/user/api/SkTextBlob_Reference#Text_Blob_Text_Intercepts
   3959    count = aBlob->getIntercepts(aBounds, nullptr);
   3960    if (count < 2) {
   3961      return;
   3962    }
   3963    aBlob->getIntercepts(aBounds, aIntercepts.AppendElements(count));
   3964  }
   3965  MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
   3966    gfxCriticalNote << "Exception occurred getting text intercepts";
   3967    aIntercepts.TruncateLength(aIntercepts.Length() - count);
   3968  }
   3969 }
   3970 
   3971 // This function, given a set of intercepts that represent each intersection
   3972 // between an under/overline and text, makes a series of calls to
   3973 // PaintDecorationLineInternal that paints a series of clip rects which
   3974 // implement the text-decoration-skip-ink property
   3975 // Logic for where to place each clipped rect, and the length of each rect is
   3976 // included here
   3977 static void SkipInk(nsIFrame* aFrame, DrawTarget& aDrawTarget,
   3978                    const nsCSSRendering::PaintDecorationLineParams& aParams,
   3979                    const nsTArray<SkScalar>& aIntercepts, Float aPadding,
   3980                    Rect& aRect) {
   3981  nsCSSRendering::PaintDecorationLineParams clipParams = aParams;
   3982  const unsigned length = aIntercepts.Length();
   3983 
   3984  // For selections, this points to the selection start, which may not be at the
   3985  // line start.
   3986  const Float relativeTextStart =
   3987      aParams.vertical ? aParams.pt.y : aParams.pt.x;
   3988  const Float relativeTextEnd = relativeTextStart + aParams.lineSize.width;
   3989  // The actual line start position needs to be adjusted by the offset of the
   3990  // start position in the frame, because the intercept positions are based off
   3991  // the whole text run.
   3992  const Float absoluteLineStart = relativeTextStart - aParams.icoordInFrame;
   3993 
   3994  // Compute the min/max positions based on inset.
   3995  const Float insetLineDrawAreaStart =
   3996      relativeTextStart + (aParams.insetLeft - aPadding);
   3997  const Float insetLineDrawAreaEnd =
   3998      relativeTextEnd - (aParams.insetRight - aPadding);
   3999 
   4000  for (unsigned i = 0; i <= length; i += 2) {
   4001    // Handle start/end edge cases and set up general case.
   4002    // While we use the inset start/end values, it is possible the inset cuts
   4003    // of the first intercept and into the next, so we will need to clamp
   4004    // the dimensions in the other case too.
   4005    SkScalar startIntercept = insetLineDrawAreaStart;
   4006    if (i > 0) {
   4007      startIntercept =
   4008          std::max(aIntercepts[i - 1] + absoluteLineStart, startIntercept);
   4009    }
   4010    SkScalar endIntercept = insetLineDrawAreaEnd;
   4011    if (i < length) {
   4012      endIntercept = std::min(aIntercepts[i] + absoluteLineStart, endIntercept);
   4013    }
   4014 
   4015    // remove padding at both ends for width
   4016    // the start of the line is calculated so the padding removes just
   4017    // enough so that the line starts at its normal position
   4018    clipParams.lineSize.width =
   4019        endIntercept - startIntercept - (2.0 * aPadding);
   4020 
   4021    // Don't draw decoration lines that have a smaller width than 1, or half
   4022    // the line-end padding dimension.
   4023    // This will catch the case of an intercept being fully removed by the
   4024    // inset values, in which case the width will be negative.
   4025    if (clipParams.lineSize.width < std::max(aPadding * 0.5, 1.0)) {
   4026      continue;
   4027    }
   4028 
   4029    // Start the line right after the intercept's location plus room for
   4030    // padding; snap the rect edges to device pixels for consistent rendering
   4031    // of dots across separate fragments of a dotted line.
   4032    if (aParams.vertical) {
   4033      clipParams.pt.y =
   4034          aParams.sidewaysLeft
   4035              ? relativeTextEnd - (endIntercept - relativeTextStart) + aPadding
   4036              : startIntercept + aPadding;
   4037      aRect.y = std::floor(clipParams.pt.y + 0.5);
   4038      aRect.SetBottomEdge(
   4039          std::floor(clipParams.pt.y + clipParams.lineSize.width + 0.5));
   4040    } else {
   4041      clipParams.pt.x = startIntercept + aPadding;
   4042      aRect.x = std::floor(clipParams.pt.x + 0.5);
   4043      aRect.SetRightEdge(
   4044          std::floor(clipParams.pt.x + clipParams.lineSize.width + 0.5));
   4045    }
   4046 
   4047    nsCSSRendering::PaintDecorationLineInternal(aFrame, aDrawTarget, clipParams,
   4048                                                aRect);
   4049  }
   4050 }
   4051 
   4052 void nsCSSRendering::PaintDecorationLine(
   4053    nsIFrame* aFrame, DrawTarget& aDrawTarget,
   4054    const PaintDecorationLineParams& aParams) {
   4055  NS_ASSERTION(aParams.style != StyleTextDecorationStyle::None,
   4056               "aStyle is none");
   4057 
   4058  Rect rect = ToRect(GetTextDecorationRectInternal(aParams.pt, aParams));
   4059  if (rect.IsEmpty() || !rect.Intersects(aParams.dirtyRect)) {
   4060    return;
   4061  }
   4062 
   4063  if (aParams.decoration != StyleTextDecorationLine::UNDERLINE &&
   4064      aParams.decoration != StyleTextDecorationLine::OVERLINE &&
   4065      aParams.decoration != StyleTextDecorationLine::LINE_THROUGH) {
   4066    MOZ_ASSERT_UNREACHABLE("Invalid text decoration value");
   4067    return;
   4068  }
   4069 
   4070  // Check if decoration line will skip past ascenders/descenders
   4071  // text-decoration-skip-ink only applies to overlines/underlines
   4072  bool skipInkEnabled =
   4073      aParams.skipInk != mozilla::StyleTextDecorationSkipInk::None &&
   4074      aParams.decoration != StyleTextDecorationLine::LINE_THROUGH &&
   4075      aParams.allowInkSkipping && aFrame->IsTextFrame();
   4076 
   4077  if (!skipInkEnabled || aParams.glyphRange.Length() == 0) {
   4078    PaintDecorationLineInternal(aFrame, aDrawTarget, aParams, rect);
   4079    return;
   4080  }
   4081 
   4082  // Must be a text frame, otherwise skipInkEnabled (above) would be false.
   4083  nsTextFrame* textFrame = static_cast<nsTextFrame*>(aFrame);
   4084 
   4085  // get text run and current text offset (for line wrapping)
   4086  gfxTextRun* textRun =
   4087      textFrame->GetTextRun(nsTextFrame::TextRunType::eInflated);
   4088 
   4089  // used for conversions from app units to device pixels
   4090  int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
   4091 
   4092  // pointer to the array of glyphs for this TextRun
   4093  gfxTextRun::CompressedGlyph* characterGlyphs = textRun->GetCharacterGlyphs();
   4094 
   4095  // get positioning info
   4096  SkPoint textPos = {0, aParams.baselineOffset};
   4097  SkScalar bounds[] = {0, 0};
   4098  Float oneCSSPixel = aFrame->PresContext()->CSSPixelsToDevPixels(1.0f);
   4099  if (!textRun->UseCenterBaseline()) {
   4100    GetPositioning(aParams, rect, oneCSSPixel, 0, bounds);
   4101  }
   4102 
   4103  // array for the text intercepts
   4104  AutoTArray<SkScalar, 256> intercepts;
   4105 
   4106  // array for spacing data
   4107  AutoTArray<gfxTextRun::PropertyProvider::Spacing, 64> spacing;
   4108  spacing.SetLength(aParams.glyphRange.Length());
   4109  if (aParams.provider != nullptr) {
   4110    aParams.provider->GetSpacing(aParams.glyphRange, spacing.Elements());
   4111  }
   4112 
   4113  // loop through each glyph run
   4114  // in most cases there will only be one
   4115  bool isRTL = textRun->IsRightToLeft();
   4116  int32_t spacingOffset = isRTL ? aParams.glyphRange.Length() - 1 : 0;
   4117  gfxTextRun::GlyphRunIterator iter(textRun, aParams.glyphRange, isRTL);
   4118 
   4119  // For any glyph run where we don't actually do skipping, we'll need to
   4120  // advance the current position by its width.
   4121  // (For runs we do process, CreateTextBlob will update the position.)
   4122  auto currentGlyphRunAdvance = [&]() {
   4123    return textRun->GetAdvanceWidth(
   4124               gfxTextRun::Range(iter.StringStart(), iter.StringEnd()),
   4125               aParams.provider) /
   4126           appUnitsPerDevPixel;
   4127  };
   4128 
   4129  for (; !iter.AtEnd(); iter.NextRun()) {
   4130    if (iter.GlyphRun()->mOrientation ==
   4131            mozilla::gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT ||
   4132        (iter.GlyphRun()->mIsCJK &&
   4133         aParams.skipInk == mozilla::StyleTextDecorationSkipInk::Auto)) {
   4134      // We don't support upright text in vertical modes currently
   4135      // (see https://bugzilla.mozilla.org/show_bug.cgi?id=1572294),
   4136      // but we do need to update textPos so that following runs will be
   4137      // correctly positioned.
   4138      // We also don't apply skip-ink to CJK text runs because many fonts
   4139      // have an underline that looks really bad if this is done
   4140      // (see https://bugzilla.mozilla.org/show_bug.cgi?id=1573249),
   4141      // when skip-ink is set to 'auto'.
   4142      textPos.fX += currentGlyphRunAdvance();
   4143      continue;
   4144    }
   4145 
   4146    gfxFont* font = iter.GlyphRun()->mFont;
   4147    // Don't try to apply skip-ink to 'sbix' fonts like Apple Color Emoji,
   4148    // because old macOS (10.9) may crash trying to retrieve glyph paths
   4149    // that don't exist.
   4150    if (font->GetFontEntry()->HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x'))) {
   4151      textPos.fX += currentGlyphRunAdvance();
   4152      continue;
   4153    }
   4154 
   4155    // get a Skia version of the glyph run's font
   4156    SkFont skiafont;
   4157    if (!GetSkFontFromGfxFont(aDrawTarget, font, skiafont)) {
   4158      PaintDecorationLineInternal(aFrame, aDrawTarget, aParams, rect);
   4159      return;
   4160    }
   4161 
   4162    // Create a text blob with correctly positioned glyphs. This also updates
   4163    // textPos.fX with the advance of the glyphs.
   4164    sk_sp<const SkTextBlob> textBlob =
   4165        CreateTextBlob(textRun, characterGlyphs, skiafont, spacing.Elements(),
   4166                       iter.StringStart(), iter.StringEnd(),
   4167                       (float)appUnitsPerDevPixel, textPos, spacingOffset);
   4168 
   4169    if (!textBlob) {
   4170      textPos.fX += currentGlyphRunAdvance();
   4171      continue;
   4172    }
   4173 
   4174    if (textRun->UseCenterBaseline()) {
   4175      // writing modes that use a center baseline need to be adjusted on a
   4176      // font-by-font basis since Skia lines up the text on a alphabetic
   4177      // baseline, but for some vertical-* writing modes the offset is from the
   4178      // center.
   4179      gfxFont::Metrics metrics = font->GetMetrics(nsFontMetrics::eHorizontal);
   4180      Float centerToBaseline = (metrics.emAscent - metrics.emDescent) / 2.0f;
   4181      GetPositioning(aParams, rect, oneCSSPixel, centerToBaseline, bounds);
   4182    }
   4183 
   4184    // compute the text intercepts that need to be skipped
   4185    GetTextIntercepts(textBlob, bounds, intercepts);
   4186  }
   4187  bool needsSkipInk = intercepts.Length() > 0;
   4188 
   4189  if (needsSkipInk) {
   4190    // Padding between glyph intercepts and the decoration line: we use the
   4191    // decoration line thickness, clamped to a minimum of 1px and a maximum
   4192    // of 0.2em.
   4193    Float padding =
   4194        std::min(std::max(aParams.lineSize.height, oneCSSPixel),
   4195                 Float(textRun->GetFontGroup()->GetStyle()->size / 5.0));
   4196    SkipInk(aFrame, aDrawTarget, aParams, intercepts, padding, rect);
   4197  } else {
   4198    PaintDecorationLineInternal(aFrame, aDrawTarget, aParams, rect);
   4199  }
   4200 }
   4201 
   4202 void nsCSSRendering::PaintDecorationLineInternal(
   4203    nsIFrame* aFrame, DrawTarget& aDrawTarget,
   4204    const PaintDecorationLineParams& aParams, Rect aRect) {
   4205  const Float lineThickness = aParams.lineSize.height;
   4206  DeviceColor color = ToDeviceColor(aParams.color);
   4207  ColorPattern colorPat(color);
   4208  StrokeOptions strokeOptions(aParams.lineSize.height);
   4209  DrawOptions drawOptions;
   4210 
   4211  Float dash[2];
   4212 
   4213  AutoPopClips autoPopClips(&aDrawTarget);
   4214 
   4215  mozilla::layout::TextDrawTarget* textDrawer = nullptr;
   4216  if (aDrawTarget.GetBackendType() == BackendType::WEBRENDER_TEXT) {
   4217    textDrawer = static_cast<mozilla::layout::TextDrawTarget*>(&aDrawTarget);
   4218  }
   4219 
   4220  switch (aParams.style) {
   4221    case StyleTextDecorationStyle::Solid:
   4222    case StyleTextDecorationStyle::Double:
   4223      break;
   4224    case StyleTextDecorationStyle::Dashed: {
   4225      autoPopClips.PushClipRect(aRect);
   4226      Float dashWidth = lineThickness * DOT_LENGTH * DASH_LENGTH;
   4227      dash[0] = dashWidth;
   4228      dash[1] = dashWidth;
   4229      strokeOptions.mDashPattern = dash;
   4230      strokeOptions.mDashLength = std::size(dash);
   4231      strokeOptions.mLineCap = CapStyle::BUTT;
   4232      aRect = ExpandPaintingRectForDecorationLine(
   4233          aFrame, aParams.style, aRect, aParams.icoordInFrame, dashWidth * 2,
   4234          aParams.vertical);
   4235      // We should continue to draw the last dash even if it is not in the rect.
   4236      aRect.width += dashWidth;
   4237      break;
   4238    }
   4239    case StyleTextDecorationStyle::Dotted: {
   4240      autoPopClips.PushClipRect(aRect);
   4241      Float dashWidth = lineThickness * DOT_LENGTH;
   4242      if (lineThickness > 2.0) {
   4243        dash[0] = 0.f;
   4244        dash[1] = dashWidth * 2.f;
   4245        strokeOptions.mLineCap = CapStyle::ROUND;
   4246      } else {
   4247        dash[0] = dashWidth;
   4248        dash[1] = dashWidth;
   4249      }
   4250      strokeOptions.mDashPattern = dash;
   4251      strokeOptions.mDashLength = std::size(dash);
   4252      aRect = ExpandPaintingRectForDecorationLine(
   4253          aFrame, aParams.style, aRect, aParams.icoordInFrame, dashWidth * 2,
   4254          aParams.vertical);
   4255      // We should continue to draw the last dot even if it is not in the rect.
   4256      aRect.width += dashWidth;
   4257      break;
   4258    }
   4259    case StyleTextDecorationStyle::Wavy:
   4260      autoPopClips.PushClipRect(aRect);
   4261      if (lineThickness > 2.0) {
   4262        drawOptions.mAntialiasMode = AntialiasMode::SUBPIXEL;
   4263      } else {
   4264        // Don't use anti-aliasing here.  Because looks like lighter color wavy
   4265        // line at this case.  And probably, users don't think the
   4266        // non-anti-aliased wavy line is not pretty.
   4267        drawOptions.mAntialiasMode = AntialiasMode::NONE;
   4268      }
   4269      break;
   4270    default:
   4271      NS_ERROR("Invalid style value!");
   4272      return;
   4273  }
   4274 
   4275  // The block-direction position should be set to the middle of the line.
   4276  if (aParams.vertical) {
   4277    aRect.x += lineThickness / 2;
   4278  } else {
   4279    aRect.y += lineThickness / 2;
   4280  }
   4281 
   4282  switch (aParams.style) {
   4283    case StyleTextDecorationStyle::Solid:
   4284    case StyleTextDecorationStyle::Dotted:
   4285    case StyleTextDecorationStyle::Dashed: {
   4286      Point p1 = aRect.TopLeft();
   4287      Point p2 = aParams.vertical ? aRect.BottomLeft() : aRect.TopRight();
   4288      if (textDrawer) {
   4289        textDrawer->AppendDecoration(p1, p2, lineThickness, aParams.vertical,
   4290                                     color, aParams.style);
   4291      } else {
   4292        aDrawTarget.StrokeLine(p1, p2, colorPat, strokeOptions, drawOptions);
   4293      }
   4294      return;
   4295    }
   4296    case StyleTextDecorationStyle::Double: {
   4297      /**
   4298       *  We are drawing double line as:
   4299       *
   4300       * +-------------------------------------------+
   4301       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
   4302       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
   4303       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
   4304       * |                                           |
   4305       * |                                           |
   4306       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
   4307       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
   4308       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
   4309       * +-------------------------------------------+
   4310       */
   4311      Point p1a = aRect.TopLeft();
   4312      Point p2a = aParams.vertical ? aRect.BottomLeft() : aRect.TopRight();
   4313 
   4314      if (aParams.vertical) {
   4315        aRect.width -= lineThickness;
   4316      } else {
   4317        aRect.height -= lineThickness;
   4318      }
   4319 
   4320      Point p1b = aParams.vertical ? aRect.TopRight() : aRect.BottomLeft();
   4321      Point p2b = aRect.BottomRight();
   4322 
   4323      if (textDrawer) {
   4324        textDrawer->AppendDecoration(p1a, p2a, lineThickness, aParams.vertical,
   4325                                     color, StyleTextDecorationStyle::Solid);
   4326        textDrawer->AppendDecoration(p1b, p2b, lineThickness, aParams.vertical,
   4327                                     color, StyleTextDecorationStyle::Solid);
   4328      } else {
   4329        aDrawTarget.StrokeLine(p1a, p2a, colorPat, strokeOptions, drawOptions);
   4330        aDrawTarget.StrokeLine(p1b, p2b, colorPat, strokeOptions, drawOptions);
   4331      }
   4332      return;
   4333    }
   4334    case StyleTextDecorationStyle::Wavy: {
   4335      /**
   4336       *  We are drawing wavy line as:
   4337       *
   4338       *  P: Path, X: Painted pixel
   4339       *
   4340       *     +---------------------------------------+
   4341       *   XX|X            XXXXXX            XXXXXX  |
   4342       *   PP|PX          XPPPPPPX          XPPPPPPX |    ^
   4343       *   XX|XPX        XPXXXXXXPX        XPXXXXXXPX|    |
   4344       *     | XPX      XPX      XPX      XPX      XP|X   |adv
   4345       *     |  XPXXXXXXPX        XPXXXXXXPX        X|PX  |
   4346       *     |   XPPPPPPX          XPPPPPPX          |XPX v
   4347       *     |    XXXXXX            XXXXXX           | XX
   4348       *     +---------------------------------------+
   4349       *      <---><--->                                ^
   4350       *      adv  flatLengthAtVertex                   rightMost
   4351       *
   4352       *  1. Always starts from top-left of the drawing area, however, we need
   4353       *     to draw  the line from outside of the rect.  Because the start
   4354       *     point of the line is not good style if we draw from inside it.
   4355       *  2. First, draw horizontal line from outside the rect to top-left of
   4356       *     the rect;
   4357       *  3. Goes down to bottom of the area at 45 degrees.
   4358       *  4. Slides to right horizontaly, see |flatLengthAtVertex|.
   4359       *  5. Goes up to top of the area at 45 degrees.
   4360       *  6. Slides to right horizontaly.
   4361       *  7. Repeat from 2 until reached to right-most edge of the area.
   4362       *
   4363       * In the vertical case, swap horizontal and vertical coordinates and
   4364       * directions in the above description.
   4365       */
   4366 
   4367      Float& rectICoord = aParams.vertical ? aRect.y : aRect.x;
   4368      Float& rectISize = aParams.vertical ? aRect.height : aRect.width;
   4369      const Float rectBSize = aParams.vertical ? aRect.width : aRect.height;
   4370 
   4371      const Float adv = rectBSize - lineThickness;
   4372      const Float flatLengthAtVertex =
   4373          std::max((lineThickness - 1.0) * 2.0, 1.0);
   4374 
   4375      // Align the start of wavy lines to the nearest ancestor block.
   4376      const Float cycleLength = 2 * (adv + flatLengthAtVertex);
   4377      aRect = ExpandPaintingRectForDecorationLine(
   4378          aFrame, aParams.style, aRect, aParams.icoordInFrame, cycleLength,
   4379          aParams.vertical);
   4380 
   4381      if (textDrawer) {
   4382        // Undo attempted centering
   4383        Float& rectBCoord = aParams.vertical ? aRect.x : aRect.y;
   4384        rectBCoord -= lineThickness / 2;
   4385 
   4386        textDrawer->AppendWavyDecoration(aRect, lineThickness, aParams.vertical,
   4387                                         color);
   4388        return;
   4389      }
   4390 
   4391      // figure out if we can trim whole cycles from the left and right edges
   4392      // of the line, to try and avoid creating an unnecessarily long and
   4393      // complex path (but don't do this for webrender, )
   4394      const Float dirtyRectICoord =
   4395          aParams.vertical ? aParams.dirtyRect.y : aParams.dirtyRect.x;
   4396      int32_t skipCycles = floor((dirtyRectICoord - rectICoord) / cycleLength);
   4397      if (skipCycles > 0) {
   4398        rectICoord += skipCycles * cycleLength;
   4399        rectISize -= skipCycles * cycleLength;
   4400      }
   4401 
   4402      rectICoord += lineThickness / 2.0;
   4403 
   4404      Point pt(aRect.TopLeft());
   4405      Float& ptICoord = aParams.vertical ? pt.y.value : pt.x.value;
   4406      Float& ptBCoord = aParams.vertical ? pt.x.value : pt.y.value;
   4407      if (aParams.vertical) {
   4408        ptBCoord += adv;
   4409      }
   4410      Float iCoordLimit = ptICoord + rectISize + lineThickness;
   4411 
   4412      const Float dirtyRectIMost = aParams.vertical ? aParams.dirtyRect.YMost()
   4413                                                    : aParams.dirtyRect.XMost();
   4414      skipCycles = floor((iCoordLimit - dirtyRectIMost) / cycleLength);
   4415      if (skipCycles > 0) {
   4416        iCoordLimit -= skipCycles * cycleLength;
   4417      }
   4418 
   4419      RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
   4420      RefPtr<Path> path;
   4421 
   4422      ptICoord -= lineThickness;
   4423      builder->MoveTo(pt);  // 1
   4424 
   4425      ptICoord = rectICoord;
   4426      builder->LineTo(pt);  // 2
   4427 
   4428      // In vertical mode, to go "down" relative to the text we need to
   4429      // decrease the block coordinate, whereas in horizontal we increase
   4430      // it. So the sense of this flag is effectively inverted.
   4431      bool goDown = !aParams.vertical;
   4432      uint32_t iter = 0;
   4433      while (ptICoord < iCoordLimit) {
   4434        if (++iter > 1000) {
   4435          // stroke the current path and start again, to avoid pathological
   4436          // behavior in cairo with huge numbers of path segments
   4437          path = builder->Finish();
   4438          aDrawTarget.Stroke(path, colorPat, strokeOptions, drawOptions);
   4439          builder = aDrawTarget.CreatePathBuilder();
   4440          builder->MoveTo(pt);
   4441          iter = 0;
   4442        }
   4443        ptICoord += adv;
   4444        ptBCoord += goDown ? adv : -adv;
   4445 
   4446        builder->LineTo(pt);  // 3 and 5
   4447 
   4448        ptICoord += flatLengthAtVertex;
   4449        builder->LineTo(pt);  // 4 and 6
   4450 
   4451        goDown = !goDown;
   4452      }
   4453      path = builder->Finish();
   4454      aDrawTarget.Stroke(path, colorPat, strokeOptions, drawOptions);
   4455      return;
   4456    }
   4457    default:
   4458      NS_ERROR("Invalid style value!");
   4459  }
   4460 }
   4461 
   4462 Rect nsCSSRendering::DecorationLineToPath(
   4463    const PaintDecorationLineParams& aParams) {
   4464  NS_ASSERTION(aParams.style != StyleTextDecorationStyle::None,
   4465               "aStyle is none");
   4466 
   4467  Rect path;  // To benefit from RVO, we return this from all return points
   4468 
   4469  Rect rect = ToRect(GetTextDecorationRectInternal(aParams.pt, aParams));
   4470  if (rect.IsEmpty() || !rect.Intersects(aParams.dirtyRect)) {
   4471    return path;
   4472  }
   4473 
   4474  if (aParams.decoration != StyleTextDecorationLine::UNDERLINE &&
   4475      aParams.decoration != StyleTextDecorationLine::OVERLINE &&
   4476      aParams.decoration != StyleTextDecorationLine::LINE_THROUGH) {
   4477    MOZ_ASSERT_UNREACHABLE("Invalid text decoration value");
   4478    return path;
   4479  }
   4480 
   4481  if (aParams.style != StyleTextDecorationStyle::Solid) {
   4482    // For the moment, we support only solid text decorations.
   4483    return path;
   4484  }
   4485 
   4486  const Float lineThickness = aParams.lineSize.height;
   4487  // The block-direction position should be set to the middle of the line.
   4488  if (aParams.vertical) {
   4489    rect.x += lineThickness / 2;
   4490    path = Rect(rect.TopLeft() - Point(lineThickness / 2, 0.0),
   4491                Size(lineThickness, rect.Height()));
   4492  } else {
   4493    rect.y += lineThickness / 2;
   4494    path = Rect(rect.TopLeft() - Point(0.0, lineThickness / 2),
   4495                Size(rect.Width(), lineThickness));
   4496  }
   4497 
   4498  return path;
   4499 }
   4500 
   4501 nsRect nsCSSRendering::GetTextDecorationRect(
   4502    nsPresContext* aPresContext, const DecorationRectParams& aParams) {
   4503  NS_ASSERTION(aPresContext, "aPresContext is null");
   4504  NS_ASSERTION(aParams.style != StyleTextDecorationStyle::None,
   4505               "aStyle is none");
   4506 
   4507  gfxRect rect = GetTextDecorationRectInternal(Point(0, 0), aParams);
   4508  // The rect values are already rounded to nearest device pixels.
   4509  nsRect r;
   4510  r.x = aPresContext->GfxUnitsToAppUnits(rect.X());
   4511  r.y = aPresContext->GfxUnitsToAppUnits(rect.Y());
   4512  r.width = aPresContext->GfxUnitsToAppUnits(rect.Width());
   4513  r.height = aPresContext->GfxUnitsToAppUnits(rect.Height());
   4514  return r;
   4515 }
   4516 
   4517 gfxRect nsCSSRendering::GetTextDecorationRectInternal(
   4518    const Point& aPt, const DecorationRectParams& aParams) {
   4519  NS_ASSERTION(aParams.style <= StyleTextDecorationStyle::Wavy,
   4520               "Invalid aStyle value");
   4521 
   4522  if (aParams.style == StyleTextDecorationStyle::None) {
   4523    return gfxRect(0, 0, 0, 0);
   4524  }
   4525 
   4526  bool canLiftUnderline = aParams.descentLimit >= 0.0;
   4527 
   4528  gfxFloat iCoord = aParams.vertical ? aPt.y : aPt.x;
   4529  gfxFloat bCoord = aParams.vertical ? aPt.x : aPt.y;
   4530 
   4531  // 'left' and 'right' are relative to the line, so for vertical writing modes
   4532  // they will actually become top and bottom of the rendered line.
   4533  // Similarly, aLineSize.width and .height are actually length and thickness
   4534  // of the line, which runs horizontally or vertically according to aVertical.
   4535  const gfxFloat left = floor(iCoord + 0.5),
   4536                 right = floor(iCoord + aParams.lineSize.width + 0.5);
   4537 
   4538  // We compute |r| as if for a horizontal text run, and then swap vertical
   4539  // and horizontal coordinates at the end if vertical was requested.
   4540  gfxRect r(left, 0, right - left, 0);
   4541 
   4542  const gfxFloat lineThickness = aParams.lineSize.height;
   4543  gfxFloat defaultLineThickness = NS_round(aParams.defaultLineThickness);
   4544  defaultLineThickness = std::max(defaultLineThickness, 1.0);
   4545 
   4546  gfxFloat ascent = NS_round(aParams.ascent);
   4547  gfxFloat descentLimit = floor(aParams.descentLimit);
   4548 
   4549  gfxFloat suggestedMaxRectHeight =
   4550      std::max(std::min(ascent, descentLimit), 1.0);
   4551  r.height = lineThickness;
   4552  if (aParams.style == StyleTextDecorationStyle::Double) {
   4553    /**
   4554     *  We will draw double line as:
   4555     *
   4556     * +-------------------------------------------+
   4557     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
   4558     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
   4559     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
   4560     * |                                           | ^
   4561     * |                                           | | gap
   4562     * |                                           | v
   4563     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
   4564     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
   4565     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
   4566     * +-------------------------------------------+
   4567     */
   4568    gfxFloat gap = NS_round(lineThickness / 2.0);
   4569    gap = std::max(gap, 1.0);
   4570    r.height = lineThickness * 2.0 + gap;
   4571    if (canLiftUnderline) {
   4572      if (r.Height() > suggestedMaxRectHeight) {
   4573        // Don't shrink the line height, because the thickness has some meaning.
   4574        // We can just shrink the gap at this time.
   4575        r.height = std::max(suggestedMaxRectHeight, lineThickness * 2.0 + 1.0);
   4576      }
   4577    }
   4578  } else if (aParams.style == StyleTextDecorationStyle::Wavy) {
   4579    /**
   4580     *  We will draw wavy line as:
   4581     *
   4582     * +-------------------------------------------+
   4583     * |XXXXX            XXXXXX            XXXXXX  | ^
   4584     * |XXXXXX          XXXXXXXX          XXXXXXXX | | lineThickness
   4585     * |XXXXXXX        XXXXXXXXXX        XXXXXXXXXX| v
   4586     * |     XXX      XXX      XXX      XXX      XX|
   4587     * |      XXXXXXXXXX        XXXXXXXXXX        X|
   4588     * |       XXXXXXXX          XXXXXXXX          |
   4589     * |        XXXXXX            XXXXXX           |
   4590     * +-------------------------------------------+
   4591     */
   4592    r.height = lineThickness > 2.0 ? lineThickness * 4.0 : lineThickness * 3.0;
   4593    if (canLiftUnderline) {
   4594      if (r.Height() > suggestedMaxRectHeight) {
   4595        // Don't shrink the line height even if there is not enough space,
   4596        // because the thickness has some meaning.  E.g., the 1px wavy line and
   4597        // 2px wavy line can be used for different meaning in IME selections
   4598        // at same time.
   4599        r.height = std::max(suggestedMaxRectHeight, lineThickness * 2.0);
   4600      }
   4601    }
   4602  }
   4603 
   4604  gfxFloat baseline = floor(bCoord + aParams.ascent + 0.5);
   4605 
   4606  // Calculate adjusted offset based on writing-mode/orientation and thickness
   4607  // of decoration line. The input value aParams.offset is the nominal position
   4608  // (offset from baseline) where we would draw a single, infinitely-thin line;
   4609  // but for a wavy or double line, we'll need to move the bounding rect of the
   4610  // decoration outwards from the baseline so that an underline remains below
   4611  // the glyphs, and an overline above them, despite the increased block-dir
   4612  // extent of the decoration.
   4613  //
   4614  // So adjustments by r.Height() are used to make the wider line styles (wavy
   4615  // and double) "grow" in the appropriate direction compared to the basic
   4616  // single line.
   4617  //
   4618  // Note that at this point, the decoration rect is being calculated in line-
   4619  // relative coordinates, where 'x' is line-rightwards, and 'y' is line-
   4620  // upwards. We'll swap them to be physical coords at the end.
   4621  gfxFloat offset = 0.0;
   4622 
   4623  if (aParams.decoration == StyleTextDecorationLine::UNDERLINE) {
   4624    offset = aParams.offset;
   4625    if (canLiftUnderline) {
   4626      if (descentLimit < -offset + r.Height()) {
   4627        // If we can ignore the offset and the decoration line is overflowing,
   4628        // we should align the bottom edge of the decoration line rect if it's
   4629        // possible.  Otherwise, we should lift up the top edge of the rect as
   4630        // far as possible.
   4631        gfxFloat offsetBottomAligned = -descentLimit + r.Height();
   4632        gfxFloat offsetTopAligned = 0.0;
   4633        offset = std::min(offsetBottomAligned, offsetTopAligned);
   4634      }
   4635    }
   4636  } else if (aParams.decoration == StyleTextDecorationLine::OVERLINE) {
   4637    // For overline, we adjust the offset by defaultlineThickness (the default
   4638    // thickness of a single decoration line) because empirically it looks
   4639    // better to draw the overline just inside rather than outside the font's
   4640    // ascent, which is what nsTextFrame passes as aParams.offset (as fonts
   4641    // don't provide an explicit overline-offset).
   4642    offset = aParams.offset - defaultLineThickness + r.Height();
   4643  } else if (aParams.decoration == StyleTextDecorationLine::LINE_THROUGH) {
   4644    // To maintain a consistent mid-point for line-through decorations,
   4645    // we adjust the offset by half of the decoration rect's height.
   4646    gfxFloat extra = floor(r.Height() / 2.0 + 0.5);
   4647    extra = std::max(extra, lineThickness);
   4648    // computes offset for when user specifies a decoration width since
   4649    // aParams.offset is derived from the font metric's line height
   4650    gfxFloat decorationThicknessOffset =
   4651        (lineThickness - defaultLineThickness) / 2.0;
   4652    offset = aParams.offset - lineThickness + extra + decorationThicknessOffset;
   4653  } else {
   4654    MOZ_ASSERT_UNREACHABLE("Invalid text decoration value");
   4655  }
   4656 
   4657  // Take text decoration inset into account.
   4658  r.x += aParams.insetLeft;
   4659  r.width -= aParams.insetLeft + aParams.insetRight;
   4660  r.width = std::max(r.width, 0.0);
   4661 
   4662  // Convert line-relative coordinate system (x = line-right, y = line-up)
   4663  // to physical coords, and move the decoration rect to the calculated
   4664  // offset from baseline.
   4665  if (aParams.vertical) {
   4666    std::swap(r.x, r.y);
   4667    std::swap(r.width, r.height);
   4668    // line-upwards in vertical mode = physical-right, so we /add/ offset
   4669    // to baseline. Except in sideways-lr mode, where line-upwards will be
   4670    // physical leftwards.
   4671    if (aParams.sidewaysLeft) {
   4672      r.x = baseline - floor(offset + 0.5);
   4673    } else {
   4674      r.x = baseline + floor(offset - r.Width() + 0.5);
   4675    }
   4676  } else {
   4677    // line-upwards in horizontal mode = physical-up, but our physical coord
   4678    // system works downwards, so we /subtract/ offset from baseline.
   4679    r.y = baseline - floor(offset + 0.5);
   4680  }
   4681 
   4682  return r;
   4683 }
   4684 
   4685 #define MAX_BLUR_RADIUS 300
   4686 #define MAX_SPREAD_RADIUS 50
   4687 
   4688 static inline gfxPoint ComputeBlurStdDev(nscoord aBlurRadius,
   4689                                         int32_t aAppUnitsPerDevPixel,
   4690                                         gfxFloat aScaleX, gfxFloat aScaleY) {
   4691  // http://dev.w3.org/csswg/css3-background/#box-shadow says that the
   4692  // standard deviation of the blur should be half the given blur value.
   4693  gfxFloat blurStdDev = gfxFloat(aBlurRadius) / gfxFloat(aAppUnitsPerDevPixel);
   4694 
   4695  return gfxPoint(
   4696      std::min((blurStdDev * aScaleX), gfxFloat(MAX_BLUR_RADIUS)) / 2.0,
   4697      std::min((blurStdDev * aScaleY), gfxFloat(MAX_BLUR_RADIUS)) / 2.0);
   4698 }
   4699 
   4700 static inline IntSize ComputeBlurRadius(nscoord aBlurRadius,
   4701                                        int32_t aAppUnitsPerDevPixel,
   4702                                        gfxFloat aScaleX = 1.0,
   4703                                        gfxFloat aScaleY = 1.0) {
   4704  gfxPoint scaledBlurStdDev =
   4705      ComputeBlurStdDev(aBlurRadius, aAppUnitsPerDevPixel, aScaleX, aScaleY);
   4706  return gfxGaussianBlur::CalculateBlurRadius(scaledBlurStdDev);
   4707 }
   4708 
   4709 // -----
   4710 // nsContextBoxBlur
   4711 // -----
   4712 gfxContext* nsContextBoxBlur::Init(const nsRect& aRect, nscoord aSpreadRadius,
   4713                                   nscoord aBlurRadius,
   4714                                   int32_t aAppUnitsPerDevPixel,
   4715                                   gfxContext* aDestinationCtx,
   4716                                   const nsRect& aDirtyRect,
   4717                                   const gfxRect* aSkipRect, uint32_t aFlags) {
   4718  if (aRect.IsEmpty()) {
   4719    mContext = nullptr;
   4720    return nullptr;
   4721  }
   4722 
   4723  IntSize blurRadius;
   4724  IntSize spreadRadius;
   4725  GetBlurAndSpreadRadius(aDestinationCtx->GetDrawTarget(), aAppUnitsPerDevPixel,
   4726                         aBlurRadius, aSpreadRadius, blurRadius, spreadRadius);
   4727 
   4728  mDestinationCtx = aDestinationCtx;
   4729 
   4730  // If not blurring, draw directly onto the destination device
   4731  if (blurRadius.width <= 0 && blurRadius.height <= 0 &&
   4732      spreadRadius.width <= 0 && spreadRadius.height <= 0 &&
   4733      !(aFlags & FORCE_MASK)) {
   4734    mContext = aDestinationCtx;
   4735    return mContext;
   4736  }
   4737 
   4738  // Convert from app units to device pixels
   4739  gfxRect rect = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerDevPixel);
   4740 
   4741  gfxRect dirtyRect =
   4742      nsLayoutUtils::RectToGfxRect(aDirtyRect, aAppUnitsPerDevPixel);
   4743  dirtyRect.RoundOut();
   4744 
   4745  gfxMatrix transform = aDestinationCtx->CurrentMatrixDouble();
   4746  rect = transform.TransformBounds(rect);
   4747 
   4748  mPreTransformed = !transform.IsIdentity();
   4749 
   4750  // Create the temporary surface for blurring
   4751  dirtyRect = transform.TransformBounds(dirtyRect);
   4752  if (aSkipRect) {
   4753    gfxRect skipRect = transform.TransformBounds(*aSkipRect);
   4754    mOwnedContext = mGaussianBlur.Init(aDestinationCtx, rect, spreadRadius,
   4755                                       blurRadius, &dirtyRect, &skipRect);
   4756  } else {
   4757    mOwnedContext = mGaussianBlur.Init(aDestinationCtx, rect, spreadRadius,
   4758                                       blurRadius, &dirtyRect, nullptr);
   4759  }
   4760  mContext = mOwnedContext.get();
   4761 
   4762  if (mContext) {
   4763    // we don't need to blur if skipRect is equal to rect
   4764    // and mContext will be nullptr
   4765    mContext->Multiply(transform);
   4766  }
   4767  return mContext;
   4768 }
   4769 
   4770 void nsContextBoxBlur::DoPaint() {
   4771  if (mContext == mDestinationCtx) {
   4772    return;
   4773  }
   4774 
   4775  gfxContextMatrixAutoSaveRestore saveMatrix(mDestinationCtx);
   4776 
   4777  if (mPreTransformed) {
   4778    mDestinationCtx->SetMatrix(Matrix());
   4779  }
   4780 
   4781  mGaussianBlur.Paint(mDestinationCtx);
   4782 }
   4783 
   4784 gfxContext* nsContextBoxBlur::GetContext() { return mContext; }
   4785 
   4786 /* static */
   4787 nsMargin nsContextBoxBlur::GetBlurRadiusMargin(nscoord aBlurRadius,
   4788                                               int32_t aAppUnitsPerDevPixel) {
   4789  IntSize blurRadius = ComputeBlurRadius(aBlurRadius, aAppUnitsPerDevPixel);
   4790 
   4791  nsMargin result;
   4792  result.top = result.bottom = blurRadius.height * aAppUnitsPerDevPixel;
   4793  result.left = result.right = blurRadius.width * aAppUnitsPerDevPixel;
   4794  return result;
   4795 }
   4796 
   4797 /* static */
   4798 void nsContextBoxBlur::BlurRectangle(
   4799    gfxContext* aDestinationCtx, const nsRect& aRect,
   4800    int32_t aAppUnitsPerDevPixel, RectCornerRadii* aCornerRadii,
   4801    nscoord aBlurRadius, const sRGBColor& aShadowColor,
   4802    const nsRect& aDirtyRect, const gfxRect& aSkipRect) {
   4803  DrawTarget& aDestDrawTarget = *aDestinationCtx->GetDrawTarget();
   4804 
   4805  if (aRect.IsEmpty()) {
   4806    return;
   4807  }
   4808 
   4809  Rect shadowGfxRect = NSRectToRect(aRect, aAppUnitsPerDevPixel);
   4810 
   4811  if (aBlurRadius <= 0) {
   4812    ColorPattern color(ToDeviceColor(aShadowColor));
   4813    if (aCornerRadii) {
   4814      RefPtr<Path> roundedRect =
   4815          MakePathForRoundedRect(aDestDrawTarget, shadowGfxRect, *aCornerRadii);
   4816      aDestDrawTarget.Fill(roundedRect, color);
   4817    } else {
   4818      aDestDrawTarget.FillRect(shadowGfxRect, color);
   4819    }
   4820    return;
   4821  }
   4822 
   4823  gfxFloat scaleX = 1;
   4824  gfxFloat scaleY = 1;
   4825 
   4826  // Do blurs in device space when possible.
   4827  // Chrome/Skia always does the blurs in device space
   4828  // and will sometimes get incorrect results (e.g. rotated blurs)
   4829  gfxMatrix transform = aDestinationCtx->CurrentMatrixDouble();
   4830  // XXX: we could probably handle negative scales but for now it's easier just
   4831  // to fallback
   4832  if (!transform.HasNonAxisAlignedTransform() && transform._11 > 0.0 &&
   4833      transform._22 > 0.0) {
   4834    scaleX = transform._11;
   4835    scaleY = transform._22;
   4836    aDestinationCtx->SetMatrix(Matrix());
   4837  } else {
   4838    transform = gfxMatrix();
   4839  }
   4840 
   4841  gfxPoint blurStdDev =
   4842      ComputeBlurStdDev(aBlurRadius, aAppUnitsPerDevPixel, scaleX, scaleY);
   4843 
   4844  gfxRect dirtyRect =
   4845      nsLayoutUtils::RectToGfxRect(aDirtyRect, aAppUnitsPerDevPixel);
   4846  dirtyRect.RoundOut();
   4847 
   4848  gfxRect shadowThebesRect =
   4849      transform.TransformBounds(ThebesRect(shadowGfxRect));
   4850  dirtyRect = transform.TransformBounds(dirtyRect);
   4851  gfxRect skipRect = transform.TransformBounds(aSkipRect);
   4852 
   4853  if (aCornerRadii) {
   4854    aCornerRadii->Scale(scaleX, scaleY);
   4855  }
   4856 
   4857  gfxGaussianBlur::BlurRectangle(aDestinationCtx, shadowThebesRect,
   4858                                 aCornerRadii, blurStdDev, aShadowColor,
   4859                                 dirtyRect, skipRect);
   4860 }
   4861 
   4862 /* static */
   4863 void nsContextBoxBlur::GetBlurAndSpreadRadius(
   4864    DrawTarget* aDestDrawTarget, int32_t aAppUnitsPerDevPixel,
   4865    nscoord aBlurRadius, nscoord aSpreadRadius, IntSize& aOutBlurRadius,
   4866    IntSize& aOutSpreadRadius, bool aConstrainSpreadRadius) {
   4867  // Do blurs in device space when possible.
   4868  // Chrome/Skia always does the blurs in device space
   4869  // and will sometimes get incorrect results (e.g. rotated blurs)
   4870  Matrix transform = aDestDrawTarget->GetTransform();
   4871  // XXX: we could probably handle negative scales but for now it's easier just
   4872  // to fallback
   4873  gfxFloat scaleX, scaleY;
   4874  if (transform.HasNonAxisAlignedTransform() || transform._11 <= 0.0 ||
   4875      transform._22 <= 0.0) {
   4876    scaleX = 1;
   4877    scaleY = 1;
   4878  } else {
   4879    scaleX = transform._11;
   4880    scaleY = transform._22;
   4881  }
   4882 
   4883  // compute a large or smaller blur radius
   4884  aOutBlurRadius =
   4885      ComputeBlurRadius(aBlurRadius, aAppUnitsPerDevPixel, scaleX, scaleY);
   4886  aOutSpreadRadius =
   4887      IntSize(int32_t(aSpreadRadius * scaleX / aAppUnitsPerDevPixel),
   4888              int32_t(aSpreadRadius * scaleY / aAppUnitsPerDevPixel));
   4889 
   4890  if (aConstrainSpreadRadius) {
   4891    aOutSpreadRadius.width =
   4892        std::min(aOutSpreadRadius.width, int32_t(MAX_SPREAD_RADIUS));
   4893    aOutSpreadRadius.height =
   4894        std::min(aOutSpreadRadius.height, int32_t(MAX_SPREAD_RADIUS));
   4895  }
   4896 }
   4897 
   4898 /* static */
   4899 bool nsContextBoxBlur::InsetBoxBlur(
   4900    gfxContext* aDestinationCtx, Rect aDestinationRect, Rect aShadowClipRect,
   4901    sRGBColor& aShadowColor, nscoord aBlurRadiusAppUnits,
   4902    nscoord aSpreadDistanceAppUnits, int32_t aAppUnitsPerDevPixel,
   4903    bool aHasBorderRadius, RectCornerRadii& aInnerClipRectRadii, Rect aSkipRect,
   4904    Point aShadowOffset) {
   4905  if (aDestinationRect.IsEmpty()) {
   4906    mContext = nullptr;
   4907    return false;
   4908  }
   4909 
   4910  gfxContextAutoSaveRestore autoRestore(aDestinationCtx);
   4911 
   4912  IntSize blurRadius;
   4913  IntSize spreadRadius;
   4914  // Convert the blur and spread radius to device pixels
   4915  bool constrainSpreadRadius = false;
   4916  GetBlurAndSpreadRadius(aDestinationCtx->GetDrawTarget(), aAppUnitsPerDevPixel,
   4917                         aBlurRadiusAppUnits, aSpreadDistanceAppUnits,
   4918                         blurRadius, spreadRadius, constrainSpreadRadius);
   4919 
   4920  // The blur and spread radius are scaled already, so scale all
   4921  // input data to the blur. This way, we don't have to scale the min
   4922  // inset blur to the invert of the dest context, then rescale it back
   4923  // when we draw to the destination surface.
   4924  auto scale = aDestinationCtx->CurrentMatrix().ScaleFactors();
   4925  Matrix transform = aDestinationCtx->CurrentMatrix();
   4926 
   4927  // XXX: we could probably handle negative scales but for now it's easier just
   4928  // to fallback
   4929  if (!transform.HasNonAxisAlignedTransform() && transform._11 > 0.0 &&
   4930      transform._22 > 0.0) {
   4931    // If we don't have a rotation, we're pre-transforming all the rects.
   4932    aDestinationCtx->SetMatrix(Matrix());
   4933  } else {
   4934    // Don't touch anything, we have a rotation.
   4935    transform = Matrix();
   4936  }
   4937 
   4938  Rect transformedDestRect = transform.TransformBounds(aDestinationRect);
   4939  Rect transformedShadowClipRect = transform.TransformBounds(aShadowClipRect);
   4940  Rect transformedSkipRect = transform.TransformBounds(aSkipRect);
   4941 
   4942  transformedDestRect.Round();
   4943  transformedShadowClipRect.Round();
   4944  transformedSkipRect.RoundIn();
   4945 
   4946  for (auto corner : AllPhysicalCorners()) {
   4947    aInnerClipRectRadii[corner].width =
   4948        std::floor(scale.xScale * aInnerClipRectRadii[corner].width);
   4949    aInnerClipRectRadii[corner].height =
   4950        std::floor(scale.yScale * aInnerClipRectRadii[corner].height);
   4951  }
   4952 
   4953  mGaussianBlur.BlurInsetBox(aDestinationCtx, transformedDestRect,
   4954                             transformedShadowClipRect, blurRadius,
   4955                             aShadowColor,
   4956                             aHasBorderRadius ? &aInnerClipRectRadii : nullptr,
   4957                             transformedSkipRect, aShadowOffset);
   4958  return true;
   4959 }