LCOV - code coverage report
Current view: top level - layout/painting - nsCSSRenderingBorders.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 371 1659 22.4 %
Date: 2017-07-14 16:53:18 Functions: 21 61 34.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : // vim:cindent:ts=2:et:sw=2:
       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             : #include "nsCSSRenderingBorders.h"
       8             : 
       9             : #include "gfxUtils.h"
      10             : #include "mozilla/ArrayUtils.h"
      11             : #include "mozilla/gfx/2D.h"
      12             : #include "mozilla/gfx/Helpers.h"
      13             : #include "mozilla/gfx/PathHelpers.h"
      14             : #include "BorderConsts.h"
      15             : #include "DashedCornerFinder.h"
      16             : #include "DottedCornerFinder.h"
      17             : #include "nsLayoutUtils.h"
      18             : #include "nsStyleConsts.h"
      19             : #include "nsContentUtils.h"
      20             : #include "nsCSSColorUtils.h"
      21             : #include "GeckoProfiler.h"
      22             : #include "nsExpirationTracker.h"
      23             : #include "RoundedRect.h"
      24             : #include "nsIScriptError.h"
      25             : #include "nsClassHashtable.h"
      26             : #include "nsPresContext.h"
      27             : #include "nsStyleStruct.h"
      28             : #include "mozilla/gfx/2D.h"
      29             : #include "gfx2DGlue.h"
      30             : #include "gfxGradientCache.h"
      31             : #include "mozilla/layers/StackingContextHelper.h"
      32             : #include "mozilla/layers/WebRenderDisplayItemLayer.h"
      33             : #include "mozilla/Range.h"
      34             : #include <algorithm>
      35             : 
      36             : using namespace mozilla;
      37             : using namespace mozilla::gfx;
      38             : 
      39             : #define MAX_COMPOSITE_BORDER_WIDTH LayoutDeviceIntCoord(10000)
      40             : 
      41             : /**
      42             :  * nsCSSRendering::PaintBorder
      43             :  * nsCSSRendering::PaintOutline
      44             :  *   -> DrawBorders
      45             :  *
      46             :  * DrawBorders
      47             :  *   -> Ability to use specialized approach?
      48             :  *      |- Draw using specialized function
      49             :  *   |- separate corners?
      50             :  *   |- dashed side mask
      51             :  *   |
      52             :  *   -> can border be drawn in 1 pass? (e.g., solid border same color all around)
      53             :  *      |- DrawBorderSides with all 4 sides
      54             :  *   -> more than 1 pass?
      55             :  *      |- for each corner
      56             :  *         |- clip to DoCornerClipSubPath
      57             :  *         |- for each side adjacent to corner
      58             :  *            |- clip to GetSideClipSubPath
      59             :  *            |- DrawBorderSides with one side
      60             :  *      |- for each side
      61             :  *         |- GetSideClipWithoutCornersRect
      62             :  *         |- DrawDashedOrDottedSide || DrawBorderSides with one side
      63             :  */
      64             : 
      65             : static void ComputeBorderCornerDimensions(const Float* aBorderWidths,
      66             :                                           const RectCornerRadii& aRadii,
      67             :                                           RectCornerRadii *aDimsResult);
      68             : 
      69             : // given a side index, get the previous and next side index
      70             : #define NEXT_SIDE(_s) mozilla::Side(((_s) + 1) & 3)
      71             : #define PREV_SIDE(_s) mozilla::Side(((_s) + 3) & 3)
      72             : 
      73             : // given a corner index, get the previous and next corner index
      74             : #define NEXT_CORNER(_s) Corner(((_s) + 1) & 3)
      75             : #define PREV_CORNER(_s) Corner(((_s) + 3) & 3)
      76             : 
      77             : // from the given base color and the background color, turn
      78             : // color into a color for the given border pattern style
      79             : static Color MakeBorderColor(nscolor aColor,
      80             :                              nscolor aBackgroundColor,
      81             :                              BorderColorStyle aBorderColorStyle);
      82             : 
      83             : 
      84             : // Given a line index (an index starting from the outside of the
      85             : // border going inwards) and an array of line styles, calculate the
      86             : // color that that stripe of the border should be rendered in.
      87             : static Color ComputeColorForLine(uint32_t aLineIndex,
      88             :                                    const BorderColorStyle* aBorderColorStyle,
      89             :                                    uint32_t aBorderColorStyleCount,
      90             :                                    nscolor aBorderColor,
      91             :                                    nscolor aBackgroundColor);
      92             : 
      93             : static Color ComputeCompositeColorForLine(uint32_t aLineIndex,
      94             :                                           const nsBorderColors* aBorderColors);
      95             : 
      96             : // little helper function to check if the array of 4 floats given are
      97             : // equal to the given value
      98             : static bool
      99          34 : CheckFourFloatsEqual(const Float *vals, Float k)
     100             : {
     101          34 :   return (vals[0] == k &&
     102           0 :           vals[1] == k &&
     103          34 :           vals[2] == k &&
     104          34 :           vals[3] == k);
     105             : }
     106             : 
     107             : static bool
     108         170 : IsZeroSize(const Size& sz) {
     109         170 :   return sz.width == 0.0 || sz.height == 0.0;
     110             : }
     111             : 
     112             : /* static */ bool
     113          68 : nsCSSBorderRenderer::AllCornersZeroSize(const RectCornerRadii& corners) {
     114         102 :   return IsZeroSize(corners[eCornerTopLeft]) &&
     115          68 :     IsZeroSize(corners[eCornerTopRight]) &&
     116         136 :     IsZeroSize(corners[eCornerBottomRight]) &&
     117         102 :     IsZeroSize(corners[eCornerBottomLeft]);
     118             : }
     119             : 
     120             : static mozilla::Side
     121           0 : GetHorizontalSide(Corner aCorner)
     122             : {
     123           0 :   return (aCorner == C_TL || aCorner == C_TR) ? eSideTop : eSideBottom;
     124             : }
     125             : 
     126             : static mozilla::Side
     127           0 : GetVerticalSide(Corner aCorner)
     128             : {
     129           0 :   return (aCorner == C_TL || aCorner == C_BL) ? eSideLeft : eSideRight;
     130             : }
     131             : 
     132             : static Corner
     133           0 : GetCWCorner(mozilla::Side aSide)
     134             : {
     135           0 :   return Corner(NEXT_SIDE(aSide));
     136             : }
     137             : 
     138             : static Corner
     139           0 : GetCCWCorner(mozilla::Side aSide)
     140             : {
     141           0 :   return Corner(aSide);
     142             : }
     143             : 
     144             : static bool
     145           0 : IsSingleSide(int aSides)
     146             : {
     147           0 :   return aSides == eSideBitsTop || aSides == eSideBitsRight ||
     148           0 :          aSides == eSideBitsBottom || aSides == eSideBitsLeft;
     149             : }
     150             : 
     151             : static bool
     152           0 : IsHorizontalSide(mozilla::Side aSide)
     153             : {
     154           0 :   return aSide == eSideTop || aSide == eSideBottom;
     155             : }
     156             : 
     157             : typedef enum {
     158             :   // Normal solid square corner.  Will be rectangular, the size of the
     159             :   // adjacent sides.  If the corner has a border radius, the corner
     160             :   // will always be solid, since we don't do dotted/dashed etc.
     161             :   CORNER_NORMAL,
     162             : 
     163             :   // Paint the corner in whatever style is not dotted/dashed of the
     164             :   // adjacent corners.
     165             :   CORNER_SOLID,
     166             : 
     167             :   // Paint the corner as a dot, the size of the bigger of the adjacent
     168             :   // sides.
     169             :   CORNER_DOT
     170             : } CornerStyle;
     171             : 
     172          34 : nsCSSBorderRenderer::nsCSSBorderRenderer(nsPresContext* aPresContext,
     173             :                                          const nsIDocument* aDocument,
     174             :                                          DrawTarget* aDrawTarget,
     175             :                                          const Rect& aDirtyRect,
     176             :                                          Rect& aOuterRect,
     177             :                                          const uint8_t* aBorderStyles,
     178             :                                          const Float* aBorderWidths,
     179             :                                          RectCornerRadii& aBorderRadii,
     180             :                                          const nscolor* aBorderColors,
     181             :                                          nsBorderColors* const* aCompositeColors,
     182          34 :                                          nscolor aBackgroundColor)
     183             :   : mPresContext(aPresContext),
     184             :     mDocument(aDocument),
     185             :     mDrawTarget(aDrawTarget),
     186             :     mDirtyRect(aDirtyRect),
     187             :     mOuterRect(aOuterRect),
     188             :     mBorderRadii(aBorderRadii),
     189          34 :     mBackgroundColor(aBackgroundColor)
     190             : {
     191          34 :   PodCopy(mBorderStyles, aBorderStyles, 4);
     192          34 :   PodCopy(mBorderWidths, aBorderWidths, 4);
     193          34 :   PodCopy(mBorderColors, aBorderColors, 4);
     194          34 :   if (aCompositeColors) {
     195          34 :     PodCopy(mCompositeColors, aCompositeColors, 4);
     196             :   } else {
     197             :     static nsBorderColors * const noColors[4] = { nullptr };
     198           0 :     PodCopy(mCompositeColors, noColors, 4);
     199             :   }
     200             : 
     201          34 :   mInnerRect = mOuterRect;
     202          34 :   mInnerRect.Deflate(
     203         136 :       Margin(mBorderStyles[0] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[0] : 0,
     204          34 :              mBorderStyles[1] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[1] : 0,
     205          34 :              mBorderStyles[2] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[2] : 0,
     206          68 :              mBorderStyles[3] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[3] : 0));
     207             : 
     208          34 :   ComputeBorderCornerDimensions(mBorderWidths,
     209          34 :                                 mBorderRadii, &mBorderCornerDimensions);
     210             : 
     211          34 :   mOneUnitBorder = CheckFourFloatsEqual(mBorderWidths, 1.0);
     212          34 :   mNoBorderRadius = AllCornersZeroSize(mBorderRadii);
     213          34 :   mAvoidStroke = false;
     214          34 : }
     215             : 
     216             : /* static */ void
     217          17 : nsCSSBorderRenderer::ComputeInnerRadii(const RectCornerRadii& aRadii,
     218             :                                        const Float* aBorderSizes,
     219             :                                        RectCornerRadii* aInnerRadiiRet)
     220             : {
     221          17 :   RectCornerRadii& iRadii = *aInnerRadiiRet;
     222             : 
     223          17 :   iRadii[C_TL].width = std::max(0.f, aRadii[C_TL].width - aBorderSizes[eSideLeft]);
     224          17 :   iRadii[C_TL].height = std::max(0.f, aRadii[C_TL].height - aBorderSizes[eSideTop]);
     225             : 
     226          17 :   iRadii[C_TR].width = std::max(0.f, aRadii[C_TR].width - aBorderSizes[eSideRight]);
     227          17 :   iRadii[C_TR].height = std::max(0.f, aRadii[C_TR].height - aBorderSizes[eSideTop]);
     228             : 
     229          17 :   iRadii[C_BR].width = std::max(0.f, aRadii[C_BR].width - aBorderSizes[eSideRight]);
     230          17 :   iRadii[C_BR].height = std::max(0.f, aRadii[C_BR].height - aBorderSizes[eSideBottom]);
     231             : 
     232          17 :   iRadii[C_BL].width = std::max(0.f, aRadii[C_BL].width - aBorderSizes[eSideLeft]);
     233          17 :   iRadii[C_BL].height = std::max(0.f, aRadii[C_BL].height - aBorderSizes[eSideBottom]);
     234          17 : }
     235             : 
     236             : /* static */ void
     237           9 : nsCSSBorderRenderer::ComputeOuterRadii(const RectCornerRadii& aRadii,
     238             :                                        const Float* aBorderSizes,
     239             :                                        RectCornerRadii* aOuterRadiiRet)
     240             : {
     241           9 :   RectCornerRadii& oRadii = *aOuterRadiiRet;
     242             : 
     243             :   // default all corners to sharp corners
     244           9 :   oRadii = RectCornerRadii(0.f);
     245             : 
     246             :   // round the edges that have radii > 0.0 to start with
     247           9 :   if (aRadii[C_TL].width > 0.f && aRadii[C_TL].height > 0.f) {
     248           9 :     oRadii[C_TL].width = std::max(0.f, aRadii[C_TL].width + aBorderSizes[eSideLeft]);
     249           9 :     oRadii[C_TL].height = std::max(0.f, aRadii[C_TL].height + aBorderSizes[eSideTop]);
     250             :   }
     251             : 
     252           9 :   if (aRadii[C_TR].width > 0.f && aRadii[C_TR].height > 0.f) {
     253           9 :     oRadii[C_TR].width = std::max(0.f, aRadii[C_TR].width + aBorderSizes[eSideRight]);
     254           9 :     oRadii[C_TR].height = std::max(0.f, aRadii[C_TR].height + aBorderSizes[eSideTop]);
     255             :   }
     256             : 
     257           9 :   if (aRadii[C_BR].width > 0.f && aRadii[C_BR].height > 0.f) {
     258           9 :     oRadii[C_BR].width = std::max(0.f, aRadii[C_BR].width + aBorderSizes[eSideRight]);
     259           9 :     oRadii[C_BR].height = std::max(0.f, aRadii[C_BR].height + aBorderSizes[eSideBottom]);
     260             :   }
     261             : 
     262           9 :   if (aRadii[C_BL].width > 0.f && aRadii[C_BL].height > 0.f) {
     263           9 :     oRadii[C_BL].width = std::max(0.f, aRadii[C_BL].width + aBorderSizes[eSideLeft]);
     264           9 :     oRadii[C_BL].height = std::max(0.f, aRadii[C_BL].height + aBorderSizes[eSideBottom]);
     265             :   }
     266           9 : }
     267             : 
     268             : /*static*/ void
     269          34 : ComputeBorderCornerDimensions(const Float* aBorderWidths,
     270             :                               const RectCornerRadii& aRadii,
     271             :                               RectCornerRadii* aDimsRet)
     272             : {
     273          34 :   Float leftWidth = aBorderWidths[eSideLeft];
     274          34 :   Float topWidth = aBorderWidths[eSideTop];
     275          34 :   Float rightWidth = aBorderWidths[eSideRight];
     276          34 :   Float bottomWidth = aBorderWidths[eSideBottom];
     277             : 
     278          34 :   if (nsCSSBorderRenderer::AllCornersZeroSize(aRadii)) {
     279             :     // These will always be in pixel units from CSS
     280          17 :     (*aDimsRet)[C_TL] = Size(leftWidth, topWidth);
     281          17 :     (*aDimsRet)[C_TR] = Size(rightWidth, topWidth);
     282          17 :     (*aDimsRet)[C_BR] = Size(rightWidth, bottomWidth);
     283          17 :     (*aDimsRet)[C_BL] = Size(leftWidth, bottomWidth);
     284             :   } else {
     285             :     // Always round up to whole pixels for the corners; it's safe to
     286             :     // make the corners bigger than necessary, and this way we ensure
     287             :     // that we avoid seams.
     288          34 :     (*aDimsRet)[C_TL] = Size(ceil(std::max(leftWidth, aRadii[C_TL].width)),
     289          17 :                              ceil(std::max(topWidth, aRadii[C_TL].height)));
     290          34 :     (*aDimsRet)[C_TR] = Size(ceil(std::max(rightWidth, aRadii[C_TR].width)),
     291          17 :                              ceil(std::max(topWidth, aRadii[C_TR].height)));
     292          34 :     (*aDimsRet)[C_BR] = Size(ceil(std::max(rightWidth, aRadii[C_BR].width)),
     293          17 :                              ceil(std::max(bottomWidth, aRadii[C_BR].height)));
     294          34 :     (*aDimsRet)[C_BL] = Size(ceil(std::max(leftWidth, aRadii[C_BL].width)),
     295          17 :                              ceil(std::max(bottomWidth, aRadii[C_BL].height)));
     296             :   }
     297          34 : }
     298             : 
     299             : bool
     300         102 : nsCSSBorderRenderer::AreBorderSideFinalStylesSame(uint8_t aSides)
     301             : {
     302         102 :   NS_ASSERTION(aSides != 0 && (aSides & ~eSideBitsAll) == 0,
     303             :                "AreBorderSidesSame: invalid whichSides!");
     304             : 
     305             :   /* First check if the specified styles and colors are the same for all sides */
     306         102 :   int firstStyle = 0;
     307         425 :   NS_FOR_CSS_SIDES (i) {
     308         367 :     if (firstStyle == i) {
     309         136 :       if (((1 << i) & aSides) == 0)
     310          34 :         firstStyle++;
     311         136 :       continue;
     312             :     }
     313             : 
     314         231 :     if (((1 << i) & aSides) == 0) {
     315          90 :       continue;
     316             :     }
     317             : 
     318         379 :     if (mBorderStyles[firstStyle] != mBorderStyles[i] ||
     319         238 :         mBorderColors[firstStyle] != mBorderColors[i] ||
     320         194 :         !nsBorderColors::Equal(mCompositeColors[firstStyle],
     321          97 :                                mCompositeColors[i]))
     322          44 :       return false;
     323             :   }
     324             : 
     325             :   /* Then if it's one of the two-tone styles and we're not
     326             :    * just comparing the TL or BR sides */
     327          58 :   switch (mBorderStyles[firstStyle]) {
     328             :     case NS_STYLE_BORDER_STYLE_GROOVE:
     329             :     case NS_STYLE_BORDER_STYLE_RIDGE:
     330             :     case NS_STYLE_BORDER_STYLE_INSET:
     331             :     case NS_STYLE_BORDER_STYLE_OUTSET:
     332           0 :       return ((aSides & ~(eSideBitsTop | eSideBitsLeft)) == 0 ||
     333           0 :               (aSides & ~(eSideBitsBottom | eSideBitsRight)) == 0);
     334             :   }
     335             : 
     336          58 :   return true;
     337             : }
     338             : 
     339             : bool
     340           0 : nsCSSBorderRenderer::IsSolidCornerStyle(uint8_t aStyle, Corner aCorner)
     341             : {
     342           0 :   switch (aStyle) {
     343             :     case NS_STYLE_BORDER_STYLE_SOLID:
     344           0 :       return true;
     345             : 
     346             :     case NS_STYLE_BORDER_STYLE_INSET:
     347             :     case NS_STYLE_BORDER_STYLE_OUTSET:
     348           0 :       return (aCorner == eCornerTopLeft || aCorner == eCornerBottomRight);
     349             : 
     350             :     case NS_STYLE_BORDER_STYLE_GROOVE:
     351             :     case NS_STYLE_BORDER_STYLE_RIDGE:
     352           0 :       return mOneUnitBorder && (aCorner == eCornerTopLeft || aCorner == eCornerBottomRight);
     353             : 
     354             :     case NS_STYLE_BORDER_STYLE_DOUBLE:
     355           0 :       return mOneUnitBorder;
     356             : 
     357             :     default:
     358           0 :       return false;
     359             :   }
     360             : }
     361             : 
     362             : bool
     363           0 : nsCSSBorderRenderer::IsCornerMergeable(Corner aCorner)
     364             : {
     365             :   // Corner between dotted borders with same width and small radii is
     366             :   // merged into single dot.
     367             :   //
     368             :   //  widthH / 2.0
     369             :   // |<---------->|
     370             :   // |            |
     371             :   // |radius.width|
     372             :   // |<--->|      |
     373             :   // |     |      |
     374             :   // |    _+------+------------+-----
     375             :   // |  /      ###|###         |
     376             :   // |/    #######|#######     |
     377             :   // +   #########|#########   |
     378             :   // |  ##########|##########  |
     379             :   // | ###########|########### |
     380             :   // | ###########|########### |
     381             :   // |############|############|
     382             :   // +------------+############|
     383             :   // |#########################|
     384             :   // | ####################### |
     385             :   // | ####################### |
     386             :   // |  #####################  |
     387             :   // |   ###################   |
     388             :   // |     ###############     |
     389             :   // |         #######         |
     390             :   // +-------------------------+----
     391             :   // |                         |
     392             :   // |                         |
     393           0 :   mozilla::Side sideH(GetHorizontalSide(aCorner));
     394           0 :   mozilla::Side sideV(GetVerticalSide(aCorner));
     395           0 :   uint8_t styleH = mBorderStyles[sideH];
     396           0 :   uint8_t styleV = mBorderStyles[sideV];
     397           0 :   if (styleH != styleV || styleH != NS_STYLE_BORDER_STYLE_DOTTED) {
     398           0 :     return false;
     399             :   }
     400             : 
     401           0 :   Float widthH = mBorderWidths[sideH];
     402           0 :   Float widthV = mBorderWidths[sideV];
     403           0 :   if (widthH != widthV) {
     404           0 :     return false;
     405             :   }
     406             : 
     407           0 :   Size radius = mBorderRadii[aCorner];
     408           0 :   return IsZeroSize(radius) ||
     409           0 :          (radius.width < widthH / 2.0f && radius.height < widthH / 2.0f);
     410             : }
     411             : 
     412             : BorderColorStyle
     413           0 : nsCSSBorderRenderer::BorderColorStyleForSolidCorner(uint8_t aStyle, Corner aCorner)
     414             : {
     415             :   // note that this function assumes that the corner is already solid,
     416             :   // as per the earlier function
     417           0 :   switch (aStyle) {
     418             :     case NS_STYLE_BORDER_STYLE_SOLID:
     419             :     case NS_STYLE_BORDER_STYLE_DOUBLE:
     420           0 :       return BorderColorStyleSolid;
     421             : 
     422             :     case NS_STYLE_BORDER_STYLE_INSET:
     423             :     case NS_STYLE_BORDER_STYLE_GROOVE:
     424           0 :       if (aCorner == eCornerTopLeft)
     425           0 :         return BorderColorStyleDark;
     426           0 :       else if (aCorner == eCornerBottomRight)
     427           0 :         return BorderColorStyleLight;
     428           0 :       break;
     429             : 
     430             :     case NS_STYLE_BORDER_STYLE_OUTSET:
     431             :     case NS_STYLE_BORDER_STYLE_RIDGE:
     432           0 :       if (aCorner == eCornerTopLeft)
     433           0 :         return BorderColorStyleLight;
     434           0 :       else if (aCorner == eCornerBottomRight)
     435           0 :         return BorderColorStyleDark;
     436           0 :       break;
     437             :   }
     438             : 
     439           0 :   return BorderColorStyleNone;
     440             : }
     441             : 
     442             : Rect
     443           0 : nsCSSBorderRenderer::GetCornerRect(Corner aCorner)
     444             : {
     445           0 :   Point offset(0.f, 0.f);
     446             : 
     447           0 :   if (aCorner == C_TR || aCorner == C_BR)
     448           0 :     offset.x = mOuterRect.Width() - mBorderCornerDimensions[aCorner].width;
     449           0 :   if (aCorner == C_BR || aCorner == C_BL)
     450           0 :     offset.y = mOuterRect.Height() - mBorderCornerDimensions[aCorner].height;
     451             : 
     452           0 :   return Rect(mOuterRect.TopLeft() + offset,
     453           0 :               mBorderCornerDimensions[aCorner]);
     454             : }
     455             : 
     456             : Rect
     457           0 : nsCSSBorderRenderer::GetSideClipWithoutCornersRect(mozilla::Side aSide)
     458             : {
     459           0 :   Point offset(0.f, 0.f);
     460             : 
     461             :   // The offset from the outside rect to the start of this side's
     462             :   // box.  For the top and bottom sides, the height of the box
     463             :   // must be the border height; the x start must take into account
     464             :   // the corner size (which may be bigger than the right or left
     465             :   // side's width).  The same applies to the right and left sides.
     466           0 :   if (aSide == eSideTop) {
     467           0 :     offset.x = mBorderCornerDimensions[C_TL].width;
     468           0 :   } else if (aSide == eSideRight) {
     469           0 :     offset.x = mOuterRect.Width() - mBorderWidths[eSideRight];
     470           0 :     offset.y = mBorderCornerDimensions[C_TR].height;
     471           0 :   } else if (aSide == eSideBottom) {
     472           0 :     offset.x = mBorderCornerDimensions[C_BL].width;
     473           0 :     offset.y = mOuterRect.Height() - mBorderWidths[eSideBottom];
     474           0 :   } else if (aSide == eSideLeft) {
     475           0 :     offset.y = mBorderCornerDimensions[C_TL].height;
     476             :   }
     477             : 
     478             :   // The sum of the width & height of the corners adjacent to the
     479             :   // side.  This relies on the relationship between side indexing and
     480             :   // corner indexing; that is, 0 == SIDE_TOP and 0 == CORNER_TOP_LEFT,
     481             :   // with both proceeding clockwise.
     482           0 :   Size sideCornerSum = mBorderCornerDimensions[GetCCWCorner(aSide)]
     483           0 :                      + mBorderCornerDimensions[GetCWCorner(aSide)];
     484           0 :   Rect rect(mOuterRect.TopLeft() + offset,
     485           0 :             mOuterRect.Size() - sideCornerSum);
     486             : 
     487           0 :   if (IsHorizontalSide(aSide))
     488           0 :     rect.height = mBorderWidths[aSide];
     489             :   else
     490           0 :     rect.width = mBorderWidths[aSide];
     491             : 
     492           0 :   return rect;
     493             : }
     494             : 
     495             : // The side border type and the adjacent border types are
     496             : // examined and one of the different types of clipping (listed
     497             : // below) is selected.
     498             : 
     499             : typedef enum {
     500             :   // clip to the trapezoid formed by the corners of the
     501             :   // inner and outer rectangles for the given side
     502             :   //
     503             :   // +---------------
     504             :   // |\%%%%%%%%%%%%%%
     505             :   // |  \%%%%%%%%%%%%
     506             :   // |   \%%%%%%%%%%%
     507             :   // |     \%%%%%%%%%
     508             :   // |      +--------
     509             :   // |      |
     510             :   // |      |
     511             :   SIDE_CLIP_TRAPEZOID,
     512             : 
     513             :   // clip to the trapezoid formed by the outer rectangle
     514             :   // corners and the center of the region, making sure
     515             :   // that diagonal lines all go directly from the outside
     516             :   // corner to the inside corner, but that they then continue on
     517             :   // to the middle.
     518             :   //
     519             :   // This is needed for correctly clipping rounded borders,
     520             :   // which might extend past the SIDE_CLIP_TRAPEZOID trap.
     521             :   //
     522             :   // +-------__--+---
     523             :   //  \%%%%_-%%%%%%%%
     524             :   //    \+-%%%%%%%%%%
     525             :   //    / \%%%%%%%%%%
     526             :   //   /   \%%%%%%%%%
     527             :   //  |     +%%_-+---
     528             :   // |        +%%%%%%
     529             :   // |       / \%%%%%
     530             :   // +      +    \%%%
     531             :   // |      |      +-
     532             :   SIDE_CLIP_TRAPEZOID_FULL,
     533             : 
     534             :   // clip to the rectangle formed by the given side including corner.
     535             :   // This is used by the non-dotted side next to dotted side.
     536             :   //
     537             :   // +---------------
     538             :   // |%%%%%%%%%%%%%%%
     539             :   // |%%%%%%%%%%%%%%%
     540             :   // |%%%%%%%%%%%%%%%
     541             :   // |%%%%%%%%%%%%%%%
     542             :   // +------+--------
     543             :   // |      |
     544             :   // |      |
     545             :   SIDE_CLIP_RECTANGLE_CORNER,
     546             : 
     547             :   // clip to the rectangle formed by the given side excluding corner.
     548             :   // This is used by the dotted side next to non-dotted side.
     549             :   //
     550             :   // +------+--------
     551             :   // |      |%%%%%%%%
     552             :   // |      |%%%%%%%%
     553             :   // |      |%%%%%%%%
     554             :   // |      |%%%%%%%%
     555             :   // |      +--------
     556             :   // |      |
     557             :   // |      |
     558             :   SIDE_CLIP_RECTANGLE_NO_CORNER,
     559             : } SideClipType;
     560             : 
     561             : // Given three points, p0, p1, and midPoint, move p1 further in to the
     562             : // rectangle (of which aMidPoint is the center) so that it reaches the
     563             : // closer of the horizontal or vertical lines intersecting the midpoint,
     564             : // while maintaing the slope of the line.  If p0 and p1 are the same,
     565             : // just move p1 to midPoint (since there's no slope to maintain).
     566             : // FIXME: Extending only to the midpoint isn't actually sufficient for
     567             : // boxes with asymmetric radii.
     568             : static void
     569           0 : MaybeMoveToMidPoint(Point& aP0, Point& aP1, const Point& aMidPoint)
     570             : {
     571           0 :   Point ps = aP1 - aP0;
     572             : 
     573           0 :   if (ps.x == 0.0) {
     574           0 :     if (ps.y == 0.0) {
     575           0 :       aP1 = aMidPoint;
     576             :     } else {
     577           0 :       aP1.y = aMidPoint.y;
     578             :     }
     579             :   } else {
     580           0 :     if (ps.y == 0.0) {
     581           0 :       aP1.x = aMidPoint.x;
     582             :     } else {
     583           0 :       Float k = std::min((aMidPoint.x - aP0.x) / ps.x,
     584           0 :                          (aMidPoint.y - aP0.y) / ps.y);
     585           0 :       aP1 = aP0 + ps * k;
     586             :     }
     587             :   }
     588           0 : }
     589             : 
     590             : already_AddRefed<Path>
     591           0 : nsCSSBorderRenderer::GetSideClipSubPath(mozilla::Side aSide)
     592             : {
     593             :   // the clip proceeds clockwise from the top left corner;
     594             :   // so "start" in each case is the start of the region from that side.
     595             :   //
     596             :   // the final path will be formed like:
     597             :   // s0 ------- e0
     598             :   // |         /
     599             :   // s1 ----- e1
     600             :   //
     601             :   // that is, the second point will always be on the inside
     602             : 
     603           0 :   Point start[2];
     604           0 :   Point end[2];
     605             : 
     606             : #define IS_DOTTED(_s)  ((_s) == NS_STYLE_BORDER_STYLE_DOTTED)
     607           0 :   bool isDotted      = IS_DOTTED(mBorderStyles[aSide]);
     608           0 :   bool startIsDotted = IS_DOTTED(mBorderStyles[PREV_SIDE(aSide)]);
     609           0 :   bool endIsDotted   = IS_DOTTED(mBorderStyles[NEXT_SIDE(aSide)]);
     610             : #undef IS_DOTTED
     611             : 
     612           0 :   SideClipType startType = SIDE_CLIP_TRAPEZOID;
     613           0 :   SideClipType endType = SIDE_CLIP_TRAPEZOID;
     614             : 
     615           0 :   if (!IsZeroSize(mBorderRadii[GetCCWCorner(aSide)])) {
     616           0 :     startType = SIDE_CLIP_TRAPEZOID_FULL;
     617           0 :   } else if (startIsDotted && !isDotted) {
     618           0 :     startType = SIDE_CLIP_RECTANGLE_CORNER;
     619           0 :   } else if (!startIsDotted && isDotted) {
     620           0 :     startType = SIDE_CLIP_RECTANGLE_NO_CORNER;
     621             :   }
     622             : 
     623           0 :   if (!IsZeroSize(mBorderRadii[GetCWCorner(aSide)])) {
     624           0 :     endType = SIDE_CLIP_TRAPEZOID_FULL;
     625           0 :   } else if (endIsDotted && !isDotted) {
     626           0 :     endType = SIDE_CLIP_RECTANGLE_CORNER;
     627           0 :   } else if (!endIsDotted && isDotted) {
     628           0 :     endType = SIDE_CLIP_RECTANGLE_NO_CORNER;
     629             :   }
     630             : 
     631           0 :   Point midPoint = mInnerRect.Center();
     632             : 
     633           0 :   start[0] = mOuterRect.CCWCorner(aSide);
     634           0 :   start[1] = mInnerRect.CCWCorner(aSide);
     635             : 
     636           0 :   end[0] = mOuterRect.CWCorner(aSide);
     637           0 :   end[1] = mInnerRect.CWCorner(aSide);
     638             : 
     639           0 :   if (startType == SIDE_CLIP_TRAPEZOID_FULL) {
     640           0 :     MaybeMoveToMidPoint(start[0], start[1], midPoint);
     641           0 :   } else if (startType == SIDE_CLIP_RECTANGLE_CORNER) {
     642           0 :     if (IsHorizontalSide(aSide)) {
     643           0 :       start[1] = Point(mOuterRect.CCWCorner(aSide).x, mInnerRect.CCWCorner(aSide).y);
     644             :     } else {
     645           0 :       start[1] = Point(mInnerRect.CCWCorner(aSide).x, mOuterRect.CCWCorner(aSide).y);
     646             :     }
     647           0 :   } else if (startType == SIDE_CLIP_RECTANGLE_NO_CORNER) {
     648           0 :     if (IsHorizontalSide(aSide)) {
     649           0 :       start[0] = Point(mInnerRect.CCWCorner(aSide).x, mOuterRect.CCWCorner(aSide).y);
     650             :     } else {
     651           0 :       start[0] = Point(mOuterRect.CCWCorner(aSide).x, mInnerRect.CCWCorner(aSide).y);
     652             :     }
     653             :   }
     654             : 
     655           0 :   if (endType == SIDE_CLIP_TRAPEZOID_FULL) {
     656           0 :     MaybeMoveToMidPoint(end[0], end[1], midPoint);
     657           0 :   } else if (endType == SIDE_CLIP_RECTANGLE_CORNER) {
     658           0 :     if (IsHorizontalSide(aSide)) {
     659           0 :       end[1] = Point(mOuterRect.CWCorner(aSide).x, mInnerRect.CWCorner(aSide).y);
     660             :     } else {
     661           0 :       end[1] = Point(mInnerRect.CWCorner(aSide).x, mOuterRect.CWCorner(aSide).y);
     662             :     }
     663           0 :   } else if (endType == SIDE_CLIP_RECTANGLE_NO_CORNER) {
     664           0 :     if (IsHorizontalSide(aSide)) {
     665           0 :       end[0] = Point(mInnerRect.CWCorner(aSide).x, mOuterRect.CWCorner(aSide).y);
     666             :     } else {
     667           0 :       end[0] = Point(mOuterRect.CWCorner(aSide).x, mInnerRect.CWCorner(aSide).y);
     668             :     }
     669             :   }
     670             : 
     671           0 :   RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
     672           0 :   builder->MoveTo(start[0]);
     673           0 :   builder->LineTo(end[0]);
     674           0 :   builder->LineTo(end[1]);
     675           0 :   builder->LineTo(start[1]);
     676           0 :   builder->Close();
     677           0 :   return builder->Finish();
     678             : }
     679             : 
     680             : Point
     681           0 : nsCSSBorderRenderer::GetStraightBorderPoint(mozilla::Side aSide,
     682             :                                             Corner aCorner,
     683             :                                             bool* aIsUnfilled,
     684             :                                             Float aDotOffset)
     685             : {
     686             :   // Calculate the end point of the side for dashed/dotted border, that is also
     687             :   // the end point of the corner curve.  The point is specified by aSide and
     688             :   // aCorner. (e.g. eSideTop and C_TL means the left end of border-top)
     689             :   //
     690             :   //
     691             :   //  aCorner        aSide
     692             :   //         +--------------------
     693             :   //         |
     694             :   //         |
     695             :   //         |         +----------
     696             :   //         |    the end point
     697             :   //         |
     698             :   //         |         +----------
     699             :   //         |         |
     700             :   //         |         |
     701             :   //         |         |
     702             :   //
     703             :   // The position of the point depends on the border-style, border-width, and
     704             :   // border-radius of the side, corner, and the adjacent side beyond the corner,
     705             :   // to make those sides (and corner) interact well.
     706             :   //
     707             :   // If the style of aSide is dotted and the dot at the point should be
     708             :   // unfilled, true is stored to *aIsUnfilled, otherwise false is stored.
     709             : 
     710             :   const Float signsList[4][2] = {
     711             :     { +1.0f, +1.0f },
     712             :     { -1.0f, +1.0f },
     713             :     { -1.0f, -1.0f },
     714             :     { +1.0f, -1.0f }
     715           0 :   };
     716           0 :   const Float (& signs)[2] = signsList[aCorner];
     717             : 
     718           0 :   *aIsUnfilled = false;
     719             : 
     720           0 :   Point P = mOuterRect.AtCorner(aCorner);
     721           0 :   uint8_t style = mBorderStyles[aSide];
     722           0 :   Float borderWidth = mBorderWidths[aSide];
     723           0 :   Size dim = mBorderCornerDimensions[aCorner];
     724           0 :   bool isHorizontal = IsHorizontalSide(aSide);
     725             :   //
     726             :   //    aCorner      aSide
     727             :   //           +--------------
     728             :   //           |
     729             :   //           |   +----------
     730             :   //           |   |
     731             :   // otherSide |   |
     732             :   //           |   |
     733           0 :   mozilla::Side otherSide = ((uint8_t)aSide == (uint8_t)aCorner)
     734           0 :                                  ? PREV_SIDE(aSide)
     735           0 :                                  : NEXT_SIDE(aSide);
     736           0 :   uint8_t otherStyle = mBorderStyles[otherSide];
     737           0 :   Float otherBorderWidth = mBorderWidths[otherSide];
     738           0 :   Size radius = mBorderRadii[aCorner];
     739           0 :   if (IsZeroSize(radius)) {
     740           0 :     radius.width = 0.0f;
     741           0 :     radius.height = 0.0f;
     742             :   }
     743           0 :   if (style == NS_STYLE_BORDER_STYLE_DOTTED) {
     744             :     // Offset the dot's location along the side toward the corner by a
     745             :     // multiple of its width.
     746           0 :     if (isHorizontal) {
     747           0 :       P.x -= signs[0] * aDotOffset * borderWidth;
     748             :     } else {
     749           0 :       P.y -= signs[1] * aDotOffset * borderWidth;
     750             :     }
     751             :   }
     752           0 :   if (style == NS_STYLE_BORDER_STYLE_DOTTED &&
     753             :       otherStyle == NS_STYLE_BORDER_STYLE_DOTTED) {
     754           0 :     if (borderWidth == otherBorderWidth) {
     755           0 :       if (radius.width < borderWidth / 2.0f &&
     756           0 :           radius.height < borderWidth / 2.0f) {
     757             :         // Two dots are merged into one and placed at the corner.
     758             :         //
     759             :         //  borderWidth / 2.0
     760             :         // |<---------->|
     761             :         // |            |
     762             :         // |radius.width|
     763             :         // |<--->|      |
     764             :         // |     |      |
     765             :         // |    _+------+------------+-----
     766             :         // |  /      ###|###         |
     767             :         // |/    #######|#######     |
     768             :         // +   #########|#########   |
     769             :         // |  ##########|##########  |
     770             :         // | ###########|########### |
     771             :         // | ###########|########### |
     772             :         // |############|############|
     773             :         // +------------+############|
     774             :         // |########### P ###########|
     775             :         // | ####################### |
     776             :         // | ####################### |
     777             :         // |  #####################  |
     778             :         // |   ###################   |
     779             :         // |     ###############     |
     780             :         // |         #######         |
     781             :         // +-------------------------+----
     782             :         // |                         |
     783             :         // |                         |
     784           0 :         P.x += signs[0] * borderWidth / 2.0f;
     785           0 :         P.y += signs[1] * borderWidth / 2.0f;
     786             :       } else {
     787             :         // Two dots are drawn separately.
     788             :         //
     789             :         //    borderWidth * 1.5
     790             :         //   |<------------>|
     791             :         //   |              |
     792             :         //   |radius.width  |
     793             :         //   |<----->|      |
     794             :         //   |       |      |
     795             :         //   |    _--+-+----+---
     796             :         //   |  _-     |  ##|##
     797             :         //   | /       | ###|###
     798             :         //   |/        |####|####
     799             :         //   |         |####+####
     800             :         //   |         |### P ###
     801             :         //   +         | ###|###
     802             :         //   |         |  ##|##
     803             :         //   +---------+----+---
     804             :         //   |  #####  |
     805             :         //   | ####### |
     806             :         //   |#########|
     807             :         //   +----+----+
     808             :         //   |#########|
     809             :         //   | ####### |
     810             :         //   |  #####  |
     811             :         //   |         |
     812             :         //
     813             :         // There should be enough gap between 2 dots even if radius.width is
     814             :         // small but larger than borderWidth / 2.0.  borderWidth * 1.5 is the
     815             :         // value that there's imaginally unfilled dot at the corner.  The
     816             :         // unfilled dot may overflow from the outer curve, but filled dots
     817             :         // doesn't, so this could be acceptable solution at least for now.
     818             :         // We may have to find better model/value.
     819             :         //
     820             :         //    imaginally unfilled dot at the corner
     821             :         //        |
     822             :         //        v    +----+---
     823             :         //      *****  |  ##|##
     824             :         //     ******* | ###|###
     825             :         //    *********|####|####
     826             :         //    *********|####+####
     827             :         //    *********|### P ###
     828             :         //     ******* | ###|###
     829             :         //      *****  |  ##|##
     830             :         //   +---------+----+---
     831             :         //   |  #####  |
     832             :         //   | ####### |
     833             :         //   |#########|
     834             :         //   +----+----+
     835             :         //   |#########|
     836             :         //   | ####### |
     837             :         //   |  #####  |
     838             :         //   |         |
     839           0 :         Float minimum = borderWidth * 1.5f;
     840           0 :         if (isHorizontal) {
     841           0 :           P.x += signs[0] * std::max(radius.width, minimum);
     842           0 :           P.y += signs[1] * borderWidth / 2.0f;
     843             :         } else {
     844           0 :           P.x += signs[0] * borderWidth / 2.0f;
     845           0 :           P.y += signs[1] * std::max(radius.height, minimum);
     846             :         }
     847             :       }
     848             : 
     849           0 :       return P;
     850             :     }
     851             : 
     852           0 :     if (borderWidth < otherBorderWidth) {
     853             :       // This side is smaller than other side, other side draws the corner.
     854             :       //
     855             :       //  otherBorderWidth + borderWidth / 2.0
     856             :       // |<---------->|
     857             :       // |            |
     858             :       // +---------+--+--------
     859             :       // |  #####  | *|*  ###
     860             :       // | ####### |**|**#####
     861             :       // |#########|**+**##+##
     862             :       // |####+####|* P *#####
     863             :       // |#########| ***  ###
     864             :       // | ####### +-----------
     865             :       // |  #####  |  ^
     866             :       // |         |  |
     867             :       // |         | first dot is not filled
     868             :       // |         |
     869             :       //
     870             :       //      radius.width
     871             :       // |<----------------->|
     872             :       // |                   |
     873             :       // |             ___---+-------------
     874             :       // |         __--     #|#       ###
     875             :       // |       _-        ##|##     #####
     876             :       // |     /           ##+##     ##+##
     877             :       // |   /             # P #     #####
     878             :       // |  |               #|#       ###
     879             :       // | |             __--+-------------
     880             :       // ||            _-    ^
     881             :       // ||           /      |
     882             :       // |           /      first dot is filled
     883             :       // |          |
     884             :       // |          |
     885             :       // |  #####  |
     886             :       // | ####### |
     887             :       // |#########|
     888             :       // +----+----+
     889             :       // |#########|
     890             :       // | ####### |
     891             :       // |  #####  |
     892           0 :       Float minimum = otherBorderWidth + borderWidth / 2.0f;
     893           0 :       if (isHorizontal) {
     894           0 :         if (radius.width < minimum) {
     895           0 :           *aIsUnfilled = true;
     896           0 :           P.x += signs[0] * minimum;
     897             :         } else {
     898           0 :           P.x += signs[0] * radius.width;
     899             :         }
     900           0 :         P.y += signs[1] * borderWidth / 2.0f;
     901             :       } else {
     902           0 :         P.x += signs[0] * borderWidth / 2.0f;
     903           0 :         if (radius.height < minimum) {
     904           0 :           *aIsUnfilled = true;
     905           0 :           P.y += signs[1] * minimum;
     906             :         } else {
     907           0 :           P.y += signs[1] * radius.height;
     908             :         }
     909             :       }
     910             : 
     911           0 :       return P;
     912             :     }
     913             : 
     914             :     // This side is larger than other side, this side draws the corner.
     915             :     //
     916             :     //  borderWidth / 2.0
     917             :     // |<-->|
     918             :     // |    |
     919             :     // +----+---------------------
     920             :     // |  ##|##           #####
     921             :     // | ###|###         #######
     922             :     // |####|####       #########
     923             :     // |####+####       ####+####
     924             :     // |### P ###       #########
     925             :     // | #######         #######
     926             :     // |  #####           #####
     927             :     // +-----+---------------------
     928             :     // | *** |
     929             :     // |*****|
     930             :     // |**+**| <-- first dot in other side is not filled
     931             :     // |*****|
     932             :     // | *** |
     933             :     // | ### |
     934             :     // |#####|
     935             :     // |##+##|
     936             :     // |#####|
     937             :     // | ### |
     938             :     // |     |
     939           0 :     if (isHorizontal) {
     940           0 :       P.x += signs[0] * std::max(radius.width, borderWidth / 2.0f);
     941           0 :       P.y += signs[1] * borderWidth / 2.0f;
     942             :     } else {
     943           0 :       P.x += signs[0] * borderWidth / 2.0f;
     944           0 :       P.y += signs[1] * std::max(radius.height, borderWidth / 2.0f);
     945             :     }
     946           0 :     return P;
     947             :   }
     948             : 
     949           0 :   if (style == NS_STYLE_BORDER_STYLE_DOTTED) {
     950             :     // If only this side is dotted, other side draws the corner.
     951             :     //
     952             :     //  otherBorderWidth + borderWidth / 2.0
     953             :     // |<------->|
     954             :     // |         |
     955             :     // +------+--+--------
     956             :     // |##  ##| *|*  ###
     957             :     // |##  ##|**|**#####
     958             :     // |##  ##|**+**##+##
     959             :     // |##  ##|* P *#####
     960             :     // |##  ##| ***  ###
     961             :     // |##  ##+-----------
     962             :     // |##  ##|  ^
     963             :     // |##  ##|  |
     964             :     // |##  ##| first dot is not filled
     965             :     // |##  ##|
     966             :     //
     967             :     //      radius.width
     968             :     // |<----------------->|
     969             :     // |                   |
     970             :     // |             ___---+-------------
     971             :     // |         __--     #|#       ###
     972             :     // |       _-        ##|##     #####
     973             :     // |     /           ##+##     ##+##
     974             :     // |   /             # P #     #####
     975             :     // |  |               #|#       ###
     976             :     // | |             __--+-------------
     977             :     // ||            _-    ^
     978             :     // ||          /       |
     979             :     // |         /        first dot is filled
     980             :     // |        |
     981             :     // |       |
     982             :     // |      |
     983             :     // |      |
     984             :     // |      |
     985             :     // +------+
     986             :     // |##  ##|
     987             :     // |##  ##|
     988             :     // |##  ##|
     989           0 :     Float minimum = otherBorderWidth + borderWidth / 2.0f;
     990           0 :     if (isHorizontal) {
     991           0 :       if (radius.width < minimum) {
     992           0 :         *aIsUnfilled = true;
     993           0 :         P.x += signs[0] * minimum;
     994             :       } else {
     995           0 :         P.x += signs[0] * radius.width;
     996             :       }
     997           0 :       P.y += signs[1] * borderWidth / 2.0f;
     998             :     } else {
     999           0 :       P.x += signs[0] * borderWidth / 2.0f;
    1000           0 :       if (radius.height < minimum) {
    1001           0 :         *aIsUnfilled = true;
    1002           0 :         P.y += signs[1] * minimum;
    1003             :       } else {
    1004           0 :         P.y += signs[1] * radius.height;
    1005             :       }
    1006             :     }
    1007           0 :     return P;
    1008             :   }
    1009             : 
    1010           0 :   if (otherStyle == NS_STYLE_BORDER_STYLE_DOTTED && IsZeroSize(radius)) {
    1011             :     // If other side is dotted and radius=0, draw side to the end of corner.
    1012             :     //
    1013             :     //   +-------------------------------
    1014             :     //   |##########          ##########
    1015             :     // P +##########          ##########
    1016             :     //   |##########          ##########
    1017             :     //   +-----+-------------------------
    1018             :     //   | *** |
    1019             :     //   |*****|
    1020             :     //   |**+**| <-- first dot in other side is not filled
    1021             :     //   |*****|
    1022             :     //   | *** |
    1023             :     //   | ### |
    1024             :     //   |#####|
    1025             :     //   |##+##|
    1026             :     //   |#####|
    1027             :     //   | ### |
    1028             :     //   |     |
    1029           0 :     if (isHorizontal) {
    1030           0 :       P.y += signs[1] * borderWidth / 2.0f;
    1031             :     } else {
    1032           0 :       P.x += signs[0] * borderWidth / 2.0f;
    1033             :     }
    1034           0 :     return P;
    1035             :   }
    1036             : 
    1037             :   // Other cases.
    1038             :   //
    1039             :   //  dim.width
    1040             :   // |<----------------->|
    1041             :   // |                   |
    1042             :   // |             ___---+------------------
    1043             :   // |         __--      |#######        ###
    1044             :   // |       _-        P +#######        ###
    1045             :   // |     /             |#######        ###
    1046             :   // |   /          __---+------------------
    1047             :   // |  |       __--
    1048             :   // | |       /
    1049             :   // ||      /
    1050             :   // ||     |
    1051             :   // |     |
    1052             :   // |    |
    1053             :   // |   |
    1054             :   // |   |
    1055             :   // +-+-+
    1056             :   // |###|
    1057             :   // |###|
    1058             :   // |###|
    1059             :   // |###|
    1060             :   // |###|
    1061             :   // |   |
    1062             :   // |   |
    1063           0 :   if (isHorizontal) {
    1064           0 :     P.x += signs[0] * dim.width;
    1065           0 :     P.y += signs[1] * borderWidth / 2.0f;
    1066             :   } else {
    1067           0 :     P.x += signs[0] * borderWidth / 2.0f;
    1068           0 :     P.y += signs[1] * dim.height;
    1069             :   }
    1070             : 
    1071           0 :   return P;
    1072             : }
    1073             : 
    1074             : void
    1075           0 : nsCSSBorderRenderer::GetOuterAndInnerBezier(Bezier* aOuterBezier,
    1076             :                                             Bezier* aInnerBezier,
    1077             :                                             Corner aCorner)
    1078             : {
    1079             :   // Return bezier control points for outer and inner curve for given corner.
    1080             :   //
    1081             :   //               ___---+ outer curve
    1082             :   //           __--      |
    1083             :   //         _-          |
    1084             :   //       /             |
    1085             :   //     /               |
    1086             :   //    |                |
    1087             :   //   |             __--+ inner curve
    1088             :   //  |            _-
    1089             :   //  |           /
    1090             :   // |           /
    1091             :   // |          |
    1092             :   // |          |
    1093             :   // |         |
    1094             :   // |         |
    1095             :   // |         |
    1096             :   // +---------+
    1097             : 
    1098           0 :   mozilla::Side sideH(GetHorizontalSide(aCorner));
    1099           0 :   mozilla::Side sideV(GetVerticalSide(aCorner));
    1100             : 
    1101           0 :   Size outerCornerSize(ceil(mBorderRadii[aCorner].width),
    1102           0 :                        ceil(mBorderRadii[aCorner].height));
    1103           0 :   Size innerCornerSize(ceil(std::max(0.0f, mBorderRadii[aCorner].width -
    1104           0 :                                      mBorderWidths[sideV])),
    1105           0 :                        ceil(std::max(0.0f, mBorderRadii[aCorner].height -
    1106           0 :                                      mBorderWidths[sideH])));
    1107             : 
    1108           0 :   GetBezierPointsForCorner(aOuterBezier, aCorner, mOuterRect.AtCorner(aCorner),
    1109           0 :                            outerCornerSize);
    1110             : 
    1111           0 :   GetBezierPointsForCorner(aInnerBezier, aCorner, mInnerRect.AtCorner(aCorner),
    1112           0 :                            innerCornerSize);
    1113           0 : }
    1114             : 
    1115             : void
    1116           0 : nsCSSBorderRenderer::FillSolidBorder(const Rect& aOuterRect,
    1117             :                                      const Rect& aInnerRect,
    1118             :                                      const RectCornerRadii& aBorderRadii,
    1119             :                                      const Float* aBorderSizes,
    1120             :                                      int aSides,
    1121             :                                      const ColorPattern& aColor)
    1122             : {
    1123             :   // Note that this function is allowed to draw more than just the
    1124             :   // requested sides.
    1125             : 
    1126             :   // If we have a border radius, do full rounded rectangles
    1127             :   // and fill, regardless of what sides we're asked to draw.
    1128           0 :   if (!AllCornersZeroSize(aBorderRadii)) {
    1129           0 :     RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
    1130             : 
    1131           0 :     RectCornerRadii innerRadii;
    1132           0 :     ComputeInnerRadii(aBorderRadii, aBorderSizes, &innerRadii);
    1133             : 
    1134             :     // do the outer border
    1135           0 :     AppendRoundedRectToPath(builder, aOuterRect, aBorderRadii, true);
    1136             : 
    1137             :     // then do the inner border CCW
    1138           0 :     AppendRoundedRectToPath(builder, aInnerRect, innerRadii, false);
    1139             : 
    1140           0 :     RefPtr<Path> path = builder->Finish();
    1141             : 
    1142           0 :     mDrawTarget->Fill(path, aColor);
    1143           0 :     return;
    1144             :   }
    1145             : 
    1146             :   // If we're asked to draw all sides of an equal-sized border,
    1147             :   // stroking is fastest.  This is a fairly common path, but partial
    1148             :   // sides is probably second in the list -- there are a bunch of
    1149             :   // common border styles, such as inset and outset, that are
    1150             :   // top-left/bottom-right split.
    1151           0 :   if (aSides == eSideBitsAll &&
    1152           0 :       CheckFourFloatsEqual(aBorderSizes, aBorderSizes[0]) &&
    1153           0 :       !mAvoidStroke)
    1154             :   {
    1155           0 :     Float strokeWidth = aBorderSizes[0];
    1156           0 :     Rect r(aOuterRect);
    1157           0 :     r.Deflate(strokeWidth / 2.f);
    1158           0 :     mDrawTarget->StrokeRect(r, aColor, StrokeOptions(strokeWidth));
    1159           0 :     return;
    1160             :   }
    1161             : 
    1162             :   // Otherwise, we have unequal sized borders or we're only
    1163             :   // drawing some sides; create rectangles for each side
    1164             :   // and fill them.
    1165             : 
    1166           0 :   Rect r[4];
    1167             : 
    1168             :   // compute base rects for each side
    1169           0 :   if (aSides & eSideBitsTop) {
    1170           0 :     r[eSideTop] =
    1171           0 :         Rect(aOuterRect.X(), aOuterRect.Y(),
    1172             :              aOuterRect.Width(), aBorderSizes[eSideTop]);
    1173             :   }
    1174             : 
    1175           0 :   if (aSides & eSideBitsBottom) {
    1176           0 :     r[eSideBottom] =
    1177           0 :         Rect(aOuterRect.X(), aOuterRect.YMost() - aBorderSizes[eSideBottom],
    1178           0 :              aOuterRect.Width(), aBorderSizes[eSideBottom]);
    1179             :   }
    1180             : 
    1181           0 :   if (aSides & eSideBitsLeft) {
    1182           0 :     r[eSideLeft] =
    1183           0 :         Rect(aOuterRect.X(), aOuterRect.Y(),
    1184           0 :              aBorderSizes[eSideLeft], aOuterRect.Height());
    1185             :   }
    1186             : 
    1187           0 :   if (aSides & eSideBitsRight) {
    1188           0 :     r[eSideRight] =
    1189           0 :         Rect(aOuterRect.XMost() - aBorderSizes[eSideRight], aOuterRect.Y(),
    1190           0 :              aBorderSizes[eSideRight], aOuterRect.Height());
    1191             :   }
    1192             : 
    1193             :   // If two sides meet at a corner that we're rendering, then
    1194             :   // make sure that we adjust one of the sides to avoid overlap.
    1195             :   // This is especially important in the case of colors with
    1196             :   // an alpha channel.
    1197             : 
    1198           0 :   if ((aSides & (eSideBitsTop | eSideBitsLeft)) == (eSideBitsTop | eSideBitsLeft)) {
    1199             :     // adjust the left's top down a bit
    1200           0 :     r[eSideLeft].y += aBorderSizes[eSideTop];
    1201           0 :     r[eSideLeft].height -= aBorderSizes[eSideTop];
    1202             :   }
    1203             : 
    1204           0 :   if ((aSides & (eSideBitsTop | eSideBitsRight)) == (eSideBitsTop | eSideBitsRight)) {
    1205             :     // adjust the top's left a bit
    1206           0 :     r[eSideTop].width -= aBorderSizes[eSideRight];
    1207             :   }
    1208             : 
    1209           0 :   if ((aSides & (eSideBitsBottom | eSideBitsRight)) == (eSideBitsBottom | eSideBitsRight)) {
    1210             :     // adjust the right's bottom a bit
    1211           0 :     r[eSideRight].height -= aBorderSizes[eSideBottom];
    1212             :   }
    1213             : 
    1214           0 :   if ((aSides & (eSideBitsBottom | eSideBitsLeft)) == (eSideBitsBottom | eSideBitsLeft)) {
    1215             :     // adjust the bottom's left a bit
    1216           0 :     r[eSideBottom].x += aBorderSizes[eSideLeft];
    1217           0 :     r[eSideBottom].width -= aBorderSizes[eSideLeft];
    1218             :   }
    1219             : 
    1220             :   // Filling these one by one is faster than filling them all at once.
    1221           0 :   for (uint32_t i = 0; i < 4; i++) {
    1222           0 :     if (aSides & (1 << i)) {
    1223           0 :       MaybeSnapToDevicePixels(r[i], *mDrawTarget, true);
    1224           0 :       mDrawTarget->FillRect(r[i], aColor);
    1225             :     }
    1226             :   }
    1227             : }
    1228             : 
    1229             : Color
    1230           0 : MakeBorderColor(nscolor aColor, nscolor aBackgroundColor,
    1231             :                 BorderColorStyle aBorderColorStyle)
    1232             : {
    1233             :   nscolor colors[2];
    1234           0 :   int k = 0;
    1235             : 
    1236           0 :   switch (aBorderColorStyle) {
    1237             :     case BorderColorStyleNone:
    1238           0 :       return Color(0.f, 0.f, 0.f, 0.f); // transparent black
    1239             : 
    1240             :     case BorderColorStyleLight:
    1241           0 :       k = 1;
    1242             :       MOZ_FALLTHROUGH;
    1243             :     case BorderColorStyleDark:
    1244           0 :       NS_GetSpecial3DColors(colors, aBackgroundColor, aColor);
    1245           0 :       return Color::FromABGR(colors[k]);
    1246             : 
    1247             :     case BorderColorStyleSolid:
    1248             :     default:
    1249           0 :       return Color::FromABGR(aColor);
    1250             :   }
    1251             : }
    1252             : 
    1253             : Color
    1254           0 : ComputeColorForLine(uint32_t aLineIndex,
    1255             :                     const BorderColorStyle* aBorderColorStyle,
    1256             :                     uint32_t aBorderColorStyleCount,
    1257             :                     nscolor aBorderColor,
    1258             :                     nscolor aBackgroundColor)
    1259             : {
    1260           0 :   NS_ASSERTION(aLineIndex < aBorderColorStyleCount, "Invalid lineIndex given");
    1261             : 
    1262             :   return MakeBorderColor(aBorderColor, aBackgroundColor,
    1263           0 :                          aBorderColorStyle[aLineIndex]);
    1264             : }
    1265             : 
    1266             : Color
    1267           0 : ComputeCompositeColorForLine(uint32_t aLineIndex,
    1268             :                              const nsBorderColors* aBorderColors)
    1269             : {
    1270           0 :   while (aLineIndex-- && aBorderColors->mNext)
    1271           0 :     aBorderColors = aBorderColors->mNext;
    1272             : 
    1273           0 :   return Color::FromABGR(aBorderColors->mColor);
    1274             : }
    1275             : 
    1276             : void
    1277           0 : nsCSSBorderRenderer::DrawBorderSidesCompositeColors(int aSides, const nsBorderColors *aCompositeColors)
    1278             : {
    1279           0 :   RectCornerRadii radii = mBorderRadii;
    1280             : 
    1281             :   // the generic composite colors path; each border is 1px in size
    1282           0 :   Rect soRect = mOuterRect;
    1283           0 :   Float maxBorderWidth = 0;
    1284           0 :   NS_FOR_CSS_SIDES (i) {
    1285           0 :     maxBorderWidth = std::max(maxBorderWidth, Float(mBorderWidths[i]));
    1286             :   }
    1287             : 
    1288             :   Float fakeBorderSizes[4];
    1289             : 
    1290           0 :   Point itl = mInnerRect.TopLeft();
    1291           0 :   Point ibr = mInnerRect.BottomRight();
    1292             : 
    1293           0 :   for (uint32_t i = 0; i < uint32_t(maxBorderWidth); i++) {
    1294           0 :     ColorPattern color(ToDeviceColor(
    1295           0 :                          ComputeCompositeColorForLine(i, aCompositeColors)));
    1296             : 
    1297           0 :     Rect siRect = soRect;
    1298           0 :     siRect.Deflate(1.0);
    1299             : 
    1300             :     // now cap the rects to the real mInnerRect
    1301           0 :     Point tl = siRect.TopLeft();
    1302           0 :     Point br = siRect.BottomRight();
    1303             : 
    1304           0 :     tl.x = std::min(tl.x, itl.x);
    1305           0 :     tl.y = std::min(tl.y, itl.y);
    1306             : 
    1307           0 :     br.x = std::max(br.x, ibr.x);
    1308           0 :     br.y = std::max(br.y, ibr.y);
    1309             : 
    1310           0 :     siRect = Rect(tl.x, tl.y, br.x - tl.x , br.y - tl.y);
    1311             : 
    1312           0 :     fakeBorderSizes[eSideTop] = siRect.TopLeft().y - soRect.TopLeft().y;
    1313           0 :     fakeBorderSizes[eSideRight] = soRect.TopRight().x - siRect.TopRight().x;
    1314           0 :     fakeBorderSizes[eSideBottom] = soRect.BottomRight().y - siRect.BottomRight().y;
    1315           0 :     fakeBorderSizes[eSideLeft] = siRect.BottomLeft().x - soRect.BottomLeft().x;
    1316             : 
    1317           0 :     FillSolidBorder(soRect, siRect, radii, fakeBorderSizes, aSides, color);
    1318             : 
    1319           0 :     soRect = siRect;
    1320             : 
    1321           0 :     ComputeInnerRadii(radii, fakeBorderSizes, &radii);
    1322             :   }
    1323           0 : }
    1324             : 
    1325             : void
    1326           0 : nsCSSBorderRenderer::DrawBorderSides(int aSides)
    1327             : {
    1328           0 :   if (aSides == 0 || (aSides & ~eSideBitsAll) != 0) {
    1329           0 :     NS_WARNING("DrawBorderSides: invalid sides!");
    1330           0 :     return;
    1331             :   }
    1332             : 
    1333           0 :   uint8_t borderRenderStyle = NS_STYLE_BORDER_STYLE_NONE;
    1334             :   nscolor borderRenderColor;
    1335           0 :   const nsBorderColors *compositeColors = nullptr;
    1336             : 
    1337           0 :   uint32_t borderColorStyleCount = 0;
    1338             :   BorderColorStyle borderColorStyleTopLeft[3], borderColorStyleBottomRight[3];
    1339           0 :   BorderColorStyle *borderColorStyle = nullptr;
    1340             : 
    1341           0 :   NS_FOR_CSS_SIDES (i) {
    1342           0 :     if ((aSides & (1 << i)) == 0)
    1343           0 :       continue;
    1344           0 :     borderRenderStyle = mBorderStyles[i];
    1345           0 :     borderRenderColor = mBorderColors[i];
    1346           0 :     compositeColors = mCompositeColors[i];
    1347           0 :     break;
    1348             :   }
    1349             : 
    1350           0 :   if (borderRenderStyle == NS_STYLE_BORDER_STYLE_NONE ||
    1351             :       borderRenderStyle == NS_STYLE_BORDER_STYLE_HIDDEN)
    1352           0 :     return;
    1353             : 
    1354           0 :   if (borderRenderStyle == NS_STYLE_BORDER_STYLE_DASHED ||
    1355             :       borderRenderStyle == NS_STYLE_BORDER_STYLE_DOTTED) {
    1356             :     // Draw each corner separately, with the given side's color.
    1357           0 :     if (aSides & eSideBitsTop) {
    1358           0 :       DrawDashedOrDottedCorner(eSideTop, C_TL);
    1359           0 :     } else if (aSides & eSideBitsLeft) {
    1360           0 :       DrawDashedOrDottedCorner(eSideLeft, C_TL);
    1361             :     }
    1362             : 
    1363           0 :     if (aSides & eSideBitsTop) {
    1364           0 :       DrawDashedOrDottedCorner(eSideTop, C_TR);
    1365           0 :     } else if (aSides & eSideBitsRight) {
    1366           0 :       DrawDashedOrDottedCorner(eSideRight, C_TR);
    1367             :     }
    1368             : 
    1369           0 :     if (aSides & eSideBitsBottom) {
    1370           0 :       DrawDashedOrDottedCorner(eSideBottom, C_BL);
    1371           0 :     } else if (aSides & eSideBitsLeft) {
    1372           0 :       DrawDashedOrDottedCorner(eSideLeft, C_BL);
    1373             :     }
    1374             : 
    1375           0 :     if (aSides & eSideBitsBottom) {
    1376           0 :       DrawDashedOrDottedCorner(eSideBottom, C_BR);
    1377           0 :     } else if (aSides & eSideBitsRight) {
    1378           0 :       DrawDashedOrDottedCorner(eSideRight, C_BR);
    1379             :     }
    1380           0 :     return;
    1381             :   }
    1382             : 
    1383             :   // -moz-border-colors is a hack; if we have it for a border, then
    1384             :   // it's always drawn solid, and each color is given 1px.  The last
    1385             :   // color is used for the remainder of the border's size.  Just
    1386             :   // hand off to another function to do all that.
    1387           0 :   if (compositeColors) {
    1388           0 :     Float maxBorderWidth(0);
    1389           0 :     NS_FOR_CSS_SIDES (i) {
    1390           0 :       maxBorderWidth = std::max(maxBorderWidth, mBorderWidths[i]);
    1391             :     }
    1392           0 :     if (maxBorderWidth <= MAX_COMPOSITE_BORDER_WIDTH) {
    1393           0 :       DrawBorderSidesCompositeColors(aSides, compositeColors);
    1394           0 :       return;
    1395             :     }
    1396           0 :     NS_WARNING("DrawBorderSides: too large border width for composite colors");
    1397             :  }
    1398             : 
    1399             :   // We're not doing compositeColors, so we can calculate the
    1400             :   // borderColorStyle based on the specified style.  The
    1401             :   // borderColorStyle array goes from the outer to the inner style.
    1402             :   //
    1403             :   // If the border width is 1, we need to change the borderRenderStyle
    1404             :   // a bit to make sure that we get the right colors -- e.g. 'ridge'
    1405             :   // with a 1px border needs to look like solid, not like 'outset'.
    1406           0 :   if (mOneUnitBorder &&
    1407           0 :       (borderRenderStyle == NS_STYLE_BORDER_STYLE_RIDGE ||
    1408           0 :        borderRenderStyle == NS_STYLE_BORDER_STYLE_GROOVE ||
    1409             :        borderRenderStyle == NS_STYLE_BORDER_STYLE_DOUBLE))
    1410           0 :     borderRenderStyle = NS_STYLE_BORDER_STYLE_SOLID;
    1411             : 
    1412           0 :   switch (borderRenderStyle) {
    1413             :     case NS_STYLE_BORDER_STYLE_SOLID:
    1414           0 :       borderColorStyleTopLeft[0] = BorderColorStyleSolid;
    1415             : 
    1416           0 :       borderColorStyleBottomRight[0] = BorderColorStyleSolid;
    1417             : 
    1418           0 :       borderColorStyleCount = 1;
    1419           0 :       break;
    1420             : 
    1421             :     case NS_STYLE_BORDER_STYLE_GROOVE:
    1422           0 :       borderColorStyleTopLeft[0] = BorderColorStyleDark;
    1423           0 :       borderColorStyleTopLeft[1] = BorderColorStyleLight;
    1424             : 
    1425           0 :       borderColorStyleBottomRight[0] = BorderColorStyleLight;
    1426           0 :       borderColorStyleBottomRight[1] = BorderColorStyleDark;
    1427             : 
    1428           0 :       borderColorStyleCount = 2;
    1429           0 :       break;
    1430             : 
    1431             :     case NS_STYLE_BORDER_STYLE_RIDGE:
    1432           0 :       borderColorStyleTopLeft[0] = BorderColorStyleLight;
    1433           0 :       borderColorStyleTopLeft[1] = BorderColorStyleDark;
    1434             : 
    1435           0 :       borderColorStyleBottomRight[0] = BorderColorStyleDark;
    1436           0 :       borderColorStyleBottomRight[1] = BorderColorStyleLight;
    1437             : 
    1438           0 :       borderColorStyleCount = 2;
    1439           0 :       break;
    1440             : 
    1441             :     case NS_STYLE_BORDER_STYLE_DOUBLE:
    1442           0 :       borderColorStyleTopLeft[0] = BorderColorStyleSolid;
    1443           0 :       borderColorStyleTopLeft[1] = BorderColorStyleNone;
    1444           0 :       borderColorStyleTopLeft[2] = BorderColorStyleSolid;
    1445             : 
    1446           0 :       borderColorStyleBottomRight[0] = BorderColorStyleSolid;
    1447           0 :       borderColorStyleBottomRight[1] = BorderColorStyleNone;
    1448           0 :       borderColorStyleBottomRight[2] = BorderColorStyleSolid;
    1449             : 
    1450           0 :       borderColorStyleCount = 3;
    1451           0 :       break;
    1452             : 
    1453             :     case NS_STYLE_BORDER_STYLE_INSET:
    1454           0 :       borderColorStyleTopLeft[0] = BorderColorStyleDark;
    1455           0 :       borderColorStyleBottomRight[0] = BorderColorStyleLight;
    1456             : 
    1457           0 :       borderColorStyleCount = 1;
    1458           0 :       break;
    1459             : 
    1460             :     case NS_STYLE_BORDER_STYLE_OUTSET:
    1461           0 :       borderColorStyleTopLeft[0] = BorderColorStyleLight;
    1462           0 :       borderColorStyleBottomRight[0] = BorderColorStyleDark;
    1463             : 
    1464           0 :       borderColorStyleCount = 1;
    1465           0 :       break;
    1466             : 
    1467             :     default:
    1468           0 :       NS_NOTREACHED("Unhandled border style!!");
    1469           0 :       break;
    1470             :   }
    1471             : 
    1472             :   // The only way to get to here is by having a
    1473             :   // borderColorStyleCount < 1 or > 3; this should never happen,
    1474             :   // since -moz-border-colors doesn't get handled here.
    1475           0 :   NS_ASSERTION(borderColorStyleCount > 0 && borderColorStyleCount < 4,
    1476             :                "Non-border-colors case with borderColorStyleCount < 1 or > 3; what happened?");
    1477             : 
    1478             :   // The caller should never give us anything with a mix
    1479             :   // of TL/BR if the border style would require a
    1480             :   // TL/BR split.
    1481           0 :   if (aSides & (eSideBitsBottom | eSideBitsRight))
    1482           0 :     borderColorStyle = borderColorStyleBottomRight;
    1483             :   else
    1484           0 :     borderColorStyle = borderColorStyleTopLeft;
    1485             : 
    1486             :   // Distribute the border across the available space.
    1487             :   Float borderWidths[3][4];
    1488             : 
    1489           0 :   if (borderColorStyleCount == 1) {
    1490           0 :     NS_FOR_CSS_SIDES (i) {
    1491           0 :       borderWidths[0][i] = mBorderWidths[i];
    1492             :     }
    1493           0 :   } else if (borderColorStyleCount == 2) {
    1494             :     // with 2 color styles, any extra pixel goes to the outside
    1495           0 :     NS_FOR_CSS_SIDES (i) {
    1496           0 :       borderWidths[0][i] = int32_t(mBorderWidths[i]) / 2 + int32_t(mBorderWidths[i]) % 2;
    1497           0 :       borderWidths[1][i] = int32_t(mBorderWidths[i]) / 2;
    1498             :     }
    1499           0 :   } else if (borderColorStyleCount == 3) {
    1500             :     // with 3 color styles, any extra pixel (or lack of extra pixel)
    1501             :     // goes to the middle
    1502           0 :     NS_FOR_CSS_SIDES (i) {
    1503           0 :       if (mBorderWidths[i] == 1.0) {
    1504           0 :         borderWidths[0][i] = 1.f;
    1505           0 :         borderWidths[1][i] = borderWidths[2][i] = 0.f;
    1506             :       } else {
    1507           0 :         int32_t rest = int32_t(mBorderWidths[i]) % 3;
    1508           0 :         borderWidths[0][i] = borderWidths[2][i] = borderWidths[1][i] = (int32_t(mBorderWidths[i]) - rest) / 3;
    1509             : 
    1510           0 :         if (rest == 1) {
    1511           0 :           borderWidths[1][i] += 1.f;
    1512           0 :         } else if (rest == 2) {
    1513           0 :           borderWidths[0][i] += 1.f;
    1514           0 :           borderWidths[2][i] += 1.f;
    1515             :         }
    1516             :       }
    1517             :     }
    1518             :   }
    1519             : 
    1520             :   // make a copy that we can modify
    1521           0 :   RectCornerRadii radii = mBorderRadii;
    1522             : 
    1523           0 :   Rect soRect(mOuterRect);
    1524           0 :   Rect siRect(mOuterRect);
    1525             : 
    1526             :   // If adjacent side is dotted and radius=0, draw side to the end of corner.
    1527             :   //
    1528             :   // +--------------------------------
    1529             :   // |################################
    1530             :   // |
    1531             :   // |################################
    1532             :   // +-----+--------------------------
    1533             :   // |     |
    1534             :   // |     |
    1535             :   // |     |
    1536             :   // |     |
    1537             :   // |     |
    1538             :   // | ### |
    1539             :   // |#####|
    1540             :   // |#####|
    1541             :   // |#####|
    1542             :   // | ### |
    1543             :   // |     |
    1544           0 :   bool noMarginTop = false;
    1545           0 :   bool noMarginRight = false;
    1546           0 :   bool noMarginBottom = false;
    1547           0 :   bool noMarginLeft = false;
    1548             : 
    1549             :   // If there is at least one dotted side, every side is rendered separately.
    1550           0 :   if (IsSingleSide(aSides)) {
    1551           0 :     if (aSides == eSideBitsTop) {
    1552           0 :       if (mBorderStyles[eSideRight] == NS_STYLE_BORDER_STYLE_DOTTED &&
    1553           0 :           IsZeroSize(mBorderRadii[C_TR])) {
    1554           0 :         noMarginRight = true;
    1555             :       }
    1556           0 :       if (mBorderStyles[eSideLeft] == NS_STYLE_BORDER_STYLE_DOTTED &&
    1557           0 :           IsZeroSize(mBorderRadii[C_TL])) {
    1558           0 :         noMarginLeft = true;
    1559             :       }
    1560           0 :     } else if (aSides == eSideBitsRight) {
    1561           0 :       if (mBorderStyles[eSideTop] == NS_STYLE_BORDER_STYLE_DOTTED &&
    1562           0 :           IsZeroSize(mBorderRadii[C_TR])) {
    1563           0 :         noMarginTop = true;
    1564             :       }
    1565           0 :       if (mBorderStyles[eSideBottom] == NS_STYLE_BORDER_STYLE_DOTTED &&
    1566           0 :           IsZeroSize(mBorderRadii[C_BR])) {
    1567           0 :         noMarginBottom = true;
    1568             :       }
    1569           0 :     } else if (aSides == eSideBitsBottom) {
    1570           0 :       if (mBorderStyles[eSideRight] == NS_STYLE_BORDER_STYLE_DOTTED &&
    1571           0 :           IsZeroSize(mBorderRadii[C_BR])) {
    1572           0 :         noMarginRight = true;
    1573             :       }
    1574           0 :       if (mBorderStyles[eSideLeft] == NS_STYLE_BORDER_STYLE_DOTTED &&
    1575           0 :           IsZeroSize(mBorderRadii[C_BL])) {
    1576           0 :         noMarginLeft = true;
    1577             :       }
    1578             :     } else {
    1579           0 :       if (mBorderStyles[eSideTop] == NS_STYLE_BORDER_STYLE_DOTTED &&
    1580           0 :           IsZeroSize(mBorderRadii[C_TL])) {
    1581           0 :         noMarginTop = true;
    1582             :       }
    1583           0 :       if (mBorderStyles[eSideBottom] == NS_STYLE_BORDER_STYLE_DOTTED &&
    1584           0 :           IsZeroSize(mBorderRadii[C_BL])) {
    1585           0 :         noMarginBottom = true;
    1586             :       }
    1587             :     }
    1588             :   }
    1589             : 
    1590           0 :   for (unsigned int i = 0; i < borderColorStyleCount; i++) {
    1591             :     // walk siRect inwards at the start of the loop to get the
    1592             :     // correct inner rect.
    1593             :     //
    1594             :     // If noMarginTop is false:
    1595             :     //   --------------------+
    1596             :     //                      /|
    1597             :     //                     / |
    1598             :     //                    L  |
    1599             :     //   ----------------+   |
    1600             :     //                   |   |
    1601             :     //                   |   |
    1602             :     //
    1603             :     // If noMarginTop is true:
    1604             :     //   ----------------+<--+
    1605             :     //                   |   |
    1606             :     //                   |   |
    1607             :     //                   |   |
    1608             :     //                   |   |
    1609             :     //                   |   |
    1610             :     //                   |   |
    1611           0 :     siRect.Deflate(Margin(noMarginTop ? 0 : borderWidths[i][0],
    1612             :                           noMarginRight ? 0 : borderWidths[i][1],
    1613             :                           noMarginBottom ? 0 : borderWidths[i][2],
    1614           0 :                           noMarginLeft ? 0 : borderWidths[i][3]));
    1615             : 
    1616           0 :     if (borderColorStyle[i] != BorderColorStyleNone) {
    1617             :       Color c = ComputeColorForLine(i, borderColorStyle, borderColorStyleCount,
    1618           0 :                                     borderRenderColor, mBackgroundColor);
    1619           0 :       ColorPattern color(ToDeviceColor(c));
    1620             : 
    1621           0 :       FillSolidBorder(soRect, siRect, radii, borderWidths[i], aSides, color);
    1622             :     }
    1623             : 
    1624           0 :     ComputeInnerRadii(radii, borderWidths[i], &radii);
    1625             : 
    1626             :     // And now soRect is the same as siRect, for the next line in.
    1627           0 :     soRect = siRect;
    1628             :   }
    1629             : }
    1630             : 
    1631             : void
    1632           0 : nsCSSBorderRenderer::SetupDashedOptions(StrokeOptions* aStrokeOptions,
    1633             :                                         Float aDash[2],
    1634             :                                         mozilla::Side aSide,
    1635             :                                         Float aBorderLength, bool isCorner)
    1636             : {
    1637           0 :   uint8_t style = mBorderStyles[aSide];
    1638           0 :   Float borderWidth = mBorderWidths[aSide];
    1639             : 
    1640             :   // Dashed line starts and ends with half segment in most case.
    1641             :   //
    1642             :   // __--+---+---+---+---+---+---+---+---+--__
    1643             :   //     |###|   |   |###|###|   |   |###|
    1644             :   //     |###|   |   |###|###|   |   |###|
    1645             :   //     |###|   |   |###|###|   |   |###|
    1646             :   // __--+---+---+---+---+---+---+---+---+--__
    1647             :   //
    1648             :   // If radius=0 and other side is either dotted or 0-width, it starts or ends
    1649             :   // with full segment.
    1650             :   //
    1651             :   // +---+---+---+---+---+---+---+---+---+---+
    1652             :   // |###|###|   |   |###|###|   |   |###|###|
    1653             :   // |###|###|   |   |###|###|   |   |###|###|
    1654             :   // |###|###|   |   |###|###|   |   |###|###|
    1655             :   // +---++--+---+---+---+---+---+---+--++---+
    1656             :   // |    |                             |    |
    1657             :   // |    |                             |    |
    1658             :   // |    |                             |    |
    1659             :   // |    |                             |    |
    1660             :   // | ## |                             | ## |
    1661             :   // |####|                             |####|
    1662             :   // |####|                             |####|
    1663             :   // | ## |                             | ## |
    1664             :   // |    |                             |    |
    1665           0 :   bool fullStart = false, fullEnd = false;
    1666             :   Float halfDash;
    1667           0 :   if (style == NS_STYLE_BORDER_STYLE_DASHED) {
    1668           0 :     if (IsZeroSize(mBorderRadii[GetCCWCorner(aSide)]) &&
    1669           0 :         (mBorderStyles[PREV_SIDE(aSide)] == NS_STYLE_BORDER_STYLE_DOTTED ||
    1670           0 :          mBorderWidths[PREV_SIDE(aSide)] == 0.0f ||
    1671             :          borderWidth <= 1.0f)) {
    1672           0 :       fullStart = true;
    1673             :     }
    1674             : 
    1675           0 :     if (IsZeroSize(mBorderRadii[GetCWCorner(aSide)]) &&
    1676           0 :         (mBorderStyles[NEXT_SIDE(aSide)] == NS_STYLE_BORDER_STYLE_DOTTED ||
    1677           0 :          mBorderWidths[NEXT_SIDE(aSide)] == 0.0f)) {
    1678           0 :       fullEnd = true;
    1679             :     }
    1680             : 
    1681           0 :     halfDash = borderWidth * DOT_LENGTH * DASH_LENGTH / 2.0f;
    1682             :   } else {
    1683           0 :     halfDash = borderWidth * DOT_LENGTH / 2.0f;
    1684             :   }
    1685             : 
    1686           0 :   if (style == NS_STYLE_BORDER_STYLE_DASHED && aBorderLength > 0.0f) {
    1687             :     // The number of half segments, with maximum dash length.
    1688           0 :     int32_t count = floor(aBorderLength / halfDash);
    1689           0 :     Float minHalfDash = borderWidth * DOT_LENGTH / 2.0f;
    1690             : 
    1691           0 :     if (fullStart && fullEnd) {
    1692             :       // count should be 4n + 2
    1693             :       //
    1694             :       //   1 +       4       +        4      + 1
    1695             :       //
    1696             :       // |   |               |               |   |
    1697             :       // +---+---+---+---+---+---+---+---+---+---+
    1698             :       // |###|###|   |   |###|###|   |   |###|###|
    1699             :       // |###|###|   |   |###|###|   |   |###|###|
    1700             :       // |###|###|   |   |###|###|   |   |###|###|
    1701             :       // +---+---+---+---+---+---+---+---+---+---+
    1702             : 
    1703             :       // If border is too short, draw solid line.
    1704           0 :       if (aBorderLength < 6.0f * minHalfDash)
    1705           0 :         return;
    1706             : 
    1707           0 :       if (count % 4 == 0) {
    1708           0 :         count += 2;
    1709           0 :       } else if (count % 4 == 1) {
    1710           0 :         count += 1;
    1711           0 :       } else if (count % 4 == 3) {
    1712           0 :         count += 3;
    1713             :       }
    1714           0 :     } else if (fullStart || fullEnd) {
    1715             :       // count should be 4n + 1
    1716             :       //
    1717             :       //   1 +       4       +        4
    1718             :       //
    1719             :       // |   |               |               |
    1720             :       // +---+---+---+---+---+---+---+---+---+
    1721             :       // |###|###|   |   |###|###|   |   |###|
    1722             :       // |###|###|   |   |###|###|   |   |###|
    1723             :       // |###|###|   |   |###|###|   |   |###|
    1724             :       // +---+---+---+---+---+---+---+---+---+
    1725             :       //
    1726             :       //         4       +        4      + 1
    1727             :       //
    1728             :       // |               |               |   |
    1729             :       // +---+---+---+---+---+---+---+---+---+
    1730             :       // |###|   |   |###|###|   |   |###|###|
    1731             :       // |###|   |   |###|###|   |   |###|###|
    1732             :       // |###|   |   |###|###|   |   |###|###|
    1733             :       // +---+---+---+---+---+---+---+---+---+
    1734             : 
    1735             :       // If border is too short, draw solid line.
    1736           0 :       if (aBorderLength < 5.0f * minHalfDash)
    1737           0 :         return;
    1738             : 
    1739           0 :       if (count % 4 == 0) {
    1740           0 :         count += 1;
    1741           0 :       } else if (count % 4 == 2) {
    1742           0 :         count += 3;
    1743           0 :       } else if (count % 4 == 3) {
    1744           0 :         count += 2;
    1745             :       }
    1746             :     } else {
    1747             :       // count should be 4n
    1748             :       //
    1749             :       //         4       +        4
    1750             :       //
    1751             :       // |               |               |
    1752             :       // +---+---+---+---+---+---+---+---+
    1753             :       // |###|   |   |###|###|   |   |###|
    1754             :       // |###|   |   |###|###|   |   |###|
    1755             :       // |###|   |   |###|###|   |   |###|
    1756             :       // +---+---+---+---+---+---+---+---+
    1757             : 
    1758             :       // If border is too short, draw solid line.
    1759           0 :       if (aBorderLength < 4.0f * minHalfDash)
    1760           0 :         return;
    1761             : 
    1762           0 :       if (count % 4 == 1) {
    1763           0 :         count += 3;
    1764           0 :       } else if (count % 4 == 2) {
    1765           0 :         count += 2;
    1766           0 :       } else if (count % 4 == 3) {
    1767           0 :         count += 1;
    1768             :       }
    1769             :     }
    1770           0 :     halfDash = aBorderLength / count;
    1771             :   }
    1772             : 
    1773           0 :   Float fullDash = halfDash * 2.0f;
    1774             : 
    1775           0 :   aDash[0] = fullDash;
    1776           0 :   aDash[1] = fullDash;
    1777             : 
    1778           0 :   if (style == NS_STYLE_BORDER_STYLE_DASHED && fullDash > 1.0f) {
    1779           0 :     if (!fullStart) {
    1780             :       // Draw half segments on both ends.
    1781           0 :       aStrokeOptions->mDashOffset = halfDash;
    1782             :     }
    1783           0 :   } else if (style != NS_STYLE_BORDER_STYLE_DOTTED && isCorner) {
    1784             :     // If side ends with filled full segment, corner should start with unfilled
    1785             :     // full segment. Not needed for dotted corners, as they overlap one dot with
    1786             :     // the side's end.
    1787             :     //
    1788             :     //     corner            side
    1789             :     //   ------------>|<---------------------------
    1790             :     //                |
    1791             :     //          __+---+---+---+---+---+---+---+---+
    1792             :     //       _+-  |   |###|###|   |   |###|###|   |
    1793             :     //     /##|   |   |###|###|   |   |###|###|   |
    1794             :     //    +####|   |  |###|###|   |   |###|###|   |
    1795             :     //   /#\####| _+--+---+---+---+---+---+---+---+
    1796             :     //  |####\##+-
    1797             :     //  |#####+-
    1798             :     //  +--###/
    1799             :     //  |  --+
    1800           0 :     aStrokeOptions->mDashOffset = fullDash;
    1801             :   }
    1802             : 
    1803           0 :   aStrokeOptions->mDashPattern = aDash;
    1804           0 :   aStrokeOptions->mDashLength = 2;
    1805             : 
    1806           0 :   PrintAsFormatString("dash: %f %f\n", aDash[0], aDash[1]);
    1807             : }
    1808             : 
    1809             : static Float
    1810           0 : GetBorderLength(mozilla::Side aSide,
    1811             :                 const Point& aStart, const Point& aEnd)
    1812             : {
    1813           0 :   if (aSide == eSideTop) {
    1814           0 :     return aEnd.x - aStart.x;
    1815             :   }
    1816           0 :   if (aSide == eSideRight) {
    1817           0 :     return aEnd.y - aStart.y;
    1818             :   }
    1819           0 :   if (aSide == eSideBottom) {
    1820           0 :     return aStart.x - aEnd.x;
    1821             :   }
    1822           0 :   return aStart.y - aEnd.y;
    1823             : }
    1824             : 
    1825             : void
    1826           0 : nsCSSBorderRenderer::DrawDashedOrDottedSide(mozilla::Side aSide)
    1827             : {
    1828             :   // Draw dashed/dotted side with following approach.
    1829             :   //
    1830             :   // dashed side
    1831             :   //   Draw dashed line along the side, with appropriate dash length and gap
    1832             :   //   to make the side symmetric as far as possible.  Dash length equals to
    1833             :   //   the gap, and the ratio of the dash length to border-width is the maximum
    1834             :   //   value in in [1, 3] range.
    1835             :   //   In most case, line ends with half segment, to joint with corner easily.
    1836             :   //   If adjacent side is dotted or 0px and border-radius for the corner
    1837             :   //   between them is 0, the line ends with full segment.
    1838             :   //   (see comment for GetStraightBorderPoint for more detail)
    1839             :   //
    1840             :   // dotted side
    1841             :   //   If border-width <= 2.0, draw 1:1 dashed line.
    1842             :   //   Otherwise, draw circles along the side, with appropriate gap that makes
    1843             :   //   the side symmetric as far as possible.  The ratio of the gap to
    1844             :   //   border-width is the maximum value in [0.5, 1] range in most case.
    1845             :   //   if the side is too short and there's only 2 dots, it can be more smaller.
    1846             :   //   If there's no space to place 2 dots at the side, draw single dot at the
    1847             :   //   middle of the side.
    1848             :   //   In most case, line ends with filled dot, to joint with corner easily,
    1849             :   //   If adjacent side is dotted with larger border-width, or other style,
    1850             :   //   the line ends with unfilled dot.
    1851             :   //   (see comment for GetStraightBorderPoint for more detail)
    1852             : 
    1853           0 :   NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DASHED ||
    1854             :                mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DOTTED,
    1855             :                "Style should be dashed or dotted.");
    1856             : 
    1857           0 :   Float borderWidth = mBorderWidths[aSide];
    1858           0 :   if (borderWidth == 0.0f) {
    1859           0 :     return;
    1860             :   }
    1861             : 
    1862           0 :   if (mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DOTTED &&
    1863             :       borderWidth > 2.0f) {
    1864           0 :     DrawDottedSideSlow(aSide);
    1865           0 :     return;
    1866             :   }
    1867             : 
    1868           0 :   nscolor borderColor = mBorderColors[aSide];
    1869             :   bool ignored;
    1870             :   // Get the start and end points of the side, ensuring that any dot origins get
    1871             :   // pushed outward to account for stroking.
    1872           0 :   Point start = GetStraightBorderPoint(aSide, GetCCWCorner(aSide), &ignored, 0.5f);
    1873           0 :   Point end = GetStraightBorderPoint(aSide, GetCWCorner(aSide), &ignored, 0.5f);
    1874           0 :   if (borderWidth < 2.0f) {
    1875             :     // Round start to draw dot on each pixel.
    1876           0 :     if (IsHorizontalSide(aSide)) {
    1877           0 :       start.x = round(start.x);
    1878             :     } else {
    1879           0 :       start.y = round(start.y);
    1880             :     }
    1881             :   }
    1882             : 
    1883           0 :   Float borderLength = GetBorderLength(aSide, start, end);
    1884           0 :   if (borderLength < 0.0f) {
    1885           0 :     return;
    1886             :   }
    1887             : 
    1888           0 :   StrokeOptions strokeOptions(borderWidth);
    1889             :   Float dash[2];
    1890           0 :   SetupDashedOptions(&strokeOptions, dash, aSide, borderLength, false);
    1891             : 
    1892             :   // For dotted sides that can merge with their prior dotted sides, advance the
    1893             :   // dash offset to measure the distance around the combined path. This prevents
    1894             :   // two dots from bunching together at a corner.
    1895           0 :   mozilla::Side mergeSide = aSide;
    1896           0 :   while (IsCornerMergeable(GetCCWCorner(mergeSide))) {
    1897           0 :     mergeSide = PREV_SIDE(mergeSide);
    1898             :     // If we looped all the way around, measure starting at the top side, since
    1899             :     // we need to pick a fixed location to start measuring distance from still.
    1900           0 :     if (mergeSide == aSide) {
    1901           0 :       mergeSide = eSideTop;
    1902           0 :       break;
    1903             :     }
    1904             :   }
    1905           0 :   while (mergeSide != aSide) {
    1906             :     // Measure the length of the merged side starting from a possibly unmergeable
    1907             :     // corner up to the merged corner. A merged corner effectively has no border
    1908             :     // radius, so we can just use the cheaper AtCorner to find the end point.
    1909             :     Float mergeLength =
    1910             :       GetBorderLength(mergeSide,
    1911           0 :                       GetStraightBorderPoint(mergeSide, GetCCWCorner(mergeSide), &ignored, 0.5f),
    1912           0 :                       mOuterRect.AtCorner(GetCWCorner(mergeSide)));
    1913             :     // Add in the merged side length. Also offset the dash progress by an extra
    1914             :     // dot's width to avoid drawing a dot that would overdraw where the merged side
    1915             :     // would have ended in a gap, i.e. O_O_
    1916             :     //                                    O
    1917           0 :     strokeOptions.mDashOffset += mergeLength + borderWidth;
    1918           0 :     mergeSide = NEXT_SIDE(mergeSide);
    1919             :   }
    1920             : 
    1921           0 :   DrawOptions drawOptions;
    1922           0 :   if (mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DOTTED) {
    1923           0 :     drawOptions.mAntialiasMode = AntialiasMode::NONE;
    1924             :   }
    1925             : 
    1926           0 :   mDrawTarget->StrokeLine(start, end,
    1927           0 :                           ColorPattern(ToDeviceColor(borderColor)),
    1928             :                           strokeOptions,
    1929           0 :                           drawOptions);
    1930             : }
    1931             : 
    1932             : void
    1933           0 : nsCSSBorderRenderer::DrawDottedSideSlow(mozilla::Side aSide)
    1934             : {
    1935             :   // Draw each circles separately for dotted with borderWidth > 2.0.
    1936             :   // Dashed line with CapStyle::ROUND doesn't render perfect circles.
    1937             : 
    1938           0 :   NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DOTTED,
    1939             :                "Style should be dotted.");
    1940             : 
    1941           0 :   Float borderWidth = mBorderWidths[aSide];
    1942           0 :   if (borderWidth == 0.0f) {
    1943           0 :     return;
    1944             :   }
    1945             : 
    1946           0 :   nscolor borderColor = mBorderColors[aSide];
    1947             :   bool isStartUnfilled, isEndUnfilled;
    1948             :   Point start = GetStraightBorderPoint(aSide, GetCCWCorner(aSide),
    1949           0 :                                        &isStartUnfilled);
    1950             :   Point end = GetStraightBorderPoint(aSide, GetCWCorner(aSide),
    1951           0 :                                      &isEndUnfilled);
    1952             :   enum {
    1953             :     // Corner is not mergeable.
    1954             :     NO_MERGE,
    1955             : 
    1956             :     // Corner between different colors.
    1957             :     // Two dots are merged into one, and both side draw half dot.
    1958             :     MERGE_HALF,
    1959             : 
    1960             :     // Corner between same colors, CCW corner of the side.
    1961             :     // Two dots are merged into one, and this side draw entire dot.
    1962             :     //
    1963             :     // MERGE_ALL               MERGE_NONE
    1964             :     //   |                       |
    1965             :     //   v                       v
    1966             :     // +-----------------------+----+
    1967             :     // | ##      ##      ##    | ## |
    1968             :     // |####    ####    ####   |####|
    1969             :     // |####    ####    ####   |####|
    1970             :     // | ##      ##      ##    | ## |
    1971             :     // +----+------------------+    |
    1972             :     // |    |                  |    |
    1973             :     // |    |                  |    |
    1974             :     // |    |                  |    |
    1975             :     // | ## |                  | ## |
    1976             :     // |####|                  |####|
    1977             :     MERGE_ALL,
    1978             : 
    1979             :     // Corner between same colors, CW corner of the side.
    1980             :     // Two dots are merged into one, and this side doesn't draw dot.
    1981             :     MERGE_NONE
    1982           0 :   } mergeStart = NO_MERGE, mergeEnd = NO_MERGE;
    1983           0 :   if (IsCornerMergeable(GetCCWCorner(aSide))) {
    1984           0 :     if (borderColor == mBorderColors[PREV_SIDE(aSide)]) {
    1985           0 :       mergeStart = MERGE_ALL;
    1986             :     } else {
    1987           0 :       mergeStart = MERGE_HALF;
    1988             :     }
    1989             :   }
    1990             : 
    1991           0 :   if (IsCornerMergeable(GetCWCorner(aSide))) {
    1992           0 :     if (borderColor == mBorderColors[NEXT_SIDE(aSide)]) {
    1993           0 :       mergeEnd = MERGE_NONE;
    1994             :     } else {
    1995           0 :       mergeEnd = MERGE_HALF;
    1996             :     }
    1997             :   }
    1998             : 
    1999           0 :   Float borderLength = GetBorderLength(aSide, start, end);
    2000           0 :   if (borderLength < 0.0f) {
    2001           0 :     if (isStartUnfilled || isEndUnfilled) {
    2002           0 :       return;
    2003             :     }
    2004           0 :     borderLength = 0.0f;
    2005           0 :     start = end = (start + end) / 2.0f;
    2006             :   }
    2007             : 
    2008           0 :   Float dotWidth = borderWidth * DOT_LENGTH;
    2009           0 :   Float radius = borderWidth / 2.0f;
    2010           0 :   if (borderLength < dotWidth) {
    2011             :     // If dots on start and end may overlap, draw a dot at the middle of them.
    2012             :     //
    2013             :     //     ___---+-------+---___
    2014             :     // __--      | ##### |      --__
    2015             :     //          #|#######|#
    2016             :     //         ##|#######|##
    2017             :     //        ###|#######|###
    2018             :     //        ###+###+###+###
    2019             :     //         start ## end #
    2020             :     //         ##|#######|##
    2021             :     //          #|#######|#
    2022             :     //           | ##### |
    2023             :     //       __--+-------+--__
    2024             :     //     _-                 -_
    2025             :     //
    2026             :     // If that circle overflows from outer rect, do not draw it.
    2027             :     //
    2028             :     //           +-------+
    2029             :     //           | ##### |
    2030             :     //          #|#######|#
    2031             :     //         ##|#######|##
    2032             :     //        ###|#######|###
    2033             :     //        ###|###+###|###
    2034             :     //        ###|#######|###
    2035             :     //         ##|#######|##
    2036             :     //          #|#######|#
    2037             :     //           | ##### |
    2038             :     //           +--+-+--+
    2039             :     //           |  | |  |
    2040             :     //           |  | |  |
    2041           0 :     if (!mOuterRect.Contains(Rect(start.x - radius, start.y - radius,
    2042             :                                   borderWidth, borderWidth))) {
    2043           0 :       return;
    2044             :     }
    2045             : 
    2046           0 :     if (isStartUnfilled || isEndUnfilled) {
    2047           0 :       return;
    2048             :     }
    2049             : 
    2050           0 :     Point P = (start + end) / 2;
    2051           0 :     RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
    2052           0 :     builder->MoveTo(Point(P.x + radius, P.y));
    2053           0 :     builder->Arc(P, radius, 0.0f, Float(2.0 * M_PI));
    2054           0 :     RefPtr<Path> path = builder->Finish();
    2055           0 :     mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
    2056           0 :     return;
    2057             :   }
    2058             : 
    2059           0 :   if (mergeStart == MERGE_HALF || mergeEnd == MERGE_HALF) {
    2060             :     // MERGE_HALF
    2061             :     //               Eo
    2062             :     //   -------+----+
    2063             :     //        ##### /
    2064             :     //       ######/
    2065             :     //      ######/
    2066             :     //      ####+
    2067             :     //      ##/ end
    2068             :     //       /
    2069             :     //      /
    2070             :     //   --+
    2071             :     //     Ei
    2072             :     //
    2073             :     // other (NO_MERGE, MERGE_ALL, MERGE_NONE)
    2074             :     //               Eo
    2075             :     //   ------------+
    2076             :     //        #####  |
    2077             :     //       ####### |
    2078             :     //      #########|
    2079             :     //      ####+####|
    2080             :     //      ## end ##|
    2081             :     //       ####### |
    2082             :     //        #####  |
    2083             :     //   ------------+
    2084             :     //               Ei
    2085             : 
    2086           0 :     Point I(0.0f, 0.0f), J(0.0f, 0.0f);
    2087           0 :     if (aSide == eSideTop) {
    2088           0 :       I.x = 1.0f;
    2089           0 :       J.y = 1.0f;
    2090           0 :     } else if (aSide == eSideRight) {
    2091           0 :       I.y = 1.0f;
    2092           0 :       J.x = -1.0f;
    2093           0 :     } else if (aSide == eSideBottom) {
    2094           0 :       I.x = -1.0f;
    2095           0 :       J.y = -1.0f;
    2096           0 :     } else if (aSide == eSideLeft) {
    2097           0 :       I.y = -1.0f;
    2098           0 :       J.x = 1.0f;
    2099             :     }
    2100             : 
    2101           0 :     Point So, Si, Eo, Ei;
    2102             : 
    2103           0 :     So = (start + (-I + -J) * borderWidth / 2.0f);
    2104             :     Si = (mergeStart == MERGE_HALF)
    2105           0 :          ? (start + (I + J) * borderWidth / 2.0f)
    2106           0 :          : (start + (-I + J) * borderWidth / 2.0f);
    2107           0 :     Eo = (end + (I - J) * borderWidth / 2.0f);
    2108             :     Ei = (mergeEnd == MERGE_HALF)
    2109           0 :          ? (end + (-I + J) * borderWidth / 2.0f)
    2110           0 :          : (end + (I + J) * borderWidth / 2.0f);
    2111             : 
    2112           0 :     RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
    2113           0 :     builder->MoveTo(So);
    2114           0 :     builder->LineTo(Eo);
    2115           0 :     builder->LineTo(Ei);
    2116           0 :     builder->LineTo(Si);
    2117           0 :     builder->Close();
    2118           0 :     RefPtr<Path> path = builder->Finish();
    2119             : 
    2120           0 :     mDrawTarget->PushClip(path);
    2121             :   }
    2122             : 
    2123           0 :   size_t count = round(borderLength / dotWidth);
    2124           0 :   if (isStartUnfilled == isEndUnfilled) {
    2125             :     // Split into 2n segments.
    2126           0 :     if (count % 2) {
    2127           0 :       count++;
    2128             :     }
    2129             :   } else {
    2130             :     // Split into 2n+1 segments.
    2131           0 :     if (count % 2 == 0) {
    2132           0 :       count++;
    2133             :     }
    2134             :   }
    2135             : 
    2136             :   // A: radius == borderWidth / 2.0
    2137             :   // B: borderLength / count == borderWidth * (1 - overlap)
    2138             :   //
    2139             :   //   A      B         B        B        B     A
    2140             :   // |<-->|<------>|<------>|<------>|<------>|<-->|
    2141             :   // |    |        |        |        |        |    |
    2142             :   // +----+--------+--------+--------+--------+----+
    2143             :   // |  ##|##    **|**    ##|##    **|**    ##|##  |
    2144             :   // | ###|###  ***|***  ###|###  ***|***  ###|### |
    2145             :   // |####|####****|****####|####****|****####|####|
    2146             :   // |####+####****+****####+####****+****####+####|
    2147             :   // |# start #****|****####|####****|****## end ##|
    2148             :   // | ###|###  ***|***  ###|###  ***|***  ###|### |
    2149             :   // |  ##|##    **|**    ##|##    **|**    ##|##  |
    2150             :   // +----+----+---+--------+--------+---+----+----+
    2151             :   // |         |                         |         |
    2152             :   // |         |                         |         |
    2153             : 
    2154             :   // If isStartUnfilled is true, draw dots on 2j+1 points, if not, draw dots on
    2155             :   // 2j points.
    2156           0 :   size_t from = isStartUnfilled ? 1 : 0;
    2157             : 
    2158             :   // If mergeEnd == MERGE_NONE, last dot is drawn by next side.
    2159           0 :   size_t to = count;
    2160           0 :   if (mergeEnd == MERGE_NONE) {
    2161           0 :     if (to > 2) {
    2162           0 :       to -= 2;
    2163             :     } else {
    2164           0 :       to = 0;
    2165             :     }
    2166             :   }
    2167             : 
    2168           0 :   Point fromP = (start * (count - from) + end * from) / count;
    2169           0 :   Point toP = (start * (count - to) + end * to) / count;
    2170             :   // Extend dirty rect to avoid clipping pixel for anti-aliasing.
    2171           0 :   const Float AA_MARGIN = 2.0f;
    2172             : 
    2173           0 :   if (aSide == eSideTop) {
    2174             :     // Tweak |from| and |to| to fit into |mDirtyRect + radius margin|,
    2175             :     // to render only paths that may overlap mDirtyRect.
    2176             :     //
    2177             :     //                mDirtyRect + radius margin
    2178             :     //              +--+---------------------+--+
    2179             :     //              |                           |
    2180             :     //              |         mDirtyRect        |
    2181             :     //              +  +---------------------+  +
    2182             :     // from   ===>  |from                    to |   <===  to
    2183             :     //    +-----+-----+-----+-----+-----+-----+-----+-----+
    2184             :     //   ###        |###         ###         ###|        ###
    2185             :     //  #####       #####       #####       #####       #####
    2186             :     //  #####       #####       #####       #####       #####
    2187             :     //  #####       #####       #####       #####       #####
    2188             :     //   ###        |###         ###         ###|        ###
    2189             :     //              |  |                     |  |
    2190             :     //              +  +---------------------+  +
    2191             :     //              |                           |
    2192             :     //              |                           |
    2193             :     //              +--+---------------------+--+
    2194             : 
    2195           0 :     Float left = mDirtyRect.x - radius - AA_MARGIN;
    2196           0 :     if (fromP.x < left) {
    2197           0 :       size_t tmp = ceil(count * (left - start.x) / (end.x - start.x));
    2198           0 :       if (tmp > from) {
    2199             :         // We increment by 2, so odd/even should match between before/after.
    2200           0 :         if ((tmp & 1) != (from & 1)) {
    2201           0 :           from = tmp - 1;
    2202             :         } else {
    2203           0 :           from = tmp;
    2204             :         }
    2205             :       }
    2206             :     }
    2207           0 :     Float right = mDirtyRect.x + mDirtyRect.width + radius + AA_MARGIN;
    2208           0 :     if (toP.x > right) {
    2209           0 :       size_t tmp = floor(count * (right - start.x) / (end.x - start.x));
    2210           0 :       if (tmp < to) {
    2211           0 :         if ((tmp & 1) != (to & 1)) {
    2212           0 :           to = tmp + 1;
    2213             :         } else {
    2214           0 :           to = tmp;
    2215             :         }
    2216             :       }
    2217             :     }
    2218           0 :   } else if (aSide == eSideRight) {
    2219           0 :     Float top = mDirtyRect.y - radius - AA_MARGIN;
    2220           0 :     if (fromP.y < top) {
    2221           0 :       size_t tmp = ceil(count * (top - start.y) / (end.y - start.y));
    2222           0 :       if (tmp > from) {
    2223           0 :         if ((tmp & 1) != (from & 1)) {
    2224           0 :           from = tmp - 1;
    2225             :         } else {
    2226           0 :           from = tmp;
    2227             :         }
    2228             :       }
    2229             :     }
    2230           0 :     Float bottom = mDirtyRect.y + mDirtyRect.height + radius + AA_MARGIN;
    2231           0 :     if (toP.y > bottom) {
    2232           0 :       size_t tmp = floor(count * (bottom - start.y) / (end.y - start.y));
    2233           0 :       if (tmp < to) {
    2234           0 :         if ((tmp & 1) != (to & 1)) {
    2235           0 :           to = tmp + 1;
    2236             :         } else {
    2237           0 :           to = tmp;
    2238             :         }
    2239             :       }
    2240             :     }
    2241           0 :   } else if (aSide == eSideBottom) {
    2242           0 :     Float right = mDirtyRect.x + mDirtyRect.width + radius + AA_MARGIN;
    2243           0 :     if (fromP.x > right) {
    2244           0 :       size_t tmp = ceil(count * (right - start.x) / (end.x - start.x));
    2245           0 :       if (tmp > from) {
    2246           0 :         if ((tmp & 1) != (from & 1)) {
    2247           0 :           from = tmp - 1;
    2248             :         } else {
    2249           0 :           from = tmp;
    2250             :         }
    2251             :       }
    2252             :     }
    2253           0 :     Float left = mDirtyRect.x - radius - AA_MARGIN;
    2254           0 :     if (toP.x < left) {
    2255           0 :       size_t tmp = floor(count * (left - start.x) / (end.x - start.x));
    2256           0 :       if (tmp < to) {
    2257           0 :         if ((tmp & 1) != (to & 1)) {
    2258           0 :           to = tmp + 1;
    2259             :         } else {
    2260           0 :           to = tmp;
    2261             :         }
    2262             :       }
    2263             :     }
    2264           0 :   } else if (aSide == eSideLeft) {
    2265           0 :     Float bottom = mDirtyRect.y + mDirtyRect.height + radius + AA_MARGIN;
    2266           0 :     if (fromP.y > bottom) {
    2267           0 :       size_t tmp = ceil(count * (bottom - start.y) / (end.y - start.y));
    2268           0 :       if (tmp > from) {
    2269           0 :         if ((tmp & 1) != (from & 1)) {
    2270           0 :           from = tmp - 1;
    2271             :         } else {
    2272           0 :           from = tmp;
    2273             :         }
    2274             :       }
    2275             :     }
    2276           0 :     Float top = mDirtyRect.y - radius - AA_MARGIN;
    2277           0 :     if (toP.y < top) {
    2278           0 :       size_t tmp = floor(count * (top - start.y) / (end.y - start.y));
    2279           0 :       if (tmp < to) {
    2280           0 :         if ((tmp & 1) != (to & 1)) {
    2281           0 :           to = tmp + 1;
    2282             :         } else {
    2283           0 :           to = tmp;
    2284             :         }
    2285             :       }
    2286             :     }
    2287             :   }
    2288             : 
    2289           0 :   RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
    2290           0 :   size_t segmentCount = 0;
    2291           0 :   for (size_t i = from; i <= to; i += 2) {
    2292           0 :     if (segmentCount > BORDER_SEGMENT_COUNT_MAX) {
    2293           0 :       RefPtr<Path> path = builder->Finish();
    2294           0 :       mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
    2295           0 :       builder = mDrawTarget->CreatePathBuilder();
    2296           0 :       segmentCount = 0;
    2297             :     }
    2298             : 
    2299           0 :     Point P = (start * (count - i) + end * i) / count;
    2300           0 :     builder->MoveTo(Point(P.x + radius, P.y));
    2301           0 :     builder->Arc(P, radius, 0.0f, Float(2.0 * M_PI));
    2302           0 :     segmentCount++;
    2303             :   }
    2304           0 :   RefPtr<Path> path = builder->Finish();
    2305           0 :   mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
    2306             : 
    2307           0 :   if (mergeStart == MERGE_HALF || mergeEnd == MERGE_HALF) {
    2308           0 :     mDrawTarget->PopClip();
    2309             :   }
    2310             : }
    2311             : 
    2312             : void
    2313           0 : nsCSSBorderRenderer::DrawDashedOrDottedCorner(mozilla::Side aSide,
    2314             :                                               Corner aCorner)
    2315             : {
    2316             :   // Draw dashed/dotted corner with following approach.
    2317             :   //
    2318             :   // dashed corner
    2319             :   //   If both side has same border-width and border-width <= 2.0, draw dashed
    2320             :   //   line along the corner, with appropriate dash length and gap to make the
    2321             :   //   corner symmetric as far as possible.  Dash length equals to the gap, and
    2322             :   //   the ratio of the dash length to border-width is the maximum value in in
    2323             :   //   [1, 3] range.
    2324             :   //   Otherwise, draw dashed segments along the corner, keeping same dash
    2325             :   //   length ratio to border-width at that point.
    2326             :   //   (see DashedCornerFinder.h for more detail)
    2327             :   //   Line ends with half segments, to joint with both side easily.
    2328             :   //
    2329             :   // dotted corner
    2330             :   //   If both side has same border-width and border-width <= 2.0, draw 1:1
    2331             :   //   dashed line along the corner.
    2332             :   //   Otherwise Draw circles along the corner, with appropriate gap that makes
    2333             :   //   the corner symmetric as far as possible.  The size of the circle may
    2334             :   //   change along the corner, that is tangent to the outer curver and the
    2335             :   //   inner curve.  The ratio of the gap to circle diameter is the maximum
    2336             :   //   value in [0.5, 1] range.
    2337             :   //   (see DottedCornerFinder.h for more detail)
    2338             :   //   Corner ends with filled dots but those dots are drawn by
    2339             :   //   DrawDashedOrDottedSide.  So this may draw no circles if there's no space
    2340             :   //   between 2 dots at both ends.
    2341             : 
    2342           0 :   NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DASHED ||
    2343             :                mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DOTTED,
    2344             :                "Style should be dashed or dotted.");
    2345             : 
    2346           0 :   if (IsCornerMergeable(aCorner)) {
    2347             :     // DrawDashedOrDottedSide will draw corner.
    2348           0 :      return;
    2349             :    }
    2350             : 
    2351           0 :   mozilla::Side sideH(GetHorizontalSide(aCorner));
    2352           0 :   mozilla::Side sideV(GetVerticalSide(aCorner));
    2353           0 :   Float borderWidthH = mBorderWidths[sideH];
    2354           0 :   Float borderWidthV = mBorderWidths[sideV];
    2355           0 :   if (borderWidthH == 0.0f && borderWidthV == 0.0f) {
    2356           0 :     return;
    2357             :   }
    2358             : 
    2359           0 :   Float styleH = mBorderStyles[sideH];
    2360           0 :   Float styleV = mBorderStyles[sideV];
    2361             : 
    2362             :   // Corner between dotted and others with radius=0 is drawn by side.
    2363           0 :   if (IsZeroSize(mBorderRadii[aCorner]) &&
    2364           0 :       (styleV == NS_STYLE_BORDER_STYLE_DOTTED ||
    2365             :        styleH == NS_STYLE_BORDER_STYLE_DOTTED)) {
    2366           0 :     return;
    2367             :   }
    2368             : 
    2369           0 :   Float maxRadius = std::max(mBorderRadii[aCorner].width,
    2370           0 :                              mBorderRadii[aCorner].height);
    2371           0 :   if (maxRadius > BORDER_DOTTED_CORNER_MAX_RADIUS) {
    2372           0 :     DrawFallbackSolidCorner(aSide, aCorner);
    2373           0 :     return;
    2374             :   }
    2375             : 
    2376           0 :   if (borderWidthH != borderWidthV || borderWidthH > 2.0f) {
    2377           0 :     uint8_t style = mBorderStyles[aSide];
    2378           0 :     if (style == NS_STYLE_BORDER_STYLE_DOTTED) {
    2379           0 :       DrawDottedCornerSlow(aSide, aCorner);
    2380             :     } else {
    2381           0 :       DrawDashedCornerSlow(aSide, aCorner);
    2382             :     }
    2383           0 :     return;
    2384             :   }
    2385             : 
    2386           0 :   nscolor borderColor = mBorderColors[aSide];
    2387           0 :   Point points[4];
    2388             :   bool ignored;
    2389             :   // Get the start and end points of the corner arc, ensuring that any dot
    2390             :   // origins get pushed backwards towards the edges of the corner rect to
    2391             :   // account for stroking.
    2392           0 :   points[0] = GetStraightBorderPoint(sideH, aCorner, &ignored, -0.5f);
    2393           0 :   points[3] = GetStraightBorderPoint(sideV, aCorner, &ignored, -0.5f);
    2394             :   // Round points to draw dot on each pixel.
    2395           0 :   if (borderWidthH < 2.0f) {
    2396           0 :     points[0].x = round(points[0].x);
    2397             :   }
    2398           0 :   if (borderWidthV < 2.0f) {
    2399           0 :     points[3].y = round(points[3].y);
    2400             :   }
    2401           0 :   points[1] = points[0];
    2402           0 :   points[1].x += kKappaFactor * (points[3].x - points[0].x);
    2403           0 :   points[2] = points[3];
    2404           0 :   points[2].y += kKappaFactor * (points[0].y - points[3].y);
    2405             : 
    2406           0 :   Float len = GetQuarterEllipticArcLength(fabs(points[0].x - points[3].x),
    2407           0 :                                           fabs(points[0].y - points[3].y));
    2408             : 
    2409             :   Float dash[2];
    2410           0 :   StrokeOptions strokeOptions(borderWidthH);
    2411           0 :   SetupDashedOptions(&strokeOptions, dash, aSide, len, true);
    2412             : 
    2413           0 :   RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
    2414           0 :   builder->MoveTo(points[0]);
    2415           0 :   builder->BezierTo(points[1], points[2], points[3]);
    2416           0 :   RefPtr<Path> path = builder->Finish();
    2417           0 :   mDrawTarget->Stroke(path, ColorPattern(ToDeviceColor(borderColor)),
    2418           0 :                       strokeOptions);
    2419             : }
    2420             : 
    2421             : void
    2422           0 : nsCSSBorderRenderer::DrawDottedCornerSlow(mozilla::Side aSide,
    2423             :                                           Corner aCorner)
    2424             : {
    2425           0 :   NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DOTTED,
    2426             :                "Style should be dotted.");
    2427             : 
    2428           0 :   mozilla::Side sideH(GetHorizontalSide(aCorner));
    2429           0 :   mozilla::Side sideV(GetVerticalSide(aCorner));
    2430           0 :   Float R0 = mBorderWidths[sideH] / 2.0f;
    2431           0 :   Float Rn = mBorderWidths[sideV] / 2.0f;
    2432           0 :   if (R0 == 0.0f && Rn == 0.0f) {
    2433           0 :     return;
    2434             :   }
    2435             : 
    2436           0 :   nscolor borderColor = mBorderColors[aSide];
    2437           0 :   Bezier outerBezier;
    2438           0 :   Bezier innerBezier;
    2439           0 :   GetOuterAndInnerBezier(&outerBezier, &innerBezier, aCorner);
    2440             : 
    2441             :   bool ignored;
    2442           0 :   Point C0 = GetStraightBorderPoint(sideH, aCorner, &ignored);
    2443           0 :   Point Cn = GetStraightBorderPoint(sideV, aCorner, &ignored);
    2444             :   DottedCornerFinder finder(outerBezier, innerBezier, aCorner,
    2445           0 :                             mBorderRadii[aCorner].width,
    2446           0 :                             mBorderRadii[aCorner].height,
    2447           0 :                             C0, R0, Cn, Rn, mBorderCornerDimensions[aCorner]);
    2448             : 
    2449           0 :   RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
    2450           0 :   size_t segmentCount = 0;
    2451           0 :   const Float AA_MARGIN = 2.0f;
    2452           0 :   Rect marginedDirtyRect = mDirtyRect;
    2453           0 :   marginedDirtyRect.Inflate(std::max(R0, Rn) + AA_MARGIN);
    2454           0 :   bool entered = false;
    2455           0 :   while (finder.HasMore()) {
    2456           0 :     if (segmentCount > BORDER_SEGMENT_COUNT_MAX) {
    2457           0 :       RefPtr<Path> path = builder->Finish();
    2458           0 :       mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
    2459           0 :       builder = mDrawTarget->CreatePathBuilder();
    2460           0 :       segmentCount = 0;
    2461             :     }
    2462             : 
    2463           0 :     DottedCornerFinder::Result result = finder.Next();
    2464             : 
    2465           0 :     if (marginedDirtyRect.Contains(result.C) && result.r > 0) {
    2466           0 :       entered = true;
    2467           0 :       builder->MoveTo(Point(result.C.x + result.r, result.C.y));
    2468           0 :       builder->Arc(result.C, result.r, 0, Float(2.0 * M_PI));
    2469           0 :       segmentCount++;
    2470           0 :     } else if (entered) {
    2471           0 :       break;
    2472             :     }
    2473             :   }
    2474           0 :   RefPtr<Path> path = builder->Finish();
    2475           0 :   mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
    2476             : }
    2477             : 
    2478             : static inline bool
    2479           0 : DashedPathOverlapsRect(Rect& pathRect,
    2480             :                        const Rect& marginedDirtyRect,
    2481             :                        DashedCornerFinder::Result& result)
    2482             : {
    2483             :   // Calculate a rect that contains all control points of the |result| path,
    2484             :   // and check if it intersects with |marginedDirtyRect|.
    2485           0 :   pathRect.SetRect(result.outerSectionBezier.mPoints[0].x,
    2486           0 :                    result.outerSectionBezier.mPoints[0].y, 0, 0);
    2487           0 :   pathRect.ExpandToEnclose(result.outerSectionBezier.mPoints[1]);
    2488           0 :   pathRect.ExpandToEnclose(result.outerSectionBezier.mPoints[2]);
    2489           0 :   pathRect.ExpandToEnclose(result.outerSectionBezier.mPoints[3]);
    2490           0 :   pathRect.ExpandToEnclose(result.innerSectionBezier.mPoints[0]);
    2491           0 :   pathRect.ExpandToEnclose(result.innerSectionBezier.mPoints[1]);
    2492           0 :   pathRect.ExpandToEnclose(result.innerSectionBezier.mPoints[2]);
    2493           0 :   pathRect.ExpandToEnclose(result.innerSectionBezier.mPoints[3]);
    2494             : 
    2495           0 :   return pathRect.Intersects(marginedDirtyRect);
    2496             : }
    2497             : 
    2498             : void
    2499           0 : nsCSSBorderRenderer::DrawDashedCornerSlow(mozilla::Side aSide,
    2500             :                                           Corner aCorner)
    2501             : {
    2502           0 :   NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DASHED,
    2503             :                "Style should be dashed.");
    2504             : 
    2505           0 :   mozilla::Side sideH(GetHorizontalSide(aCorner));
    2506           0 :   mozilla::Side sideV(GetVerticalSide(aCorner));
    2507           0 :   Float borderWidthH = mBorderWidths[sideH];
    2508           0 :   Float borderWidthV = mBorderWidths[sideV];
    2509           0 :   if (borderWidthH == 0.0f && borderWidthV == 0.0f) {
    2510           0 :     return;
    2511             :   }
    2512             : 
    2513           0 :   nscolor borderColor = mBorderColors[aSide];
    2514           0 :   Bezier outerBezier;
    2515           0 :   Bezier innerBezier;
    2516           0 :   GetOuterAndInnerBezier(&outerBezier, &innerBezier, aCorner);
    2517             : 
    2518             :   DashedCornerFinder finder(outerBezier, innerBezier,
    2519             :                             borderWidthH, borderWidthV,
    2520           0 :                             mBorderCornerDimensions[aCorner]);
    2521             : 
    2522           0 :   RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
    2523           0 :   size_t segmentCount = 0;
    2524           0 :   const Float AA_MARGIN = 2.0f;
    2525           0 :   Rect marginedDirtyRect = mDirtyRect;
    2526           0 :   marginedDirtyRect.Inflate(AA_MARGIN);
    2527           0 :   Rect pathRect;
    2528           0 :   bool entered = false;
    2529           0 :   while (finder.HasMore()) {
    2530           0 :     if (segmentCount > BORDER_SEGMENT_COUNT_MAX) {
    2531           0 :       RefPtr<Path> path = builder->Finish();
    2532           0 :       mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
    2533           0 :       builder = mDrawTarget->CreatePathBuilder();
    2534           0 :       segmentCount = 0;
    2535             :     }
    2536             : 
    2537           0 :     DashedCornerFinder::Result result = finder.Next();
    2538             : 
    2539           0 :     if (DashedPathOverlapsRect(pathRect, marginedDirtyRect, result)) {
    2540           0 :       entered = true;
    2541           0 :       builder->MoveTo(result.outerSectionBezier.mPoints[0]);
    2542           0 :       builder->BezierTo(result.outerSectionBezier.mPoints[1],
    2543             :                         result.outerSectionBezier.mPoints[2],
    2544           0 :                         result.outerSectionBezier.mPoints[3]);
    2545           0 :       builder->LineTo(result.innerSectionBezier.mPoints[3]);
    2546           0 :       builder->BezierTo(result.innerSectionBezier.mPoints[2],
    2547             :                         result.innerSectionBezier.mPoints[1],
    2548           0 :                         result.innerSectionBezier.mPoints[0]);
    2549           0 :       builder->LineTo(result.outerSectionBezier.mPoints[0]);
    2550           0 :       segmentCount++;
    2551           0 :     } else if (entered) {
    2552           0 :       break;
    2553             :     }
    2554             :   }
    2555             : 
    2556           0 :   if (outerBezier.mPoints[0].x != innerBezier.mPoints[0].x) {
    2557             :     // Fill gap before the first section.
    2558             :     //
    2559             :     //     outnerPoint[0]
    2560             :     //         |
    2561             :     //         v
    2562             :     //        _+-----------+--
    2563             :     //      /   \##########|
    2564             :     //    /      \#########|
    2565             :     //   +        \########|
    2566             :     //   |\         \######|
    2567             :     //   |  \        \#####|
    2568             :     //   |    \       \####|
    2569             :     //   |      \       \##|
    2570             :     //   |        \      \#|
    2571             :     //   |          \     \|
    2572             :     //   |            \  _-+--
    2573             :     //   +--------------+  ^
    2574             :     //   |              |  |
    2575             :     //   |              |  innerPoint[0]
    2576             :     //   |              |
    2577           0 :     builder->MoveTo(outerBezier.mPoints[0]);
    2578           0 :     builder->LineTo(innerBezier.mPoints[0]);
    2579           0 :     builder->LineTo(Point(innerBezier.mPoints[0].x, outerBezier.mPoints[0].y));
    2580           0 :     builder->LineTo(outerBezier.mPoints[0]);
    2581             :   }
    2582             : 
    2583           0 :   if (outerBezier.mPoints[3].y != innerBezier.mPoints[3].y) {
    2584             :     // Fill gap after the last section.
    2585             :     //
    2586             :     // outnerPoint[3]
    2587             :     //   |
    2588             :     //   |
    2589             :     //   |    _+-----------+--
    2590             :     //   |  /   \          |
    2591             :     //   v/      \         |
    2592             :     //   +        \        |
    2593             :     //   |\         \      |
    2594             :     //   |##\        \     |
    2595             :     //   |####\       \    |
    2596             :     //   |######\       \  |
    2597             :     //   |########\      \ |
    2598             :     //   |##########\     \|
    2599             :     //   |############\  _-+--
    2600             :     //   +--------------+<-- innerPoint[3]
    2601             :     //   |              |
    2602             :     //   |              |
    2603             :     //   |              |
    2604           0 :     builder->MoveTo(outerBezier.mPoints[3]);
    2605           0 :     builder->LineTo(innerBezier.mPoints[3]);
    2606           0 :     builder->LineTo(Point(outerBezier.mPoints[3].x, innerBezier.mPoints[3].y));
    2607           0 :     builder->LineTo(outerBezier.mPoints[3]);
    2608             :   }
    2609             : 
    2610           0 :   RefPtr<Path> path = builder->Finish();
    2611           0 :   mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
    2612             : }
    2613             : 
    2614             : void
    2615           0 : nsCSSBorderRenderer::DrawFallbackSolidCorner(mozilla::Side aSide,
    2616             :                                              Corner aCorner)
    2617             : {
    2618             :   // Render too large dashed or dotted corner with solid style, to avoid hangup
    2619             :   // inside DashedCornerFinder and DottedCornerFinder.
    2620             : 
    2621           0 :   NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DASHED ||
    2622             :                mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DOTTED,
    2623             :                "Style should be dashed or dotted.");
    2624             : 
    2625           0 :   nscolor borderColor = mBorderColors[aSide];
    2626           0 :   Bezier outerBezier;
    2627           0 :   Bezier innerBezier;
    2628           0 :   GetOuterAndInnerBezier(&outerBezier, &innerBezier, aCorner);
    2629             : 
    2630           0 :   RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
    2631             : 
    2632           0 :   builder->MoveTo(outerBezier.mPoints[0]);
    2633           0 :   builder->BezierTo(outerBezier.mPoints[1],
    2634             :                     outerBezier.mPoints[2],
    2635           0 :                     outerBezier.mPoints[3]);
    2636           0 :   builder->LineTo(innerBezier.mPoints[3]);
    2637           0 :   builder->BezierTo(innerBezier.mPoints[2],
    2638             :                     innerBezier.mPoints[1],
    2639           0 :                     innerBezier.mPoints[0]);
    2640           0 :   builder->LineTo(outerBezier.mPoints[0]);
    2641             : 
    2642           0 :   RefPtr<Path> path = builder->Finish();
    2643           0 :   mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
    2644             : 
    2645           0 :   if (mDocument) {
    2646           0 :     if (!mPresContext->HasWarnedAboutTooLargeDashedOrDottedRadius()) {
    2647           0 :       mPresContext->SetHasWarnedAboutTooLargeDashedOrDottedRadius();
    2648           0 :       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
    2649           0 :                                       NS_LITERAL_CSTRING("CSS"),
    2650             :                                       mDocument,
    2651             :                                       nsContentUtils::eCSS_PROPERTIES,
    2652           0 :                                       mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DASHED
    2653             :                                       ? "TooLargeDashedRadius"
    2654           0 :                                       : "TooLargeDottedRadius");
    2655             :     }
    2656             :   }
    2657           0 : }
    2658             : 
    2659             : bool
    2660          34 : nsCSSBorderRenderer::AllBordersSameWidth()
    2661             : {
    2662          56 :   if (mBorderWidths[0] == mBorderWidths[1] &&
    2663          39 :       mBorderWidths[0] == mBorderWidths[2] &&
    2664          17 :       mBorderWidths[0] == mBorderWidths[3])
    2665             :   {
    2666          17 :     return true;
    2667             :   }
    2668             : 
    2669          17 :   return false;
    2670             : }
    2671             : 
    2672             : bool
    2673          17 : nsCSSBorderRenderer::AllBordersSolid(bool *aHasCompositeColors)
    2674             : {
    2675          17 :   *aHasCompositeColors = false;
    2676          85 :   NS_FOR_CSS_SIDES(i) {
    2677          68 :     if (mCompositeColors[i] != nullptr) {
    2678           0 :       *aHasCompositeColors = true;
    2679             :     }
    2680         109 :     if (mBorderStyles[i] == NS_STYLE_BORDER_STYLE_SOLID ||
    2681          41 :         mBorderStyles[i] == NS_STYLE_BORDER_STYLE_NONE ||
    2682           0 :         mBorderStyles[i] == NS_STYLE_BORDER_STYLE_HIDDEN)
    2683             :     {
    2684          68 :       continue;
    2685             :     }
    2686           0 :     return false;
    2687             :   }
    2688             : 
    2689          17 :   return true;
    2690             : }
    2691             : 
    2692         136 : bool IsVisible(int aStyle)
    2693             : {
    2694         136 :   if (aStyle != NS_STYLE_BORDER_STYLE_NONE &&
    2695             :       aStyle != NS_STYLE_BORDER_STYLE_HIDDEN) {
    2696          54 :         return true;
    2697             :   }
    2698          82 :   return false;
    2699             : }
    2700             : 
    2701             : struct twoFloats
    2702             : {
    2703             :     Float a, b;
    2704             : 
    2705         108 :     twoFloats operator*(const Size& aSize) const {
    2706         108 :       return { a * aSize.width, b * aSize.height };
    2707             :     }
    2708             : 
    2709          26 :     twoFloats operator*(Float aScale) const {
    2710          26 :       return { a * aScale, b * aScale };
    2711             :     }
    2712             : 
    2713           0 :     twoFloats operator+(const Point& aPoint) const {
    2714           0 :       return { a + aPoint.x, b + aPoint.y };
    2715             :     }
    2716             : 
    2717         147 :     operator Point() const {
    2718         147 :       return Point(a, b);
    2719             :     }
    2720             : };
    2721             : 
    2722             : void
    2723           0 : nsCSSBorderRenderer::DrawSingleWidthSolidBorder()
    2724             : {
    2725             :   // Easy enough to deal with.
    2726           0 :   Rect rect = mOuterRect;
    2727           0 :   rect.Deflate(0.5);
    2728             : 
    2729             :   const twoFloats cornerAdjusts[4] = { { +0.5,  0   },
    2730             :                                        {    0, +0.5 },
    2731             :                                        { -0.5,  0   },
    2732           0 :                                        {    0, -0.5 } };
    2733           0 :   NS_FOR_CSS_SIDES(side) {
    2734           0 :     Point firstCorner = rect.CCWCorner(side) + cornerAdjusts[side];
    2735           0 :     Point secondCorner = rect.CWCorner(side) + cornerAdjusts[side];
    2736             : 
    2737           0 :     ColorPattern color(ToDeviceColor(mBorderColors[side]));
    2738             : 
    2739           0 :     mDrawTarget->StrokeLine(firstCorner, secondCorner, color);
    2740             :   }
    2741           0 : }
    2742             : 
    2743             : // Intersect a ray from the inner corner to the outer corner
    2744             : // with the border radius, yielding the intersection point.
    2745             : static Point
    2746           0 : IntersectBorderRadius(const Point& aCenter, const Size& aRadius,
    2747             :                       const Point& aInnerCorner,
    2748             :                       const Point& aCornerDirection)
    2749             : {
    2750           0 :   Point toCorner = aCornerDirection;
    2751             :   // transform to-corner ray to unit-circle space
    2752           0 :   toCorner.x /= aRadius.width;
    2753           0 :   toCorner.y /= aRadius.height;
    2754             :   // normalize to-corner ray
    2755           0 :   Float cornerDist = toCorner.Length();
    2756           0 :   if (cornerDist < 1.0e-6f) {
    2757           0 :     return aInnerCorner;
    2758             :   }
    2759           0 :   toCorner = toCorner / cornerDist;
    2760             :   // ray from inner corner to border radius center
    2761           0 :   Point toCenter = aCenter - aInnerCorner;
    2762             :   // transform to-center ray to unit-circle space
    2763           0 :   toCenter.x /= aRadius.width;
    2764           0 :   toCenter.y /= aRadius.height;
    2765             :   // compute offset of intersection with border radius unit circle
    2766           0 :   Float offset = toCenter.DotProduct(toCorner);
    2767             :   // compute discriminant to check for intersections
    2768           0 :   Float discrim = 1.0f - toCenter.DotProduct(toCenter) + offset * offset;
    2769             :   // choose farthest intersection
    2770           0 :   offset += sqrtf(std::max(discrim, 0.0f));
    2771             :   // transform to-corner ray back out of unit-circle space
    2772           0 :   toCorner.x *= aRadius.width;
    2773           0 :   toCorner.y *= aRadius.height;
    2774           0 :   return aInnerCorner + toCorner * offset;
    2775             : }
    2776             : 
    2777             : // Calculate the split point and split angle for a border radius with
    2778             : // differing sides.
    2779             : static inline void
    2780           0 : SplitBorderRadius(const Point& aCenter, const Size& aRadius,
    2781             :                   const Point& aOuterCorner, const Point& aInnerCorner,
    2782             :                   const twoFloats& aCornerMults, Float aStartAngle,
    2783             :                   Point& aSplit, Float& aSplitAngle)
    2784             : {
    2785           0 :   Point cornerDir = aOuterCorner - aInnerCorner;
    2786           0 :   if (cornerDir.x == cornerDir.y && aRadius.IsSquare()) {
    2787             :     // optimize 45-degree intersection with circle since we can assume
    2788             :     // the circle center lies along the intersection edge
    2789           0 :     aSplit = aCenter - aCornerMults * (aRadius * Float(1.0f / M_SQRT2));
    2790           0 :     aSplitAngle = aStartAngle + 0.5f * M_PI / 2.0f;
    2791             :   } else {
    2792           0 :     aSplit = IntersectBorderRadius(aCenter, aRadius, aInnerCorner, cornerDir);
    2793           0 :     aSplitAngle =
    2794           0 :       atan2f((aSplit.y - aCenter.y) / aRadius.height,
    2795           0 :              (aSplit.x - aCenter.x) / aRadius.width);
    2796             :   }
    2797           0 : }
    2798             : 
    2799             : // Compute the size of the skirt needed, given the color alphas
    2800             : // of each corner side and the slope between them.
    2801             : static void
    2802           0 : ComputeCornerSkirtSize(Float aAlpha1, Float aAlpha2,
    2803             :                        Float aSlopeY, Float aSlopeX,
    2804             :                        Float& aSizeResult, Float& aSlopeResult)
    2805             : {
    2806             :   // If either side is (almost) invisible or there is no diagonal edge,
    2807             :   // then don't try to render a skirt.
    2808           0 :   if (aAlpha1 < 0.01f || aAlpha2 < 0.01f) {
    2809           0 :     return;
    2810             :   }
    2811           0 :   aSlopeX = fabs(aSlopeX);
    2812           0 :   aSlopeY = fabs(aSlopeY);
    2813           0 :   if (aSlopeX < 1.0e-6f || aSlopeY < 1.0e-6f) {
    2814           0 :     return;
    2815             :   }
    2816             : 
    2817             :   // If first and second color don't match, we need to split the corner in
    2818             :   // half. The diagonal edges created may not have full pixel coverage given
    2819             :   // anti-aliasing, so we need to compute a small subpixel skirt edge. This
    2820             :   // assumes each half has half coverage to start with, and that coverage
    2821             :   // increases as the skirt is pushed over, with the end result that we want
    2822             :   // to roughly preserve the alpha value along this edge.
    2823             :   // Given slope m, alphas a and A, use quadratic formula to solve for S in:
    2824             :   //   a*(1 - 0.5*(1-S)*(1-mS))*(1 - 0.5*A) + 0.5*A = A
    2825             :   // yielding:
    2826             :   //   S = ((1+m) - sqrt((1+m)*(1+m) + 4*m*(1 - A/(a*(1-0.5*A))))) / (2*m)
    2827             :   // and substitute k = (1+m)/(2*m):
    2828             :   //   S = k - sqrt(k*k + (1 - A/(a*(1-0.5*A)))/m)
    2829           0 :   Float slope = aSlopeY / aSlopeX;
    2830           0 :   Float slopeScale = (1.0f + slope) / (2.0f * slope);
    2831             :   Float discrim =
    2832           0 :     slopeScale*slopeScale +
    2833           0 :       (1 - aAlpha2 / (aAlpha1 * (1.0f - 0.49f * aAlpha2))) / slope;
    2834           0 :   if (discrim >= 0) {
    2835           0 :     aSizeResult = slopeScale - sqrtf(discrim);
    2836           0 :     aSlopeResult = slope;
    2837             :   }
    2838             : }
    2839             : 
    2840             : // Draws a border radius with possibly different sides.
    2841             : // A skirt is drawn underneath the corner intersection to hide possible
    2842             : // seams when anti-aliased drawing is used.
    2843             : static void
    2844           0 : DrawBorderRadius(DrawTarget* aDrawTarget,
    2845             :                  Corner c,
    2846             :                  const Point& aOuterCorner, const Point& aInnerCorner,
    2847             :                  const twoFloats& aCornerMultPrev, const twoFloats& aCornerMultNext,
    2848             :                  const Size& aCornerDims,
    2849             :                  const Size& aOuterRadius, const Size& aInnerRadius,
    2850             :                  const Color& aFirstColor, const Color& aSecondColor,
    2851             :                  Float aSkirtSize, Float aSkirtSlope)
    2852             : {
    2853             :   // Connect edge to outer arc start point
    2854           0 :   Point outerCornerStart = aOuterCorner + aCornerMultPrev * aCornerDims;
    2855             :   // Connect edge to outer arc end point
    2856           0 :   Point outerCornerEnd = aOuterCorner + aCornerMultNext * aCornerDims;
    2857             :   // Connect edge to inner arc start point
    2858             :   Point innerCornerStart =
    2859             :     outerCornerStart +
    2860           0 :       aCornerMultNext * (aCornerDims - aInnerRadius);
    2861             :   // Connect edge to inner arc end point
    2862             :   Point innerCornerEnd =
    2863             :     outerCornerEnd +
    2864           0 :       aCornerMultPrev * (aCornerDims - aInnerRadius);
    2865             : 
    2866             :   // Outer arc start point
    2867           0 :   Point outerArcStart = aOuterCorner + aCornerMultPrev * aOuterRadius;
    2868             :   // Outer arc end point
    2869           0 :   Point outerArcEnd = aOuterCorner + aCornerMultNext * aOuterRadius;
    2870             :   // Inner arc start point
    2871           0 :   Point innerArcStart = aInnerCorner + aCornerMultPrev * aInnerRadius;
    2872             :   // Inner arc end point
    2873           0 :   Point innerArcEnd = aInnerCorner + aCornerMultNext * aInnerRadius;
    2874             : 
    2875             :   // Outer radius center
    2876           0 :   Point outerCenter = aOuterCorner + (aCornerMultPrev + aCornerMultNext) * aOuterRadius;
    2877             :   // Inner radius center
    2878           0 :   Point innerCenter = aInnerCorner + (aCornerMultPrev + aCornerMultNext) * aInnerRadius;
    2879             : 
    2880           0 :   RefPtr<PathBuilder> builder;
    2881           0 :   RefPtr<Path> path;
    2882             : 
    2883           0 :   if (aFirstColor.a > 0) {
    2884           0 :     builder = aDrawTarget->CreatePathBuilder();
    2885           0 :     builder->MoveTo(outerCornerStart);
    2886             :   }
    2887             : 
    2888           0 :   if (aFirstColor != aSecondColor) {
    2889             :     // Start and end angles of corner quadrant
    2890           0 :     Float startAngle = (c * M_PI) / 2.0f - M_PI,
    2891           0 :           endAngle = startAngle + M_PI / 2.0f,
    2892             :           outerSplitAngle, innerSplitAngle;
    2893           0 :     Point outerSplit, innerSplit;
    2894             : 
    2895             :     // Outer half-way point
    2896             :     SplitBorderRadius(outerCenter, aOuterRadius, aOuterCorner, aInnerCorner,
    2897           0 :                       aCornerMultPrev + aCornerMultNext, startAngle,
    2898           0 :                       outerSplit, outerSplitAngle);
    2899             :     // Inner half-way point
    2900           0 :     if (aInnerRadius.IsEmpty()) {
    2901           0 :       innerSplit = aInnerCorner;
    2902           0 :       innerSplitAngle = endAngle;
    2903             :     } else {
    2904             :       SplitBorderRadius(innerCenter, aInnerRadius, aOuterCorner, aInnerCorner,
    2905           0 :                         aCornerMultPrev + aCornerMultNext, startAngle,
    2906           0 :                         innerSplit, innerSplitAngle);
    2907             :     }
    2908             : 
    2909             :     // Draw first half with first color
    2910           0 :     if (aFirstColor.a > 0) {
    2911           0 :       AcuteArcToBezier(builder.get(), outerCenter, aOuterRadius,
    2912           0 :                        outerArcStart, outerSplit, startAngle, outerSplitAngle);
    2913             :       // Draw skirt as part of first half
    2914           0 :       if (aSkirtSize > 0) {
    2915           0 :         builder->LineTo(outerSplit + aCornerMultNext * aSkirtSize);
    2916           0 :         builder->LineTo(innerSplit - aCornerMultPrev * (aSkirtSize * aSkirtSlope));
    2917             :       }
    2918           0 :       AcuteArcToBezier(builder.get(), innerCenter, aInnerRadius,
    2919           0 :                        innerSplit, innerArcStart, innerSplitAngle, startAngle);
    2920           0 :       if ((innerCornerStart - innerArcStart).DotProduct(aCornerMultPrev) > 0) {
    2921           0 :         builder->LineTo(innerCornerStart);
    2922             :       }
    2923           0 :       builder->Close();
    2924           0 :       path = builder->Finish();
    2925           0 :       aDrawTarget->Fill(path, ColorPattern(aFirstColor));
    2926             :     }
    2927             : 
    2928             :     // Draw second half with second color
    2929           0 :     if (aSecondColor.a > 0) {
    2930           0 :       builder = aDrawTarget->CreatePathBuilder();
    2931           0 :       builder->MoveTo(outerCornerEnd);
    2932           0 :       if ((innerArcEnd - innerCornerEnd).DotProduct(aCornerMultNext) < 0) {
    2933           0 :         builder->LineTo(innerCornerEnd);
    2934             :       }
    2935           0 :       AcuteArcToBezier(builder.get(), innerCenter, aInnerRadius,
    2936           0 :                        innerArcEnd, innerSplit, endAngle, innerSplitAngle);
    2937           0 :       AcuteArcToBezier(builder.get(), outerCenter, aOuterRadius,
    2938           0 :                        outerSplit, outerArcEnd, outerSplitAngle, endAngle);
    2939           0 :       builder->Close();
    2940           0 :       path = builder->Finish();
    2941           0 :       aDrawTarget->Fill(path, ColorPattern(aSecondColor));
    2942             :     }
    2943           0 :   } else if (aFirstColor.a > 0) {
    2944             :     // Draw corner with single color
    2945           0 :     AcuteArcToBezier(builder.get(), outerCenter, aOuterRadius,
    2946           0 :                      outerArcStart, outerArcEnd);
    2947           0 :     builder->LineTo(outerCornerEnd);
    2948           0 :     if ((innerArcEnd - innerCornerEnd).DotProduct(aCornerMultNext) < 0) {
    2949           0 :       builder->LineTo(innerCornerEnd);
    2950             :     }
    2951           0 :     AcuteArcToBezier(builder.get(), innerCenter, aInnerRadius,
    2952           0 :                      innerArcEnd, innerArcStart, -kKappaFactor);
    2953           0 :     if ((innerCornerStart - innerArcStart).DotProduct(aCornerMultPrev) > 0) {
    2954           0 :       builder->LineTo(innerCornerStart);
    2955             :     }
    2956           0 :     builder->Close();
    2957           0 :     path = builder->Finish();
    2958           0 :     aDrawTarget->Fill(path, ColorPattern(aFirstColor));
    2959             :   }
    2960           0 : }
    2961             : 
    2962             : // Draw a corner with possibly different sides.
    2963             : // A skirt is drawn underneath the corner intersection to hide possible
    2964             : // seams when anti-aliased drawing is used.
    2965             : static void
    2966           3 : DrawCorner(DrawTarget* aDrawTarget,
    2967             :            const Point& aOuterCorner, const Point& aInnerCorner,
    2968             :            const twoFloats& aCornerMultPrev, const twoFloats& aCornerMultNext,
    2969             :            const Size& aCornerDims,
    2970             :            const Color& aFirstColor, const Color& aSecondColor,
    2971             :            Float aSkirtSize, Float aSkirtSlope)
    2972             : {
    2973             :   // Corner box start point
    2974           3 :   Point cornerStart = aOuterCorner + aCornerMultPrev * aCornerDims;
    2975             :   // Corner box end point
    2976           3 :   Point cornerEnd = aOuterCorner + aCornerMultNext * aCornerDims;
    2977             : 
    2978           6 :   RefPtr<PathBuilder> builder;
    2979           6 :   RefPtr<Path> path;
    2980             : 
    2981           3 :   if (aFirstColor.a > 0) {
    2982           3 :     builder = aDrawTarget->CreatePathBuilder();
    2983           3 :     builder->MoveTo(cornerStart);
    2984             :   }
    2985             : 
    2986           3 :   if (aFirstColor != aSecondColor) {
    2987             :     // Draw first half with first color
    2988           0 :     if (aFirstColor.a > 0) {
    2989           0 :       builder->LineTo(aOuterCorner);
    2990             :       // Draw skirt as part of first half
    2991           0 :       if (aSkirtSize > 0) {
    2992           0 :         builder->LineTo(aOuterCorner + aCornerMultNext * aSkirtSize);
    2993           0 :         builder->LineTo(aInnerCorner - aCornerMultPrev * (aSkirtSize * aSkirtSlope));
    2994             :       }
    2995           0 :       builder->LineTo(aInnerCorner);
    2996           0 :       builder->Close();
    2997           0 :       path = builder->Finish();
    2998           0 :       aDrawTarget->Fill(path, ColorPattern(aFirstColor));
    2999             :     }
    3000             : 
    3001             :     // Draw second half with second color
    3002           0 :     if (aSecondColor.a > 0) {
    3003           0 :       builder = aDrawTarget->CreatePathBuilder();
    3004           0 :       builder->MoveTo(cornerEnd);
    3005           0 :       builder->LineTo(aInnerCorner);
    3006           0 :       builder->LineTo(aOuterCorner);
    3007           0 :       builder->Close();
    3008           0 :       path = builder->Finish();
    3009           0 :       aDrawTarget->Fill(path, ColorPattern(aSecondColor));
    3010             :     }
    3011           3 :   } else if (aFirstColor.a > 0) {
    3012             :     // Draw corner with single color
    3013           3 :     builder->LineTo(aOuterCorner);
    3014           3 :     builder->LineTo(cornerEnd);
    3015           3 :     builder->LineTo(aInnerCorner);
    3016           3 :     builder->Close();
    3017           3 :     path = builder->Finish();
    3018           3 :     aDrawTarget->Fill(path, ColorPattern(aFirstColor));
    3019             :   }
    3020           3 : }
    3021             : 
    3022             : void
    3023          17 : nsCSSBorderRenderer::DrawNoCompositeColorSolidBorder()
    3024             : {
    3025             :   const twoFloats cornerMults[4] = { { -1,  0 },
    3026             :                                      {  0, -1 },
    3027             :                                      { +1,  0 },
    3028          17 :                                      {  0, +1 } };
    3029             : 
    3030             :   const twoFloats centerAdjusts[4] = { { 0, +0.5 },
    3031             :                                        { -0.5, 0 },
    3032             :                                        { 0, -0.5 },
    3033          17 :                                        { +0.5, 0 } };
    3034             : 
    3035          17 :   RectCornerRadii innerRadii;
    3036          17 :   ComputeInnerRadii(mBorderRadii, mBorderWidths, &innerRadii);
    3037             : 
    3038          17 :   Rect strokeRect = mOuterRect;
    3039          51 :   strokeRect.Deflate(Margin(mBorderWidths[0] / 2.0, mBorderWidths[1] / 2.0,
    3040          51 :                             mBorderWidths[2] / 2.0, mBorderWidths[3] / 2.0));
    3041             : 
    3042          85 :   NS_FOR_CSS_SIDES(i) {
    3043             :     // We now draw the current side and the CW corner following it.
    3044             :     // The CCW corner of this side was already drawn in the previous iteration.
    3045             :     // The side will be drawn as an explicit stroke, and the CW corner will be
    3046             :     // filled separately.
    3047             :     // If the next side does not have a matching color, then we split the
    3048             :     // corner into two halves, one of each side's color and draw both.
    3049             :     // Thus, the CCW corner of the next side will end up drawn here.
    3050             : 
    3051             :     // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
    3052          68 :     Corner c = Corner((i+1) % 4);
    3053          68 :     Corner prevCorner = Corner(i);
    3054             : 
    3055             :     // i+2 and i+3 respectively.  These are used to index into the corner
    3056             :     // multiplier table, and were deduced by calculating out the long form
    3057             :     // of each corner and finding a pattern in the signs and values.
    3058          68 :     int i1 = (i+1) % 4;
    3059          68 :     int i2 = (i+2) % 4;
    3060          68 :     int i3 = (i+3) % 4;
    3061             : 
    3062          68 :     Float sideWidth = 0.0f;
    3063          68 :     Color firstColor, secondColor;
    3064          68 :     if (IsVisible(mBorderStyles[i]) && mBorderWidths[i]) {
    3065             :       // draw the side since it is visible
    3066          27 :       sideWidth = mBorderWidths[i];
    3067          27 :       firstColor = ToDeviceColor(mBorderColors[i]);
    3068             :       // if the next side is visible, use its color for corner
    3069             :       secondColor =
    3070          27 :         IsVisible(mBorderStyles[i1]) && mBorderWidths[i1] ?
    3071           3 :           ToDeviceColor(mBorderColors[i1]) :
    3072           3 :           firstColor;
    3073          41 :     } else if (IsVisible(mBorderStyles[i1]) && mBorderWidths[i1]) {
    3074             :       // assign next side's color to both corner sides
    3075          24 :       firstColor = ToDeviceColor(mBorderColors[i1]);
    3076          24 :       secondColor = firstColor;
    3077             :     } else {
    3078             :       // neither side is visible, so nothing to do
    3079          17 :       continue;
    3080             :     }
    3081             : 
    3082          51 :     Point outerCorner = mOuterRect.AtCorner(c);
    3083          51 :     Point innerCorner = mInnerRect.AtCorner(c);
    3084             : 
    3085             :     // start and end points of border side stroke between corners
    3086             :     Point sideStart =
    3087         102 :       mOuterRect.AtCorner(prevCorner) +
    3088         153 :         cornerMults[i2] * mBorderCornerDimensions[prevCorner];
    3089          51 :     Point sideEnd = outerCorner + cornerMults[i] * mBorderCornerDimensions[c];
    3090             :     // check if the side is visible and not inverted
    3091         179 :     if (sideWidth > 0 && firstColor.a > 0 &&
    3092         103 :         -(sideEnd - sideStart).DotProduct(cornerMults[i]) > 0) {
    3093          65 :       mDrawTarget->StrokeLine(sideStart + centerAdjusts[i] * sideWidth,
    3094          26 :                               sideEnd + centerAdjusts[i] * sideWidth,
    3095          26 :                               ColorPattern(firstColor),
    3096          52 :                               StrokeOptions(sideWidth));
    3097             :     }
    3098             : 
    3099          51 :     Float skirtSize = 0.0f, skirtSlope = 0.0f;
    3100             :     // the sides don't match, so compute a skirt
    3101          51 :     if (firstColor != secondColor &&
    3102           0 :         mPresContext->Type() != nsPresContext::eContext_Print) {
    3103           0 :       Point cornerDir = outerCorner - innerCorner;
    3104           0 :       ComputeCornerSkirtSize(firstColor.a, secondColor.a,
    3105           0 :                              cornerDir.DotProduct(cornerMults[i]),
    3106           0 :                              cornerDir.DotProduct(cornerMults[i3]),
    3107           0 :                              skirtSize, skirtSlope);
    3108             :     }
    3109             : 
    3110          51 :     if (!mBorderRadii[c].IsEmpty()) {
    3111             :       // the corner has a border radius
    3112           0 :       DrawBorderRadius(mDrawTarget,
    3113             :                        c, outerCorner, innerCorner,
    3114           0 :                        cornerMults[i], cornerMults[i3],
    3115           0 :                        mBorderCornerDimensions[c],
    3116           0 :                        mBorderRadii[c], innerRadii[c],
    3117           0 :                        firstColor, secondColor, skirtSize, skirtSlope);
    3118          51 :     } else if (!mBorderCornerDimensions[c].IsEmpty()) {
    3119             :       // a corner with no border radius
    3120           6 :       DrawCorner(mDrawTarget,
    3121             :                  outerCorner, innerCorner,
    3122           3 :                  cornerMults[i], cornerMults[i3],
    3123           3 :                  mBorderCornerDimensions[c],
    3124           6 :                  firstColor, secondColor, skirtSize, skirtSlope);
    3125             :     }
    3126             :   }
    3127          17 : }
    3128             : 
    3129             : void
    3130           0 : nsCSSBorderRenderer::DrawRectangularCompositeColors()
    3131             : {
    3132             :   nsBorderColors *currentColors[4];
    3133           0 :   memcpy(currentColors, mCompositeColors, sizeof(nsBorderColors*) * 4);
    3134           0 :   Rect rect = mOuterRect;
    3135           0 :   rect.Deflate(0.5);
    3136             : 
    3137             :   const twoFloats cornerAdjusts[4] = { { +0.5,  0   },
    3138             :                                         {    0, +0.5 },
    3139             :                                         { -0.5,  0   },
    3140           0 :                                         {    0, -0.5 } };
    3141             : 
    3142           0 :   for (int i = 0; i < mBorderWidths[0]; i++) {
    3143           0 :     NS_FOR_CSS_SIDES(side) {
    3144           0 :       int sideNext = (side + 1) % 4;
    3145             : 
    3146           0 :       Point firstCorner = rect.CCWCorner(side) + cornerAdjusts[side];
    3147           0 :       Point secondCorner = rect.CWCorner(side) - cornerAdjusts[side];
    3148             : 
    3149             :       Color currentColor = Color::FromABGR(
    3150           0 :         currentColors[side] ? currentColors[side]->mColor
    3151           0 :                             : mBorderColors[side]);
    3152             : 
    3153           0 :       mDrawTarget->StrokeLine(firstCorner, secondCorner,
    3154           0 :                               ColorPattern(ToDeviceColor(currentColor)));
    3155             : 
    3156           0 :       Point cornerTopLeft = rect.CWCorner(side) - Point(0.5, 0.5);
    3157             :       Color nextColor = Color::FromABGR(
    3158           0 :         currentColors[sideNext] ? currentColors[sideNext]->mColor
    3159           0 :                                 : mBorderColors[sideNext]);
    3160             : 
    3161           0 :       Color cornerColor((currentColor.r + nextColor.r) / 2.f,
    3162           0 :                         (currentColor.g + nextColor.g) / 2.f,
    3163           0 :                         (currentColor.b + nextColor.b) / 2.f,
    3164           0 :                         (currentColor.a + nextColor.a) / 2.f);
    3165           0 :       mDrawTarget->FillRect(Rect(cornerTopLeft, Size(1, 1)),
    3166           0 :                             ColorPattern(ToDeviceColor(cornerColor)));
    3167             : 
    3168           0 :       if (side != 0) {
    3169             :         // We'll have to keep side 0 for the color averaging on side 3.
    3170           0 :         if (currentColors[side] && currentColors[side]->mNext) {
    3171           0 :           currentColors[side] = currentColors[side]->mNext;
    3172             :         }
    3173             :       }
    3174             :     }
    3175             :     // Now advance the color for side 0.
    3176           0 :     if (currentColors[0] && currentColors[0]->mNext) {
    3177           0 :       currentColors[0] = currentColors[0]->mNext;
    3178             :     }
    3179           0 :     rect.Deflate(1);
    3180             :   }
    3181           0 : }
    3182             : 
    3183             : void
    3184          34 : nsCSSBorderRenderer::DrawBorders()
    3185             : {
    3186          34 :   bool forceSeparateCorners = false;
    3187             : 
    3188             :   // Examine the border style to figure out if we can draw it in one
    3189             :   // go or not.
    3190          34 :   bool tlBordersSame = AreBorderSideFinalStylesSame(eSideBitsTop | eSideBitsLeft);
    3191          34 :   bool brBordersSame = AreBorderSideFinalStylesSame(eSideBitsBottom | eSideBitsRight);
    3192          34 :   bool allBordersSame = AreBorderSideFinalStylesSame(eSideBitsAll);
    3193          51 :   if (allBordersSame &&
    3194          34 :       ((mCompositeColors[0] == nullptr &&
    3195          34 :        (mBorderStyles[0] == NS_STYLE_BORDER_STYLE_NONE ||
    3196          34 :         mBorderStyles[0] == NS_STYLE_BORDER_STYLE_HIDDEN ||
    3197          34 :         mBorderColors[0] == NS_RGBA(0,0,0,0))) ||
    3198          17 :        (mCompositeColors[0] &&
    3199           0 :         (mCompositeColors[0]->mColor == NS_RGBA(0,0,0,0) &&
    3200           0 :          !mCompositeColors[0]->mNext))))
    3201             :   {
    3202             :     // All borders are the same style, and the style is either none or hidden, or the color
    3203             :     // is transparent.
    3204             :     // This also checks if the first composite color is transparent, and there are
    3205             :     // no others. It doesn't check if there are subsequent transparent ones, because
    3206             :     // that would be very silly.
    3207          34 :     return;
    3208             :   }
    3209             : 
    3210          34 :   AutoRestoreTransform autoRestoreTransform;
    3211          34 :   Matrix mat = mDrawTarget->GetTransform();
    3212             : 
    3213             :   // Clamp the CTM to be pixel-aligned; we do this only
    3214             :   // for translation-only matrices now, but we could do it
    3215             :   // if the matrix has just a scale as well.  We should not
    3216             :   // do it if there's a rotation.
    3217          34 :   if (mat.HasNonTranslation()) {
    3218           0 :     if (!mat.HasNonAxisAlignedTransform()) {
    3219             :       // Scale + transform. Avoid stroke fast-paths so that we have a chance
    3220             :       // of snapping to pixel boundaries.
    3221           0 :       mAvoidStroke = true;
    3222             :     }
    3223             :   } else {
    3224          34 :     mat._31 = floor(mat._31 + 0.5);
    3225          34 :     mat._32 = floor(mat._32 + 0.5);
    3226          34 :     autoRestoreTransform.Init(mDrawTarget);
    3227          34 :     mDrawTarget->SetTransform(mat);
    3228             : 
    3229             :     // round mOuterRect and mInnerRect; they're already an integer
    3230             :     // number of pixels apart and should stay that way after
    3231             :     // rounding. We don't do this if there's a scale in the current transform
    3232             :     // since this loses information that might be relevant when we're scaling.
    3233          34 :     mOuterRect.Round();
    3234          34 :     mInnerRect.Round();
    3235             :   }
    3236             : 
    3237          34 :   bool allBordersSameWidth = AllBordersSameWidth();
    3238             : 
    3239          34 :   if (allBordersSameWidth && mBorderWidths[0] == 0.0) {
    3240             :     // Some of the allBordersSameWidth codepaths depend on the border
    3241             :     // width being greater than zero.
    3242           0 :     return;
    3243             :   }
    3244             : 
    3245             :   // Initial values only used when the border colors/widths are all the same:
    3246          34 :   ColorPattern color(ToDeviceColor(mBorderColors[eSideTop]));
    3247          34 :   StrokeOptions strokeOptions(mBorderWidths[eSideTop]); // stroke width
    3248             : 
    3249             :   bool allBordersSolid;
    3250             : 
    3251             :   // First there's a couple of 'special cases' that have specifically optimized
    3252             :   // drawing paths, when none of these can be used we move on to the generalized
    3253             :   // border drawing code.
    3254          51 :   if (allBordersSame &&
    3255          34 :       mCompositeColors[0] == nullptr &&
    3256          17 :       allBordersSameWidth &&
    3257          34 :       mBorderStyles[0] == NS_STYLE_BORDER_STYLE_SOLID &&
    3258          17 :       mNoBorderRadius &&
    3259           0 :       !mAvoidStroke)
    3260             :   {
    3261             :     // Very simple case.
    3262           0 :     Rect rect = mOuterRect;
    3263           0 :     rect.Deflate(mBorderWidths[0] / 2.0);
    3264           0 :     mDrawTarget->StrokeRect(rect, color, strokeOptions);
    3265           0 :     return;
    3266             :   }
    3267             : 
    3268          51 :   if (allBordersSame &&
    3269          34 :       mCompositeColors[0] == nullptr &&
    3270          34 :       mBorderStyles[0] == NS_STYLE_BORDER_STYLE_SOLID &&
    3271          34 :       !mAvoidStroke &&
    3272          17 :       !mNoBorderRadius)
    3273             :   {
    3274             :     // Relatively simple case.
    3275          17 :     gfxRect outerRect = ThebesRect(mOuterRect);
    3276          17 :     RoundedRect borderInnerRect(outerRect, mBorderRadii);
    3277          51 :     borderInnerRect.Deflate(mBorderWidths[eSideTop],
    3278          17 :                             mBorderWidths[eSideBottom],
    3279          17 :                             mBorderWidths[eSideLeft],
    3280          34 :                             mBorderWidths[eSideRight]);
    3281             : 
    3282             :     // Instead of stroking we just use two paths: an inner and an outer.
    3283             :     // This allows us to draw borders that we couldn't when stroking. For example,
    3284             :     // borders with a border width >= the border radius. (i.e. when there are
    3285             :     // square corners on the inside)
    3286             :     //
    3287             :     // Further, this approach can be more efficient because the backend
    3288             :     // doesn't need to compute an offset curve to stroke the path. We know that
    3289             :     // the rounded parts are elipses we can offset exactly and can just compute
    3290             :     // a new cubic approximation.
    3291          34 :     RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
    3292          17 :     AppendRoundedRectToPath(builder, mOuterRect, mBorderRadii, true);
    3293          17 :     AppendRoundedRectToPath(builder, ToRect(borderInnerRect.rect), borderInnerRect.corners, false);
    3294          34 :     RefPtr<Path> path = builder->Finish();
    3295          17 :     mDrawTarget->Fill(path, color);
    3296          17 :     return;
    3297             :   }
    3298             : 
    3299             :   bool hasCompositeColors;
    3300             : 
    3301          17 :   allBordersSolid = AllBordersSolid(&hasCompositeColors);
    3302             :   // This leaves the border corners non-interpolated for single width borders.
    3303             :   // Doing this is slightly faster and shouldn't be a problem visually.
    3304          17 :   if (allBordersSolid &&
    3305           0 :       allBordersSameWidth &&
    3306           0 :       mCompositeColors[0] == nullptr &&
    3307           0 :       mBorderWidths[0] == 1 &&
    3308           0 :       mNoBorderRadius &&
    3309           0 :       !mAvoidStroke)
    3310             :   {
    3311           0 :     DrawSingleWidthSolidBorder();
    3312           0 :     return;
    3313             :   }
    3314             : 
    3315          34 :   if (allBordersSolid && !hasCompositeColors &&
    3316          17 :       !mAvoidStroke)
    3317             :   {
    3318          17 :     DrawNoCompositeColorSolidBorder();
    3319          17 :     return;
    3320             :   }
    3321             : 
    3322           0 :   if (allBordersSolid &&
    3323           0 :       allBordersSameWidth &&
    3324           0 :       mNoBorderRadius &&
    3325           0 :       !mAvoidStroke)
    3326             :   {
    3327             :     // Easy enough to deal with.
    3328           0 :     DrawRectangularCompositeColors();
    3329           0 :     return;
    3330             :   }
    3331             : 
    3332             :   // If we have composite colors -and- border radius,
    3333             :   // then use separate corners so we get OP_ADD for the corners.
    3334             :   // Otherwise, we'll get artifacts as we draw stacked 1px-wide curves.
    3335           0 :   if (allBordersSame && mCompositeColors[0] != nullptr && !mNoBorderRadius)
    3336           0 :     forceSeparateCorners = true;
    3337             : 
    3338           0 :   PrintAsString(" mOuterRect: "); PrintAsString(mOuterRect); PrintAsStringNewline();
    3339           0 :   PrintAsString(" mInnerRect: "); PrintAsString(mInnerRect); PrintAsStringNewline();
    3340           0 :   PrintAsFormatString(" mBorderColors: 0x%08x 0x%08x 0x%08x 0x%08x\n", mBorderColors[0], mBorderColors[1], mBorderColors[2], mBorderColors[3]);
    3341             : 
    3342             :   // if conditioning the outside rect failed, then bail -- the outside
    3343             :   // rect is supposed to enclose the entire border
    3344             :   {
    3345           0 :     gfxRect outerRect = ThebesRect(mOuterRect);
    3346           0 :     gfxUtils::ConditionRect(outerRect);
    3347           0 :     if (outerRect.IsEmpty())
    3348           0 :       return;
    3349           0 :     mOuterRect = ToRect(outerRect);
    3350             : 
    3351           0 :     gfxRect innerRect = ThebesRect(mInnerRect);
    3352           0 :     gfxUtils::ConditionRect(innerRect);
    3353           0 :     mInnerRect = ToRect(innerRect);
    3354             :   }
    3355             : 
    3356           0 :   int dashedSides = 0;
    3357             : 
    3358           0 :   NS_FOR_CSS_SIDES(i) {
    3359           0 :     uint8_t style = mBorderStyles[i];
    3360           0 :     if (style == NS_STYLE_BORDER_STYLE_DASHED ||
    3361             :         style == NS_STYLE_BORDER_STYLE_DOTTED)
    3362             :     {
    3363             :       // pretend that all borders aren't the same; we need to draw
    3364             :       // things separately for dashed/dotting
    3365           0 :       allBordersSame = false;
    3366           0 :       dashedSides |= (1 << i);
    3367             :     }
    3368             :   }
    3369             : 
    3370           0 :   PrintAsFormatString(" allBordersSame: %d dashedSides: 0x%02x\n", allBordersSame, dashedSides);
    3371             : 
    3372           0 :   if (allBordersSame && !forceSeparateCorners) {
    3373             :     /* Draw everything in one go */
    3374           0 :     DrawBorderSides(eSideBitsAll);
    3375           0 :     PrintAsStringNewline("---------------- (1)");
    3376             :   } else {
    3377           0 :     AUTO_PROFILER_LABEL("nsCSSBorderRenderer::DrawBorders:multipass", GRAPHICS);
    3378             : 
    3379             :     /* We have more than one pass to go.  Draw the corners separately from the sides. */
    3380             : 
    3381             :     /*
    3382             :      * If we have a 1px-wide border, the corners are going to be
    3383             :      * negligible, so don't bother doing anything fancy.  Just extend
    3384             :      * the top and bottom borders to the right 1px and the left border
    3385             :      * to the bottom 1px.  We do this by twiddling the corner dimensions,
    3386             :      * which causes the right to happen later on.  Only do this if we have
    3387             :      * a 1.0 unit border all around and no border radius.
    3388             :      */
    3389             : 
    3390           0 :     NS_FOR_CSS_FULL_CORNERS(corner) {
    3391           0 :       const mozilla::Side sides[2] = { mozilla::Side(corner), PREV_SIDE(corner) };
    3392             : 
    3393           0 :       if (!IsZeroSize(mBorderRadii[corner]))
    3394           0 :         continue;
    3395             : 
    3396           0 :       if (mBorderWidths[sides[0]] == 1.0 && mBorderWidths[sides[1]] == 1.0) {
    3397           0 :         if (corner == eCornerTopLeft || corner == eCornerTopRight)
    3398           0 :           mBorderCornerDimensions[corner].width = 0.0;
    3399             :         else
    3400           0 :           mBorderCornerDimensions[corner].height = 0.0;
    3401             :       }
    3402             :     }
    3403             : 
    3404             :     // First, the corners
    3405           0 :     NS_FOR_CSS_FULL_CORNERS(corner) {
    3406             :       // if there's no corner, don't do all this work for it
    3407           0 :       if (IsZeroSize(mBorderCornerDimensions[corner]))
    3408           0 :         continue;
    3409             : 
    3410           0 :       const int sides[2] = { corner, PREV_SIDE(corner) };
    3411           0 :       int sideBits = (1 << sides[0]) | (1 << sides[1]);
    3412             : 
    3413           0 :       bool simpleCornerStyle = mCompositeColors[sides[0]] == nullptr &&
    3414           0 :                                  mCompositeColors[sides[1]] == nullptr &&
    3415           0 :                                  AreBorderSideFinalStylesSame(sideBits);
    3416             : 
    3417             :       // If we don't have anything complex going on in this corner,
    3418             :       // then we can just fill the corner with a solid color, and avoid
    3419             :       // the potentially expensive clip.
    3420           0 :       if (simpleCornerStyle &&
    3421           0 :           IsZeroSize(mBorderRadii[corner]) &&
    3422           0 :           IsSolidCornerStyle(mBorderStyles[sides[0]], corner))
    3423             :       {
    3424           0 :         Color color = MakeBorderColor(mBorderColors[sides[0]],
    3425             :                                       mBackgroundColor,
    3426           0 :                                       BorderColorStyleForSolidCorner(mBorderStyles[sides[0]], corner));
    3427           0 :         mDrawTarget->FillRect(GetCornerRect(corner),
    3428           0 :                               ColorPattern(ToDeviceColor(color)));
    3429           0 :         continue;
    3430             :       }
    3431             : 
    3432             :       // clip to the corner
    3433           0 :       mDrawTarget->PushClipRect(GetCornerRect(corner));
    3434             : 
    3435           0 :       if (simpleCornerStyle) {
    3436             :         // we don't need a group for this corner, the sides are the same,
    3437             :         // but we weren't able to render just a solid block for the corner.
    3438           0 :         DrawBorderSides(sideBits);
    3439             :       } else {
    3440             :         // Sides are different.  We could draw using OP_ADD to
    3441             :         // get correct color blending behaviour at the seam.  We'd need
    3442             :         // to do it in an offscreen surface to ensure that we're
    3443             :         // always compositing on transparent black.  If the colors
    3444             :         // don't have transparency and the current destination surface
    3445             :         // has an alpha channel, we could just clear the region and
    3446             :         // avoid the temporary, but that situation doesn't happen all
    3447             :         // that often in practice (we double buffer to no-alpha
    3448             :         // surfaces). We choose just to seam though, as the performance
    3449             :         // advantages outway the modest easthetic improvement.
    3450             : 
    3451           0 :         for (int cornerSide = 0; cornerSide < 2; cornerSide++) {
    3452           0 :           mozilla::Side side = mozilla::Side(sides[cornerSide]);
    3453           0 :           uint8_t style = mBorderStyles[side];
    3454             : 
    3455           0 :           PrintAsFormatString("corner: %d cornerSide: %d side: %d style: %d\n", corner, cornerSide, side, style);
    3456             : 
    3457           0 :           RefPtr<Path> path = GetSideClipSubPath(side);
    3458           0 :           mDrawTarget->PushClip(path);
    3459             : 
    3460           0 :           DrawBorderSides(1 << side);
    3461             : 
    3462           0 :           mDrawTarget->PopClip();
    3463             :         }
    3464             :       }
    3465             : 
    3466           0 :       mDrawTarget->PopClip();
    3467             : 
    3468           0 :       PrintAsStringNewline();
    3469             :     }
    3470             : 
    3471             :     // in the case of a single-unit border, we already munged the
    3472             :     // corners up above; so we can just draw the top left and bottom
    3473             :     // right sides separately, if they're the same.
    3474             :     //
    3475             :     // We need to check for mNoBorderRadius, because when there is
    3476             :     // one, FillSolidBorder always draws the full rounded rectangle
    3477             :     // and expects there to be a clip in place.
    3478           0 :     int alreadyDrawnSides = 0;
    3479           0 :     if (mOneUnitBorder &&
    3480           0 :         mNoBorderRadius &&
    3481           0 :         (dashedSides & (eSideBitsTop | eSideBitsLeft)) == 0)
    3482             :     {
    3483           0 :       if (tlBordersSame) {
    3484           0 :         DrawBorderSides(eSideBitsTop | eSideBitsLeft);
    3485           0 :         alreadyDrawnSides |= (eSideBitsTop | eSideBitsLeft);
    3486             :       }
    3487             : 
    3488           0 :       if (brBordersSame && (dashedSides & (eSideBitsBottom | eSideBitsRight)) == 0) {
    3489           0 :         DrawBorderSides(eSideBitsBottom | eSideBitsRight);
    3490           0 :         alreadyDrawnSides |= (eSideBitsBottom | eSideBitsRight);
    3491             :       }
    3492             :     }
    3493             : 
    3494             :     // We're done with the corners, now draw the sides.
    3495           0 :     NS_FOR_CSS_SIDES (side) {
    3496             :       // if we drew it above, skip it
    3497           0 :       if (alreadyDrawnSides & (1 << side))
    3498           0 :         continue;
    3499             : 
    3500             :       // If there's no border on this side, skip it
    3501           0 :       if (mBorderWidths[side] == 0.0 ||
    3502           0 :           mBorderStyles[side] == NS_STYLE_BORDER_STYLE_HIDDEN ||
    3503           0 :           mBorderStyles[side] == NS_STYLE_BORDER_STYLE_NONE)
    3504           0 :         continue;
    3505             : 
    3506             : 
    3507           0 :       if (dashedSides & (1 << side)) {
    3508             :         // Dashed sides will always draw just the part ignoring the
    3509             :         // corners for the side, so no need to clip.
    3510           0 :         DrawDashedOrDottedSide(side);
    3511             : 
    3512           0 :         PrintAsStringNewline("---------------- (d)");
    3513           0 :         continue;
    3514             :       }
    3515             : 
    3516             :       // Undashed sides will currently draw the entire side,
    3517             :       // including parts that would normally be covered by a corner,
    3518             :       // so we need to clip.
    3519             :       //
    3520             :       // XXX Optimization -- it would be good to make this work like
    3521             :       // DrawDashedOrDottedSide, and have a DrawOneSide function that just
    3522             :       // draws one side and not the corners, because then we can
    3523             :       // avoid the potentially expensive clip.
    3524           0 :       mDrawTarget->PushClipRect(GetSideClipWithoutCornersRect(side));
    3525             : 
    3526           0 :       DrawBorderSides(1 << side);
    3527             : 
    3528           0 :       mDrawTarget->PopClip();
    3529             : 
    3530           0 :       PrintAsStringNewline("---------------- (*)");
    3531             :     }
    3532             :   }
    3533             : }
    3534             : 
    3535             : bool
    3536           0 : nsCSSBorderRenderer::CanCreateWebRenderCommands()
    3537             : {
    3538           0 :   NS_FOR_CSS_SIDES(i) {
    3539           0 :     if (mCompositeColors[i] != nullptr) {
    3540           0 :       return false;
    3541             :     }
    3542             : 
    3543           0 :     if (mBorderStyles[i] == NS_STYLE_BORDER_STYLE_DOUBLE ||
    3544           0 :         mBorderStyles[i] == NS_STYLE_BORDER_STYLE_DOTTED ||
    3545           0 :         mBorderStyles[i] == NS_STYLE_BORDER_STYLE_DASHED) {
    3546           0 :       return false;
    3547             :     }
    3548             :   }
    3549             : 
    3550           0 :   return true;
    3551             : }
    3552             : 
    3553             : void
    3554           0 : nsCSSBorderRenderer::CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder,
    3555             :                                              const layers::StackingContextHelper& aSc)
    3556             : {
    3557           0 :   LayoutDeviceRect outerRect = LayoutDeviceRect::FromUnknownRect(mOuterRect);
    3558           0 :   WrRect transformedRect = aSc.ToRelativeWrRect(outerRect);
    3559             :   WrBorderSide side[4];
    3560           0 :   NS_FOR_CSS_SIDES(i) {
    3561           0 :     side[i] = wr::ToWrBorderSide(ToDeviceColor(mBorderColors[i]), mBorderStyles[i]);
    3562             :   }
    3563             : 
    3564           0 :   WrBorderRadius borderRadius = wr::ToWrBorderRadius(LayerSize(mBorderRadii[0].width, mBorderRadii[0].height),
    3565           0 :                                                      LayerSize(mBorderRadii[1].width, mBorderRadii[1].height),
    3566           0 :                                                      LayerSize(mBorderRadii[3].width, mBorderRadii[3].height),
    3567           0 :                                                      LayerSize(mBorderRadii[2].width, mBorderRadii[2].height));
    3568           0 :   Range<const WrBorderSide> wrsides(side, 4);
    3569             :   aBuilder.PushBorder(transformedRect,
    3570             :                       transformedRect,
    3571           0 :                       wr::ToWrBorderWidths(mBorderWidths[0], mBorderWidths[1], mBorderWidths[2], mBorderWidths[3]),
    3572             :                       wrsides,
    3573           0 :                       borderRadius);
    3574           0 : }
    3575             : 
    3576             : /* static */Maybe<nsCSSBorderImageRenderer>
    3577           3 : nsCSSBorderImageRenderer::CreateBorderImageRenderer(nsPresContext* aPresContext,
    3578             :                                                     nsIFrame* aForFrame,
    3579             :                                                     const nsRect& aBorderArea,
    3580             :                                                     const nsStyleBorder& aStyleBorder,
    3581             :                                                     const nsRect& aDirtyRect,
    3582             :                                                     Sides aSkipSides,
    3583             :                                                     uint32_t aFlags,
    3584             :                                                     DrawResult* aDrawResult)
    3585             : {
    3586           3 :   MOZ_ASSERT(aDrawResult);
    3587             : 
    3588           3 :   if (aDirtyRect.IsEmpty()) {
    3589           0 :     *aDrawResult = DrawResult::SUCCESS;
    3590           0 :     return Nothing();
    3591             :   }
    3592             : 
    3593           6 :   nsImageRenderer imgRenderer(aForFrame, &aStyleBorder.mBorderImageSource, aFlags);
    3594           3 :   if (!imgRenderer.PrepareImage()) {
    3595           0 :     *aDrawResult = imgRenderer.PrepareResult();
    3596           0 :     return Nothing();
    3597             :   }
    3598             : 
    3599             :   // Ensure we get invalidated for loads and animations of the image.
    3600             :   // We need to do this here because this might be the only code that
    3601             :   // knows about the association of the style data with the frame.
    3602             :   // XXX We shouldn't really... since if anybody is passing in a
    3603             :   // different style, they'll potentially have the wrong size for the
    3604             :   // border too.
    3605           3 :   aForFrame->AssociateImage(aStyleBorder.mBorderImageSource, aPresContext);
    3606             : 
    3607             :   nsCSSBorderImageRenderer renderer(aForFrame, aBorderArea,
    3608           6 :                                     aStyleBorder, aSkipSides, imgRenderer);
    3609           3 :   *aDrawResult = DrawResult::SUCCESS;
    3610           3 :   return Some(renderer);
    3611             : }
    3612             : 
    3613             : DrawResult
    3614           3 : nsCSSBorderImageRenderer::DrawBorderImage(nsPresContext* aPresContext,
    3615             :                                           gfxContext& aRenderingContext,
    3616             :                                           nsIFrame* aForFrame,
    3617             :                                           const nsRect& aDirtyRect)
    3618             : {
    3619             :   // NOTE: no Save() yet, we do that later by calling autoSR.EnsureSaved()
    3620             :   // in case we need it.
    3621           6 :   gfxContextAutoSaveRestore autoSR;
    3622             : 
    3623           3 :   if (!mClip.IsEmpty()) {
    3624           0 :     autoSR.EnsureSaved(&aRenderingContext);
    3625           0 :     aRenderingContext.Clip(NSRectToSnappedRect(mClip,
    3626           0 :                            aForFrame->PresContext()->AppUnitsPerDevPixel(),
    3627           0 :                            *aRenderingContext.GetDrawTarget()));
    3628             :   }
    3629             : 
    3630             :   // intrinsicSize.CanComputeConcreteSize() return false means we can not
    3631             :   // read intrinsic size from aStyleBorder.mBorderImageSource.
    3632             :   // In this condition, we pass imageSize(a resolved size comes from
    3633             :   // default sizing algorithm) to renderer as the viewport size.
    3634           3 :   CSSSizeOrRatio intrinsicSize = mImageRenderer.ComputeIntrinsicSize();
    3635           3 :   Maybe<nsSize> svgViewportSize = intrinsicSize.CanComputeConcreteSize() ?
    3636           6 :                                   Nothing() : Some(mImageSize);
    3637           3 :   bool hasIntrinsicRatio = intrinsicSize.HasRatio();
    3638           3 :   mImageRenderer.PurgeCacheForViewportChange(svgViewportSize, hasIntrinsicRatio);
    3639             : 
    3640             :   // These helper tables recharacterize the 'slice' and 'width' margins
    3641             :   // in a more convenient form: they are the x/y/width/height coords
    3642             :   // required for various bands of the border, and they have been transformed
    3643             :   // to be relative to the innerRect (for 'slice') or the page (for 'border').
    3644             :   enum {
    3645             :     LEFT, MIDDLE, RIGHT,
    3646             :     TOP = LEFT, BOTTOM = RIGHT
    3647             :   };
    3648             :   const nscoord borderX[3] = {
    3649           3 :     mArea.x + 0,
    3650           3 :     mArea.x + mWidths.left,
    3651           3 :     mArea.x + mArea.width - mWidths.right,
    3652           9 :   };
    3653             :   const nscoord borderY[3] = {
    3654           3 :     mArea.y + 0,
    3655           3 :     mArea.y + mWidths.top,
    3656           3 :     mArea.y + mArea.height - mWidths.bottom,
    3657           9 :   };
    3658             :   const nscoord borderWidth[3] = {
    3659           3 :     mWidths.left,
    3660           3 :     mArea.width - mWidths.left - mWidths.right,
    3661           3 :     mWidths.right,
    3662           9 :   };
    3663             :   const nscoord borderHeight[3] = {
    3664           3 :     mWidths.top,
    3665           3 :     mArea.height - mWidths.top - mWidths.bottom,
    3666           3 :     mWidths.bottom,
    3667           9 :   };
    3668             :   const int32_t sliceX[3] = {
    3669             :     0,
    3670           3 :     mSlice.left,
    3671           3 :     mImageSize.width - mSlice.right,
    3672           9 :   };
    3673             :   const int32_t sliceY[3] = {
    3674             :     0,
    3675           3 :     mSlice.top,
    3676           3 :     mImageSize.height - mSlice.bottom,
    3677           9 :   };
    3678             :   const int32_t sliceWidth[3] = {
    3679           3 :     mSlice.left,
    3680           6 :     std::max(mImageSize.width - mSlice.left - mSlice.right, 0),
    3681           3 :     mSlice.right,
    3682           9 :   };
    3683             :   const int32_t sliceHeight[3] = {
    3684           3 :     mSlice.top,
    3685           6 :     std::max(mImageSize.height - mSlice.top - mSlice.bottom, 0),
    3686           3 :     mSlice.bottom,
    3687           9 :   };
    3688             : 
    3689           3 :   DrawResult result = DrawResult::SUCCESS;
    3690             : 
    3691          12 :   for (int i = LEFT; i <= RIGHT; i++) {
    3692          36 :     for (int j = TOP; j <= BOTTOM; j++) {
    3693             :       uint8_t fillStyleH, fillStyleV;
    3694          27 :       nsSize unitSize;
    3695             : 
    3696          27 :       if (i == MIDDLE && j == MIDDLE) {
    3697             :         // Discard the middle portion unless set to fill.
    3698           3 :         if (NS_STYLE_BORDER_IMAGE_SLICE_NOFILL == mFill) {
    3699           6 :           continue;
    3700             :         }
    3701             : 
    3702           0 :         NS_ASSERTION(NS_STYLE_BORDER_IMAGE_SLICE_FILL == mFill,
    3703             :                      "Unexpected border image fill");
    3704             : 
    3705             :         // css-background:
    3706             :         //     The middle image's width is scaled by the same factor as the
    3707             :         //     top image unless that factor is zero or infinity, in which
    3708             :         //     case the scaling factor of the bottom is substituted, and
    3709             :         //     failing that, the width is not scaled. The height of the
    3710             :         //     middle image is scaled by the same factor as the left image
    3711             :         //     unless that factor is zero or infinity, in which case the
    3712             :         //     scaling factor of the right image is substituted, and failing
    3713             :         //     that, the height is not scaled.
    3714             :         gfxFloat hFactor, vFactor;
    3715             : 
    3716           0 :         if (0 < mWidths.left && 0 < mSlice.left)
    3717           0 :           vFactor = gfxFloat(mWidths.left) / mSlice.left;
    3718           0 :         else if (0 < mWidths.right && 0 < mSlice.right)
    3719           0 :           vFactor = gfxFloat(mWidths.right) / mSlice.right;
    3720             :         else
    3721           0 :           vFactor = 1;
    3722             : 
    3723           0 :         if (0 < mWidths.top && 0 < mSlice.top)
    3724           0 :           hFactor = gfxFloat(mWidths.top) / mSlice.top;
    3725           0 :         else if (0 < mWidths.bottom && 0 < mSlice.bottom)
    3726           0 :           hFactor = gfxFloat(mWidths.bottom) / mSlice.bottom;
    3727             :         else
    3728           0 :           hFactor = 1;
    3729             : 
    3730           0 :         unitSize.width = sliceWidth[i] * hFactor;
    3731           0 :         unitSize.height = sliceHeight[j] * vFactor;
    3732           0 :         fillStyleH = mRepeatModeHorizontal;
    3733           0 :         fillStyleV = mRepeatModeVertical;
    3734             : 
    3735          24 :       } else if (i == MIDDLE) { // top, bottom
    3736             :         // Sides are always stretched to the thickness of their border,
    3737             :         // and stretched proportionately on the other axis.
    3738             :         gfxFloat factor;
    3739           6 :         if (0 < borderHeight[j] && 0 < sliceHeight[j])
    3740           0 :           factor = gfxFloat(borderHeight[j]) / sliceHeight[j];
    3741             :         else
    3742           6 :           factor = 1;
    3743             : 
    3744           6 :         unitSize.width = sliceWidth[i] * factor;
    3745           6 :         unitSize.height = borderHeight[j];
    3746           6 :         fillStyleH = mRepeatModeHorizontal;
    3747           6 :         fillStyleV = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
    3748             : 
    3749          18 :       } else if (j == MIDDLE) { // left, right
    3750             :         gfxFloat factor;
    3751           6 :         if (0 < borderWidth[i] && 0 < sliceWidth[i])
    3752           3 :           factor = gfxFloat(borderWidth[i]) / sliceWidth[i];
    3753             :         else
    3754           3 :           factor = 1;
    3755             : 
    3756           6 :         unitSize.width = borderWidth[i];
    3757           6 :         unitSize.height = sliceHeight[j] * factor;
    3758           6 :         fillStyleH = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
    3759           6 :         fillStyleV = mRepeatModeVertical;
    3760             : 
    3761             :       } else {
    3762             :         // Corners are always stretched to fit the corner.
    3763          12 :         unitSize.width = borderWidth[i];
    3764          12 :         unitSize.height = borderHeight[j];
    3765          12 :         fillStyleH = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
    3766          12 :         fillStyleV = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
    3767             :       }
    3768             : 
    3769          48 :       nsRect destArea(borderX[i], borderY[j], borderWidth[i], borderHeight[j]);
    3770          48 :       nsRect subArea(sliceX[i], sliceY[j], sliceWidth[i], sliceHeight[j]);
    3771          24 :       if (subArea.IsEmpty())
    3772           0 :         continue;
    3773             : 
    3774          24 :       nsIntRect intSubArea = subArea.ToOutsidePixels(nsPresContext::AppUnitsPerCSSPixel());
    3775             :       result &=
    3776             :         mImageRenderer.DrawBorderImageComponent(aPresContext,
    3777             :                                                  aRenderingContext, aDirtyRect,
    3778          48 :                                                  destArea, CSSIntRect(intSubArea.x,
    3779             :                                                                       intSubArea.y,
    3780             :                                                                       intSubArea.width,
    3781             :                                                                       intSubArea.height),
    3782             :                                                  fillStyleH, fillStyleV,
    3783          24 :                                                  unitSize, j * (RIGHT + 1) + i,
    3784          72 :                                                  svgViewportSize, hasIntrinsicRatio);
    3785             :     }
    3786             :   }
    3787             : 
    3788           6 :   return result;
    3789             : }
    3790             : 
    3791           3 : nsCSSBorderImageRenderer::nsCSSBorderImageRenderer(const nsCSSBorderImageRenderer& aRhs)
    3792             :   : mImageRenderer(aRhs.mImageRenderer)
    3793             :   , mImageSize(aRhs.mImageSize)
    3794             :   , mSlice(aRhs.mSlice)
    3795             :   , mWidths(aRhs.mWidths)
    3796             :   , mImageOutset(aRhs.mImageOutset)
    3797             :   , mArea(aRhs.mArea)
    3798             :   , mClip(aRhs.mClip)
    3799           3 :   , mRepeatModeHorizontal(aRhs.mRepeatModeHorizontal)
    3800           3 :   , mRepeatModeVertical(aRhs.mRepeatModeVertical)
    3801           9 :   , mFill(aRhs.mFill)
    3802             : {
    3803           3 :   Unused << mImageRenderer.PrepareResult();
    3804           3 : }
    3805             : 
    3806             : nsCSSBorderImageRenderer&
    3807           0 : nsCSSBorderImageRenderer::operator=(const nsCSSBorderImageRenderer& aRhs)
    3808             : {
    3809           0 :   mImageRenderer = aRhs.mImageRenderer;
    3810           0 :   mImageSize = aRhs.mImageSize;
    3811           0 :   mSlice = aRhs.mSlice;
    3812           0 :   mWidths = aRhs.mWidths;
    3813           0 :   mImageOutset = aRhs.mImageOutset;
    3814           0 :   mArea = aRhs.mArea;
    3815           0 :   mClip = aRhs.mClip;
    3816           0 :   mRepeatModeHorizontal = aRhs.mRepeatModeHorizontal;
    3817           0 :   mRepeatModeVertical = aRhs.mRepeatModeVertical;
    3818           0 :   mFill = aRhs.mFill;
    3819           0 :   Unused << mImageRenderer.PrepareResult();
    3820             : 
    3821           0 :   return *this;
    3822             : }
    3823             : 
    3824           3 : nsCSSBorderImageRenderer::nsCSSBorderImageRenderer(nsIFrame* aForFrame,
    3825             :                                                    const nsRect& aBorderArea,
    3826             :                                                    const nsStyleBorder& aStyleBorder,
    3827             :                                                    Sides aSkipSides,
    3828           3 :                                                    const nsImageRenderer& aImageRenderer)
    3829           3 :   : mImageRenderer(aImageRenderer)
    3830             : {
    3831             :   // Determine the border image area, which by default corresponds to the
    3832             :   // border box but can be modified by 'border-image-outset'.
    3833             :   // Note that 'border-radius' do not apply to 'border-image' borders per
    3834             :   // <http://dev.w3.org/csswg/css-backgrounds/#corner-clipping>.
    3835           3 :   nsMargin borderWidths(aStyleBorder.GetComputedBorder());
    3836           3 :   mImageOutset = aStyleBorder.GetImageOutset();
    3837           3 :   if (::IsBoxDecorationSlice(aStyleBorder) && !aSkipSides.IsEmpty()) {
    3838           0 :     mArea = ::BoxDecorationRectForBorder(aForFrame, aBorderArea,
    3839             :                                          aSkipSides, &aStyleBorder);
    3840           0 :     if (mArea.IsEqualEdges(aBorderArea)) {
    3841             :       // No need for a clip, just skip the sides we don't want.
    3842           0 :       borderWidths.ApplySkipSides(aSkipSides);
    3843           0 :       mImageOutset.ApplySkipSides(aSkipSides);
    3844           0 :       mArea.Inflate(mImageOutset);
    3845             :     } else {
    3846             :       // We're drawing borders around the joined continuation boxes so we need
    3847             :       // to clip that to the slice that we want for this frame.
    3848           0 :       mArea.Inflate(mImageOutset);
    3849           0 :       mImageOutset.ApplySkipSides(aSkipSides);
    3850           0 :       mClip = aBorderArea;
    3851           0 :       mClip.Inflate(mImageOutset);
    3852             :     }
    3853             :   } else {
    3854           3 :     mArea = aBorderArea;
    3855           3 :     mArea.Inflate(mImageOutset);
    3856             :   }
    3857             : 
    3858             :   // Calculate the image size used to compute slice points.
    3859           3 :   CSSSizeOrRatio intrinsicSize = mImageRenderer.ComputeIntrinsicSize();
    3860           6 :   mImageSize = nsImageRenderer::ComputeConcreteSize(CSSSizeOrRatio(),
    3861             :                                                     intrinsicSize,
    3862           9 :                                                     mArea.Size());
    3863           3 :   mImageRenderer.SetPreferredSize(intrinsicSize, mImageSize);
    3864             : 
    3865             :   // Compute the used values of 'border-image-slice' and 'border-image-width';
    3866             :   // we do them together because the latter can depend on the former.
    3867           3 :   nsMargin slice;
    3868           3 :   nsMargin border;
    3869          15 :   NS_FOR_CSS_SIDES(s) {
    3870          24 :     nsStyleCoord coord = aStyleBorder.mBorderImageSlice.Get(s);
    3871          12 :     int32_t imgDimension = SideIsVertical(s)
    3872          12 :                            ? mImageSize.width : mImageSize.height;
    3873          12 :     nscoord borderDimension = SideIsVertical(s)
    3874          12 :                            ? mArea.width : mArea.height;
    3875             :     double value;
    3876          12 :     switch (coord.GetUnit()) {
    3877             :       case eStyleUnit_Percent:
    3878           0 :         value = coord.GetPercentValue() * imgDimension;
    3879           0 :         break;
    3880             :       case eStyleUnit_Factor:
    3881          12 :         value = nsPresContext::CSSPixelsToAppUnits(
    3882          12 :           NS_lround(coord.GetFactorValue()));
    3883          12 :         break;
    3884             :       default:
    3885           0 :         NS_NOTREACHED("unexpected CSS unit for image slice");
    3886           0 :         value = 0;
    3887           0 :         break;
    3888             :     }
    3889          12 :     if (value < 0)
    3890           0 :       value = 0;
    3891          12 :     if (value > imgDimension)
    3892           0 :       value = imgDimension;
    3893          12 :     mSlice.Side(s) = value;
    3894             : 
    3895          12 :     coord = aStyleBorder.mBorderImageWidth.Get(s);
    3896          12 :     switch (coord.GetUnit()) {
    3897             :       case eStyleUnit_Coord: // absolute dimension
    3898           0 :         value = coord.GetCoordValue();
    3899           0 :         break;
    3900             :       case eStyleUnit_Percent:
    3901           0 :         value = coord.GetPercentValue() * borderDimension;
    3902           0 :         break;
    3903             :       case eStyleUnit_Factor:
    3904          12 :         value = coord.GetFactorValue() * borderWidths.Side(s);
    3905          12 :         break;
    3906             :       case eStyleUnit_Auto:  // same as the slice value, in CSS pixels
    3907           0 :         value = mSlice.Side(s);
    3908           0 :         break;
    3909             :       default:
    3910           0 :         NS_NOTREACHED("unexpected CSS unit for border image area division");
    3911           0 :         value = 0;
    3912           0 :         break;
    3913             :     }
    3914             :     // NSToCoordRoundWithClamp rounds towards infinity, but that's OK
    3915             :     // because we expect value to be non-negative.
    3916          12 :     MOZ_ASSERT(value >= 0);
    3917          12 :     mWidths.Side(s) = NSToCoordRoundWithClamp(value);
    3918          12 :     MOZ_ASSERT(mWidths.Side(s) >= 0);
    3919             :   }
    3920             : 
    3921             :   // "If two opposite border-image-width offsets are large enough that they
    3922             :   // overlap, their used values are proportionately reduced until they no
    3923             :   // longer overlap."
    3924           3 :   uint32_t combinedBorderWidth = uint32_t(mWidths.left) +
    3925           3 :                                  uint32_t(mWidths.right);
    3926           3 :   double scaleX = combinedBorderWidth > uint32_t(mArea.width)
    3927           3 :                   ? mArea.width / double(combinedBorderWidth)
    3928           3 :                   : 1.0;
    3929           3 :   uint32_t combinedBorderHeight = uint32_t(mWidths.top) +
    3930           3 :                                   uint32_t(mWidths.bottom);
    3931           3 :   double scaleY = combinedBorderHeight > uint32_t(mArea.height)
    3932           3 :                   ? mArea.height / double(combinedBorderHeight)
    3933           3 :                   : 1.0;
    3934           3 :   double scale = std::min(scaleX, scaleY);
    3935           3 :   if (scale < 1.0) {
    3936           0 :     mWidths.left *= scale;
    3937           0 :     mWidths.right *= scale;
    3938           0 :     mWidths.top *= scale;
    3939           0 :     mWidths.bottom *= scale;
    3940           0 :     NS_ASSERTION(mWidths.left + mWidths.right <= mArea.width &&
    3941             :                  mWidths.top + mWidths.bottom <= mArea.height,
    3942             :                  "rounding error in width reduction???");
    3943             :   }
    3944             : 
    3945           3 :   mRepeatModeHorizontal = aStyleBorder.mBorderImageRepeatH;
    3946           3 :   mRepeatModeVertical = aStyleBorder.mBorderImageRepeatV;
    3947           3 :   mFill = aStyleBorder.mBorderImageFill;
    3948           3 : }

Generated by: LCOV version 1.13