layout/base/nsCSSRendering.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     7 /* utility functions for drawing borders and backgrounds */
     9 #include <ctime>
    11 #include "mozilla/DebugOnly.h"
    12 #include "mozilla/HashFunctions.h"
    13 #include "mozilla/MathAlgorithms.h"
    15 #include "nsStyleConsts.h"
    16 #include "nsPresContext.h"
    17 #include "nsIFrame.h"
    18 #include "nsPoint.h"
    19 #include "nsRect.h"
    20 #include "nsIPresShell.h"
    21 #include "nsFrameManager.h"
    22 #include "nsStyleContext.h"
    23 #include "nsGkAtoms.h"
    24 #include "nsCSSAnonBoxes.h"
    25 #include "nsIContent.h"
    26 #include "nsIDocumentInlines.h"
    27 #include "nsIScrollableFrame.h"
    28 #include "imgIRequest.h"
    29 #include "imgIContainer.h"
    30 #include "ImageOps.h"
    31 #include "nsCSSRendering.h"
    32 #include "nsCSSColorUtils.h"
    33 #include "nsITheme.h"
    34 #include "nsLayoutUtils.h"
    35 #include "nsBlockFrame.h"
    36 #include "gfxContext.h"
    37 #include "nsRenderingContext.h"
    38 #include "nsStyleStructInlines.h"
    39 #include "nsCSSFrameConstructor.h"
    40 #include "nsCSSProps.h"
    41 #include "nsContentUtils.h"
    42 #include "nsSVGEffects.h"
    43 #include "nsSVGIntegrationUtils.h"
    44 #include "gfxDrawable.h"
    45 #include "GeckoProfiler.h"
    46 #include "nsCSSRenderingBorders.h"
    47 #include "mozilla/css/ImageLoader.h"
    48 #include "ImageContainer.h"
    49 #include "mozilla/Telemetry.h"
    50 #include "gfxUtils.h"
    51 #include "gfxColor.h"
    52 #include "gfxGradientCache.h"
    53 #include "GraphicsFilter.h"
    54 #include <algorithm>
    56 using namespace mozilla;
    57 using namespace mozilla::css;
    58 using namespace mozilla::gfx;
    59 using mozilla::image::ImageOps;
    60 using mozilla::CSSSizeOrRatio;
    62 static int gFrameTreeLockCount = 0;
    64 // To avoid storing this data on nsInlineFrame (bloat) and to avoid
    65 // recalculating this for each frame in a continuation (perf), hold
    66 // a cache of various coordinate information that we need in order
    67 // to paint inline backgrounds.
    68 struct InlineBackgroundData
    69 {
    70   InlineBackgroundData()
    71       : mFrame(nullptr), mBlockFrame(nullptr)
    72   {
    73   }
    75   ~InlineBackgroundData()
    76   {
    77   }
    79   void Reset()
    80   {
    81     mBoundingBox.SetRect(0,0,0,0);
    82     mContinuationPoint = mLineContinuationPoint = mUnbrokenWidth = 0;
    83     mFrame = mBlockFrame = nullptr;
    84   }
    86   nsRect GetContinuousRect(nsIFrame* aFrame)
    87   {
    88     SetFrame(aFrame);
    90     nscoord x;
    91     if (mBidiEnabled) {
    92       x = mLineContinuationPoint;
    94       // Scan continuations on the same line as aFrame and accumulate the widths
    95       // of frames that are to the left (if this is an LTR block) or right
    96       // (if it's RTL) of the current one.
    97       bool isRtlBlock = (mBlockFrame->StyleVisibility()->mDirection ==
    98                            NS_STYLE_DIRECTION_RTL);
    99       nscoord curOffset = aFrame->GetOffsetTo(mBlockFrame).x;
   101       // No need to use our GetPrevContinuation/GetNextContinuation methods
   102       // here, since ib-split siblings are certainly not on the same line.
   104       nsIFrame* inlineFrame = aFrame->GetPrevContinuation();
   105       // If the continuation is fluid we know inlineFrame is not on the same line.
   106       // If it's not fluid, we need to test further to be sure.
   107       while (inlineFrame && !inlineFrame->GetNextInFlow() &&
   108              AreOnSameLine(aFrame, inlineFrame)) {
   109         nscoord frameXOffset = inlineFrame->GetOffsetTo(mBlockFrame).x;
   110         if(isRtlBlock == (frameXOffset >= curOffset)) {
   111           x += inlineFrame->GetSize().width;
   112         }
   113         inlineFrame = inlineFrame->GetPrevContinuation();
   114       }
   116       inlineFrame = aFrame->GetNextContinuation();
   117       while (inlineFrame && !inlineFrame->GetPrevInFlow() &&
   118              AreOnSameLine(aFrame, inlineFrame)) {
   119         nscoord frameXOffset = inlineFrame->GetOffsetTo(mBlockFrame).x;
   120         if(isRtlBlock == (frameXOffset >= curOffset)) {
   121           x += inlineFrame->GetSize().width;
   122         }
   123         inlineFrame = inlineFrame->GetNextContinuation();
   124       }
   125       if (isRtlBlock) {
   126         // aFrame itself is also to the right of its left edge, so add its width.
   127         x += aFrame->GetSize().width;
   128         // x is now the distance from the left edge of aFrame to the right edge
   129         // of the unbroken content. Change it to indicate the distance from the
   130         // left edge of the unbroken content to the left edge of aFrame.
   131         x = mUnbrokenWidth - x;
   132       }
   133     } else {
   134       x = mContinuationPoint;
   135     }
   137     // Assume background-origin: border and return a rect with offsets
   138     // relative to (0,0).  If we have a different background-origin,
   139     // then our rect should be deflated appropriately by our caller.
   140     return nsRect(-x, 0, mUnbrokenWidth, mFrame->GetSize().height);
   141   }
   143   nsRect GetBoundingRect(nsIFrame* aFrame)
   144   {
   145     SetFrame(aFrame);
   147     // Move the offsets relative to (0,0) which puts the bounding box into
   148     // our coordinate system rather than our parent's.  We do this by
   149     // moving it the back distance from us to the bounding box.
   150     // This also assumes background-origin: border, so our caller will
   151     // need to deflate us if needed.
   152     nsRect boundingBox(mBoundingBox);
   153     nsPoint point = mFrame->GetPosition();
   154     boundingBox.MoveBy(-point.x, -point.y);
   156     return boundingBox;
   157   }
   159 protected:
   160   nsIFrame*     mFrame;
   161   nsBlockFrame* mBlockFrame;
   162   nsRect        mBoundingBox;
   163   nscoord       mContinuationPoint;
   164   nscoord       mUnbrokenWidth;
   165   nscoord       mLineContinuationPoint;
   166   bool          mBidiEnabled;
   168   void SetFrame(nsIFrame* aFrame)
   169   {
   170     NS_PRECONDITION(aFrame, "Need a frame");
   171     NS_ASSERTION(gFrameTreeLockCount > 0,
   172                  "Can't call this when frame tree is not locked");
   174     if (aFrame == mFrame) {
   175       return;
   176     }
   178     nsIFrame *prevContinuation = GetPrevContinuation(aFrame);
   180     if (!prevContinuation || mFrame != prevContinuation) {
   181       // Ok, we've got the wrong frame.  We have to start from scratch.
   182       Reset();
   183       Init(aFrame);
   184       return;
   185     }
   187     // Get our last frame's size and add its width to our continuation
   188     // point before we cache the new frame.
   189     mContinuationPoint += mFrame->GetSize().width;
   191     // If this a new line, update mLineContinuationPoint.
   192     if (mBidiEnabled &&
   193         (aFrame->GetPrevInFlow() || !AreOnSameLine(mFrame, aFrame))) {
   194        mLineContinuationPoint = mContinuationPoint;
   195     }
   197     mFrame = aFrame;
   198   }
   200   nsIFrame* GetPrevContinuation(nsIFrame* aFrame)
   201   {
   202     nsIFrame* prevCont = aFrame->GetPrevContinuation();
   203     if (!prevCont &&
   204         (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
   205       nsIFrame* block = static_cast<nsIFrame*>
   206         (aFrame->Properties().Get(nsIFrame::IBSplitPrevSibling()));
   207       if (block) {
   208         // The {ib} properties are only stored on first continuations
   209         NS_ASSERTION(!block->GetPrevContinuation(),
   210                      "Incorrect value for IBSplitPrevSibling");
   211         prevCont = static_cast<nsIFrame*>
   212           (block->Properties().Get(nsIFrame::IBSplitPrevSibling()));
   213         NS_ASSERTION(prevCont, "How did that happen?");
   214       }
   215     }
   216     return prevCont;
   217   }
   219   nsIFrame* GetNextContinuation(nsIFrame* aFrame)
   220   {
   221     nsIFrame* nextCont = aFrame->GetNextContinuation();
   222     if (!nextCont &&
   223         (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
   224       // The {ib} properties are only stored on first continuations
   225       aFrame = aFrame->FirstContinuation();
   226       nsIFrame* block = static_cast<nsIFrame*>
   227         (aFrame->Properties().Get(nsIFrame::IBSplitSibling()));
   228       if (block) {
   229         nextCont = static_cast<nsIFrame*>
   230           (block->Properties().Get(nsIFrame::IBSplitSibling()));
   231         NS_ASSERTION(nextCont, "How did that happen?");
   232       }
   233     }
   234     return nextCont;
   235   }
   237   void Init(nsIFrame* aFrame)
   238   {
   239     mBidiEnabled = aFrame->PresContext()->BidiEnabled();
   240     if (mBidiEnabled) {
   241       // Find the containing block frame
   242       nsIFrame* frame = aFrame;
   243       do {
   244         frame = frame->GetParent();
   245         mBlockFrame = do_QueryFrame(frame);
   246       }
   247       while (frame && frame->IsFrameOfType(nsIFrame::eLineParticipant));
   249       NS_ASSERTION(mBlockFrame, "Cannot find containing block.");
   250     }
   252     // Start with the previous flow frame as our continuation point
   253     // is the total of the widths of the previous frames.
   254     nsIFrame* inlineFrame = GetPrevContinuation(aFrame);
   256     while (inlineFrame) {
   257       nsRect rect = inlineFrame->GetRect();
   258       mContinuationPoint += rect.width;
   259       if (mBidiEnabled && !AreOnSameLine(aFrame, inlineFrame)) {
   260         mLineContinuationPoint += rect.width;
   261       }
   262       mUnbrokenWidth += rect.width;
   263       mBoundingBox.UnionRect(mBoundingBox, rect);
   264       inlineFrame = GetPrevContinuation(inlineFrame);
   265     }
   267     // Next add this frame and subsequent frames to the bounding box and
   268     // unbroken width.
   269     inlineFrame = aFrame;
   270     while (inlineFrame) {
   271       nsRect rect = inlineFrame->GetRect();
   272       mUnbrokenWidth += rect.width;
   273       mBoundingBox.UnionRect(mBoundingBox, rect);
   274       inlineFrame = GetNextContinuation(inlineFrame);
   275     }
   277     mFrame = aFrame;
   278   }
   280   bool AreOnSameLine(nsIFrame* aFrame1, nsIFrame* aFrame2) {
   281     bool isValid1, isValid2;
   282     nsBlockInFlowLineIterator it1(mBlockFrame, aFrame1, &isValid1);
   283     nsBlockInFlowLineIterator it2(mBlockFrame, aFrame2, &isValid2);
   284     return isValid1 && isValid2 &&
   285       // Make sure aFrame1 and aFrame2 are in the same continuation of
   286       // mBlockFrame.
   287       it1.GetContainer() == it2.GetContainer() &&
   288       // And on the same line in it
   289       it1.GetLine() == it2.GetLine();
   290   }
   291 };
   293 // A resolved color stop --- with a specific position along the gradient line,
   294 // and a Thebes color
   295 struct ColorStop {
   296   ColorStop(double aPosition, gfxRGBA aColor) :
   297     mPosition(aPosition), mColor(aColor) {}
   298   double mPosition; // along the gradient line; 0=start, 1=end
   299   gfxRGBA mColor;
   300 };
   302 /* Local functions */
   303 static void DrawBorderImage(nsPresContext* aPresContext,
   304                             nsRenderingContext& aRenderingContext,
   305                             nsIFrame* aForFrame,
   306                             const nsRect& aBorderArea,
   307                             const nsStyleBorder& aStyleBorder,
   308                             const nsRect& aDirtyRect);
   310 static nscolor MakeBevelColor(mozilla::css::Side whichSide, uint8_t style,
   311                               nscolor aBackgroundColor,
   312                               nscolor aBorderColor);
   314 static InlineBackgroundData* gInlineBGData = nullptr;
   316 // Initialize any static variables used by nsCSSRendering.
   317 void nsCSSRendering::Init()
   318 {
   319   NS_ASSERTION(!gInlineBGData, "Init called twice");
   320   gInlineBGData = new InlineBackgroundData();
   321 }
   323 // Clean up any global variables used by nsCSSRendering.
   324 void nsCSSRendering::Shutdown()
   325 {
   326   delete gInlineBGData;
   327   gInlineBGData = nullptr;
   328 }
   330 /**
   331  * Make a bevel color
   332  */
   333 static nscolor
   334 MakeBevelColor(mozilla::css::Side whichSide, uint8_t style,
   335                nscolor aBackgroundColor, nscolor aBorderColor)
   336 {
   338   nscolor colors[2];
   339   nscolor theColor;
   341   // Given a background color and a border color
   342   // calculate the color used for the shading
   343   NS_GetSpecial3DColors(colors, aBackgroundColor, aBorderColor);
   345   if ((style == NS_STYLE_BORDER_STYLE_OUTSET) ||
   346       (style == NS_STYLE_BORDER_STYLE_RIDGE)) {
   347     // Flip colors for these two border styles
   348     switch (whichSide) {
   349     case NS_SIDE_BOTTOM: whichSide = NS_SIDE_TOP;    break;
   350     case NS_SIDE_RIGHT:  whichSide = NS_SIDE_LEFT;   break;
   351     case NS_SIDE_TOP:    whichSide = NS_SIDE_BOTTOM; break;
   352     case NS_SIDE_LEFT:   whichSide = NS_SIDE_RIGHT;  break;
   353     }
   354   }
   356   switch (whichSide) {
   357   case NS_SIDE_BOTTOM:
   358     theColor = colors[1];
   359     break;
   360   case NS_SIDE_RIGHT:
   361     theColor = colors[1];
   362     break;
   363   case NS_SIDE_TOP:
   364     theColor = colors[0];
   365     break;
   366   case NS_SIDE_LEFT:
   367   default:
   368     theColor = colors[0];
   369     break;
   370   }
   371   return theColor;
   372 }
   374 //----------------------------------------------------------------------
   375 // Thebes Border Rendering Code Start
   377 /*
   378  * Compute the float-pixel radii that should be used for drawing
   379  * this border/outline, given the various input bits.
   380  */
   381 /* static */ void
   382 nsCSSRendering::ComputePixelRadii(const nscoord *aAppUnitsRadii,
   383                                   nscoord aAppUnitsPerPixel,
   384                                   gfxCornerSizes *oBorderRadii)
   385 {
   386   gfxFloat radii[8];
   387   NS_FOR_CSS_HALF_CORNERS(corner)
   388     radii[corner] = gfxFloat(aAppUnitsRadii[corner]) / aAppUnitsPerPixel;
   390   (*oBorderRadii)[C_TL] = gfxSize(radii[NS_CORNER_TOP_LEFT_X],
   391                                   radii[NS_CORNER_TOP_LEFT_Y]);
   392   (*oBorderRadii)[C_TR] = gfxSize(radii[NS_CORNER_TOP_RIGHT_X],
   393                                   radii[NS_CORNER_TOP_RIGHT_Y]);
   394   (*oBorderRadii)[C_BR] = gfxSize(radii[NS_CORNER_BOTTOM_RIGHT_X],
   395                                   radii[NS_CORNER_BOTTOM_RIGHT_Y]);
   396   (*oBorderRadii)[C_BL] = gfxSize(radii[NS_CORNER_BOTTOM_LEFT_X],
   397                                   radii[NS_CORNER_BOTTOM_LEFT_Y]);
   398 }
   400 void
   401 nsCSSRendering::PaintBorder(nsPresContext* aPresContext,
   402                             nsRenderingContext& aRenderingContext,
   403                             nsIFrame* aForFrame,
   404                             const nsRect& aDirtyRect,
   405                             const nsRect& aBorderArea,
   406                             nsStyleContext* aStyleContext,
   407                             int aSkipSides)
   408 {
   409   PROFILER_LABEL("nsCSSRendering", "PaintBorder");
   410   nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
   411   const nsStyleBorder *styleBorder = aStyleContext->StyleBorder();
   412   // Don't check RelevantLinkVisited here, since we want to take the
   413   // same amount of time whether or not it's true.
   414   if (!styleIfVisited) {
   415     PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
   416                                aDirtyRect, aBorderArea, *styleBorder,
   417                                aStyleContext, aSkipSides);
   418     return;
   419   }
   421   nsStyleBorder newStyleBorder(*styleBorder);
   422   // We could do something fancy to avoid the TrackImage/UntrackImage
   423   // work, but it doesn't seem worth it.  (We need to call TrackImage
   424   // since we're not going through nsRuleNode::ComputeBorderData.)
   425   newStyleBorder.TrackImage(aPresContext);
   427   NS_FOR_CSS_SIDES(side) {
   428     newStyleBorder.SetBorderColor(side,
   429       aStyleContext->GetVisitedDependentColor(
   430         nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[side]));
   431   }
   432   PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
   433                              aDirtyRect, aBorderArea, newStyleBorder,
   434                              aStyleContext, aSkipSides);
   436   // We could do something fancy to avoid the TrackImage/UntrackImage
   437   // work, but it doesn't seem worth it.  (We need to call UntrackImage
   438   // since we're not going through nsStyleBorder::Destroy.)
   439   newStyleBorder.UntrackImage(aPresContext);
   440 }
   442 void
   443 nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext* aPresContext,
   444                                            nsRenderingContext& aRenderingContext,
   445                                            nsIFrame* aForFrame,
   446                                            const nsRect& aDirtyRect,
   447                                            const nsRect& aBorderArea,
   448                                            const nsStyleBorder& aStyleBorder,
   449                                            nsStyleContext* aStyleContext,
   450                                            int aSkipSides)
   451 {
   452   nsMargin            border;
   453   nscoord             twipsRadii[8];
   454   nsCompatibility     compatMode = aPresContext->CompatibilityMode();
   456   SN("++ PaintBorder");
   458   // Check to see if we have an appearance defined.  If so, we let the theme
   459   // renderer draw the border.  DO not get the data from aForFrame, since the passed in style context
   460   // may be different!  Always use |aStyleContext|!
   461   const nsStyleDisplay* displayData = aStyleContext->StyleDisplay();
   462   if (displayData->mAppearance) {
   463     nsITheme *theme = aPresContext->GetTheme();
   464     if (theme && theme->ThemeSupportsWidget(aPresContext, aForFrame, displayData->mAppearance))
   465       return; // Let the theme handle it.
   466   }
   468   if (aStyleBorder.IsBorderImageLoaded()) {
   469     DrawBorderImage(aPresContext, aRenderingContext, aForFrame,
   470                     aBorderArea, aStyleBorder, aDirtyRect);
   471     return;
   472   }
   474   // Get our style context's color struct.
   475   const nsStyleColor* ourColor = aStyleContext->StyleColor();
   477   // in NavQuirks mode we want to use the parent's context as a starting point
   478   // for determining the background color
   479   nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame
   480     (aForFrame, compatMode == eCompatibility_NavQuirks ? true : false);
   481   nsStyleContext* bgContext = bgFrame->StyleContext();
   482   nscolor bgColor =
   483     bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
   485   border = aStyleBorder.GetComputedBorder();
   486   if ((0 == border.left) && (0 == border.right) &&
   487       (0 == border.top) && (0 == border.bottom)) {
   488     // Empty border area
   489     return;
   490   }
   492   nsSize frameSize = aForFrame->GetSize();
   493   if (&aStyleBorder == aForFrame->StyleBorder() &&
   494       frameSize == aBorderArea.Size()) {
   495     aForFrame->GetBorderRadii(twipsRadii);
   496   } else {
   497     nsIFrame::ComputeBorderRadii(aStyleBorder.mBorderRadius, frameSize,
   498                                  aBorderArea.Size(), aSkipSides, twipsRadii);
   499   }
   501   // Turn off rendering for all of the zero sized sides
   502   if (aSkipSides & SIDE_BIT_TOP) border.top = 0;
   503   if (aSkipSides & SIDE_BIT_RIGHT) border.right = 0;
   504   if (aSkipSides & SIDE_BIT_BOTTOM) border.bottom = 0;
   505   if (aSkipSides & SIDE_BIT_LEFT) border.left = 0;
   507   // get the inside and outside parts of the border
   508   nsRect outerRect(aBorderArea);
   510   SF(" outerRect: %d %d %d %d\n", outerRect.x, outerRect.y, outerRect.width, outerRect.height);
   512   // we can assume that we're already clipped to aDirtyRect -- I think? (!?)
   514   // Get our conversion values
   515   nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
   517   // convert outer and inner rects
   518   gfxRect oRect(nsLayoutUtils::RectToGfxRect(outerRect, twipsPerPixel));
   520   // convert the border widths
   521   gfxFloat borderWidths[4] = { gfxFloat(border.top / twipsPerPixel),
   522                                gfxFloat(border.right / twipsPerPixel),
   523                                gfxFloat(border.bottom / twipsPerPixel),
   524                                gfxFloat(border.left / twipsPerPixel) };
   526   // convert the radii
   527   gfxCornerSizes borderRadii;
   528   ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
   530   uint8_t borderStyles[4];
   531   nscolor borderColors[4];
   532   nsBorderColors *compositeColors[4];
   534   // pull out styles, colors, composite colors
   535   NS_FOR_CSS_SIDES (i) {
   536     bool foreground;
   537     borderStyles[i] = aStyleBorder.GetBorderStyle(i);
   538     aStyleBorder.GetBorderColor(i, borderColors[i], foreground);
   539     aStyleBorder.GetCompositeColors(i, &compositeColors[i]);
   541     if (foreground)
   542       borderColors[i] = ourColor->mColor;
   543   }
   545   SF(" borderStyles: %d %d %d %d\n", borderStyles[0], borderStyles[1], borderStyles[2], borderStyles[3]);
   547   // start drawing
   548   gfxContext *ctx = aRenderingContext.ThebesContext();
   550   ctx->Save();
   552 #if 0
   553   // this will draw a transparent red backround underneath the oRect area
   554   ctx->Save();
   555   ctx->Rectangle(oRect);
   556   ctx->SetColor(gfxRGBA(1.0, 0.0, 0.0, 0.5));
   557   ctx->Fill();
   558   ctx->Restore();
   559 #endif
   561   //SF ("borderRadii: %f %f %f %f\n", borderRadii[0], borderRadii[1], borderRadii[2], borderRadii[3]);
   563   nsCSSBorderRenderer br(twipsPerPixel,
   564                          ctx,
   565                          oRect,
   566                          borderStyles,
   567                          borderWidths,
   568                          borderRadii,
   569                          borderColors,
   570                          compositeColors,
   571                          aSkipSides,
   572                          bgColor);
   573   br.DrawBorders();
   575   ctx->Restore();
   577   SN();
   578 }
   580 static nsRect
   581 GetOutlineInnerRect(nsIFrame* aFrame)
   582 {
   583   nsRect* savedOutlineInnerRect = static_cast<nsRect*>
   584     (aFrame->Properties().Get(nsIFrame::OutlineInnerRectProperty()));
   585   if (savedOutlineInnerRect)
   586     return *savedOutlineInnerRect;
   587   NS_NOTREACHED("we should have saved a frame property");
   588   return nsRect(nsPoint(0, 0), aFrame->GetSize());
   589 }
   591 void
   592 nsCSSRendering::PaintOutline(nsPresContext* aPresContext,
   593                              nsRenderingContext& aRenderingContext,
   594                              nsIFrame* aForFrame,
   595                              const nsRect& aDirtyRect,
   596                              const nsRect& aBorderArea,
   597                              nsStyleContext* aStyleContext)
   598 {
   599   nscoord             twipsRadii[8];
   601   // Get our style context's color struct.
   602   const nsStyleOutline* ourOutline = aStyleContext->StyleOutline();
   604   nscoord width;
   605   ourOutline->GetOutlineWidth(width);
   607   if (width == 0) {
   608     // Empty outline
   609     return;
   610   }
   612   nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame
   613     (aForFrame, false);
   614   nsStyleContext* bgContext = bgFrame->StyleContext();
   615   nscolor bgColor =
   616     bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
   618   nsRect innerRect;
   619   if (
   620 #ifdef MOZ_XUL
   621       aStyleContext->GetPseudoType() == nsCSSPseudoElements::ePseudo_XULTree
   622 #else
   623       false
   624 #endif
   625      ) {
   626     innerRect = aBorderArea;
   627   } else {
   628     innerRect = GetOutlineInnerRect(aForFrame) + aBorderArea.TopLeft();
   629   }
   630   nscoord offset = ourOutline->mOutlineOffset;
   631   innerRect.Inflate(offset, offset);
   632   // If the dirty rect is completely inside the border area (e.g., only the
   633   // content is being painted), then we can skip out now
   634   // XXX this isn't exactly true for rounded borders, where the inside curves may
   635   // encroach into the content area.  A safer calculation would be to
   636   // shorten insideRect by the radius one each side before performing this test.
   637   if (innerRect.Contains(aDirtyRect))
   638     return;
   640   nsRect outerRect = innerRect;
   641   outerRect.Inflate(width, width);
   643   // get the radius for our outline
   644   nsIFrame::ComputeBorderRadii(ourOutline->mOutlineRadius, aBorderArea.Size(),
   645                                outerRect.Size(), 0, twipsRadii);
   647   // Get our conversion values
   648   nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
   650   // get the outer rectangles
   651   gfxRect oRect(nsLayoutUtils::RectToGfxRect(outerRect, twipsPerPixel));
   653   // convert the radii
   654   nsMargin outlineMargin(width, width, width, width);
   655   gfxCornerSizes outlineRadii;
   656   ComputePixelRadii(twipsRadii, twipsPerPixel, &outlineRadii);
   658   uint8_t outlineStyle = ourOutline->GetOutlineStyle();
   659   uint8_t outlineStyles[4] = { outlineStyle,
   660                                outlineStyle,
   661                                outlineStyle,
   662                                outlineStyle };
   664   // This handles treating the initial color as 'currentColor'; if we
   665   // ever want 'invert' back we'll need to do a bit of work here too.
   666   nscolor outlineColor =
   667     aStyleContext->GetVisitedDependentColor(eCSSProperty_outline_color);
   668   nscolor outlineColors[4] = { outlineColor,
   669                                outlineColor,
   670                                outlineColor,
   671                                outlineColor };
   673   // convert the border widths
   674   gfxFloat outlineWidths[4] = { gfxFloat(width / twipsPerPixel),
   675                                 gfxFloat(width / twipsPerPixel),
   676                                 gfxFloat(width / twipsPerPixel),
   677                                 gfxFloat(width / twipsPerPixel) };
   679   // start drawing
   680   gfxContext *ctx = aRenderingContext.ThebesContext();
   682   ctx->Save();
   684   nsCSSBorderRenderer br(twipsPerPixel,
   685                          ctx,
   686                          oRect,
   687                          outlineStyles,
   688                          outlineWidths,
   689                          outlineRadii,
   690                          outlineColors,
   691                          nullptr, 0,
   692                          bgColor);
   693   br.DrawBorders();
   695   ctx->Restore();
   697   SN();
   698 }
   700 void
   701 nsCSSRendering::PaintFocus(nsPresContext* aPresContext,
   702                            nsRenderingContext& aRenderingContext,
   703                            const nsRect& aFocusRect,
   704                            nscolor aColor)
   705 {
   706   nscoord oneCSSPixel = nsPresContext::CSSPixelsToAppUnits(1);
   707   nscoord oneDevPixel = aPresContext->DevPixelsToAppUnits(1);
   709   gfxRect focusRect(nsLayoutUtils::RectToGfxRect(aFocusRect, oneDevPixel));
   711   gfxCornerSizes focusRadii;
   712   {
   713     nscoord twipsRadii[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
   714     ComputePixelRadii(twipsRadii, oneDevPixel, &focusRadii);
   715   }
   716   gfxFloat focusWidths[4] = { gfxFloat(oneCSSPixel / oneDevPixel),
   717                               gfxFloat(oneCSSPixel / oneDevPixel),
   718                               gfxFloat(oneCSSPixel / oneDevPixel),
   719                               gfxFloat(oneCSSPixel / oneDevPixel) };
   721   uint8_t focusStyles[4] = { NS_STYLE_BORDER_STYLE_DOTTED,
   722                              NS_STYLE_BORDER_STYLE_DOTTED,
   723                              NS_STYLE_BORDER_STYLE_DOTTED,
   724                              NS_STYLE_BORDER_STYLE_DOTTED };
   725   nscolor focusColors[4] = { aColor, aColor, aColor, aColor };
   727   gfxContext *ctx = aRenderingContext.ThebesContext();
   729   ctx->Save();
   731   // Because this renders a dotted border, the background color
   732   // should not be used.  Therefore, we provide a value that will
   733   // be blatantly wrong if it ever does get used.  (If this becomes
   734   // something that CSS can style, this function will then have access
   735   // to a style context and can use the same logic that PaintBorder
   736   // and PaintOutline do.)
   737   nsCSSBorderRenderer br(oneDevPixel,
   738                          ctx,
   739                          focusRect,
   740                          focusStyles,
   741                          focusWidths,
   742                          focusRadii,
   743                          focusColors,
   744                          nullptr, 0,
   745                          NS_RGB(255, 0, 0));
   746   br.DrawBorders();
   748   ctx->Restore();
   750   SN();
   751 }
   753 // Thebes Border Rendering Code End
   754 //----------------------------------------------------------------------
   757 //----------------------------------------------------------------------
   759 /**
   760  * Computes the placement of a background image.
   761  *
   762  * @param aOriginBounds is the box to which the tiling position should be
   763  * relative
   764  * This should correspond to 'background-origin' for the frame,
   765  * except when painting on the canvas, in which case the origin bounds
   766  * should be the bounds of the root element's frame.
   767  * @param aTopLeft the top-left corner where an image tile should be drawn
   768  * @param aAnchorPoint a point which should be pixel-aligned by
   769  * nsLayoutUtils::DrawImage. This is the same as aTopLeft, unless CSS
   770  * specifies a percentage (including 'right' or 'bottom'), in which case
   771  * it's that percentage within of aOriginBounds. So 'right' would set
   772  * aAnchorPoint.x to aOriginBounds.XMost().
   773  *
   774  * Points are returned relative to aOriginBounds.
   775  */
   776 static void
   777 ComputeBackgroundAnchorPoint(const nsStyleBackground::Layer& aLayer,
   778                              const nsSize& aOriginBounds,
   779                              const nsSize& aImageSize,
   780                              nsPoint* aTopLeft,
   781                              nsPoint* aAnchorPoint)
   782 {
   783   double percentX = aLayer.mPosition.mXPosition.mPercent;
   784   nscoord lengthX = aLayer.mPosition.mXPosition.mLength;
   785   aAnchorPoint->x = lengthX + NSToCoordRound(percentX*aOriginBounds.width);
   786   aTopLeft->x = lengthX +
   787     NSToCoordRound(percentX*(aOriginBounds.width - aImageSize.width));
   789   double percentY = aLayer.mPosition.mYPosition.mPercent;
   790   nscoord lengthY = aLayer.mPosition.mYPosition.mLength;
   791   aAnchorPoint->y = lengthY + NSToCoordRound(percentY*aOriginBounds.height);
   792   aTopLeft->y = lengthY +
   793     NSToCoordRound(percentY*(aOriginBounds.height - aImageSize.height));
   794 }
   796 nsIFrame*
   797 nsCSSRendering::FindNonTransparentBackgroundFrame(nsIFrame* aFrame,
   798                                                   bool aStartAtParent /*= false*/)
   799 {
   800   NS_ASSERTION(aFrame, "Cannot find NonTransparentBackgroundFrame in a null frame");
   802   nsIFrame* frame = nullptr;
   803   if (aStartAtParent) {
   804     frame = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
   805   }
   806   if (!frame) {
   807     frame = aFrame;
   808   }
   810   while (frame) {
   811     // No need to call GetVisitedDependentColor because it always uses
   812     // this alpha component anyway.
   813     if (NS_GET_A(frame->StyleBackground()->mBackgroundColor) > 0)
   814       break;
   816     if (frame->IsThemed())
   817       break;
   819     nsIFrame* parent = nsLayoutUtils::GetParentOrPlaceholderFor(frame);
   820     if (!parent)
   821       break;
   823     frame = parent;
   824   }
   825   return frame;
   826 }
   828 // Returns true if aFrame is a canvas frame.
   829 // We need to treat the viewport as canvas because, even though
   830 // it does not actually paint a background, we need to get the right
   831 // background style so we correctly detect transparent documents.
   832 bool
   833 nsCSSRendering::IsCanvasFrame(nsIFrame* aFrame)
   834 {
   835   nsIAtom* frameType = aFrame->GetType();
   836   return frameType == nsGkAtoms::canvasFrame ||
   837          frameType == nsGkAtoms::rootFrame ||
   838          frameType == nsGkAtoms::pageContentFrame ||
   839          frameType == nsGkAtoms::viewportFrame;
   840 }
   842 nsIFrame*
   843 nsCSSRendering::FindBackgroundStyleFrame(nsIFrame* aForFrame)
   844 {
   845   const nsStyleBackground* result = aForFrame->StyleBackground();
   847   // Check if we need to do propagation from BODY rather than HTML.
   848   if (!result->IsTransparent()) {
   849     return aForFrame;
   850   }
   852   nsIContent* content = aForFrame->GetContent();
   853   // The root element content can't be null. We wouldn't know what
   854   // frame to create for aFrame.
   855   // Use |OwnerDoc| so it works during destruction.
   856   if (!content) {
   857     return aForFrame;
   858   }
   860   nsIDocument* document = content->OwnerDoc();
   862   dom::Element* bodyContent = document->GetBodyElement();
   863   // We need to null check the body node (bug 118829) since
   864   // there are cases, thanks to the fix for bug 5569, where we
   865   // will reflow a document with no body.  In particular, if a
   866   // SCRIPT element in the head blocks the parser and then has a
   867   // SCRIPT that does "document.location.href = 'foo'", then
   868   // nsParser::Terminate will call |DidBuildModel| methods
   869   // through to the content sink, which will call |StartLayout|
   870   // and thus |Initialize| on the pres shell.  See bug 119351
   871   // for the ugly details.
   872   if (!bodyContent) {
   873     return aForFrame;
   874   }
   876   nsIFrame *bodyFrame = bodyContent->GetPrimaryFrame();
   877   if (!bodyFrame) {
   878     return aForFrame;
   879   }
   881   return nsLayoutUtils::GetStyleFrame(bodyFrame);
   882 }
   884 /**
   885  * |FindBackground| finds the correct style data to use to paint the
   886  * background.  It is responsible for handling the following two
   887  * statements in section 14.2 of CSS2:
   888  *
   889  *   The background of the box generated by the root element covers the
   890  *   entire canvas.
   891  *
   892  *   For HTML documents, however, we recommend that authors specify the
   893  *   background for the BODY element rather than the HTML element. User
   894  *   agents should observe the following precedence rules to fill in the
   895  *   background: if the value of the 'background' property for the HTML
   896  *   element is different from 'transparent' then use it, else use the
   897  *   value of the 'background' property for the BODY element. If the
   898  *   resulting value is 'transparent', the rendering is undefined.
   899  *
   900  * Thus, in our implementation, it is responsible for ensuring that:
   901  *  + we paint the correct background on the |nsCanvasFrame|,
   902  *    |nsRootBoxFrame|, or |nsPageFrame|,
   903  *  + we don't paint the background on the root element, and
   904  *  + we don't paint the background on the BODY element in *some* cases,
   905  *    and for SGML-based HTML documents only.
   906  *
   907  * |FindBackground| returns true if a background should be painted, and
   908  * the resulting style context to use for the background information
   909  * will be filled in to |aBackground|.
   910  */
   911 nsStyleContext*
   912 nsCSSRendering::FindRootFrameBackground(nsIFrame* aForFrame)
   913 {
   914   return FindBackgroundStyleFrame(aForFrame)->StyleContext();
   915 }
   917 inline bool
   918 FindElementBackground(nsIFrame* aForFrame, nsIFrame* aRootElementFrame,
   919                       nsStyleContext** aBackgroundSC)
   920 {
   921   if (aForFrame == aRootElementFrame) {
   922     // We must have propagated our background to the viewport or canvas. Abort.
   923     return false;
   924   }
   926   *aBackgroundSC = aForFrame->StyleContext();
   928   // Return true unless the frame is for a BODY element whose background
   929   // was propagated to the viewport.
   931   nsIContent* content = aForFrame->GetContent();
   932   if (!content || content->Tag() != nsGkAtoms::body)
   933     return true; // not frame for a "body" element
   934   // It could be a non-HTML "body" element but that's OK, we'd fail the
   935   // bodyContent check below
   937   if (aForFrame->StyleContext()->GetPseudo())
   938     return true; // A pseudo-element frame.
   940   // We should only look at the <html> background if we're in an HTML document
   941   nsIDocument* document = content->OwnerDoc();
   943   dom::Element* bodyContent = document->GetBodyElement();
   944   if (bodyContent != content)
   945     return true; // this wasn't the background that was propagated
   947   // This can be called even when there's no root element yet, during frame
   948   // construction, via nsLayoutUtils::FrameHasTransparency and
   949   // nsContainerFrame::SyncFrameViewProperties.
   950   if (!aRootElementFrame)
   951     return true;
   953   const nsStyleBackground* htmlBG = aRootElementFrame->StyleBackground();
   954   return !htmlBG->IsTransparent();
   955 }
   957 bool
   958 nsCSSRendering::FindBackground(nsIFrame* aForFrame,
   959                                nsStyleContext** aBackgroundSC)
   960 {
   961   nsIFrame* rootElementFrame =
   962     aForFrame->PresContext()->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
   963   if (IsCanvasFrame(aForFrame)) {
   964     *aBackgroundSC = FindCanvasBackground(aForFrame, rootElementFrame);
   965     return true;
   966   } else {
   967     return FindElementBackground(aForFrame, rootElementFrame, aBackgroundSC);
   968   }
   969 }
   971 void
   972 nsCSSRendering::BeginFrameTreesLocked()
   973 {
   974   ++gFrameTreeLockCount;
   975 }
   977 void
   978 nsCSSRendering::EndFrameTreesLocked()
   979 {
   980   NS_ASSERTION(gFrameTreeLockCount > 0, "Unbalanced EndFrameTreeLocked");
   981   --gFrameTreeLockCount;
   982   if (gFrameTreeLockCount == 0) {
   983     gInlineBGData->Reset();
   984   }
   985 }
   987 void
   988 nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
   989                                     nsRenderingContext& aRenderingContext,
   990                                     nsIFrame* aForFrame,
   991                                     const nsRect& aFrameArea,
   992                                     const nsRect& aDirtyRect,
   993                                     float aOpacity)
   994 {
   995   const nsStyleBorder* styleBorder = aForFrame->StyleBorder();
   996   nsCSSShadowArray* shadows = styleBorder->mBoxShadow;
   997   if (!shadows)
   998     return;
   999   nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
  1001   bool hasBorderRadius;
  1002   bool nativeTheme; // mutually exclusive with hasBorderRadius
  1003   gfxCornerSizes borderRadii;
  1005   // Get any border radius, since box-shadow must also have rounded corners if the frame does
  1006   const nsStyleDisplay* styleDisplay = aForFrame->StyleDisplay();
  1007   nsITheme::Transparency transparency;
  1008   if (aForFrame->IsThemed(styleDisplay, &transparency)) {
  1009     // We don't respect border-radius for native-themed widgets
  1010     hasBorderRadius = false;
  1011     // For opaque (rectangular) theme widgets we can take the generic
  1012     // border-box path with border-radius disabled.
  1013     nativeTheme = transparency != nsITheme::eOpaque;
  1014   } else {
  1015     nativeTheme = false;
  1016     nscoord twipsRadii[8];
  1017     NS_ASSERTION(aFrameArea.Size() == aForFrame->VisualBorderRectRelativeToSelf().Size(),
  1018                  "unexpected size");
  1019     hasBorderRadius = aForFrame->GetBorderRadii(twipsRadii);
  1020     if (hasBorderRadius) {
  1021       ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
  1025   nsRect frameRect =
  1026     nativeTheme ? aForFrame->GetVisualOverflowRectRelativeToSelf() + aFrameArea.TopLeft() : aFrameArea;
  1027   gfxRect frameGfxRect(nsLayoutUtils::RectToGfxRect(frameRect, twipsPerPixel));
  1028   frameGfxRect.Round();
  1030   // We don't show anything that intersects with the frame we're blurring on. So tell the
  1031   // blurrer not to do unnecessary work there.
  1032   gfxRect skipGfxRect = frameGfxRect;
  1033   bool useSkipGfxRect = true;
  1034   if (nativeTheme) {
  1035     // Optimize non-leaf native-themed frames by skipping computing pixels
  1036     // in the padding-box. We assume the padding-box is going to be painted
  1037     // opaquely for non-leaf frames.
  1038     // XXX this may not be a safe assumption; we should make this go away
  1039     // by optimizing box-shadow drawing more for the cases where we don't have a skip-rect.
  1040     useSkipGfxRect = !aForFrame->IsLeaf();
  1041     nsRect paddingRect =
  1042       aForFrame->GetPaddingRect() - aForFrame->GetPosition() + aFrameArea.TopLeft();
  1043     skipGfxRect = nsLayoutUtils::RectToGfxRect(paddingRect, twipsPerPixel);
  1044   } else if (hasBorderRadius) {
  1045     skipGfxRect.Deflate(gfxMargin(
  1046         std::max(borderRadii[C_TL].height, borderRadii[C_TR].height), 0,
  1047         std::max(borderRadii[C_BL].height, borderRadii[C_BR].height), 0));
  1050   for (uint32_t i = shadows->Length(); i > 0; --i) {
  1051     nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
  1052     if (shadowItem->mInset)
  1053       continue;
  1055     nsRect shadowRect = frameRect;
  1056     shadowRect.MoveBy(shadowItem->mXOffset, shadowItem->mYOffset);
  1057     if (!nativeTheme) {
  1058       shadowRect.Inflate(shadowItem->mSpread, shadowItem->mSpread);
  1061     // shadowRect won't include the blur, so make an extra rect here that includes the blur
  1062     // for use in the even-odd rule below.
  1063     nsRect shadowRectPlusBlur = shadowRect;
  1064     nscoord blurRadius = shadowItem->mRadius;
  1065     shadowRectPlusBlur.Inflate(
  1066       nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, twipsPerPixel));
  1068     gfxRect shadowGfxRectPlusBlur =
  1069       nsLayoutUtils::RectToGfxRect(shadowRectPlusBlur, twipsPerPixel);
  1070     shadowGfxRectPlusBlur.RoundOut();
  1072     // Set the shadow color; if not specified, use the foreground color
  1073     nscolor shadowColor;
  1074     if (shadowItem->mHasColor)
  1075       shadowColor = shadowItem->mColor;
  1076     else
  1077       shadowColor = aForFrame->StyleColor()->mColor;
  1079     gfxRGBA gfxShadowColor(shadowColor);
  1080     gfxShadowColor.a *= aOpacity;
  1082     gfxContext* renderContext = aRenderingContext.ThebesContext();
  1083     if (nativeTheme) {
  1084       nsContextBoxBlur blurringArea;
  1086       // When getting the widget shape from the native theme, we're going
  1087       // to draw the widget into the shadow surface to create a mask.
  1088       // We need to ensure that there actually *is* a shadow surface
  1089       // and that we're not going to draw directly into renderContext.
  1090       gfxContext* shadowContext =
  1091         blurringArea.Init(shadowRect, shadowItem->mSpread,
  1092                           blurRadius, twipsPerPixel, renderContext, aDirtyRect,
  1093                           useSkipGfxRect ? &skipGfxRect : nullptr,
  1094                           nsContextBoxBlur::FORCE_MASK);
  1095       if (!shadowContext)
  1096         continue;
  1098       // shadowContext is owned by either blurringArea or aRenderingContext.
  1099       MOZ_ASSERT(shadowContext == blurringArea.GetContext());
  1101       renderContext->Save();
  1102       renderContext->SetColor(gfxShadowColor);
  1104       // Draw the shape of the frame so it can be blurred. Recall how nsContextBoxBlur
  1105       // doesn't make any temporary surfaces if blur is 0 and it just returns the original
  1106       // surface? If we have no blur, we're painting this fill on the actual content surface
  1107       // (renderContext == shadowContext) which is why we set up the color and clip
  1108       // before doing this.
  1110       // We don't clip the border-box from the shadow, nor any other box.
  1111       // We assume that the native theme is going to paint over the shadow.
  1113       // Draw the widget shape
  1114       gfxContextMatrixAutoSaveRestore save(shadowContext);
  1115       nsRefPtr<nsRenderingContext> wrapperCtx = new nsRenderingContext();
  1116       wrapperCtx->Init(aPresContext->DeviceContext(), shadowContext);
  1117       wrapperCtx->Translate(nsPoint(shadowItem->mXOffset,
  1118                                     shadowItem->mYOffset));
  1120       nsRect nativeRect;
  1121       nativeRect.IntersectRect(frameRect, aDirtyRect);
  1122       aPresContext->GetTheme()->DrawWidgetBackground(wrapperCtx, aForFrame,
  1123           styleDisplay->mAppearance, aFrameArea, nativeRect);
  1125       blurringArea.DoPaint();
  1126       renderContext->Restore();
  1127     } else {
  1128       renderContext->Save();
  1129       // Clip out the area of the actual frame so the shadow is not shown within
  1130       // the frame
  1131       renderContext->NewPath();
  1132       renderContext->Rectangle(shadowGfxRectPlusBlur);
  1133       if (hasBorderRadius) {
  1134         renderContext->RoundedRectangle(frameGfxRect, borderRadii);
  1135       } else {
  1136         renderContext->Rectangle(frameGfxRect);
  1139       renderContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
  1140       renderContext->Clip();
  1142       gfxCornerSizes clipRectRadii;
  1143       if (hasBorderRadius) {
  1144         gfxFloat spreadDistance = shadowItem->mSpread / twipsPerPixel;
  1146         gfxFloat borderSizes[4];
  1148         borderSizes[NS_SIDE_LEFT] = spreadDistance;
  1149         borderSizes[NS_SIDE_TOP] = spreadDistance;
  1150         borderSizes[NS_SIDE_RIGHT] = spreadDistance;
  1151         borderSizes[NS_SIDE_BOTTOM] = spreadDistance;
  1153         nsCSSBorderRenderer::ComputeOuterRadii(borderRadii, borderSizes,
  1154             &clipRectRadii);
  1157       nsContextBoxBlur::BlurRectangle(renderContext,
  1158                                       shadowRect,
  1159                                       twipsPerPixel,
  1160                                       hasBorderRadius ? &clipRectRadii : nullptr,
  1161                                       blurRadius,
  1162                                       gfxShadowColor,
  1163                                       aDirtyRect,
  1164                                       skipGfxRect);
  1165       renderContext->Restore();
  1171 void
  1172 nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext,
  1173                                     nsRenderingContext& aRenderingContext,
  1174                                     nsIFrame* aForFrame,
  1175                                     const nsRect& aFrameArea,
  1176                                     const nsRect& aDirtyRect)
  1178   const nsStyleBorder* styleBorder = aForFrame->StyleBorder();
  1179   nsCSSShadowArray* shadows = styleBorder->mBoxShadow;
  1180   if (!shadows)
  1181     return;
  1182   if (aForFrame->IsThemed() && aForFrame->GetContent() &&
  1183       !nsContentUtils::IsChromeDoc(aForFrame->GetContent()->GetCurrentDoc())) {
  1184     // There's no way of getting hold of a shape corresponding to a
  1185     // "padding-box" for native-themed widgets, so just don't draw
  1186     // inner box-shadows for them. But we allow chrome to paint inner
  1187     // box shadows since chrome can be aware of the platform theme.
  1188     return;
  1191   // Get any border radius, since box-shadow must also have rounded corners
  1192   // if the frame does.
  1193   nscoord twipsRadii[8];
  1194   NS_ASSERTION(aForFrame->GetType() == nsGkAtoms::fieldSetFrame ||
  1195                aFrameArea.Size() == aForFrame->GetSize(), "unexpected size");
  1196   bool hasBorderRadius = aForFrame->GetBorderRadii(twipsRadii);
  1197   nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
  1199   nsRect paddingRect = aFrameArea;
  1200   nsMargin border = aForFrame->GetUsedBorder();
  1201   aForFrame->ApplySkipSides(border);
  1202   paddingRect.Deflate(border);
  1204   gfxCornerSizes innerRadii;
  1205   if (hasBorderRadius) {
  1206     gfxCornerSizes borderRadii;
  1208     ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
  1209     gfxFloat borderSizes[4] = {
  1210       gfxFloat(border.top / twipsPerPixel),
  1211       gfxFloat(border.right / twipsPerPixel),
  1212       gfxFloat(border.bottom / twipsPerPixel),
  1213       gfxFloat(border.left / twipsPerPixel)
  1214     };
  1215     nsCSSBorderRenderer::ComputeInnerRadii(borderRadii, borderSizes,
  1216                                            &innerRadii);
  1219   for (uint32_t i = shadows->Length(); i > 0; --i) {
  1220     nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
  1221     if (!shadowItem->mInset)
  1222       continue;
  1224     /*
  1225      * shadowRect: the frame's padding rect
  1226      * shadowPaintRect: the area to paint on the temp surface, larger than shadowRect
  1227      *                  so that blurs still happen properly near the edges
  1228      * shadowClipRect: the area on the temporary surface within shadowPaintRect
  1229      *                 that we will NOT paint in
  1230      */
  1231     nscoord blurRadius = shadowItem->mRadius;
  1232     nsMargin blurMargin =
  1233       nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, twipsPerPixel);
  1234     nsRect shadowPaintRect = paddingRect;
  1235     shadowPaintRect.Inflate(blurMargin);
  1237     nsRect shadowClipRect = paddingRect;
  1238     shadowClipRect.MoveBy(shadowItem->mXOffset, shadowItem->mYOffset);
  1239     shadowClipRect.Deflate(shadowItem->mSpread, shadowItem->mSpread);
  1241     gfxCornerSizes clipRectRadii;
  1242     if (hasBorderRadius) {
  1243       // Calculate the radii the inner clipping rect will have
  1244       gfxFloat spreadDistance = shadowItem->mSpread / twipsPerPixel;
  1245       gfxFloat borderSizes[4] = {0, 0, 0, 0};
  1247       // See PaintBoxShadowOuter and bug 514670
  1248       if (innerRadii[C_TL].width > 0 || innerRadii[C_BL].width > 0) {
  1249         borderSizes[NS_SIDE_LEFT] = spreadDistance;
  1252       if (innerRadii[C_TL].height > 0 || innerRadii[C_TR].height > 0) {
  1253         borderSizes[NS_SIDE_TOP] = spreadDistance;
  1256       if (innerRadii[C_TR].width > 0 || innerRadii[C_BR].width > 0) {
  1257         borderSizes[NS_SIDE_RIGHT] = spreadDistance;
  1260       if (innerRadii[C_BL].height > 0 || innerRadii[C_BR].height > 0) {
  1261         borderSizes[NS_SIDE_BOTTOM] = spreadDistance;
  1264       nsCSSBorderRenderer::ComputeInnerRadii(innerRadii, borderSizes,
  1265                                              &clipRectRadii);
  1268     // Set the "skip rect" to the area within the frame that we don't paint in,
  1269     // including after blurring.
  1270     nsRect skipRect = shadowClipRect;
  1271     skipRect.Deflate(blurMargin);
  1272     gfxRect skipGfxRect = nsLayoutUtils::RectToGfxRect(skipRect, twipsPerPixel);
  1273     if (hasBorderRadius) {
  1274       skipGfxRect.Deflate(gfxMargin(
  1275           std::max(clipRectRadii[C_TL].height, clipRectRadii[C_TR].height), 0,
  1276           std::max(clipRectRadii[C_BL].height, clipRectRadii[C_BR].height), 0));
  1279     // When there's a blur radius, gfxAlphaBoxBlur leaves the skiprect area
  1280     // unchanged. And by construction the gfxSkipRect is not touched by the
  1281     // rendered shadow (even after blurring), so those pixels must be completely
  1282     // transparent in the shadow, so drawing them changes nothing.
  1283     gfxContext* renderContext = aRenderingContext.ThebesContext();
  1284     nsContextBoxBlur blurringArea;
  1285     gfxContext* shadowContext =
  1286       blurringArea.Init(shadowPaintRect, 0, blurRadius, twipsPerPixel,
  1287                         renderContext, aDirtyRect, &skipGfxRect);
  1288     if (!shadowContext)
  1289       continue;
  1291     // shadowContext is owned by either blurringArea or aRenderingContext.
  1292     MOZ_ASSERT(shadowContext == renderContext ||
  1293                shadowContext == blurringArea.GetContext());
  1295     // Set the shadow color; if not specified, use the foreground color
  1296     nscolor shadowColor;
  1297     if (shadowItem->mHasColor)
  1298       shadowColor = shadowItem->mColor;
  1299     else
  1300       shadowColor = aForFrame->StyleColor()->mColor;
  1302     renderContext->Save();
  1303     renderContext->SetColor(gfxRGBA(shadowColor));
  1305     // Clip the context to the area of the frame's padding rect, so no part of the
  1306     // shadow is painted outside. Also cut out anything beyond where the inset shadow
  1307     // will be.
  1308     gfxRect shadowGfxRect =
  1309       nsLayoutUtils::RectToGfxRect(paddingRect, twipsPerPixel);
  1310     shadowGfxRect.Round();
  1311     renderContext->NewPath();
  1312     if (hasBorderRadius)
  1313       renderContext->RoundedRectangle(shadowGfxRect, innerRadii, false);
  1314     else
  1315       renderContext->Rectangle(shadowGfxRect);
  1316     renderContext->Clip();
  1318     // Fill the surface minus the area within the frame that we should
  1319     // not paint in, and blur and apply it.
  1320     gfxRect shadowPaintGfxRect =
  1321       nsLayoutUtils::RectToGfxRect(shadowPaintRect, twipsPerPixel);
  1322     shadowPaintGfxRect.RoundOut();
  1323     gfxRect shadowClipGfxRect =
  1324       nsLayoutUtils::RectToGfxRect(shadowClipRect, twipsPerPixel);
  1325     shadowClipGfxRect.Round();
  1326     shadowContext->NewPath();
  1327     shadowContext->Rectangle(shadowPaintGfxRect);
  1328     if (hasBorderRadius)
  1329       shadowContext->RoundedRectangle(shadowClipGfxRect, clipRectRadii, false);
  1330     else
  1331       shadowContext->Rectangle(shadowClipGfxRect);
  1332     shadowContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
  1333     shadowContext->Fill();
  1335     blurringArea.DoPaint();
  1336     renderContext->Restore();
  1340 void
  1341 nsCSSRendering::PaintBackground(nsPresContext* aPresContext,
  1342                                 nsRenderingContext& aRenderingContext,
  1343                                 nsIFrame* aForFrame,
  1344                                 const nsRect& aDirtyRect,
  1345                                 const nsRect& aBorderArea,
  1346                                 uint32_t aFlags,
  1347                                 nsRect* aBGClipRect,
  1348                                 int32_t aLayer)
  1350   PROFILER_LABEL("nsCSSRendering", "PaintBackground");
  1351   NS_PRECONDITION(aForFrame,
  1352                   "Frame is expected to be provided to PaintBackground");
  1354   nsStyleContext *sc;
  1355   if (!FindBackground(aForFrame, &sc)) {
  1356     // We don't want to bail out if moz-appearance is set on a root
  1357     // node. If it has a parent content node, bail because it's not
  1358     // a root, otherwise keep going in order to let the theme stuff
  1359     // draw the background. The canvas really should be drawing the
  1360     // bg, but there's no way to hook that up via css.
  1361     if (!aForFrame->StyleDisplay()->mAppearance) {
  1362       return;
  1365     nsIContent* content = aForFrame->GetContent();
  1366     if (!content || content->GetParent()) {
  1367       return;
  1370     sc = aForFrame->StyleContext();
  1373   PaintBackgroundWithSC(aPresContext, aRenderingContext, aForFrame,
  1374                         aDirtyRect, aBorderArea, sc,
  1375                         *aForFrame->StyleBorder(), aFlags,
  1376                         aBGClipRect, aLayer);
  1379 void
  1380 nsCSSRendering::PaintBackgroundColor(nsPresContext* aPresContext,
  1381                                      nsRenderingContext& aRenderingContext,
  1382                                      nsIFrame* aForFrame,
  1383                                      const nsRect& aDirtyRect,
  1384                                      const nsRect& aBorderArea,
  1385                                      uint32_t aFlags)
  1387   PROFILER_LABEL("nsCSSRendering", "PaintBackgroundColor");
  1388   NS_PRECONDITION(aForFrame,
  1389                   "Frame is expected to be provided to PaintBackground");
  1391   nsStyleContext *sc;
  1392   if (!FindBackground(aForFrame, &sc)) {
  1393     // We don't want to bail out if moz-appearance is set on a root
  1394     // node. If it has a parent content node, bail because it's not
  1395     // a root, other wise keep going in order to let the theme stuff
  1396     // draw the background. The canvas really should be drawing the
  1397     // bg, but there's no way to hook that up via css.
  1398     if (!aForFrame->StyleDisplay()->mAppearance) {
  1399       return;
  1402     nsIContent* content = aForFrame->GetContent();
  1403     if (!content || content->GetParent()) {
  1404       return;
  1407     sc = aForFrame->StyleContext();
  1410   PaintBackgroundColorWithSC(aPresContext, aRenderingContext, aForFrame,
  1411                              aDirtyRect, aBorderArea, sc,
  1412                              *aForFrame->StyleBorder(), aFlags);
  1415 static bool
  1416 IsOpaqueBorderEdge(const nsStyleBorder& aBorder, mozilla::css::Side aSide)
  1418   if (aBorder.GetComputedBorder().Side(aSide) == 0)
  1419     return true;
  1420   switch (aBorder.GetBorderStyle(aSide)) {
  1421   case NS_STYLE_BORDER_STYLE_SOLID:
  1422   case NS_STYLE_BORDER_STYLE_GROOVE:
  1423   case NS_STYLE_BORDER_STYLE_RIDGE:
  1424   case NS_STYLE_BORDER_STYLE_INSET:
  1425   case NS_STYLE_BORDER_STYLE_OUTSET:
  1426     break;
  1427   default:
  1428     return false;
  1431   // If we're using a border image, assume it's not fully opaque,
  1432   // because we may not even have the image loaded at this point, and
  1433   // even if we did, checking whether the relevant tile is fully
  1434   // opaque would be too much work.
  1435   if (aBorder.mBorderImageSource.GetType() != eStyleImageType_Null)
  1436     return false;
  1438   nscolor color;
  1439   bool isForeground;
  1440   aBorder.GetBorderColor(aSide, color, isForeground);
  1442   // We don't know the foreground color here, so if it's being used
  1443   // we must assume it might be transparent.
  1444   if (isForeground)
  1445     return false;
  1447   return NS_GET_A(color) == 255;
  1450 /**
  1451  * Returns true if all border edges are either missing or opaque.
  1452  */
  1453 static bool
  1454 IsOpaqueBorder(const nsStyleBorder& aBorder)
  1456   if (aBorder.mBorderColors)
  1457     return false;
  1458   NS_FOR_CSS_SIDES(i) {
  1459     if (!IsOpaqueBorderEdge(aBorder, i))
  1460       return false;
  1462   return true;
  1465 static inline void
  1466 SetupDirtyRects(const nsRect& aBGClipArea, const nsRect& aCallerDirtyRect,
  1467                 nscoord aAppUnitsPerPixel,
  1468                 /* OUT: */
  1469                 nsRect* aDirtyRect, gfxRect* aDirtyRectGfx)
  1471   aDirtyRect->IntersectRect(aBGClipArea, aCallerDirtyRect);
  1473   // Compute the Thebes equivalent of the dirtyRect.
  1474   *aDirtyRectGfx = nsLayoutUtils::RectToGfxRect(*aDirtyRect, aAppUnitsPerPixel);
  1475   NS_WARN_IF_FALSE(aDirtyRect->IsEmpty() || !aDirtyRectGfx->IsEmpty(),
  1476                    "converted dirty rect should not be empty");
  1477   NS_ABORT_IF_FALSE(!aDirtyRect->IsEmpty() || aDirtyRectGfx->IsEmpty(),
  1478                     "second should be empty if first is");
  1481 struct BackgroundClipState {
  1482   nsRect mBGClipArea;  // Affected by mClippedRadii
  1483   nsRect mAdditionalBGClipArea;  // Not affected by mClippedRadii
  1484   nsRect mDirtyRect;
  1485   gfxRect mDirtyRectGfx;
  1487   gfxCornerSizes mClippedRadii;
  1488   bool mRadiiAreOuter;
  1489   bool mHasAdditionalBGClipArea;
  1491   // Whether we are being asked to draw with a caller provided background
  1492   // clipping area. If this is true we also disable rounded corners.
  1493   bool mCustomClip;
  1494 };
  1496 static void
  1497 GetBackgroundClip(gfxContext *aCtx, uint8_t aBackgroundClip,
  1498                   uint8_t aBackgroundAttachment,
  1499                   nsIFrame* aForFrame, const nsRect& aBorderArea,
  1500                   const nsRect& aCallerDirtyRect, bool aHaveRoundedCorners,
  1501                   const gfxCornerSizes& aBGRadii, nscoord aAppUnitsPerPixel,
  1502                   /* out */ BackgroundClipState* aClipState)
  1504   aClipState->mBGClipArea = aBorderArea;
  1505   aClipState->mHasAdditionalBGClipArea = false;
  1506   aClipState->mCustomClip = false;
  1507   aClipState->mRadiiAreOuter = true;
  1508   aClipState->mClippedRadii = aBGRadii;
  1509   if (aForFrame->GetType() == nsGkAtoms::scrollFrame &&
  1510         NS_STYLE_BG_ATTACHMENT_LOCAL == aBackgroundAttachment) {
  1511     // As of this writing, this is still in discussion in the CSS Working Group
  1512     // http://lists.w3.org/Archives/Public/www-style/2013Jul/0250.html
  1514     // The rectangle for 'background-clip' scrolls with the content,
  1515     // but the background is also clipped at a non-scrolling 'padding-box'
  1516     // like the content. (See below.)
  1517     // Therefore, only 'content-box' makes a difference here.
  1518     if (aBackgroundClip == NS_STYLE_BG_CLIP_CONTENT) {
  1519       nsIScrollableFrame* scrollableFrame = do_QueryFrame(aForFrame);
  1520       // Clip at a rectangle attached to the scrolled content.
  1521       aClipState->mHasAdditionalBGClipArea = true;
  1522       aClipState->mAdditionalBGClipArea = nsRect(
  1523         aClipState->mBGClipArea.TopLeft()
  1524           + scrollableFrame->GetScrolledFrame()->GetPosition()
  1525           // For the dir=rtl case:
  1526           + scrollableFrame->GetScrollRange().TopLeft(),
  1527         scrollableFrame->GetScrolledRect().Size());
  1528       nsMargin padding = aForFrame->GetUsedPadding();
  1529       // padding-bottom is ignored on scrollable frames:
  1530       // https://bugzilla.mozilla.org/show_bug.cgi?id=748518
  1531       padding.bottom = 0;
  1532       aForFrame->ApplySkipSides(padding);
  1533       aClipState->mAdditionalBGClipArea.Deflate(padding);
  1536     // Also clip at a non-scrolling, rounded-corner 'padding-box',
  1537     // same as the scrolled content because of the 'overflow' property.
  1538     aBackgroundClip = NS_STYLE_BG_CLIP_PADDING;
  1541   if (aBackgroundClip != NS_STYLE_BG_CLIP_BORDER) {
  1542     nsMargin border = aForFrame->GetUsedBorder();
  1543     if (aBackgroundClip == NS_STYLE_BG_CLIP_MOZ_ALMOST_PADDING) {
  1544       // Reduce |border| by 1px (device pixels) on all sides, if
  1545       // possible, so that we don't get antialiasing seams between the
  1546       // background and border.
  1547       border.top = std::max(0, border.top - aAppUnitsPerPixel);
  1548       border.right = std::max(0, border.right - aAppUnitsPerPixel);
  1549       border.bottom = std::max(0, border.bottom - aAppUnitsPerPixel);
  1550       border.left = std::max(0, border.left - aAppUnitsPerPixel);
  1551     } else if (aBackgroundClip != NS_STYLE_BG_CLIP_PADDING) {
  1552       NS_ASSERTION(aBackgroundClip == NS_STYLE_BG_CLIP_CONTENT,
  1553                    "unexpected background-clip");
  1554       border += aForFrame->GetUsedPadding();
  1556     aForFrame->ApplySkipSides(border);
  1557     aClipState->mBGClipArea.Deflate(border);
  1559     if (aHaveRoundedCorners) {
  1560       gfxFloat borderSizes[4] = {
  1561         gfxFloat(border.top / aAppUnitsPerPixel),
  1562         gfxFloat(border.right / aAppUnitsPerPixel),
  1563         gfxFloat(border.bottom / aAppUnitsPerPixel),
  1564         gfxFloat(border.left / aAppUnitsPerPixel)
  1565       };
  1566       nsCSSBorderRenderer::ComputeInnerRadii(aBGRadii, borderSizes,
  1567                                              &aClipState->mClippedRadii);
  1568       aClipState->mRadiiAreOuter = false;
  1572   if (!aHaveRoundedCorners && aClipState->mHasAdditionalBGClipArea) {
  1573     // Do the intersection here to account for the fast path(?) below.
  1574     aClipState->mBGClipArea =
  1575       aClipState->mBGClipArea.Intersect(aClipState->mAdditionalBGClipArea);
  1576     aClipState->mHasAdditionalBGClipArea = false;
  1579   SetupDirtyRects(aClipState->mBGClipArea, aCallerDirtyRect, aAppUnitsPerPixel,
  1580                   &aClipState->mDirtyRect, &aClipState->mDirtyRectGfx);
  1583 static void
  1584 SetupBackgroundClip(BackgroundClipState& aClipState, gfxContext *aCtx,
  1585                     bool aHaveRoundedCorners, nscoord aAppUnitsPerPixel,
  1586                     gfxContextAutoSaveRestore* aAutoSR)
  1588   if (aClipState.mDirtyRectGfx.IsEmpty()) {
  1589     // Our caller won't draw anything under this condition, so no need
  1590     // to set more up.
  1591     return;
  1594   if (aClipState.mCustomClip) {
  1595     // We don't support custom clips and rounded corners, arguably a bug, but
  1596     // table painting seems to depend on it.
  1597     return;
  1600   // If we have rounded corners, clip all subsequent drawing to the
  1601   // rounded rectangle defined by bgArea and bgRadii (we don't know
  1602   // whether the rounded corners intrude on the dirtyRect or not).
  1603   // Do not do this if we have a caller-provided clip rect --
  1604   // as above with bgArea, arguably a bug, but table painting seems
  1605   // to depend on it.
  1607   if (aHaveRoundedCorners || aClipState.mHasAdditionalBGClipArea) {
  1608     aAutoSR->Reset(aCtx);
  1611   if (aClipState.mHasAdditionalBGClipArea) {
  1612     gfxRect bgAreaGfx = nsLayoutUtils::RectToGfxRect(
  1613       aClipState.mAdditionalBGClipArea, aAppUnitsPerPixel);
  1614     bgAreaGfx.Round();
  1615     bgAreaGfx.Condition();
  1616     aCtx->NewPath();
  1617     aCtx->Rectangle(bgAreaGfx, true);
  1618     aCtx->Clip();
  1621   if (aHaveRoundedCorners) {
  1622     gfxRect bgAreaGfx =
  1623       nsLayoutUtils::RectToGfxRect(aClipState.mBGClipArea, aAppUnitsPerPixel);
  1624     bgAreaGfx.Round();
  1625     bgAreaGfx.Condition();
  1627     if (bgAreaGfx.IsEmpty()) {
  1628       // I think it's become possible to hit this since
  1629       // http://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.
  1630       NS_WARNING("converted background area should not be empty");
  1631       // Make our caller not do anything.
  1632       aClipState.mDirtyRectGfx.SizeTo(gfxSize(0.0, 0.0));
  1633       return;
  1636     aCtx->NewPath();
  1637     aCtx->RoundedRectangle(bgAreaGfx, aClipState.mClippedRadii, aClipState.mRadiiAreOuter);
  1638     aCtx->Clip();
  1642 static void
  1643 DrawBackgroundColor(BackgroundClipState& aClipState, gfxContext *aCtx,
  1644                     bool aHaveRoundedCorners, nscoord aAppUnitsPerPixel)
  1646   if (aClipState.mDirtyRectGfx.IsEmpty()) {
  1647     // Our caller won't draw anything under this condition, so no need
  1648     // to set more up.
  1649     return;
  1652   // We don't support custom clips and rounded corners, arguably a bug, but
  1653   // table painting seems to depend on it.
  1654   if (!aHaveRoundedCorners || aClipState.mCustomClip) {
  1655     aCtx->NewPath();
  1656     aCtx->Rectangle(aClipState.mDirtyRectGfx, true);
  1657     aCtx->Fill();
  1658     return;
  1661   gfxRect bgAreaGfx =
  1662     nsLayoutUtils::RectToGfxRect(aClipState.mBGClipArea, aAppUnitsPerPixel);
  1663   bgAreaGfx.Round();
  1664   bgAreaGfx.Condition();
  1666   if (bgAreaGfx.IsEmpty()) {
  1667     // I think it's become possible to hit this since
  1668     // http://hg.mozilla.org/mozilla-central/rev/50e934e4979b landed.
  1669     NS_WARNING("converted background area should not be empty");
  1670     // Make our caller not do anything.
  1671     aClipState.mDirtyRectGfx.SizeTo(gfxSize(0.0, 0.0));
  1672     return;
  1675   aCtx->Save();
  1676   gfxRect dirty = bgAreaGfx.Intersect(aClipState.mDirtyRectGfx);
  1678   aCtx->NewPath();
  1679   aCtx->Rectangle(dirty, true);
  1680   aCtx->Clip();
  1682   if (aClipState.mHasAdditionalBGClipArea) {
  1683     gfxRect bgAdditionalAreaGfx = nsLayoutUtils::RectToGfxRect(
  1684       aClipState.mAdditionalBGClipArea, aAppUnitsPerPixel);
  1685     bgAdditionalAreaGfx.Round();
  1686     bgAdditionalAreaGfx.Condition();
  1687     aCtx->NewPath();
  1688     aCtx->Rectangle(bgAdditionalAreaGfx, true);
  1689     aCtx->Clip();
  1692   aCtx->NewPath();
  1693   aCtx->RoundedRectangle(bgAreaGfx, aClipState.mClippedRadii,
  1694                          aClipState.mRadiiAreOuter);
  1696   aCtx->Fill();
  1697   aCtx->Restore();
  1700 nscolor
  1701 nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext,
  1702                                          nsStyleContext* aStyleContext,
  1703                                          nsIFrame* aFrame,
  1704                                          bool& aDrawBackgroundImage,
  1705                                          bool& aDrawBackgroundColor)
  1707   aDrawBackgroundImage = true;
  1708   aDrawBackgroundColor = true;
  1710   if (aFrame->HonorPrintBackgroundSettings()) {
  1711     aDrawBackgroundImage = aPresContext->GetBackgroundImageDraw();
  1712     aDrawBackgroundColor = aPresContext->GetBackgroundColorDraw();
  1715   const nsStyleBackground *bg = aStyleContext->StyleBackground();
  1716   nscolor bgColor;
  1717   if (aDrawBackgroundColor) {
  1718     bgColor =
  1719       aStyleContext->GetVisitedDependentColor(eCSSProperty_background_color);
  1720     if (NS_GET_A(bgColor) == 0) {
  1721       aDrawBackgroundColor = false;
  1723   } else {
  1724     // If GetBackgroundColorDraw() is false, we are still expected to
  1725     // draw color in the background of any frame that's not completely
  1726     // transparent, but we are expected to use white instead of whatever
  1727     // color was specified.
  1728     bgColor = NS_RGB(255, 255, 255);
  1729     if (aDrawBackgroundImage || !bg->IsTransparent()) {
  1730       aDrawBackgroundColor = true;
  1731     } else {
  1732       bgColor = NS_RGBA(0,0,0,0);
  1736   // We can skip painting the background color if a background image is opaque.
  1737   if (aDrawBackgroundColor &&
  1738       bg->BottomLayer().mRepeat.mXRepeat == NS_STYLE_BG_REPEAT_REPEAT &&
  1739       bg->BottomLayer().mRepeat.mYRepeat == NS_STYLE_BG_REPEAT_REPEAT &&
  1740       bg->BottomLayer().mImage.IsOpaque() &&
  1741       bg->BottomLayer().mBlendMode == NS_STYLE_BLEND_NORMAL) {
  1742     aDrawBackgroundColor = false;
  1745   return bgColor;
  1748 static gfxFloat
  1749 ConvertGradientValueToPixels(const nsStyleCoord& aCoord,
  1750                              gfxFloat aFillLength,
  1751                              int32_t aAppUnitsPerPixel)
  1753   switch (aCoord.GetUnit()) {
  1754     case eStyleUnit_Percent:
  1755       return aCoord.GetPercentValue() * aFillLength;
  1756     case eStyleUnit_Coord:
  1757       return NSAppUnitsToFloatPixels(aCoord.GetCoordValue(), aAppUnitsPerPixel);
  1758     case eStyleUnit_Calc: {
  1759       const nsStyleCoord::Calc *calc = aCoord.GetCalcValue();
  1760       return calc->mPercent * aFillLength +
  1761              NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
  1763     default:
  1764       NS_WARNING("Unexpected coord unit");
  1765       return 0;
  1769 // Given a box with size aBoxSize and origin (0,0), and an angle aAngle,
  1770 // and a starting point for the gradient line aStart, find the endpoint of
  1771 // the gradient line --- the intersection of the gradient line with a line
  1772 // perpendicular to aAngle that passes through the farthest corner in the
  1773 // direction aAngle.
  1774 static gfxPoint
  1775 ComputeGradientLineEndFromAngle(const gfxPoint& aStart,
  1776                                 double aAngle,
  1777                                 const gfxSize& aBoxSize)
  1779   double dx = cos(-aAngle);
  1780   double dy = sin(-aAngle);
  1781   gfxPoint farthestCorner(dx > 0 ? aBoxSize.width : 0,
  1782                           dy > 0 ? aBoxSize.height : 0);
  1783   gfxPoint delta = farthestCorner - aStart;
  1784   double u = delta.x*dy - delta.y*dx;
  1785   return farthestCorner + gfxPoint(-u*dy, u*dx);
  1788 // Compute the start and end points of the gradient line for a linear gradient.
  1789 static void
  1790 ComputeLinearGradientLine(nsPresContext* aPresContext,
  1791                           nsStyleGradient* aGradient,
  1792                           const gfxSize& aBoxSize,
  1793                           gfxPoint* aLineStart,
  1794                           gfxPoint* aLineEnd)
  1796   if (aGradient->mBgPosX.GetUnit() == eStyleUnit_None) {
  1797     double angle;
  1798     if (aGradient->mAngle.IsAngleValue()) {
  1799       angle = aGradient->mAngle.GetAngleValueInRadians();
  1800       if (!aGradient->mLegacySyntax) {
  1801         angle = M_PI_2 - angle;
  1803     } else {
  1804       angle = -M_PI_2; // defaults to vertical gradient starting from top
  1806     gfxPoint center(aBoxSize.width/2, aBoxSize.height/2);
  1807     *aLineEnd = ComputeGradientLineEndFromAngle(center, angle, aBoxSize);
  1808     *aLineStart = gfxPoint(aBoxSize.width, aBoxSize.height) - *aLineEnd;
  1809   } else if (!aGradient->mLegacySyntax) {
  1810     float xSign = aGradient->mBgPosX.GetPercentValue() * 2 - 1;
  1811     float ySign = 1 - aGradient->mBgPosY.GetPercentValue() * 2;
  1812     double angle = atan2(ySign * aBoxSize.width, xSign * aBoxSize.height);
  1813     gfxPoint center(aBoxSize.width/2, aBoxSize.height/2);
  1814     *aLineEnd = ComputeGradientLineEndFromAngle(center, angle, aBoxSize);
  1815     *aLineStart = gfxPoint(aBoxSize.width, aBoxSize.height) - *aLineEnd;
  1816   } else {
  1817     int32_t appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
  1818     *aLineStart = gfxPoint(
  1819       ConvertGradientValueToPixels(aGradient->mBgPosX, aBoxSize.width,
  1820                                    appUnitsPerPixel),
  1821       ConvertGradientValueToPixels(aGradient->mBgPosY, aBoxSize.height,
  1822                                    appUnitsPerPixel));
  1823     if (aGradient->mAngle.IsAngleValue()) {
  1824       MOZ_ASSERT(aGradient->mLegacySyntax);
  1825       double angle = aGradient->mAngle.GetAngleValueInRadians();
  1826       *aLineEnd = ComputeGradientLineEndFromAngle(*aLineStart, angle, aBoxSize);
  1827     } else {
  1828       // No angle, the line end is just the reflection of the start point
  1829       // through the center of the box
  1830       *aLineEnd = gfxPoint(aBoxSize.width, aBoxSize.height) - *aLineStart;
  1835 // Compute the start and end points of the gradient line for a radial gradient.
  1836 // Also returns the horizontal and vertical radii defining the circle or
  1837 // ellipse to use.
  1838 static void
  1839 ComputeRadialGradientLine(nsPresContext* aPresContext,
  1840                           nsStyleGradient* aGradient,
  1841                           const gfxSize& aBoxSize,
  1842                           gfxPoint* aLineStart,
  1843                           gfxPoint* aLineEnd,
  1844                           double* aRadiusX,
  1845                           double* aRadiusY)
  1847   if (aGradient->mBgPosX.GetUnit() == eStyleUnit_None) {
  1848     // Default line start point is the center of the box
  1849     *aLineStart = gfxPoint(aBoxSize.width/2, aBoxSize.height/2);
  1850   } else {
  1851     int32_t appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
  1852     *aLineStart = gfxPoint(
  1853       ConvertGradientValueToPixels(aGradient->mBgPosX, aBoxSize.width,
  1854                                    appUnitsPerPixel),
  1855       ConvertGradientValueToPixels(aGradient->mBgPosY, aBoxSize.height,
  1856                                    appUnitsPerPixel));
  1859   // Compute gradient shape: the x and y radii of an ellipse.
  1860   double radiusX, radiusY;
  1861   double leftDistance = Abs(aLineStart->x);
  1862   double rightDistance = Abs(aBoxSize.width - aLineStart->x);
  1863   double topDistance = Abs(aLineStart->y);
  1864   double bottomDistance = Abs(aBoxSize.height - aLineStart->y);
  1865   switch (aGradient->mSize) {
  1866   case NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE:
  1867     radiusX = std::min(leftDistance, rightDistance);
  1868     radiusY = std::min(topDistance, bottomDistance);
  1869     if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
  1870       radiusX = radiusY = std::min(radiusX, radiusY);
  1872     break;
  1873   case NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER: {
  1874     // Compute x and y distances to nearest corner
  1875     double offsetX = std::min(leftDistance, rightDistance);
  1876     double offsetY = std::min(topDistance, bottomDistance);
  1877     if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
  1878       radiusX = radiusY = NS_hypot(offsetX, offsetY);
  1879     } else {
  1880       // maintain aspect ratio
  1881       radiusX = offsetX*M_SQRT2;
  1882       radiusY = offsetY*M_SQRT2;
  1884     break;
  1886   case NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE:
  1887     radiusX = std::max(leftDistance, rightDistance);
  1888     radiusY = std::max(topDistance, bottomDistance);
  1889     if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
  1890       radiusX = radiusY = std::max(radiusX, radiusY);
  1892     break;
  1893   case NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER: {
  1894     // Compute x and y distances to nearest corner
  1895     double offsetX = std::max(leftDistance, rightDistance);
  1896     double offsetY = std::max(topDistance, bottomDistance);
  1897     if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_CIRCULAR) {
  1898       radiusX = radiusY = NS_hypot(offsetX, offsetY);
  1899     } else {
  1900       // maintain aspect ratio
  1901       radiusX = offsetX*M_SQRT2;
  1902       radiusY = offsetY*M_SQRT2;
  1904     break;
  1906   case NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE: {
  1907     int32_t appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
  1908     radiusX = ConvertGradientValueToPixels(aGradient->mRadiusX,
  1909                                            aBoxSize.width, appUnitsPerPixel);
  1910     radiusY = ConvertGradientValueToPixels(aGradient->mRadiusY,
  1911                                            aBoxSize.height, appUnitsPerPixel);
  1912     break;
  1914   default:
  1915     radiusX = radiusY = 0;
  1916     NS_ABORT_IF_FALSE(false, "unknown radial gradient sizing method");
  1918   *aRadiusX = radiusX;
  1919   *aRadiusY = radiusY;
  1921   double angle;
  1922   if (aGradient->mAngle.IsAngleValue()) {
  1923     angle = aGradient->mAngle.GetAngleValueInRadians();
  1924   } else {
  1925     // Default angle is 0deg
  1926     angle = 0.0;
  1929   // The gradient line end point is where the gradient line intersects
  1930   // the ellipse.
  1931   *aLineEnd = *aLineStart + gfxPoint(radiusX*cos(-angle), radiusY*sin(-angle));
  1934 // Returns aFrac*aC2 + (1 - aFrac)*C1. The interpolation is done
  1935 // in unpremultiplied space, which is what SVG gradients and cairo
  1936 // gradients expect.
  1937 static gfxRGBA
  1938 InterpolateColor(const gfxRGBA& aC1, const gfxRGBA& aC2, double aFrac)
  1940   double other = 1 - aFrac;
  1941   return gfxRGBA(aC2.r*aFrac + aC1.r*other,
  1942                  aC2.g*aFrac + aC1.g*other,
  1943                  aC2.b*aFrac + aC1.b*other,
  1944                  aC2.a*aFrac + aC1.a*other);
  1947 static nscoord
  1948 FindTileStart(nscoord aDirtyCoord, nscoord aTilePos, nscoord aTileDim)
  1950   NS_ASSERTION(aTileDim > 0, "Non-positive tile dimension");
  1951   double multiples = floor(double(aDirtyCoord - aTilePos)/aTileDim);
  1952   return NSToCoordRound(multiples*aTileDim + aTilePos);
  1955 void
  1956 nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
  1957                               nsRenderingContext& aRenderingContext,
  1958                               nsStyleGradient* aGradient,
  1959                               const nsRect& aDirtyRect,
  1960                               const nsRect& aDest,
  1961                               const nsRect& aFillArea,
  1962                               const CSSIntRect& aSrc,
  1963                               const nsSize& aIntrinsicSize)
  1965   PROFILER_LABEL("nsCSSRendering", "PaintGradient");
  1966   Telemetry::AutoTimer<Telemetry::GRADIENT_DURATION, Telemetry::Microsecond> gradientTimer;
  1967   if (aDest.IsEmpty() || aFillArea.IsEmpty()) {
  1968     return;
  1971   gfxContext *ctx = aRenderingContext.ThebesContext();
  1972   nscoord appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
  1973   gfxSize srcSize = gfxSize(gfxFloat(aIntrinsicSize.width)/appUnitsPerDevPixel,
  1974                             gfxFloat(aIntrinsicSize.height)/appUnitsPerDevPixel);
  1976   bool cellContainsFill = aDest.Contains(aFillArea);
  1978   // Compute "gradient line" start and end relative to the intrinsic size of
  1979   // the gradient.
  1980   gfxPoint lineStart, lineEnd;
  1981   double radiusX = 0, radiusY = 0; // for radial gradients only
  1982   if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
  1983     ComputeLinearGradientLine(aPresContext, aGradient, srcSize,
  1984                               &lineStart, &lineEnd);
  1985   } else {
  1986     ComputeRadialGradientLine(aPresContext, aGradient, srcSize,
  1987                               &lineStart, &lineEnd, &radiusX, &radiusY);
  1989   gfxFloat lineLength = NS_hypot(lineEnd.x - lineStart.x,
  1990                                   lineEnd.y - lineStart.y);
  1992   NS_ABORT_IF_FALSE(aGradient->mStops.Length() >= 2,
  1993                     "The parser should reject gradients with less than two stops");
  1995   // Build color stop array and compute stop positions
  1996   nsTArray<ColorStop> stops;
  1997   // If there is a run of stops before stop i that did not have specified
  1998   // positions, then this is the index of the first stop in that run, otherwise
  1999   // it's -1.
  2000   int32_t firstUnsetPosition = -1;
  2001   for (uint32_t i = 0; i < aGradient->mStops.Length(); ++i) {
  2002     const nsStyleGradientStop& stop = aGradient->mStops[i];
  2003     double position;
  2004     switch (stop.mLocation.GetUnit()) {
  2005     case eStyleUnit_None:
  2006       if (i == 0) {
  2007         // First stop defaults to position 0.0
  2008         position = 0.0;
  2009       } else if (i == aGradient->mStops.Length() - 1) {
  2010         // Last stop defaults to position 1.0
  2011         position = 1.0;
  2012       } else {
  2013         // Other stops with no specified position get their position assigned
  2014         // later by interpolation, see below.
  2015         // Remeber where the run of stops with no specified position starts,
  2016         // if it starts here.
  2017         if (firstUnsetPosition < 0) {
  2018           firstUnsetPosition = i;
  2020         stops.AppendElement(ColorStop(0, stop.mColor));
  2021         continue;
  2023       break;
  2024     case eStyleUnit_Percent:
  2025       position = stop.mLocation.GetPercentValue();
  2026       break;
  2027     case eStyleUnit_Coord:
  2028       position = lineLength < 1e-6 ? 0.0 :
  2029           stop.mLocation.GetCoordValue() / appUnitsPerDevPixel / lineLength;
  2030       break;
  2031     case eStyleUnit_Calc:
  2032       nsStyleCoord::Calc *calc;
  2033       calc = stop.mLocation.GetCalcValue();
  2034       position = calc->mPercent +
  2035           ((lineLength < 1e-6) ? 0.0 :
  2036           (NSAppUnitsToFloatPixels(calc->mLength, appUnitsPerDevPixel) / lineLength));
  2037       break;
  2038     default:
  2039       NS_ABORT_IF_FALSE(false, "Unknown stop position type");
  2042     if (i > 0) {
  2043       // Prevent decreasing stop positions by advancing this position
  2044       // to the previous stop position, if necessary
  2045       position = std::max(position, stops[i - 1].mPosition);
  2047     stops.AppendElement(ColorStop(position, stop.mColor));
  2048     if (firstUnsetPosition > 0) {
  2049       // Interpolate positions for all stops that didn't have a specified position
  2050       double p = stops[firstUnsetPosition - 1].mPosition;
  2051       double d = (stops[i].mPosition - p)/(i - firstUnsetPosition + 1);
  2052       for (uint32_t j = firstUnsetPosition; j < i; ++j) {
  2053         p += d;
  2054         stops[j].mPosition = p;
  2056       firstUnsetPosition = -1;
  2060   // Eliminate negative-position stops if the gradient is radial.
  2061   double firstStop = stops[0].mPosition;
  2062   if (aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR && firstStop < 0.0) {
  2063     if (aGradient->mRepeating) {
  2064       // Choose an instance of the repeated pattern that gives us all positive
  2065       // stop-offsets.
  2066       double lastStop = stops[stops.Length() - 1].mPosition;
  2067       double stopDelta = lastStop - firstStop;
  2068       // If all the stops are in approximately the same place then logic below
  2069       // will kick in that makes us draw just the last stop color, so don't
  2070       // try to do anything in that case. We certainly need to avoid
  2071       // dividing by zero.
  2072       if (stopDelta >= 1e-6) {
  2073         double instanceCount = ceil(-firstStop/stopDelta);
  2074         // Advance stops by instanceCount multiples of the period of the
  2075         // repeating gradient.
  2076         double offset = instanceCount*stopDelta;
  2077         for (uint32_t i = 0; i < stops.Length(); i++) {
  2078           stops[i].mPosition += offset;
  2081     } else {
  2082       // Move negative-position stops to position 0.0. We may also need
  2083       // to set the color of the stop to the color the gradient should have
  2084       // at the center of the ellipse.
  2085       for (uint32_t i = 0; i < stops.Length(); i++) {
  2086         double pos = stops[i].mPosition;
  2087         if (pos < 0.0) {
  2088           stops[i].mPosition = 0.0;
  2089           // If this is the last stop, we don't need to adjust the color,
  2090           // it will fill the entire area.
  2091           if (i < stops.Length() - 1) {
  2092             double nextPos = stops[i + 1].mPosition;
  2093             // If nextPos is approximately equal to pos, then we don't
  2094             // need to adjust the color of this stop because it's
  2095             // not going to be displayed.
  2096             // If nextPos is negative, we don't need to adjust the color of
  2097             // this stop since it's not going to be displayed because
  2098             // nextPos will also be moved to 0.0.
  2099             if (nextPos >= 0.0 && nextPos - pos >= 1e-6) {
  2100               // Compute how far the new position 0.0 is along the interval
  2101               // between pos and nextPos.
  2102               // XXX Color interpolation (in cairo, too) should use the
  2103               // CSS 'color-interpolation' property!
  2104               double frac = (0.0 - pos)/(nextPos - pos);
  2105               stops[i].mColor =
  2106                 InterpolateColor(stops[i].mColor, stops[i + 1].mColor, frac);
  2112     firstStop = stops[0].mPosition;
  2113     NS_ABORT_IF_FALSE(firstStop >= 0.0, "Failed to fix stop offsets");
  2116   if (aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR && !aGradient->mRepeating) {
  2117     // Direct2D can only handle a particular class of radial gradients because
  2118     // of the way the it specifies gradients. Setting firstStop to 0, when we
  2119     // can, will help us stay on the fast path. Currently we don't do this
  2120     // for repeating gradients but we could by adjusting the stop collection
  2121     // to start at 0
  2122     firstStop = 0;
  2125   double lastStop = stops[stops.Length() - 1].mPosition;
  2126   // Cairo gradients must have stop positions in the range [0, 1]. So,
  2127   // stop positions will be normalized below by subtracting firstStop and then
  2128   // multiplying by stopScale.
  2129   double stopScale;
  2130   double stopOrigin = firstStop;
  2131   double stopEnd = lastStop;
  2132   double stopDelta = lastStop - firstStop;
  2133   bool zeroRadius = aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR &&
  2134                       (radiusX < 1e-6 || radiusY < 1e-6);
  2135   if (stopDelta < 1e-6 || lineLength < 1e-6 || zeroRadius) {
  2136     // Stops are all at the same place. Map all stops to 0.0.
  2137     // For repeating radial gradients, or for any radial gradients with
  2138     // a zero radius, we need to fill with the last stop color, so just set
  2139     // both radii to 0.
  2140     if (aGradient->mRepeating || zeroRadius) {
  2141       radiusX = radiusY = 0.0;
  2143     stopDelta = 0.0;
  2144     lastStop = firstStop;
  2147   // Don't normalize non-repeating or degenerate gradients below 0..1
  2148   // This keeps the gradient line as large as the box and doesn't
  2149   // lets us avoiding having to get padding correct for stops
  2150   // at 0 and 1
  2151   if (!aGradient->mRepeating || stopDelta == 0.0) {
  2152     stopOrigin = std::min(stopOrigin, 0.0);
  2153     stopEnd = std::max(stopEnd, 1.0);
  2155   stopScale = 1.0/(stopEnd - stopOrigin);
  2157   // Create the gradient pattern.
  2158   nsRefPtr<gfxPattern> gradientPattern;
  2159   bool forceRepeatToCoverTiles = false;
  2160   gfxMatrix matrix;
  2161   if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
  2162     // Compute the actual gradient line ends we need to pass to cairo after
  2163     // stops have been normalized.
  2164     gfxPoint gradientStart = lineStart + (lineEnd - lineStart)*stopOrigin;
  2165     gfxPoint gradientEnd = lineStart + (lineEnd - lineStart)*stopEnd;
  2166     gfxPoint gradientStopStart = lineStart + (lineEnd - lineStart)*firstStop;
  2167     gfxPoint gradientStopEnd = lineStart + (lineEnd - lineStart)*lastStop;
  2169     if (stopDelta == 0.0) {
  2170       // Stops are all at the same place. For repeating gradients, this will
  2171       // just paint the last stop color. We don't need to do anything.
  2172       // For non-repeating gradients, this should render as two colors, one
  2173       // on each "side" of the gradient line segment, which is a point. All
  2174       // our stops will be at 0.0; we just need to set the direction vector
  2175       // correctly.
  2176       gradientEnd = gradientStart + (lineEnd - lineStart);
  2177       gradientStopEnd = gradientStopStart + (lineEnd - lineStart);
  2180     gradientPattern = new gfxPattern(gradientStart.x, gradientStart.y,
  2181                                       gradientEnd.x, gradientEnd.y);
  2183     // When the gradient line is parallel to the x axis from the left edge
  2184     // to the right edge of a tile, then we can repeat by just repeating the
  2185     // gradient.
  2186     if (!cellContainsFill &&
  2187         ((gradientStopStart.y == gradientStopEnd.y && gradientStopStart.x == 0 &&
  2188           gradientStopEnd.x == srcSize.width) ||
  2189           (gradientStopStart.x == gradientStopEnd.x && gradientStopStart.y == 0 &&
  2190           gradientStopEnd.y == srcSize.height))) {
  2191       forceRepeatToCoverTiles = true;
  2193   } else {
  2194     NS_ASSERTION(firstStop >= 0.0,
  2195                   "Negative stops not allowed for radial gradients");
  2197     // To form an ellipse, we'll stretch a circle vertically, if necessary.
  2198     // So our radii are based on radiusX.
  2199     double innerRadius = radiusX*stopOrigin;
  2200     double outerRadius = radiusX*stopEnd;
  2201     if (stopDelta == 0.0) {
  2202       // Stops are all at the same place.  See above (except we now have
  2203       // the inside vs. outside of an ellipse).
  2204       outerRadius = innerRadius + 1;
  2206     gradientPattern = new gfxPattern(lineStart.x, lineStart.y, innerRadius,
  2207                                      lineStart.x, lineStart.y, outerRadius);
  2208     if (radiusX != radiusY) {
  2209       // Stretch the circles into ellipses vertically by setting a transform
  2210       // in the pattern.
  2211       // Recall that this is the transform from user space to pattern space.
  2212       // So to stretch the ellipse by factor of P vertically, we scale
  2213       // user coordinates by 1/P.
  2214       matrix.Translate(lineStart);
  2215       matrix.Scale(1.0, radiusX/radiusY);
  2216       matrix.Translate(-lineStart);
  2219   // Use a pattern transform to take account of source and dest rects
  2220   matrix.Translate(gfxPoint(aPresContext->CSSPixelsToDevPixels(aSrc.x),
  2221                             aPresContext->CSSPixelsToDevPixels(aSrc.y)));
  2222   matrix.Scale(gfxFloat(aPresContext->CSSPixelsToAppUnits(aSrc.width))/aDest.width,
  2223                gfxFloat(aPresContext->CSSPixelsToAppUnits(aSrc.height))/aDest.height);
  2224   gradientPattern->SetMatrix(matrix);
  2226   if (gradientPattern->CairoStatus())
  2227     return;
  2229   if (stopDelta == 0.0) {
  2230     // Non-repeating gradient with all stops in same place -> just add
  2231     // first stop and last stop, both at position 0.
  2232     // Repeating gradient with all stops in the same place, or radial
  2233     // gradient with radius of 0 -> just paint the last stop color.
  2234     // We use firstStop offset to keep |stops| with same units (will later normalize to 0).
  2235     gfxRGBA firstColor(stops[0].mColor);
  2236     gfxRGBA lastColor(stops.LastElement().mColor);
  2237     stops.Clear();
  2239     if (!aGradient->mRepeating && !zeroRadius) {
  2240       stops.AppendElement(ColorStop(firstStop, firstColor));
  2242     stops.AppendElement(ColorStop(firstStop, lastColor));
  2245   bool isRepeat = aGradient->mRepeating || forceRepeatToCoverTiles;
  2247   // Now set normalized color stops in pattern.
  2248   if (!ctx->IsCairo()) {
  2249     // Offscreen gradient surface cache (not a tile):
  2250     // On some backends (e.g. D2D), the GradientStops object holds an offscreen surface
  2251     // which is a lookup table used to evaluate the gradient. This surface can use
  2252     // much memory (ram and/or GPU ram) and can be expensive to create. So we cache it.
  2253     // The cache key correlates 1:1 with the arguments for CreateGradientStops (also the implied backend type)
  2254     // Note that GradientStop is a simple struct with a stop value (while GradientStops has the surface).
  2255     nsTArray<gfx::GradientStop> rawStops(stops.Length());
  2256     rawStops.SetLength(stops.Length());
  2257     for(uint32_t i = 0; i < stops.Length(); i++) {
  2258       rawStops[i].color = gfx::Color(stops[i].mColor.r, stops[i].mColor.g, stops[i].mColor.b, stops[i].mColor.a);
  2259       rawStops[i].offset = stopScale * (stops[i].mPosition - stopOrigin);
  2261     mozilla::RefPtr<mozilla::gfx::GradientStops> gs =
  2262       gfxGradientCache::GetOrCreateGradientStops(ctx->GetDrawTarget(),
  2263                                                  rawStops,
  2264                                                  isRepeat ? gfx::ExtendMode::REPEAT : gfx::ExtendMode::CLAMP);
  2265     gradientPattern->SetColorStops(gs);
  2266   } else {
  2267     for (uint32_t i = 0; i < stops.Length(); i++) {
  2268       double pos = stopScale*(stops[i].mPosition - stopOrigin);
  2269       gradientPattern->AddColorStop(pos, stops[i].mColor);
  2271     // Set repeat mode. Default cairo extend mode is PAD.
  2272     if (isRepeat) {
  2273       gradientPattern->SetExtend(gfxPattern::EXTEND_REPEAT);
  2277   // Paint gradient tiles. This isn't terribly efficient, but doing it this
  2278   // way is simple and sure to get pixel-snapping right. We could speed things
  2279   // up by drawing tiles into temporary surfaces and copying those to the
  2280   // destination, but after pixel-snapping tiles may not all be the same size.
  2281   nsRect dirty;
  2282   if (!dirty.IntersectRect(aDirtyRect, aFillArea))
  2283     return;
  2285   gfxRect areaToFill =
  2286     nsLayoutUtils::RectToGfxRect(aFillArea, appUnitsPerDevPixel);
  2287   gfxMatrix ctm = ctx->CurrentMatrix();
  2288   bool isCTMPreservingAxisAlignedRectangles = ctm.PreservesAxisAlignedRectangles();
  2290   // xStart/yStart are the top-left corner of the top-left tile.
  2291   nscoord xStart = FindTileStart(dirty.x, aDest.x, aDest.width);
  2292   nscoord yStart = FindTileStart(dirty.y, aDest.y, aDest.height);
  2293   nscoord xEnd = forceRepeatToCoverTiles ? xStart + aDest.width : dirty.XMost();
  2294   nscoord yEnd = forceRepeatToCoverTiles ? yStart + aDest.height : dirty.YMost();
  2296   // x and y are the top-left corner of the tile to draw
  2297   for (nscoord y = yStart; y < yEnd; y += aDest.height) {
  2298     for (nscoord x = xStart; x < xEnd; x += aDest.width) {
  2299       // The coordinates of the tile
  2300       gfxRect tileRect = nsLayoutUtils::RectToGfxRect(
  2301                       nsRect(x, y, aDest.width, aDest.height),
  2302                       appUnitsPerDevPixel);
  2303       // The actual area to fill with this tile is the intersection of this
  2304       // tile with the overall area we're supposed to be filling
  2305       gfxRect fillRect =
  2306         forceRepeatToCoverTiles ? areaToFill : tileRect.Intersect(areaToFill);
  2307       // Try snapping the fill rect. Snap its top-left and bottom-right
  2308       // independently to preserve the orientation.
  2309       gfxPoint snappedFillRectTopLeft = fillRect.TopLeft();
  2310       gfxPoint snappedFillRectTopRight = fillRect.TopRight();
  2311       gfxPoint snappedFillRectBottomRight = fillRect.BottomRight();
  2312       // Snap three points instead of just two to ensure we choose the
  2313       // correct orientation if there's a reflection.
  2314       if (isCTMPreservingAxisAlignedRectangles &&
  2315           ctx->UserToDevicePixelSnapped(snappedFillRectTopLeft, true) &&
  2316           ctx->UserToDevicePixelSnapped(snappedFillRectBottomRight, true) &&
  2317           ctx->UserToDevicePixelSnapped(snappedFillRectTopRight, true)) {
  2318         if (snappedFillRectTopLeft.x == snappedFillRectBottomRight.x ||
  2319             snappedFillRectTopLeft.y == snappedFillRectBottomRight.y) {
  2320           // Nothing to draw; avoid scaling by zero and other weirdness that
  2321           // could put the context in an error state.
  2322           continue;
  2324         // Set the context's transform to the transform that maps fillRect to
  2325         // snappedFillRect. The part of the gradient that was going to
  2326         // exactly fill fillRect will fill snappedFillRect instead.
  2327         gfxMatrix transform = gfxUtils::TransformRectToRect(fillRect,
  2328             snappedFillRectTopLeft, snappedFillRectTopRight,
  2329             snappedFillRectBottomRight);
  2330         ctx->SetMatrix(transform);
  2332       ctx->NewPath();
  2333       ctx->Rectangle(fillRect);
  2334       ctx->Translate(tileRect.TopLeft());
  2335       ctx->SetPattern(gradientPattern);
  2336       ctx->Fill();
  2337       ctx->SetMatrix(ctm);
  2342 void
  2343 nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
  2344                                       nsRenderingContext& aRenderingContext,
  2345                                       nsIFrame* aForFrame,
  2346                                       const nsRect& aDirtyRect,
  2347                                       const nsRect& aBorderArea,
  2348                                       nsStyleContext* aBackgroundSC,
  2349                                       const nsStyleBorder& aBorder,
  2350                                       uint32_t aFlags,
  2351                                       nsRect* aBGClipRect,
  2352                                       int32_t aLayer)
  2354   NS_PRECONDITION(aForFrame,
  2355                   "Frame is expected to be provided to PaintBackground");
  2357   // Check to see if we have an appearance defined.  If so, we let the theme
  2358   // renderer draw the background and bail out.
  2359   // XXXzw this ignores aBGClipRect.
  2360   const nsStyleDisplay* displayData = aForFrame->StyleDisplay();
  2361   if (displayData->mAppearance) {
  2362     nsITheme *theme = aPresContext->GetTheme();
  2363     if (theme && theme->ThemeSupportsWidget(aPresContext, aForFrame,
  2364                                             displayData->mAppearance)) {
  2365       nsRect drawing(aBorderArea);
  2366       theme->GetWidgetOverflow(aPresContext->DeviceContext(),
  2367                                aForFrame, displayData->mAppearance, &drawing);
  2368       drawing.IntersectRect(drawing, aDirtyRect);
  2369       theme->DrawWidgetBackground(&aRenderingContext, aForFrame,
  2370                                   displayData->mAppearance, aBorderArea,
  2371                                   drawing);
  2372       return;
  2376   // For canvas frames (in the CSS sense) we draw the background color using
  2377   // a solid color item that gets added in nsLayoutUtils::PaintFrame,
  2378   // or nsSubDocumentFrame::BuildDisplayList (bug 488242). (The solid
  2379   // color may be moved into nsDisplayCanvasBackground by
  2380   // nsPresShell::AddCanvasBackgroundColorItem, and painted by
  2381   // nsDisplayCanvasBackground directly.) Either way we don't need to
  2382   // paint the background color here.
  2383   bool isCanvasFrame = IsCanvasFrame(aForFrame);
  2385   // Determine whether we are drawing background images and/or
  2386   // background colors.
  2387   bool drawBackgroundImage;
  2388   bool drawBackgroundColor;
  2390   nscolor bgColor = DetermineBackgroundColor(aPresContext,
  2391                                              aBackgroundSC,
  2392                                              aForFrame,
  2393                                              drawBackgroundImage,
  2394                                              drawBackgroundColor);
  2396   // If we're drawing a specific layer, we don't want to draw the
  2397   // background color.
  2398   const nsStyleBackground *bg = aBackgroundSC->StyleBackground();
  2399   if (drawBackgroundColor && aLayer >= 0) {
  2400     drawBackgroundColor = false;
  2403   // At this point, drawBackgroundImage and drawBackgroundColor are
  2404   // true if and only if we are actually supposed to paint an image or
  2405   // color into aDirtyRect, respectively.
  2406   if (!drawBackgroundImage && !drawBackgroundColor)
  2407     return;
  2409   // Compute the outermost boundary of the area that might be painted.
  2410   gfxContext *ctx = aRenderingContext.ThebesContext();
  2411   nscoord appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
  2413   // Same coordinate space as aBorderArea & aBGClipRect
  2414   gfxCornerSizes bgRadii;
  2415   bool haveRoundedCorners;
  2417     nscoord radii[8];
  2418     nsSize frameSize = aForFrame->GetSize();
  2419     if (&aBorder == aForFrame->StyleBorder() &&
  2420         frameSize == aBorderArea.Size()) {
  2421       haveRoundedCorners = aForFrame->GetBorderRadii(radii);
  2422     } else {
  2423       haveRoundedCorners = nsIFrame::ComputeBorderRadii(aBorder.mBorderRadius,
  2424                                    frameSize, aBorderArea.Size(),
  2425                                    aForFrame->GetSkipSides(), radii);
  2427     if (haveRoundedCorners)
  2428       ComputePixelRadii(radii, appUnitsPerPixel, &bgRadii);
  2431   // The 'bgClipArea' (used only by the image tiling logic, far below)
  2432   // is the caller-provided aBGClipRect if any, or else the area
  2433   // determined by the value of 'background-clip' in
  2434   // SetupCurrentBackgroundClip.  (Arguably it should be the
  2435   // intersection, but that breaks the table painter -- in particular,
  2436   // taking the intersection breaks reftests/bugs/403249-1[ab].)
  2437   BackgroundClipState clipState;
  2438   uint8_t currentBackgroundClip;
  2439   bool isSolidBorder;
  2440   if (aBGClipRect) {
  2441     clipState.mBGClipArea = *aBGClipRect;
  2442     clipState.mCustomClip = true;
  2443     SetupDirtyRects(clipState.mBGClipArea, aDirtyRect, appUnitsPerPixel,
  2444                     &clipState.mDirtyRect, &clipState.mDirtyRectGfx);
  2445   } else {
  2446     // The background is rendered over the 'background-clip' area,
  2447     // which is normally equal to the border area but may be reduced
  2448     // to the padding area by CSS.  Also, if the border is solid, we
  2449     // don't need to draw outside the padding area.  In either case,
  2450     // if the borders are rounded, make sure we use the same inner
  2451     // radii as the border code will.
  2452     // The background-color is drawn based on the bottom
  2453     // background-clip.
  2454     currentBackgroundClip = bg->BottomLayer().mClip;
  2455     isSolidBorder =
  2456       (aFlags & PAINTBG_WILL_PAINT_BORDER) && IsOpaqueBorder(aBorder);
  2457     if (isSolidBorder && currentBackgroundClip == NS_STYLE_BG_CLIP_BORDER) {
  2458       // If we have rounded corners, we need to inflate the background
  2459       // drawing area a bit to avoid seams between the border and
  2460       // background.
  2461       currentBackgroundClip = haveRoundedCorners ?
  2462         NS_STYLE_BG_CLIP_MOZ_ALMOST_PADDING : NS_STYLE_BG_CLIP_PADDING;
  2465     GetBackgroundClip(ctx, currentBackgroundClip, bg->BottomLayer().mAttachment,
  2466                       aForFrame, aBorderArea,
  2467                       aDirtyRect, haveRoundedCorners, bgRadii, appUnitsPerPixel,
  2468                       &clipState);
  2471   // If we might be using a background color, go ahead and set it now.
  2472   if (drawBackgroundColor && !isCanvasFrame)
  2473     ctx->SetColor(gfxRGBA(bgColor));
  2475   gfxContextAutoSaveRestore autoSR;
  2477   // If there is no background image, draw a color.  (If there is
  2478   // neither a background image nor a color, we wouldn't have gotten
  2479   // this far.)
  2480   if (!drawBackgroundImage) {
  2481     if (!isCanvasFrame) {
  2482       DrawBackgroundColor(clipState, ctx, haveRoundedCorners, appUnitsPerPixel);
  2484     return;
  2487   if (bg->mImageCount < 1) {
  2488     // Return if there are no background layers, all work from this point
  2489     // onwards happens iteratively on these.
  2490     return;
  2493   // Validate the layer range before we start iterating.
  2494   int32_t startLayer = aLayer;
  2495   int32_t nLayers = 1;
  2496   if (startLayer < 0) {
  2497     startLayer = (int32_t)bg->mImageCount - 1;
  2498     nLayers = bg->mImageCount;
  2501   // Ensure we get invalidated for loads of the image.  We need to do
  2502   // this here because this might be the only code that knows about the
  2503   // association of the style data with the frame.
  2504   if (aBackgroundSC != aForFrame->StyleContext()) {
  2505     NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT_WITH_RANGE(i, bg, startLayer, nLayers) {
  2506       aForFrame->AssociateImage(bg->mLayers[i].mImage, aPresContext);
  2510   // The background color is rendered over the entire dirty area,
  2511   // even if the image isn't.
  2512   if (drawBackgroundColor && !isCanvasFrame) {
  2513     DrawBackgroundColor(clipState, ctx, haveRoundedCorners, appUnitsPerPixel);
  2516   if (drawBackgroundImage) {
  2517     bool clipSet = false;
  2518     NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT_WITH_RANGE(i, bg, bg->mImageCount - 1,
  2519                                                               nLayers + (bg->mImageCount -
  2520                                                                          startLayer - 1)) {
  2521       const nsStyleBackground::Layer &layer = bg->mLayers[i];
  2522       if (!aBGClipRect) {
  2523         uint8_t newBackgroundClip = layer.mClip;
  2524         if (isSolidBorder && newBackgroundClip == NS_STYLE_BG_CLIP_BORDER) {
  2525           newBackgroundClip = haveRoundedCorners ?
  2526             NS_STYLE_BG_CLIP_MOZ_ALMOST_PADDING : NS_STYLE_BG_CLIP_PADDING;
  2528         if (currentBackgroundClip != newBackgroundClip || !clipSet) {
  2529           currentBackgroundClip = newBackgroundClip;
  2530           // If clipSet is false that means this is the bottom layer and we
  2531           // already called GetBackgroundClip above and it stored its results
  2532           // in clipState.
  2533           if (clipSet) {
  2534             GetBackgroundClip(ctx, currentBackgroundClip, layer.mAttachment, aForFrame,
  2535                               aBorderArea, aDirtyRect, haveRoundedCorners,
  2536                               bgRadii, appUnitsPerPixel, &clipState);
  2538           SetupBackgroundClip(clipState, ctx, haveRoundedCorners,
  2539                               appUnitsPerPixel, &autoSR);
  2540           clipSet = true;
  2543       if ((aLayer < 0 || i == (uint32_t)startLayer) &&
  2544           !clipState.mDirtyRectGfx.IsEmpty()) {
  2545         nsBackgroundLayerState state = PrepareBackgroundLayer(aPresContext, aForFrame,
  2546             aFlags, aBorderArea, clipState.mBGClipArea, *bg, layer);
  2547         if (!state.mFillArea.IsEmpty()) {
  2548           if (state.mCompositingOp != gfxContext::OPERATOR_OVER) {
  2549             NS_ASSERTION(ctx->CurrentOperator() == gfxContext::OPERATOR_OVER,
  2550                          "It is assumed the initial operator is OPERATOR_OVER, when it is restored later");
  2551             ctx->SetOperator(state.mCompositingOp);
  2553           state.mImageRenderer.DrawBackground(aPresContext, aRenderingContext,
  2554                                               state.mDestArea, state.mFillArea,
  2555                                               state.mAnchor + aBorderArea.TopLeft(),
  2556                                               clipState.mDirtyRect);
  2557           if (state.mCompositingOp != gfxContext::OPERATOR_OVER) {
  2558             ctx->SetOperator(gfxContext::OPERATOR_OVER);
  2566 void
  2567 nsCSSRendering::PaintBackgroundColorWithSC(nsPresContext* aPresContext,
  2568                                            nsRenderingContext& aRenderingContext,
  2569                                            nsIFrame* aForFrame,
  2570                                            const nsRect& aDirtyRect,
  2571                                            const nsRect& aBorderArea,
  2572                                            nsStyleContext* aBackgroundSC,
  2573                                            const nsStyleBorder& aBorder,
  2574                                            uint32_t aFlags)
  2576   NS_PRECONDITION(aForFrame,
  2577                   "Frame is expected to be provided to PaintBackground");
  2579   // Check to see if we have an appearance defined.  If so, we let the theme
  2580   // renderer draw the background and bail out.
  2581   const nsStyleDisplay* displayData = aForFrame->StyleDisplay();
  2582   if (displayData->mAppearance) {
  2583     nsITheme *theme = aPresContext->GetTheme();
  2584     if (theme && theme->ThemeSupportsWidget(aPresContext, aForFrame,
  2585                                             displayData->mAppearance)) {
  2586       NS_ERROR("Shouldn't be trying to paint a background color if we are themed!");
  2587       return;
  2591   NS_ASSERTION(!IsCanvasFrame(aForFrame), "Should not be trying to paint a background color for canvas frames!");
  2593   // Determine whether we are drawing background images and/or
  2594   // background colors.
  2595   bool drawBackgroundImage;
  2596   bool drawBackgroundColor;
  2597   nscolor bgColor = DetermineBackgroundColor(aPresContext,
  2598                                              aBackgroundSC,
  2599                                              aForFrame,
  2600                                              drawBackgroundImage,
  2601                                              drawBackgroundColor);
  2603   NS_ASSERTION(drawBackgroundImage || drawBackgroundColor,
  2604                "Should not be trying to paint a background if we don't have one");
  2605   if (!drawBackgroundColor) {
  2606     return;
  2609   // Compute the outermost boundary of the area that might be painted.
  2610   gfxContext *ctx = aRenderingContext.ThebesContext();
  2611   nscoord appUnitsPerPixel = aPresContext->AppUnitsPerDevPixel();
  2613   // Same coordinate space as aBorderArea
  2614   gfxCornerSizes bgRadii;
  2615   bool haveRoundedCorners;
  2617     nscoord radii[8];
  2618     nsSize frameSize = aForFrame->GetSize();
  2619     if (&aBorder == aForFrame->StyleBorder() &&
  2620         frameSize == aBorderArea.Size()) {
  2621       haveRoundedCorners = aForFrame->GetBorderRadii(radii);
  2622     } else {
  2623       haveRoundedCorners = nsIFrame::ComputeBorderRadii(aBorder.mBorderRadius,
  2624                                    frameSize, aBorderArea.Size(),
  2625                                    aForFrame->GetSkipSides(), radii);
  2627     if (haveRoundedCorners)
  2628       ComputePixelRadii(radii, appUnitsPerPixel, &bgRadii);
  2631   // The background is rendered over the 'background-clip' area,
  2632   // which is normally equal to the border area but may be reduced
  2633   // to the padding area by CSS.  Also, if the border is solid, we
  2634   // don't need to draw outside the padding area.  In either case,
  2635   // if the borders are rounded, make sure we use the same inner
  2636   // radii as the border code will.
  2637   // The background-color is drawn based on the bottom
  2638   // background-clip.
  2639   const nsStyleBackground *bg = aBackgroundSC->StyleBackground();
  2640   uint8_t currentBackgroundClip = bg->BottomLayer().mClip;
  2641   bool isSolidBorder =
  2642     (aFlags & PAINTBG_WILL_PAINT_BORDER) && IsOpaqueBorder(aBorder);
  2643   if (isSolidBorder && currentBackgroundClip == NS_STYLE_BG_CLIP_BORDER) {
  2644     // If we have rounded corners, we need to inflate the background
  2645     // drawing area a bit to avoid seams between the border and
  2646     // background.
  2647     currentBackgroundClip = haveRoundedCorners ?
  2648       NS_STYLE_BG_CLIP_MOZ_ALMOST_PADDING : NS_STYLE_BG_CLIP_PADDING;
  2651   BackgroundClipState clipState;
  2652   GetBackgroundClip(ctx, currentBackgroundClip, bg->BottomLayer().mAttachment,
  2653                     aForFrame, aBorderArea,
  2654                     aDirtyRect, haveRoundedCorners, bgRadii, appUnitsPerPixel,
  2655                     &clipState);
  2657   ctx->SetColor(gfxRGBA(bgColor));
  2659   gfxContextAutoSaveRestore autoSR;
  2660   DrawBackgroundColor(clipState, ctx, haveRoundedCorners, appUnitsPerPixel);
  2663 static inline bool
  2664 IsTransformed(nsIFrame* aForFrame, nsIFrame* aTopFrame)
  2666   for (nsIFrame* f = aForFrame; f != aTopFrame; f = f->GetParent()) {
  2667     if (f->IsTransformed()) {
  2668       return true;
  2671   return false;
  2674 nsRect
  2675 nsCSSRendering::ComputeBackgroundPositioningArea(nsPresContext* aPresContext,
  2676                                                  nsIFrame* aForFrame,
  2677                                                  const nsRect& aBorderArea,
  2678                                                  const nsStyleBackground& aBackground,
  2679                                                  const nsStyleBackground::Layer& aLayer,
  2680                                                  nsIFrame** aAttachedToFrame)
  2682   // Compute background origin area relative to aBorderArea now as we may need
  2683   // it to compute the effective image size for a CSS gradient.
  2684   nsRect bgPositioningArea(0, 0, 0, 0);
  2686   nsIAtom* frameType = aForFrame->GetType();
  2687   nsIFrame* geometryFrame = aForFrame;
  2688   if (frameType == nsGkAtoms::inlineFrame) {
  2689     // XXXjwalden Strictly speaking this is not quite faithful to how
  2690     // background-break is supposed to interact with background-origin values,
  2691     // but it's a non-trivial amount of work to make it fully conformant, and
  2692     // until the specification is more finalized (and assuming background-break
  2693     // even makes the cut) it doesn't make sense to hammer out exact behavior.
  2694     switch (aBackground.mBackgroundInlinePolicy) {
  2695     case NS_STYLE_BG_INLINE_POLICY_EACH_BOX:
  2696       bgPositioningArea = nsRect(nsPoint(0,0), aBorderArea.Size());
  2697       break;
  2698     case NS_STYLE_BG_INLINE_POLICY_BOUNDING_BOX:
  2699       bgPositioningArea = gInlineBGData->GetBoundingRect(aForFrame);
  2700       break;
  2701     default:
  2702       NS_ERROR("Unknown background-inline-policy value!  "
  2703                "Please, teach me what to do.");
  2704     case NS_STYLE_BG_INLINE_POLICY_CONTINUOUS:
  2705       bgPositioningArea = gInlineBGData->GetContinuousRect(aForFrame);
  2706       break;
  2708   } else if (frameType == nsGkAtoms::canvasFrame) {
  2709     geometryFrame = aForFrame->GetFirstPrincipalChild();
  2710     // geometryFrame might be null if this canvas is a page created
  2711     // as an overflow container (e.g. the in-flow content has already
  2712     // finished and this page only displays the continuations of
  2713     // absolutely positioned content).
  2714     if (geometryFrame) {
  2715       bgPositioningArea = geometryFrame->GetRect();
  2717   } else if (frameType == nsGkAtoms::scrollFrame &&
  2718              NS_STYLE_BG_ATTACHMENT_LOCAL == aLayer.mAttachment) {
  2719     nsIScrollableFrame* scrollableFrame = do_QueryFrame(aForFrame);
  2720     bgPositioningArea = nsRect(
  2721       scrollableFrame->GetScrolledFrame()->GetPosition()
  2722         // For the dir=rtl case:
  2723         + scrollableFrame->GetScrollRange().TopLeft(),
  2724       scrollableFrame->GetScrolledRect().Size());
  2725     // The ScrolledRect’s size does not include the borders or scrollbars,
  2726     // reverse the handling of background-origin
  2727     // compared to the common case below.
  2728     if (aLayer.mOrigin == NS_STYLE_BG_ORIGIN_BORDER) {
  2729       nsMargin border = geometryFrame->GetUsedBorder();
  2730       geometryFrame->ApplySkipSides(border);
  2731       bgPositioningArea.Inflate(border);
  2732       bgPositioningArea.Inflate(scrollableFrame->GetActualScrollbarSizes());
  2733     } else if (aLayer.mOrigin != NS_STYLE_BG_ORIGIN_PADDING) {
  2734       nsMargin padding = geometryFrame->GetUsedPadding();
  2735       geometryFrame->ApplySkipSides(padding);
  2736       bgPositioningArea.Deflate(padding);
  2737       NS_ASSERTION(aLayer.mOrigin == NS_STYLE_BG_ORIGIN_CONTENT,
  2738                    "unknown background-origin value");
  2740     *aAttachedToFrame = aForFrame;
  2741     return bgPositioningArea;
  2742   } else {
  2743     bgPositioningArea = nsRect(nsPoint(0,0), aBorderArea.Size());
  2746   // Background images are tiled over the 'background-clip' area
  2747   // but the origin of the tiling is based on the 'background-origin' area
  2748   if (aLayer.mOrigin != NS_STYLE_BG_ORIGIN_BORDER && geometryFrame) {
  2749     nsMargin border = geometryFrame->GetUsedBorder();
  2750     if (aLayer.mOrigin != NS_STYLE_BG_ORIGIN_PADDING) {
  2751       border += geometryFrame->GetUsedPadding();
  2752       NS_ASSERTION(aLayer.mOrigin == NS_STYLE_BG_ORIGIN_CONTENT,
  2753                    "unknown background-origin value");
  2755     geometryFrame->ApplySkipSides(border);
  2756     bgPositioningArea.Deflate(border);
  2759   nsIFrame* attachedToFrame = aForFrame;
  2760   if (NS_STYLE_BG_ATTACHMENT_FIXED == aLayer.mAttachment) {
  2761     // If it's a fixed background attachment, then the image is placed
  2762     // relative to the viewport, which is the area of the root frame
  2763     // in a screen context or the page content frame in a print context.
  2764     attachedToFrame = aPresContext->PresShell()->FrameManager()->GetRootFrame();
  2765     NS_ASSERTION(attachedToFrame, "no root frame");
  2766     nsIFrame* pageContentFrame = nullptr;
  2767     if (aPresContext->IsPaginated()) {
  2768       pageContentFrame =
  2769         nsLayoutUtils::GetClosestFrameOfType(aForFrame, nsGkAtoms::pageContentFrame);
  2770       if (pageContentFrame) {
  2771         attachedToFrame = pageContentFrame;
  2773       // else this is an embedded shell and its root frame is what we want
  2776     // Set the background positioning area to the viewport's area
  2777     // (relative to aForFrame)
  2778     bgPositioningArea =
  2779       nsRect(-aForFrame->GetOffsetTo(attachedToFrame), attachedToFrame->GetSize());
  2781     if (!pageContentFrame) {
  2782       // Subtract the size of scrollbars.
  2783       nsIScrollableFrame* scrollableFrame =
  2784         aPresContext->PresShell()->GetRootScrollFrameAsScrollable();
  2785       if (scrollableFrame) {
  2786         nsMargin scrollbars = scrollableFrame->GetActualScrollbarSizes();
  2787         bgPositioningArea.Deflate(scrollbars);
  2791   *aAttachedToFrame = attachedToFrame;
  2793   return bgPositioningArea;
  2796 // Apply the CSS image sizing algorithm as it applies to background images.
  2797 // See http://www.w3.org/TR/css3-background/#the-background-size .
  2798 // aIntrinsicSize is the size that the background image 'would like to be'.
  2799 // It can be found by calling nsImageRenderer::ComputeIntrinsicSize.
  2800 static nsSize
  2801 ComputeDrawnSizeForBackground(const CSSSizeOrRatio& aIntrinsicSize,
  2802                               const nsSize& aBgPositioningArea,
  2803                               const nsStyleBackground::Size& aLayerSize)
  2805   // Size is dictated by cover or contain rules.
  2806   if (aLayerSize.mWidthType == nsStyleBackground::Size::eContain ||
  2807       aLayerSize.mWidthType == nsStyleBackground::Size::eCover) {
  2808     nsImageRenderer::FitType fitType =
  2809       aLayerSize.mWidthType == nsStyleBackground::Size::eCover
  2810         ? nsImageRenderer::COVER
  2811         : nsImageRenderer::CONTAIN;
  2812     return nsImageRenderer::ComputeConstrainedSize(aBgPositioningArea,
  2813                                                    aIntrinsicSize.mRatio,
  2814                                                    fitType);
  2817   // No cover/contain constraint, use default algorithm.
  2818   CSSSizeOrRatio specifiedSize;
  2819   if (aLayerSize.mWidthType == nsStyleBackground::Size::eLengthPercentage) {
  2820     specifiedSize.SetWidth(
  2821       aLayerSize.ResolveWidthLengthPercentage(aBgPositioningArea));
  2823   if (aLayerSize.mHeightType == nsStyleBackground::Size::eLengthPercentage) {
  2824     specifiedSize.SetHeight(
  2825       aLayerSize.ResolveHeightLengthPercentage(aBgPositioningArea));
  2828   return nsImageRenderer::ComputeConcreteSize(specifiedSize,
  2829                                               aIntrinsicSize,
  2830                                               aBgPositioningArea);
  2833 nsBackgroundLayerState
  2834 nsCSSRendering::PrepareBackgroundLayer(nsPresContext* aPresContext,
  2835                                        nsIFrame* aForFrame,
  2836                                        uint32_t aFlags,
  2837                                        const nsRect& aBorderArea,
  2838                                        const nsRect& aBGClipRect,
  2839                                        const nsStyleBackground& aBackground,
  2840                                        const nsStyleBackground::Layer& aLayer)
  2842   /*
  2843    * The background properties we need to keep in mind when drawing background
  2844    * layers are:
  2846    *   background-image
  2847    *   background-repeat
  2848    *   background-attachment
  2849    *   background-position
  2850    *   background-clip
  2851    *   background-origin
  2852    *   background-size
  2853    *   background-break (-moz-background-inline-policy)
  2854    *   background-blend-mode
  2856    * (background-color applies to the entire element and not to individual
  2857    * layers, so it is irrelevant to this method.)
  2859    * These properties have the following dependencies upon each other when
  2860    * determining rendering:
  2862    *   background-image
  2863    *     no dependencies
  2864    *   background-repeat
  2865    *     no dependencies
  2866    *   background-attachment
  2867    *     no dependencies
  2868    *   background-position
  2869    *     depends upon background-size (for the image's scaled size) and
  2870    *     background-break (for the background positioning area)
  2871    *   background-clip
  2872    *     no dependencies
  2873    *   background-origin
  2874    *     depends upon background-attachment (only in the case where that value
  2875    *     is 'fixed')
  2876    *   background-size
  2877    *     depends upon background-break (for the background positioning area for
  2878    *     resolving percentages), background-image (for the image's intrinsic
  2879    *     size), background-repeat (if that value is 'round'), and
  2880    *     background-origin (for the background painting area, when
  2881    *     background-repeat is 'round')
  2882    *   background-break
  2883    *     depends upon background-origin (specifying how the boxes making up the
  2884    *     background positioning area are determined)
  2886    * As a result of only-if dependencies we don't strictly do a topological
  2887    * sort of the above properties when processing, but it's pretty close to one:
  2889    *   background-clip (by caller)
  2890    *   background-image
  2891    *   background-break, background-origin
  2892    *   background-attachment (postfix for background-{origin,break} if 'fixed')
  2893    *   background-size
  2894    *   background-position
  2895    *   background-repeat
  2896    */
  2898   uint32_t irFlags = 0;
  2899   if (aFlags & nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES) {
  2900     irFlags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
  2902   if (aFlags & nsCSSRendering::PAINTBG_TO_WINDOW) {
  2903     irFlags |= nsImageRenderer::FLAG_PAINTING_TO_WINDOW;
  2906   nsBackgroundLayerState state(aForFrame, &aLayer.mImage, irFlags);
  2907   if (!state.mImageRenderer.PrepareImage()) {
  2908     // There's no image or it's not ready to be painted.
  2909     return state;
  2912   // The frame to which the background is attached
  2913   nsIFrame* attachedToFrame = aForFrame;
  2914   // Compute background origin area relative to aBorderArea now as we may need
  2915   // it to compute the effective image size for a CSS gradient.
  2916   nsRect bgPositioningArea =
  2917     ComputeBackgroundPositioningArea(aPresContext, aForFrame, aBorderArea,
  2918                                      aBackground, aLayer, &attachedToFrame);
  2920   // For background-attachment:fixed backgrounds, we'll limit the area
  2921   // where the background can be drawn to the viewport.
  2922   nsRect bgClipRect = aBGClipRect;
  2924   // Compute the anchor point.
  2925   //
  2926   // relative to aBorderArea.TopLeft() (which is where the top-left
  2927   // of aForFrame's border-box will be rendered)
  2928   nsPoint imageTopLeft;
  2929   if (NS_STYLE_BG_ATTACHMENT_FIXED == aLayer.mAttachment) {
  2930     if ((aFlags & nsCSSRendering::PAINTBG_TO_WINDOW) &&
  2931         !IsTransformed(aForFrame, attachedToFrame)) {
  2932       // Clip background-attachment:fixed backgrounds to the viewport, if we're
  2933       // painting to the screen and not transformed. This avoids triggering
  2934       // tiling in common cases, without affecting output since drawing is
  2935       // always clipped to the viewport when we draw to the screen. (But it's
  2936       // not a pure optimization since it can affect the values of pixels at the
  2937       // edge of the viewport --- whether they're sampled from a putative "next
  2938       // tile" or not.)
  2939       bgClipRect.IntersectRect(bgClipRect, bgPositioningArea + aBorderArea.TopLeft());
  2943   // Scale the image as specified for background-size and as required for
  2944   // proper background positioning when background-position is defined with
  2945   // percentages.
  2946   CSSSizeOrRatio intrinsicSize = state.mImageRenderer.ComputeIntrinsicSize();
  2947   nsSize bgPositionSize = bgPositioningArea.Size();
  2948   nsSize imageSize = ComputeDrawnSizeForBackground(intrinsicSize,
  2949                                                    bgPositionSize,
  2950                                                    aLayer.mSize);
  2951   if (imageSize.width <= 0 || imageSize.height <= 0)
  2952     return state;
  2954   state.mImageRenderer.SetPreferredSize(intrinsicSize,
  2955                                         imageSize);
  2957   // Compute the position of the background now that the background's size is
  2958   // determined.
  2959   ComputeBackgroundAnchorPoint(aLayer, bgPositionSize, imageSize,
  2960                                &imageTopLeft, &state.mAnchor);
  2961   imageTopLeft += bgPositioningArea.TopLeft();
  2962   state.mAnchor += bgPositioningArea.TopLeft();
  2964   state.mDestArea = nsRect(imageTopLeft + aBorderArea.TopLeft(), imageSize);
  2965   state.mFillArea = state.mDestArea;
  2966   int repeatX = aLayer.mRepeat.mXRepeat;
  2967   int repeatY = aLayer.mRepeat.mYRepeat;
  2968   if (repeatX == NS_STYLE_BG_REPEAT_REPEAT) {
  2969     state.mFillArea.x = bgClipRect.x;
  2970     state.mFillArea.width = bgClipRect.width;
  2972   if (repeatY == NS_STYLE_BG_REPEAT_REPEAT) {
  2973     state.mFillArea.y = bgClipRect.y;
  2974     state.mFillArea.height = bgClipRect.height;
  2976   state.mFillArea.IntersectRect(state.mFillArea, bgClipRect);
  2978   state.mCompositingOp = GetGFXBlendMode(aLayer.mBlendMode);
  2980   return state;
  2983 nsRect
  2984 nsCSSRendering::GetBackgroundLayerRect(nsPresContext* aPresContext,
  2985                                        nsIFrame* aForFrame,
  2986                                        const nsRect& aBorderArea,
  2987                                        const nsRect& aClipRect,
  2988                                        const nsStyleBackground& aBackground,
  2989                                        const nsStyleBackground::Layer& aLayer,
  2990                                        uint32_t aFlags)
  2992   nsBackgroundLayerState state =
  2993       PrepareBackgroundLayer(aPresContext, aForFrame, aFlags, aBorderArea,
  2994                              aClipRect, aBackground, aLayer);
  2995   return state.mFillArea;
  2998 /* static */ bool
  2999 nsCSSRendering::IsBackgroundImageDecodedForStyleContextAndLayer(
  3000   const nsStyleBackground *aBackground, uint32_t aLayer)
  3002   const nsStyleImage* image = &aBackground->mLayers[aLayer].mImage;
  3003   if (image->GetType() == eStyleImageType_Image) {
  3004     nsCOMPtr<imgIContainer> img;
  3005     if (NS_SUCCEEDED(image->GetImageData()->GetImage(getter_AddRefs(img)))) {
  3006       if (!img->IsDecoded()) {
  3007         return false;
  3011   return true;
  3014 /* static */ bool
  3015 nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(nsIFrame* aFrame)
  3017   const nsStyleBackground *bg = aFrame->StyleContext()->StyleBackground();
  3018   NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
  3019     if (!IsBackgroundImageDecodedForStyleContextAndLayer(bg, i)) {
  3020       return false;
  3023   return true;
  3026 static void
  3027 DrawBorderImage(nsPresContext*       aPresContext,
  3028                 nsRenderingContext&  aRenderingContext,
  3029                 nsIFrame*            aForFrame,
  3030                 const nsRect&        aBorderArea,
  3031                 const nsStyleBorder& aStyleBorder,
  3032                 const nsRect&        aDirtyRect)
  3034   NS_PRECONDITION(aStyleBorder.IsBorderImageLoaded(),
  3035                   "drawing border image that isn't successfully loaded");
  3037   if (aDirtyRect.IsEmpty())
  3038     return;
  3040   nsImageRenderer renderer(aForFrame, &aStyleBorder.mBorderImageSource, 0);
  3042   // Ensure we get invalidated for loads and animations of the image.
  3043   // We need to do this here because this might be the only code that
  3044   // knows about the association of the style data with the frame.
  3045   // XXX We shouldn't really... since if anybody is passing in a
  3046   // different style, they'll potentially have the wrong size for the
  3047   // border too.
  3048   aForFrame->AssociateImage(aStyleBorder.mBorderImageSource, aPresContext);
  3050   if (!renderer.PrepareImage()) {
  3051     return;
  3054   // Determine the border image area, which by default corresponds to the
  3055   // border box but can be modified by 'border-image-outset'.
  3056   nsRect borderImgArea(aBorderArea);
  3057   borderImgArea.Inflate(aStyleBorder.GetImageOutset());
  3059   // Calculate the image size used to compute slice points.
  3060   CSSSizeOrRatio intrinsicSize = renderer.ComputeIntrinsicSize();
  3061   nsSize imageSize = nsImageRenderer::ComputeConcreteSize(CSSSizeOrRatio(),
  3062                                                           intrinsicSize,
  3063                                                           borderImgArea.Size());
  3064   renderer.SetPreferredSize(intrinsicSize, imageSize);
  3066   // Compute the used values of 'border-image-slice' and 'border-image-width';
  3067   // we do them together because the latter can depend on the former.
  3068   nsMargin slice;
  3069   nsMargin border;
  3070   NS_FOR_CSS_SIDES(s) {
  3071     nsStyleCoord coord = aStyleBorder.mBorderImageSlice.Get(s);
  3072     int32_t imgDimension = NS_SIDE_IS_VERTICAL(s)
  3073                            ? imageSize.width : imageSize.height;
  3074     nscoord borderDimension = NS_SIDE_IS_VERTICAL(s)
  3075                            ? borderImgArea.width : borderImgArea.height;
  3076     double value;
  3077     switch (coord.GetUnit()) {
  3078       case eStyleUnit_Percent:
  3079         value = coord.GetPercentValue() * imgDimension;
  3080         break;
  3081       case eStyleUnit_Factor:
  3082         value = nsPresContext::CSSPixelsToAppUnits(
  3083           NS_lround(coord.GetFactorValue()));
  3084         break;
  3085       default:
  3086         NS_NOTREACHED("unexpected CSS unit for image slice");
  3087         value = 0;
  3088         break;
  3090     if (value < 0)
  3091       value = 0;
  3092     if (value > imgDimension)
  3093       value = imgDimension;
  3094     slice.Side(s) = value;
  3096     nsMargin borderWidths(aStyleBorder.GetComputedBorder());
  3097     coord = aStyleBorder.mBorderImageWidth.Get(s);
  3098     switch (coord.GetUnit()) {
  3099       case eStyleUnit_Coord: // absolute dimension
  3100         value = coord.GetCoordValue();
  3101         break;
  3102       case eStyleUnit_Percent:
  3103         value = coord.GetPercentValue() * borderDimension;
  3104         break;
  3105       case eStyleUnit_Factor:
  3106         value = coord.GetFactorValue() * borderWidths.Side(s);
  3107         break;
  3108       case eStyleUnit_Auto:  // same as the slice value, in CSS pixels
  3109         value = slice.Side(s);
  3110         break;
  3111       default:
  3112         NS_NOTREACHED("unexpected CSS unit for border image area division");
  3113         value = 0;
  3114         break;
  3116     // NSToCoordRoundWithClamp rounds towards infinity, but that's OK
  3117     // because we expect value to be non-negative.
  3118     MOZ_ASSERT(value >= 0);
  3119     border.Side(s) = NSToCoordRoundWithClamp(value);
  3120     MOZ_ASSERT(border.Side(s) >= 0);
  3123   // "If two opposite border-image-width offsets are large enough that they
  3124   // overlap, their used values are proportionately reduced until they no
  3125   // longer overlap."
  3126   uint32_t combinedBorderWidth = uint32_t(border.left) +
  3127                                  uint32_t(border.right);
  3128   double scaleX = combinedBorderWidth > uint32_t(borderImgArea.width)
  3129                   ? borderImgArea.width / double(combinedBorderWidth)
  3130                   : 1.0;
  3131   uint32_t combinedBorderHeight = uint32_t(border.top) +
  3132                                   uint32_t(border.bottom);
  3133   double scaleY = combinedBorderHeight > uint32_t(borderImgArea.height)
  3134                   ? borderImgArea.height / double(combinedBorderHeight)
  3135                   : 1.0;
  3136   double scale = std::min(scaleX, scaleY);
  3137   if (scale < 1.0) {
  3138     border.left *= scale;
  3139     border.right *= scale;
  3140     border.top *= scale;
  3141     border.bottom *= scale;
  3142     NS_ASSERTION(border.left + border.right <= borderImgArea.width &&
  3143                  border.top + border.bottom <= borderImgArea.height,
  3144                  "rounding error in width reduction???");
  3147   // These helper tables recharacterize the 'slice' and 'width' margins
  3148   // in a more convenient form: they are the x/y/width/height coords
  3149   // required for various bands of the border, and they have been transformed
  3150   // to be relative to the innerRect (for 'slice') or the page (for 'border').
  3151   enum {
  3152     LEFT, MIDDLE, RIGHT,
  3153     TOP = LEFT, BOTTOM = RIGHT
  3154   };
  3155   const nscoord borderX[3] = {
  3156     borderImgArea.x + 0,
  3157     borderImgArea.x + border.left,
  3158     borderImgArea.x + borderImgArea.width - border.right,
  3159   };
  3160   const nscoord borderY[3] = {
  3161     borderImgArea.y + 0,
  3162     borderImgArea.y + border.top,
  3163     borderImgArea.y + borderImgArea.height - border.bottom,
  3164   };
  3165   const nscoord borderWidth[3] = {
  3166     border.left,
  3167     borderImgArea.width - border.left - border.right,
  3168     border.right,
  3169   };
  3170   const nscoord borderHeight[3] = {
  3171     border.top,
  3172     borderImgArea.height - border.top - border.bottom,
  3173     border.bottom,
  3174   };
  3175   const int32_t sliceX[3] = {
  3176     0,
  3177     slice.left,
  3178     imageSize.width - slice.right,
  3179   };
  3180   const int32_t sliceY[3] = {
  3181     0,
  3182     slice.top,
  3183     imageSize.height - slice.bottom,
  3184   };
  3185   const int32_t sliceWidth[3] = {
  3186     slice.left,
  3187     std::max(imageSize.width - slice.left - slice.right, 0),
  3188     slice.right,
  3189   };
  3190   const int32_t sliceHeight[3] = {
  3191     slice.top,
  3192     std::max(imageSize.height - slice.top - slice.bottom, 0),
  3193     slice.bottom,
  3194   };
  3196   for (int i = LEFT; i <= RIGHT; i++) {
  3197     for (int j = TOP; j <= BOTTOM; j++) {
  3198       uint8_t fillStyleH, fillStyleV;
  3199       nsSize unitSize;
  3201       if (i == MIDDLE && j == MIDDLE) {
  3202         // Discard the middle portion unless set to fill.
  3203         if (NS_STYLE_BORDER_IMAGE_SLICE_NOFILL ==
  3204             aStyleBorder.mBorderImageFill) {
  3205           continue;
  3208         NS_ASSERTION(NS_STYLE_BORDER_IMAGE_SLICE_FILL ==
  3209                      aStyleBorder.mBorderImageFill,
  3210                      "Unexpected border image fill");
  3212         // css-background:
  3213         //     The middle image's width is scaled by the same factor as the
  3214         //     top image unless that factor is zero or infinity, in which
  3215         //     case the scaling factor of the bottom is substituted, and
  3216         //     failing that, the width is not scaled. The height of the
  3217         //     middle image is scaled by the same factor as the left image
  3218         //     unless that factor is zero or infinity, in which case the
  3219         //     scaling factor of the right image is substituted, and failing
  3220         //     that, the height is not scaled.
  3221         gfxFloat hFactor, vFactor;
  3223         if (0 < border.left && 0 < slice.left)
  3224           vFactor = gfxFloat(border.left)/slice.left;
  3225         else if (0 < border.right && 0 < slice.right)
  3226           vFactor = gfxFloat(border.right)/slice.right;
  3227         else
  3228           vFactor = 1;
  3230         if (0 < border.top && 0 < slice.top)
  3231           hFactor = gfxFloat(border.top)/slice.top;
  3232         else if (0 < border.bottom && 0 < slice.bottom)
  3233           hFactor = gfxFloat(border.bottom)/slice.bottom;
  3234         else
  3235           hFactor = 1;
  3237         unitSize.width = sliceWidth[i]*hFactor;
  3238         unitSize.height = sliceHeight[j]*vFactor;
  3239         fillStyleH = aStyleBorder.mBorderImageRepeatH;
  3240         fillStyleV = aStyleBorder.mBorderImageRepeatV;
  3242       } else if (i == MIDDLE) { // top, bottom
  3243         // Sides are always stretched to the thickness of their border,
  3244         // and stretched proportionately on the other axis.
  3245         gfxFloat factor;
  3246         if (0 < borderHeight[j] && 0 < sliceHeight[j])
  3247           factor = gfxFloat(borderHeight[j])/sliceHeight[j];
  3248         else
  3249           factor = 1;
  3251         unitSize.width = sliceWidth[i]*factor;
  3252         unitSize.height = borderHeight[j];
  3253         fillStyleH = aStyleBorder.mBorderImageRepeatH;
  3254         fillStyleV = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
  3256       } else if (j == MIDDLE) { // left, right
  3257         gfxFloat factor;
  3258         if (0 < borderWidth[i] && 0 < sliceWidth[i])
  3259           factor = gfxFloat(borderWidth[i])/sliceWidth[i];
  3260         else
  3261           factor = 1;
  3263         unitSize.width = borderWidth[i];
  3264         unitSize.height = sliceHeight[j]*factor;
  3265         fillStyleH = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
  3266         fillStyleV = aStyleBorder.mBorderImageRepeatV;
  3268       } else {
  3269         // Corners are always stretched to fit the corner.
  3270         unitSize.width = borderWidth[i];
  3271         unitSize.height = borderHeight[j];
  3272         fillStyleH = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
  3273         fillStyleV = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
  3276       nsRect destArea(borderX[i], borderY[j], borderWidth[i], borderHeight[j]);
  3277       nsRect subArea(sliceX[i], sliceY[j], sliceWidth[i], sliceHeight[j]);
  3278       nsIntRect intSubArea = subArea.ToOutsidePixels(nsPresContext::AppUnitsPerCSSPixel());
  3280       renderer.DrawBorderImageComponent(aPresContext,
  3281                                         aRenderingContext, aDirtyRect,
  3282                                         destArea, CSSIntRect(intSubArea.x,
  3283                                                              intSubArea.y,
  3284                                                              intSubArea.width,
  3285                                                              intSubArea.height),
  3286                                         fillStyleH, fillStyleV,
  3287                                         unitSize, j * (RIGHT + 1) + i);
  3292 // Begin table border-collapsing section
  3293 // These functions were written to not disrupt the normal ones and yet satisfy some additional requirements
  3294 // At some point, all functions should be unified to include the additional functionality that these provide
  3296 static nscoord
  3297 RoundIntToPixel(nscoord aValue,
  3298                 nscoord aTwipsPerPixel,
  3299                 bool    aRoundDown = false)
  3301   if (aTwipsPerPixel <= 0)
  3302     // We must be rendering to a device that has a resolution greater than Twips!
  3303     // In that case, aValue is as accurate as it's going to get.
  3304     return aValue;
  3306   nscoord halfPixel = NSToCoordRound(aTwipsPerPixel / 2.0f);
  3307   nscoord extra = aValue % aTwipsPerPixel;
  3308   nscoord finalValue = (!aRoundDown && (extra >= halfPixel)) ? aValue + (aTwipsPerPixel - extra) : aValue - extra;
  3309   return finalValue;
  3312 static nscoord
  3313 RoundFloatToPixel(float   aValue,
  3314                   nscoord aTwipsPerPixel,
  3315                   bool    aRoundDown = false)
  3317   return RoundIntToPixel(NSToCoordRound(aValue), aTwipsPerPixel, aRoundDown);
  3320 static void
  3321 SetPoly(const nsRect& aRect,
  3322         nsPoint*      poly)
  3324   poly[0].x = aRect.x;
  3325   poly[0].y = aRect.y;
  3326   poly[1].x = aRect.x + aRect.width;
  3327   poly[1].y = aRect.y;
  3328   poly[2].x = aRect.x + aRect.width;
  3329   poly[2].y = aRect.y + aRect.height;
  3330   poly[3].x = aRect.x;
  3331   poly[3].y = aRect.y + aRect.height;
  3332   poly[4].x = aRect.x;
  3333   poly[4].y = aRect.y;
  3336 static void
  3337 DrawSolidBorderSegment(nsRenderingContext& aContext,
  3338                        nsRect               aRect,
  3339                        nscoord              aTwipsPerPixel,
  3340                        uint8_t              aStartBevelSide = 0,
  3341                        nscoord              aStartBevelOffset = 0,
  3342                        uint8_t              aEndBevelSide = 0,
  3343                        nscoord              aEndBevelOffset = 0)
  3346   if ((aRect.width == aTwipsPerPixel) || (aRect.height == aTwipsPerPixel) ||
  3347       ((0 == aStartBevelOffset) && (0 == aEndBevelOffset))) {
  3348     // simple line or rectangle
  3349     if ((NS_SIDE_TOP == aStartBevelSide) || (NS_SIDE_BOTTOM == aStartBevelSide)) {
  3350       if (1 == aRect.height)
  3351         aContext.DrawLine(aRect.TopLeft(), aRect.BottomLeft());
  3352       else
  3353         aContext.FillRect(aRect);
  3355     else {
  3356       if (1 == aRect.width)
  3357         aContext.DrawLine(aRect.TopLeft(), aRect.TopRight());
  3358       else
  3359         aContext.FillRect(aRect);
  3362   else {
  3363     // polygon with beveling
  3364     nsPoint poly[5];
  3365     SetPoly(aRect, poly);
  3366     switch(aStartBevelSide) {
  3367     case NS_SIDE_TOP:
  3368       poly[0].x += aStartBevelOffset;
  3369       poly[4].x = poly[0].x;
  3370       break;
  3371     case NS_SIDE_BOTTOM:
  3372       poly[3].x += aStartBevelOffset;
  3373       break;
  3374     case NS_SIDE_RIGHT:
  3375       poly[1].y += aStartBevelOffset;
  3376       break;
  3377     case NS_SIDE_LEFT:
  3378       poly[0].y += aStartBevelOffset;
  3379       poly[4].y = poly[0].y;
  3382     switch(aEndBevelSide) {
  3383     case NS_SIDE_TOP:
  3384       poly[1].x -= aEndBevelOffset;
  3385       break;
  3386     case NS_SIDE_BOTTOM:
  3387       poly[2].x -= aEndBevelOffset;
  3388       break;
  3389     case NS_SIDE_RIGHT:
  3390       poly[2].y -= aEndBevelOffset;
  3391       break;
  3392     case NS_SIDE_LEFT:
  3393       poly[3].y -= aEndBevelOffset;
  3396     aContext.FillPolygon(poly, 5);
  3402 static void
  3403 GetDashInfo(nscoord  aBorderLength,
  3404             nscoord  aDashLength,
  3405             nscoord  aTwipsPerPixel,
  3406             int32_t& aNumDashSpaces,
  3407             nscoord& aStartDashLength,
  3408             nscoord& aEndDashLength)
  3410   aNumDashSpaces = 0;
  3411   if (aStartDashLength + aDashLength + aEndDashLength >= aBorderLength) {
  3412     aStartDashLength = aBorderLength;
  3413     aEndDashLength = 0;
  3415   else {
  3416     aNumDashSpaces = (aBorderLength - aDashLength)/ (2 * aDashLength); // round down
  3417     nscoord extra = aBorderLength - aStartDashLength - aEndDashLength - (((2 * aNumDashSpaces) - 1) * aDashLength);
  3418     if (extra > 0) {
  3419       nscoord half = RoundIntToPixel(extra / 2, aTwipsPerPixel);
  3420       aStartDashLength += half;
  3421       aEndDashLength += (extra - half);
  3426 void
  3427 nsCSSRendering::DrawTableBorderSegment(nsRenderingContext&     aContext,
  3428                                        uint8_t                  aBorderStyle,
  3429                                        nscolor                  aBorderColor,
  3430                                        const nsStyleBackground* aBGColor,
  3431                                        const nsRect&            aBorder,
  3432                                        int32_t                  aAppUnitsPerCSSPixel,
  3433                                        uint8_t                  aStartBevelSide,
  3434                                        nscoord                  aStartBevelOffset,
  3435                                        uint8_t                  aEndBevelSide,
  3436                                        nscoord                  aEndBevelOffset)
  3438   aContext.SetColor (aBorderColor);
  3440   bool horizontal = ((NS_SIDE_TOP == aStartBevelSide) || (NS_SIDE_BOTTOM == aStartBevelSide));
  3441   nscoord twipsPerPixel = NSIntPixelsToAppUnits(1, aAppUnitsPerCSSPixel);
  3442   uint8_t ridgeGroove = NS_STYLE_BORDER_STYLE_RIDGE;
  3444   if ((twipsPerPixel >= aBorder.width) || (twipsPerPixel >= aBorder.height) ||
  3445       (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) || (NS_STYLE_BORDER_STYLE_DOTTED == aBorderStyle)) {
  3446     // no beveling for 1 pixel border, dash or dot
  3447     aStartBevelOffset = 0;
  3448     aEndBevelOffset = 0;
  3451   gfxContext *ctx = aContext.ThebesContext();
  3452   gfxContext::AntialiasMode oldMode = ctx->CurrentAntialiasMode();
  3453   ctx->SetAntialiasMode(gfxContext::MODE_ALIASED);
  3455   switch (aBorderStyle) {
  3456   case NS_STYLE_BORDER_STYLE_NONE:
  3457   case NS_STYLE_BORDER_STYLE_HIDDEN:
  3458     //NS_ASSERTION(false, "style of none or hidden");
  3459     break;
  3460   case NS_STYLE_BORDER_STYLE_DOTTED:
  3461   case NS_STYLE_BORDER_STYLE_DASHED:
  3463       nscoord dashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) ? DASH_LENGTH : DOT_LENGTH;
  3464       // make the dash length proportional to the border thickness
  3465       dashLength *= (horizontal) ? aBorder.height : aBorder.width;
  3466       // make the min dash length for the ends 1/2 the dash length
  3467       nscoord minDashLength = (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle)
  3468                               ? RoundFloatToPixel(((float)dashLength) / 2.0f, twipsPerPixel) : dashLength;
  3469       minDashLength = std::max(minDashLength, twipsPerPixel);
  3470       nscoord numDashSpaces = 0;
  3471       nscoord startDashLength = minDashLength;
  3472       nscoord endDashLength   = minDashLength;
  3473       if (horizontal) {
  3474         GetDashInfo(aBorder.width, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength);
  3475         nsRect rect(aBorder.x, aBorder.y, startDashLength, aBorder.height);
  3476         DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
  3477         for (int32_t spaceX = 0; spaceX < numDashSpaces; spaceX++) {
  3478           rect.x += rect.width + dashLength;
  3479           rect.width = (spaceX == (numDashSpaces - 1)) ? endDashLength : dashLength;
  3480           DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
  3483       else {
  3484         GetDashInfo(aBorder.height, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength);
  3485         nsRect rect(aBorder.x, aBorder.y, aBorder.width, startDashLength);
  3486         DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
  3487         for (int32_t spaceY = 0; spaceY < numDashSpaces; spaceY++) {
  3488           rect.y += rect.height + dashLength;
  3489           rect.height = (spaceY == (numDashSpaces - 1)) ? endDashLength : dashLength;
  3490           DrawSolidBorderSegment(aContext, rect, twipsPerPixel);
  3494     break;
  3495   case NS_STYLE_BORDER_STYLE_GROOVE:
  3496     ridgeGroove = NS_STYLE_BORDER_STYLE_GROOVE; // and fall through to ridge
  3497   case NS_STYLE_BORDER_STYLE_RIDGE:
  3498     if ((horizontal && (twipsPerPixel >= aBorder.height)) ||
  3499         (!horizontal && (twipsPerPixel >= aBorder.width))) {
  3500       // a one pixel border
  3501       DrawSolidBorderSegment(aContext, aBorder, twipsPerPixel, aStartBevelSide, aStartBevelOffset,
  3502                              aEndBevelSide, aEndBevelOffset);
  3504     else {
  3505       nscoord startBevel = (aStartBevelOffset > 0)
  3506                             ? RoundFloatToPixel(0.5f * (float)aStartBevelOffset, twipsPerPixel, true) : 0;
  3507       nscoord endBevel =   (aEndBevelOffset > 0)
  3508                             ? RoundFloatToPixel(0.5f * (float)aEndBevelOffset, twipsPerPixel, true) : 0;
  3509       mozilla::css::Side ridgeGrooveSide = (horizontal) ? NS_SIDE_TOP : NS_SIDE_LEFT;
  3510       // FIXME: In theory, this should use the visited-dependent
  3511       // background color, but I don't care.
  3512       aContext.SetColor (
  3513         MakeBevelColor(ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor));
  3514       nsRect rect(aBorder);
  3515       nscoord half;
  3516       if (horizontal) { // top, bottom
  3517         half = RoundFloatToPixel(0.5f * (float)aBorder.height, twipsPerPixel);
  3518         rect.height = half;
  3519         if (NS_SIDE_TOP == aStartBevelSide) {
  3520           rect.x += startBevel;
  3521           rect.width -= startBevel;
  3523         if (NS_SIDE_TOP == aEndBevelSide) {
  3524           rect.width -= endBevel;
  3526         DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
  3527                                startBevel, aEndBevelSide, endBevel);
  3529       else { // left, right
  3530         half = RoundFloatToPixel(0.5f * (float)aBorder.width, twipsPerPixel);
  3531         rect.width = half;
  3532         if (NS_SIDE_LEFT == aStartBevelSide) {
  3533           rect.y += startBevel;
  3534           rect.height -= startBevel;
  3536         if (NS_SIDE_LEFT == aEndBevelSide) {
  3537           rect.height -= endBevel;
  3539         DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
  3540                                startBevel, aEndBevelSide, endBevel);
  3543       rect = aBorder;
  3544       ridgeGrooveSide = (NS_SIDE_TOP == ridgeGrooveSide) ? NS_SIDE_BOTTOM : NS_SIDE_RIGHT;
  3545       // FIXME: In theory, this should use the visited-dependent
  3546       // background color, but I don't care.
  3547       aContext.SetColor (
  3548         MakeBevelColor(ridgeGrooveSide, ridgeGroove, aBGColor->mBackgroundColor, aBorderColor));
  3549       if (horizontal) {
  3550         rect.y = rect.y + half;
  3551         rect.height = aBorder.height - half;
  3552         if (NS_SIDE_BOTTOM == aStartBevelSide) {
  3553           rect.x += startBevel;
  3554           rect.width -= startBevel;
  3556         if (NS_SIDE_BOTTOM == aEndBevelSide) {
  3557           rect.width -= endBevel;
  3559         DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
  3560                                startBevel, aEndBevelSide, endBevel);
  3562       else {
  3563         rect.x = rect.x + half;
  3564         rect.width = aBorder.width - half;
  3565         if (NS_SIDE_RIGHT == aStartBevelSide) {
  3566           rect.y += aStartBevelOffset - startBevel;
  3567           rect.height -= startBevel;
  3569         if (NS_SIDE_RIGHT == aEndBevelSide) {
  3570           rect.height -= endBevel;
  3572         DrawSolidBorderSegment(aContext, rect, twipsPerPixel, aStartBevelSide,
  3573                                startBevel, aEndBevelSide, endBevel);
  3576     break;
  3577   case NS_STYLE_BORDER_STYLE_DOUBLE:
  3578     // We can only do "double" borders if the thickness of the border
  3579     // is more than 2px.  Otherwise, we fall through to painting a
  3580     // solid border.
  3581     if ((aBorder.width > 2*twipsPerPixel || horizontal) &&
  3582         (aBorder.height > 2*twipsPerPixel || !horizontal)) {
  3583       nscoord startBevel = (aStartBevelOffset > 0)
  3584                             ? RoundFloatToPixel(0.333333f * (float)aStartBevelOffset, twipsPerPixel) : 0;
  3585       nscoord endBevel =   (aEndBevelOffset > 0)
  3586                             ? RoundFloatToPixel(0.333333f * (float)aEndBevelOffset, twipsPerPixel) : 0;
  3587       if (horizontal) { // top, bottom
  3588         nscoord thirdHeight = RoundFloatToPixel(0.333333f * (float)aBorder.height, twipsPerPixel);
  3590         // draw the top line or rect
  3591         nsRect topRect(aBorder.x, aBorder.y, aBorder.width, thirdHeight);
  3592         if (NS_SIDE_TOP == aStartBevelSide) {
  3593           topRect.x += aStartBevelOffset - startBevel;
  3594           topRect.width -= aStartBevelOffset - startBevel;
  3596         if (NS_SIDE_TOP == aEndBevelSide) {
  3597           topRect.width -= aEndBevelOffset - endBevel;
  3599         DrawSolidBorderSegment(aContext, topRect, twipsPerPixel, aStartBevelSide,
  3600                                startBevel, aEndBevelSide, endBevel);
  3602         // draw the botom line or rect
  3603         nscoord heightOffset = aBorder.height - thirdHeight;
  3604         nsRect bottomRect(aBorder.x, aBorder.y + heightOffset, aBorder.width, aBorder.height - heightOffset);
  3605         if (NS_SIDE_BOTTOM == aStartBevelSide) {
  3606           bottomRect.x += aStartBevelOffset - startBevel;
  3607           bottomRect.width -= aStartBevelOffset - startBevel;
  3609         if (NS_SIDE_BOTTOM == aEndBevelSide) {
  3610           bottomRect.width -= aEndBevelOffset - endBevel;
  3612         DrawSolidBorderSegment(aContext, bottomRect, twipsPerPixel, aStartBevelSide,
  3613                                startBevel, aEndBevelSide, endBevel);
  3615       else { // left, right
  3616         nscoord thirdWidth = RoundFloatToPixel(0.333333f * (float)aBorder.width, twipsPerPixel);
  3618         nsRect leftRect(aBorder.x, aBorder.y, thirdWidth, aBorder.height);
  3619         if (NS_SIDE_LEFT == aStartBevelSide) {
  3620           leftRect.y += aStartBevelOffset - startBevel;
  3621           leftRect.height -= aStartBevelOffset - startBevel;
  3623         if (NS_SIDE_LEFT == aEndBevelSide) {
  3624           leftRect.height -= aEndBevelOffset - endBevel;
  3626         DrawSolidBorderSegment(aContext, leftRect, twipsPerPixel, aStartBevelSide,
  3627                                startBevel, aEndBevelSide, endBevel);
  3629         nscoord widthOffset = aBorder.width - thirdWidth;
  3630         nsRect rightRect(aBorder.x + widthOffset, aBorder.y, aBorder.width - widthOffset, aBorder.height);
  3631         if (NS_SIDE_RIGHT == aStartBevelSide) {
  3632           rightRect.y += aStartBevelOffset - startBevel;
  3633           rightRect.height -= aStartBevelOffset - startBevel;
  3635         if (NS_SIDE_RIGHT == aEndBevelSide) {
  3636           rightRect.height -= aEndBevelOffset - endBevel;
  3638         DrawSolidBorderSegment(aContext, rightRect, twipsPerPixel, aStartBevelSide,
  3639                                startBevel, aEndBevelSide, endBevel);
  3641       break;
  3643     // else fall through to solid
  3644   case NS_STYLE_BORDER_STYLE_SOLID:
  3645     DrawSolidBorderSegment(aContext, aBorder, twipsPerPixel, aStartBevelSide,
  3646                            aStartBevelOffset, aEndBevelSide, aEndBevelOffset);
  3647     break;
  3648   case NS_STYLE_BORDER_STYLE_OUTSET:
  3649   case NS_STYLE_BORDER_STYLE_INSET:
  3650     NS_ASSERTION(false, "inset, outset should have been converted to groove, ridge");
  3651     break;
  3652   case NS_STYLE_BORDER_STYLE_AUTO:
  3653     NS_ASSERTION(false, "Unexpected 'auto' table border");
  3654     break;
  3657   ctx->SetAntialiasMode(oldMode);
  3660 // End table border-collapsing section
  3662 gfxRect
  3663 nsCSSRendering::ExpandPaintingRectForDecorationLine(nsIFrame* aFrame,
  3664                                                     const uint8_t aStyle,
  3665                                                     const gfxRect& aClippedRect,
  3666                                                     const gfxFloat aXInFrame,
  3667                                                     const gfxFloat aCycleLength)
  3669   switch (aStyle) {
  3670     case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
  3671     case NS_STYLE_TEXT_DECORATION_STYLE_DASHED:
  3672     case NS_STYLE_TEXT_DECORATION_STYLE_WAVY:
  3673       break;
  3674     default:
  3675       NS_ERROR("Invalid style was specified");
  3676       return aClippedRect;
  3679   nsBlockFrame* block = nullptr;
  3680   // Note that when we paint the decoration lines in relative positioned
  3681   // box, we should paint them like all of the boxes are positioned as static.
  3682   nscoord frameXInBlockAppUnits = 0;
  3683   for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
  3684     block = do_QueryFrame(f);
  3685     if (block) {
  3686       break;
  3688     frameXInBlockAppUnits += f->GetNormalPosition().x;
  3691   NS_ENSURE_TRUE(block, aClippedRect);
  3693   nsPresContext *pc = aFrame->PresContext();
  3694   gfxFloat frameXInBlock = pc->AppUnitsToGfxUnits(frameXInBlockAppUnits);
  3695   int32_t rectXInBlock = int32_t(NS_round(frameXInBlock + aXInFrame));
  3696   int32_t extraLeft =
  3697     rectXInBlock - (rectXInBlock / int32_t(aCycleLength) * aCycleLength);
  3698   gfxRect rect(aClippedRect);
  3699   rect.x -= extraLeft;
  3700   rect.width += extraLeft;
  3701   return rect;
  3704 void
  3705 nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame,
  3706                                     gfxContext* aGfxContext,
  3707                                     const gfxRect& aDirtyRect,
  3708                                     const nscolor aColor,
  3709                                     const gfxPoint& aPt,
  3710                                     const gfxFloat aXInFrame,
  3711                                     const gfxSize& aLineSize,
  3712                                     const gfxFloat aAscent,
  3713                                     const gfxFloat aOffset,
  3714                                     const uint8_t aDecoration,
  3715                                     const uint8_t aStyle,
  3716                                     const gfxFloat aDescentLimit)
  3718   NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none");
  3720   gfxRect rect =
  3721     GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset,
  3722                                   aDecoration, aStyle, aDescentLimit);
  3723   if (rect.IsEmpty() || !rect.Intersects(aDirtyRect)) {
  3724     return;
  3727   if (aDecoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE &&
  3728       aDecoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE &&
  3729       aDecoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
  3730     NS_ERROR("Invalid decoration value!");
  3731     return;
  3734   gfxFloat lineHeight = std::max(NS_round(aLineSize.height), 1.0);
  3735   bool contextIsSaved = false;
  3737   gfxFloat oldLineWidth;
  3738   nsRefPtr<gfxPattern> oldPattern;
  3740   switch (aStyle) {
  3741     case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
  3742     case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
  3743       oldLineWidth = aGfxContext->CurrentLineWidth();
  3744       oldPattern = aGfxContext->GetPattern();
  3745       break;
  3746     case NS_STYLE_TEXT_DECORATION_STYLE_DASHED: {
  3747       aGfxContext->Save();
  3748       contextIsSaved = true;
  3749       aGfxContext->Clip(rect);
  3750       gfxFloat dashWidth = lineHeight * DOT_LENGTH * DASH_LENGTH;
  3751       gfxFloat dash[2] = { dashWidth, dashWidth };
  3752       aGfxContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
  3753       aGfxContext->SetDash(dash, 2, 0.0);
  3754       rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect,
  3755                                                  aXInFrame, dashWidth * 2);
  3756       // We should continue to draw the last dash even if it is not in the rect.
  3757       rect.width += dashWidth;
  3758       break;
  3760     case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED: {
  3761       aGfxContext->Save();
  3762       contextIsSaved = true;
  3763       aGfxContext->Clip(rect);
  3764       gfxFloat dashWidth = lineHeight * DOT_LENGTH;
  3765       gfxFloat dash[2];
  3766       if (lineHeight > 2.0) {
  3767         dash[0] = 0.0;
  3768         dash[1] = dashWidth * 2.0;
  3769         aGfxContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
  3770       } else {
  3771         dash[0] = dashWidth;
  3772         dash[1] = dashWidth;
  3774       aGfxContext->SetDash(dash, 2, 0.0);
  3775       rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect,
  3776                                                  aXInFrame, dashWidth * 2);
  3777       // We should continue to draw the last dot even if it is not in the rect.
  3778       rect.width += dashWidth;
  3779       break;
  3781     case NS_STYLE_TEXT_DECORATION_STYLE_WAVY:
  3782       aGfxContext->Save();
  3783       contextIsSaved = true;
  3784       aGfxContext->Clip(rect);
  3785       if (lineHeight > 2.0) {
  3786         aGfxContext->SetAntialiasMode(gfxContext::MODE_COVERAGE);
  3787       } else {
  3788         // Don't use anti-aliasing here.  Because looks like lighter color wavy
  3789         // line at this case.  And probably, users don't think the
  3790         // non-anti-aliased wavy line is not pretty.
  3791         aGfxContext->SetAntialiasMode(gfxContext::MODE_ALIASED);
  3793       break;
  3794     default:
  3795       NS_ERROR("Invalid style value!");
  3796       return;
  3799   // The y position should be set to the middle of the line.
  3800   rect.y += lineHeight / 2;
  3802   aGfxContext->SetColor(gfxRGBA(aColor));
  3803   aGfxContext->SetLineWidth(lineHeight);
  3804   switch (aStyle) {
  3805     case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
  3806       aGfxContext->NewPath();
  3807       aGfxContext->MoveTo(rect.TopLeft());
  3808       aGfxContext->LineTo(rect.TopRight());
  3809       aGfxContext->Stroke();
  3810       break;
  3811     case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
  3812       /**
  3813        *  We are drawing double line as:
  3815        * +-------------------------------------------+
  3816        * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
  3817        * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
  3818        * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
  3819        * |                                           |
  3820        * |                                           |
  3821        * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
  3822        * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
  3823        * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
  3824        * +-------------------------------------------+
  3825        */
  3826       aGfxContext->NewPath();
  3827       aGfxContext->MoveTo(rect.TopLeft());
  3828       aGfxContext->LineTo(rect.TopRight());
  3829       rect.height -= lineHeight;
  3830       aGfxContext->MoveTo(rect.BottomLeft());
  3831       aGfxContext->LineTo(rect.BottomRight());
  3832       aGfxContext->Stroke();
  3833       break;
  3834     case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
  3835     case NS_STYLE_TEXT_DECORATION_STYLE_DASHED:
  3836       aGfxContext->NewPath();
  3837       aGfxContext->MoveTo(rect.TopLeft());
  3838       aGfxContext->LineTo(rect.TopRight());
  3839       aGfxContext->Stroke();
  3840       break;
  3841     case NS_STYLE_TEXT_DECORATION_STYLE_WAVY: {
  3842       /**
  3843        *  We are drawing wavy line as:
  3845        *  P: Path, X: Painted pixel
  3847        *     +---------------------------------------+
  3848        *   XX|X            XXXXXX            XXXXXX  |
  3849        *   PP|PX          XPPPPPPX          XPPPPPPX |    ^
  3850        *   XX|XPX        XPXXXXXXPX        XPXXXXXXPX|    |
  3851        *     | XPX      XPX      XPX      XPX      XP|X   |adv
  3852        *     |  XPXXXXXXPX        XPXXXXXXPX        X|PX  |
  3853        *     |   XPPPPPPX          XPPPPPPX          |XPX v
  3854        *     |    XXXXXX            XXXXXX           | XX
  3855        *     +---------------------------------------+
  3856        *      <---><--->                                ^
  3857        *      adv  flatLengthAtVertex                   rightMost
  3859        *  1. Always starts from top-left of the drawing area, however, we need
  3860        *     to draw  the line from outside of the rect.  Because the start
  3861        *     point of the line is not good style if we draw from inside it.
  3862        *  2. First, draw horizontal line from outside the rect to top-left of
  3863        *     the rect;
  3864        *  3. Goes down to bottom of the area at 45 degrees.
  3865        *  4. Slides to right horizontaly, see |flatLengthAtVertex|.
  3866        *  5. Goes up to top of the area at 45 degrees.
  3867        *  6. Slides to right horizontaly.
  3868        *  7. Repeat from 2 until reached to right-most edge of the area.
  3869        */
  3871       gfxFloat adv = rect.Height() - lineHeight;
  3872       gfxFloat flatLengthAtVertex = std::max((lineHeight - 1.0) * 2.0, 1.0);
  3874       // Align the start of wavy lines to the nearest ancestor block.
  3875       gfxFloat cycleLength = 2 * (adv + flatLengthAtVertex);
  3876       rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect,
  3877                                                  aXInFrame, cycleLength);
  3878       // figure out if we can trim whole cycles from the left and right edges
  3879       // of the line, to try and avoid creating an unnecessarily long and
  3880       // complex path
  3881       int32_t skipCycles = floor((aDirtyRect.x - rect.x) / cycleLength);
  3882       if (skipCycles > 0) {
  3883         rect.x += skipCycles * cycleLength;
  3884         rect.width -= skipCycles * cycleLength;
  3887       rect.x += lineHeight / 2.0;
  3888       gfxPoint pt(rect.TopLeft());
  3889       gfxFloat rightMost = pt.x + rect.Width() + lineHeight;
  3891       skipCycles = floor((rightMost - aDirtyRect.XMost()) / cycleLength);
  3892       if (skipCycles > 0) {
  3893         rightMost -= skipCycles * cycleLength;
  3896       aGfxContext->NewPath();
  3898       pt.x -= lineHeight;
  3899       aGfxContext->MoveTo(pt); // 1
  3901       pt.x = rect.X();
  3902       aGfxContext->LineTo(pt); // 2
  3904       bool goDown = true;
  3905       uint32_t iter = 0;
  3906       while (pt.x < rightMost) {
  3907         if (++iter > 1000) {
  3908           // stroke the current path and start again, to avoid pathological
  3909           // behavior in cairo with huge numbers of path segments
  3910           aGfxContext->Stroke();
  3911           aGfxContext->NewPath();
  3912           aGfxContext->MoveTo(pt);
  3913           iter = 0;
  3915         pt.x += adv;
  3916         pt.y += goDown ? adv : -adv;
  3918         aGfxContext->LineTo(pt); // 3 and 5
  3920         pt.x += flatLengthAtVertex;
  3921         aGfxContext->LineTo(pt); // 4 and 6
  3923         goDown = !goDown;
  3925       aGfxContext->Stroke();
  3926       break;
  3928     default:
  3929       NS_ERROR("Invalid style value!");
  3930       break;
  3933   if (contextIsSaved) {
  3934     aGfxContext->Restore();
  3935   } else {
  3936     aGfxContext->SetPattern(oldPattern);
  3937     aGfxContext->SetLineWidth(oldLineWidth);
  3941 void
  3942 nsCSSRendering::DecorationLineToPath(nsIFrame* aFrame,
  3943                                      gfxContext* aGfxContext,
  3944                                      const gfxRect& aDirtyRect,
  3945                                      const nscolor aColor,
  3946                                      const gfxPoint& aPt,
  3947                                      const gfxFloat aXInFrame,
  3948                                      const gfxSize& aLineSize,
  3949                                      const gfxFloat aAscent,
  3950                                      const gfxFloat aOffset,
  3951                                      const uint8_t aDecoration,
  3952                                      const uint8_t aStyle,
  3953                                      const gfxFloat aDescentLimit)
  3955   NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none");
  3957   aGfxContext->NewPath();
  3959   gfxRect rect =
  3960     GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset,
  3961                                   aDecoration, aStyle, aDescentLimit);
  3962   if (rect.IsEmpty() || !rect.Intersects(aDirtyRect)) {
  3963     return;
  3966   if (aDecoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE &&
  3967       aDecoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE &&
  3968       aDecoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
  3969     NS_ERROR("Invalid decoration value!");
  3970     return;
  3973   if (aStyle != NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
  3974     // For the moment, we support only solid text decorations.
  3975     return;
  3978   gfxFloat lineHeight = std::max(NS_round(aLineSize.height), 1.0);
  3980   // The y position should be set to the middle of the line.
  3981   rect.y += lineHeight / 2;
  3983   aGfxContext->Rectangle
  3984     (gfxRect(gfxPoint(rect.TopLeft() - gfxPoint(0.0, lineHeight / 2)),
  3985              gfxSize(rect.Width(), lineHeight)));
  3988 nsRect
  3989 nsCSSRendering::GetTextDecorationRect(nsPresContext* aPresContext,
  3990                                       const gfxSize& aLineSize,
  3991                                       const gfxFloat aAscent,
  3992                                       const gfxFloat aOffset,
  3993                                       const uint8_t aDecoration,
  3994                                       const uint8_t aStyle,
  3995                                       const gfxFloat aDescentLimit)
  3997   NS_ASSERTION(aPresContext, "aPresContext is null");
  3998   NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none");
  4000   gfxRect rect =
  4001     GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize, aAscent, aOffset,
  4002                                   aDecoration, aStyle, aDescentLimit);
  4003   // The rect values are already rounded to nearest device pixels.
  4004   nsRect r;
  4005   r.x = aPresContext->GfxUnitsToAppUnits(rect.X());
  4006   r.y = aPresContext->GfxUnitsToAppUnits(rect.Y());
  4007   r.width = aPresContext->GfxUnitsToAppUnits(rect.Width());
  4008   r.height = aPresContext->GfxUnitsToAppUnits(rect.Height());
  4009   return r;
  4012 gfxRect
  4013 nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
  4014                                               const gfxSize& aLineSize,
  4015                                               const gfxFloat aAscent,
  4016                                               const gfxFloat aOffset,
  4017                                               const uint8_t aDecoration,
  4018                                               const uint8_t aStyle,
  4019                                               const gfxFloat aDescentLimit)
  4021   NS_ASSERTION(aStyle <= NS_STYLE_TEXT_DECORATION_STYLE_WAVY,
  4022                "Invalid aStyle value");
  4024   if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE)
  4025     return gfxRect(0, 0, 0, 0);
  4027   bool canLiftUnderline = aDescentLimit >= 0.0;
  4029   const gfxFloat left  = floor(aPt.x + 0.5),
  4030                  right = floor(aPt.x + aLineSize.width + 0.5);
  4031   gfxRect r(left, 0, right - left, 0);
  4033   gfxFloat lineHeight = NS_round(aLineSize.height);
  4034   lineHeight = std::max(lineHeight, 1.0);
  4036   gfxFloat ascent = NS_round(aAscent);
  4037   gfxFloat descentLimit = floor(aDescentLimit);
  4039   gfxFloat suggestedMaxRectHeight = std::max(std::min(ascent, descentLimit), 1.0);
  4040   r.height = lineHeight;
  4041   if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE) {
  4042     /**
  4043      *  We will draw double line as:
  4045      * +-------------------------------------------+
  4046      * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
  4047      * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
  4048      * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
  4049      * |                                           | ^
  4050      * |                                           | | gap
  4051      * |                                           | v
  4052      * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
  4053      * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
  4054      * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
  4055      * +-------------------------------------------+
  4056      */
  4057     gfxFloat gap = NS_round(lineHeight / 2.0);
  4058     gap = std::max(gap, 1.0);
  4059     r.height = lineHeight * 2.0 + gap;
  4060     if (canLiftUnderline) {
  4061       if (r.Height() > suggestedMaxRectHeight) {
  4062         // Don't shrink the line height, because the thickness has some meaning.
  4063         // We can just shrink the gap at this time.
  4064         r.height = std::max(suggestedMaxRectHeight, lineHeight * 2.0 + 1.0);
  4067   } else if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_WAVY) {
  4068     /**
  4069      *  We will draw wavy line as:
  4071      * +-------------------------------------------+
  4072      * |XXXXX            XXXXXX            XXXXXX  | ^
  4073      * |XXXXXX          XXXXXXXX          XXXXXXXX | | lineHeight
  4074      * |XXXXXXX        XXXXXXXXXX        XXXXXXXXXX| v
  4075      * |     XXX      XXX      XXX      XXX      XX|
  4076      * |      XXXXXXXXXX        XXXXXXXXXX        X|
  4077      * |       XXXXXXXX          XXXXXXXX          |
  4078      * |        XXXXXX            XXXXXX           |
  4079      * +-------------------------------------------+
  4080      */
  4081     r.height = lineHeight > 2.0 ? lineHeight * 4.0 : lineHeight * 3.0;
  4082     if (canLiftUnderline) {
  4083       if (r.Height() > suggestedMaxRectHeight) {
  4084         // Don't shrink the line height even if there is not enough space,
  4085         // because the thickness has some meaning.  E.g., the 1px wavy line and
  4086         // 2px wavy line can be used for different meaning in IME selections
  4087         // at same time.
  4088         r.height = std::max(suggestedMaxRectHeight, lineHeight * 2.0);
  4093   gfxFloat baseline = floor(aPt.y + aAscent + 0.5);
  4094   gfxFloat offset = 0.0;
  4095   switch (aDecoration) {
  4096     case NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE:
  4097       offset = aOffset;
  4098       if (canLiftUnderline) {
  4099         if (descentLimit < -offset + r.Height()) {
  4100           // If we can ignore the offset and the decoration line is overflowing,
  4101           // we should align the bottom edge of the decoration line rect if it's
  4102           // possible.  Otherwise, we should lift up the top edge of the rect as
  4103           // far as possible.
  4104           gfxFloat offsetBottomAligned = -descentLimit + r.Height();
  4105           gfxFloat offsetTopAligned = 0.0;
  4106           offset = std::min(offsetBottomAligned, offsetTopAligned);
  4109       break;
  4110     case NS_STYLE_TEXT_DECORATION_LINE_OVERLINE:
  4111       offset = aOffset - lineHeight + r.Height();
  4112       break;
  4113     case NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH: {
  4114       gfxFloat extra = floor(r.Height() / 2.0 + 0.5);
  4115       extra = std::max(extra, lineHeight);
  4116       offset = aOffset - lineHeight + extra;
  4117       break;
  4119     default:
  4120       NS_ERROR("Invalid decoration value!");
  4122   r.y = baseline - floor(offset + 0.5);
  4123   return r;
  4126 // ------------------
  4127 // ImageRenderer
  4128 // ------------------
  4129 nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame,
  4130                                  const nsStyleImage* aImage,
  4131                                  uint32_t aFlags)
  4132   : mForFrame(aForFrame)
  4133   , mImage(aImage)
  4134   , mType(aImage->GetType())
  4135   , mImageContainer(nullptr)
  4136   , mGradientData(nullptr)
  4137   , mPaintServerFrame(nullptr)
  4138   , mIsReady(false)
  4139   , mSize(0, 0)
  4140   , mFlags(aFlags)
  4144 nsImageRenderer::~nsImageRenderer()
  4148 bool
  4149 nsImageRenderer::PrepareImage()
  4151   if (mImage->IsEmpty())
  4152     return false;
  4154   if (!mImage->IsComplete()) {
  4155     // Make sure the image is actually decoding
  4156     mImage->StartDecoding();
  4158     // check again to see if we finished
  4159     if (!mImage->IsComplete()) {
  4160       // We can not prepare the image for rendering if it is not fully loaded.
  4161       //
  4162       // Special case: If we requested a sync decode and we have an image, push
  4163       // on through because the Draw() will do a sync decode then
  4164       nsCOMPtr<imgIContainer> img;
  4165       if (!((mFlags & FLAG_SYNC_DECODE_IMAGES) &&
  4166             (mType == eStyleImageType_Image) &&
  4167             (NS_SUCCEEDED(mImage->GetImageData()->GetImage(getter_AddRefs(img))))))
  4168         return false;
  4172   switch (mType) {
  4173     case eStyleImageType_Image:
  4175       nsCOMPtr<imgIContainer> srcImage;
  4176       DebugOnly<nsresult> rv =
  4177         mImage->GetImageData()->GetImage(getter_AddRefs(srcImage));
  4178       NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv) && srcImage,
  4179                         "If GetImage() is failing, mImage->IsComplete() "
  4180                         "should have returned false");
  4182       if (!mImage->GetCropRect()) {
  4183         mImageContainer.swap(srcImage);
  4184       } else {
  4185         nsIntRect actualCropRect;
  4186         bool isEntireImage;
  4187         bool success =
  4188           mImage->ComputeActualCropRect(actualCropRect, &isEntireImage);
  4189         NS_ASSERTION(success, "ComputeActualCropRect() should not fail here");
  4190         if (!success || actualCropRect.IsEmpty()) {
  4191           // The cropped image has zero size
  4192           return false;
  4194         if (isEntireImage) {
  4195           // The cropped image is identical to the source image
  4196           mImageContainer.swap(srcImage);
  4197         } else {
  4198           nsCOMPtr<imgIContainer> subImage = ImageOps::Clip(srcImage, actualCropRect);
  4199           mImageContainer.swap(subImage);
  4202       mIsReady = true;
  4203       break;
  4205     case eStyleImageType_Gradient:
  4206       mGradientData = mImage->GetGradientData();
  4207       mIsReady = true;
  4208       break;
  4209     case eStyleImageType_Element:
  4211       nsAutoString elementId =
  4212         NS_LITERAL_STRING("#") + nsDependentString(mImage->GetElementId());
  4213       nsCOMPtr<nsIURI> targetURI;
  4214       nsCOMPtr<nsIURI> base = mForFrame->GetContent()->GetBaseURI();
  4215       nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), elementId,
  4216                                                 mForFrame->GetContent()->GetCurrentDoc(), base);
  4217       nsSVGPaintingProperty* property = nsSVGEffects::GetPaintingPropertyForURI(
  4218           targetURI, mForFrame->FirstContinuation(),
  4219           nsSVGEffects::BackgroundImageProperty());
  4220       if (!property)
  4221         return false;
  4222       mPaintServerFrame = property->GetReferencedFrame();
  4224       // If the referenced element doesn't have a frame we might still be able
  4225       // to paint it if it's an <img>, <canvas>, or <video> element.
  4226       if (!mPaintServerFrame) {
  4227         mImageElementSurface =
  4228           nsLayoutUtils::SurfaceFromElement(property->GetReferencedElement());
  4229         if (!mImageElementSurface.mSourceSurface)
  4230           return false;
  4232       mIsReady = true;
  4233       break;
  4235     case eStyleImageType_Null:
  4236     default:
  4237       break;
  4240   return mIsReady;
  4243 nsSize
  4244 CSSSizeOrRatio::ComputeConcreteSize() const
  4246   NS_ASSERTION(CanComputeConcreteSize(), "Cannot compute");
  4247   if (mHasWidth && mHasHeight) {
  4248     return nsSize(mWidth, mHeight);
  4250   if (mHasWidth) {
  4251     nscoord height = NSCoordSaturatingNonnegativeMultiply(
  4252       mWidth,
  4253       double(mRatio.height) / mRatio.width);
  4254     return nsSize(mWidth, height);
  4257   MOZ_ASSERT(mHasHeight);
  4258   nscoord width = NSCoordSaturatingNonnegativeMultiply(
  4259     mHeight,
  4260     double(mRatio.width) / mRatio.height);
  4261   return nsSize(width, mHeight);
  4264 CSSSizeOrRatio
  4265 nsImageRenderer::ComputeIntrinsicSize()
  4267   NS_ASSERTION(mIsReady, "Ensure PrepareImage() has returned true "
  4268                          "before calling me");
  4270   CSSSizeOrRatio result;
  4271   switch (mType) {
  4272     case eStyleImageType_Image:
  4274       bool haveWidth, haveHeight;
  4275       nsIntSize imageIntSize;
  4276       nsLayoutUtils::ComputeSizeForDrawing(mImageContainer, imageIntSize,
  4277                                            result.mRatio, haveWidth, haveHeight);
  4278       if (haveWidth) {
  4279         result.SetWidth(nsPresContext::CSSPixelsToAppUnits(imageIntSize.width));
  4281       if (haveHeight) {
  4282         result.SetHeight(nsPresContext::CSSPixelsToAppUnits(imageIntSize.height));
  4284       break;
  4286     case eStyleImageType_Element:
  4288       // XXX element() should have the width/height of the referenced element,
  4289       //     and that element's ratio, if it matches.  If it doesn't match, it
  4290       //     should have no width/height or ratio.  See element() in CSS images:
  4291       //     <http://dev.w3.org/csswg/css-images-4/#element-notation>.
  4292       //     Make sure to change nsStyleBackground::Size::DependsOnFrameSize
  4293       //     when fixing this!
  4294       if (mPaintServerFrame) {
  4295         // SVG images have no intrinsic size
  4296         if (!mPaintServerFrame->IsFrameOfType(nsIFrame::eSVG)) {
  4297           // The intrinsic image size for a generic nsIFrame paint server is
  4298           // the union of the border-box rects of all of its continuations,
  4299           // rounded to device pixels.
  4300           int32_t appUnitsPerDevPixel =
  4301             mForFrame->PresContext()->AppUnitsPerDevPixel();
  4302           result.SetSize(
  4303             nsSVGIntegrationUtils::GetContinuationUnionSize(mPaintServerFrame).
  4304               ToNearestPixels(appUnitsPerDevPixel).
  4305               ToAppUnits(appUnitsPerDevPixel));
  4307       } else {
  4308         NS_ASSERTION(mImageElementSurface.mSourceSurface, "Surface should be ready.");
  4309         gfxIntSize surfaceSize = mImageElementSurface.mSize;
  4310         result.SetSize(
  4311           nsSize(nsPresContext::CSSPixelsToAppUnits(surfaceSize.width),
  4312                  nsPresContext::CSSPixelsToAppUnits(surfaceSize.height)));
  4314       break;
  4316     case eStyleImageType_Gradient:
  4317       // Per <http://dev.w3.org/csswg/css3-images/#gradients>, gradients have no
  4318       // intrinsic dimensions.
  4319     case eStyleImageType_Null:
  4320     default:
  4321       break;
  4324   return result;
  4327 /* static */ nsSize
  4328 nsImageRenderer::ComputeConcreteSize(const CSSSizeOrRatio& aSpecifiedSize,
  4329                                      const CSSSizeOrRatio& aIntrinsicSize,
  4330                                      const nsSize& aDefaultSize)
  4332   // The specified size is fully specified, just use that
  4333   if (aSpecifiedSize.IsConcrete()) {
  4334     return aSpecifiedSize.ComputeConcreteSize();
  4337   MOZ_ASSERT(!aSpecifiedSize.mHasWidth || !aSpecifiedSize.mHasHeight);
  4339   if (!aSpecifiedSize.mHasWidth && !aSpecifiedSize.mHasHeight) {
  4340     // no specified size, try using the intrinsic size
  4341     if (aIntrinsicSize.CanComputeConcreteSize()) {
  4342       return aIntrinsicSize.ComputeConcreteSize();
  4345     if (aIntrinsicSize.mHasWidth) {
  4346       return nsSize(aIntrinsicSize.mWidth, aDefaultSize.height);
  4348     if (aIntrinsicSize.mHasHeight) {
  4349       return nsSize(aDefaultSize.width, aIntrinsicSize.mHeight);
  4352     // couldn't use the intrinsic size either, revert to using the default size
  4353     return ComputeConstrainedSize(aDefaultSize,
  4354                                   aIntrinsicSize.mRatio,
  4355                                   CONTAIN);
  4358   MOZ_ASSERT(aSpecifiedSize.mHasWidth || aSpecifiedSize.mHasHeight);
  4360   // The specified height is partial, try to compute the missing part.
  4361   if (aSpecifiedSize.mHasWidth) {
  4362     nscoord height;
  4363     if (aIntrinsicSize.HasRatio()) {
  4364       height = NSCoordSaturatingNonnegativeMultiply(
  4365         aSpecifiedSize.mWidth,
  4366         double(aIntrinsicSize.mRatio.height) / aIntrinsicSize.mRatio.width);
  4367     } else if (aIntrinsicSize.mHasHeight) {
  4368       height = aIntrinsicSize.mHeight;
  4369     } else {
  4370       height = aDefaultSize.height;
  4372     return nsSize(aSpecifiedSize.mWidth, height);
  4375   MOZ_ASSERT(aSpecifiedSize.mHasHeight);
  4376   nscoord width;
  4377   if (aIntrinsicSize.HasRatio()) {
  4378     width = NSCoordSaturatingNonnegativeMultiply(
  4379       aSpecifiedSize.mHeight,
  4380       double(aIntrinsicSize.mRatio.width) / aIntrinsicSize.mRatio.height);
  4381   } else if (aIntrinsicSize.mHasWidth) {
  4382     width = aIntrinsicSize.mWidth;
  4383   } else {
  4384     width = aDefaultSize.width;
  4386   return nsSize(width, aSpecifiedSize.mHeight);
  4389 /* static */ nsSize
  4390 nsImageRenderer::ComputeConstrainedSize(const nsSize& aConstrainingSize,
  4391                                         const nsSize& aIntrinsicRatio,
  4392                                         FitType aFitType)
  4394   if (aIntrinsicRatio.width <= 0 && aIntrinsicRatio.height <= 0) {
  4395     return aConstrainingSize;
  4398   float scaleX = double(aConstrainingSize.width) / aIntrinsicRatio.width;
  4399   float scaleY = double(aConstrainingSize.height) / aIntrinsicRatio.height;
  4400   nsSize size;
  4401   if ((aFitType == CONTAIN) == (scaleX < scaleY)) {
  4402     size.width = aConstrainingSize.width;
  4403     size.height = NSCoordSaturatingNonnegativeMultiply(
  4404                     aIntrinsicRatio.height, scaleX);
  4405   } else {
  4406     size.width = NSCoordSaturatingNonnegativeMultiply(
  4407                    aIntrinsicRatio.width, scaleY);
  4408     size.height = aConstrainingSize.height;
  4410   return size;
  4413 /**
  4414  * mSize is the image's "preferred" size for this particular rendering, while
  4415  * the drawn (aka concrete) size is the actual rendered size after accounting
  4416  * for background-size etc..  The preferred size is most often the image's
  4417  * intrinsic dimensions.  But for images with incomplete intrinsic dimensions,
  4418  * the preferred size varies, depending on the specified and default sizes, see
  4419  * nsImageRenderer::Compute*Size.
  4421  * This distinction is necessary because the components of a vector image are
  4422  * specified with respect to its preferred size for a rendering situation, not
  4423  * to its actual rendered size.  For example, consider a 4px wide background
  4424  * vector image with no height which contains a left-aligned
  4425  * 2px wide black rectangle with height 100%.  If the background-size width is
  4426  * auto (or 4px), the vector image will render 4px wide, and the black rectangle
  4427  * will be 2px wide.  If the background-size width is 8px, the vector image will
  4428  * render 8px wide, and the black rectangle will be 4px wide -- *not* 2px wide.
  4429  * In both cases mSize.width will be 4px; but in the first case the returned
  4430  * width will be 4px, while in the second case the returned width will be 8px.
  4431  */
  4432 void
  4433 nsImageRenderer::SetPreferredSize(const CSSSizeOrRatio& aIntrinsicSize,
  4434                                   const nsSize& aDefaultSize)
  4436   mSize.width = aIntrinsicSize.mHasWidth
  4437                   ? aIntrinsicSize.mWidth
  4438                   : aDefaultSize.width;
  4439   mSize.height = aIntrinsicSize.mHasHeight
  4440                   ? aIntrinsicSize.mHeight
  4441                   : aDefaultSize.height;
  4444 // Convert from nsImageRenderer flags to the flags we want to use for drawing in
  4445 // the imgIContainer namespace.
  4446 static uint32_t
  4447 ConvertImageRendererToDrawFlags(uint32_t aImageRendererFlags)
  4449   uint32_t drawFlags = imgIContainer::FLAG_NONE;
  4450   if (aImageRendererFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES) {
  4451     drawFlags |= imgIContainer::FLAG_SYNC_DECODE;
  4453   if (aImageRendererFlags & nsImageRenderer::FLAG_PAINTING_TO_WINDOW) {
  4454     drawFlags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
  4456   return drawFlags;
  4459 void
  4460 nsImageRenderer::Draw(nsPresContext*       aPresContext,
  4461                       nsRenderingContext&  aRenderingContext,
  4462                       const nsRect&        aDirtyRect,
  4463                       const nsRect&        aFill,
  4464                       const nsRect&        aDest,
  4465                       const CSSIntRect&    aSrc)
  4467   if (!mIsReady) {
  4468     NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
  4469     return;
  4471   if (aDest.IsEmpty() || aFill.IsEmpty() ||
  4472       mSize.width <= 0 || mSize.height <= 0) {
  4473     return;
  4476   GraphicsFilter graphicsFilter =
  4477     nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame);
  4479   switch (mType) {
  4480     case eStyleImageType_Image:
  4482       nsLayoutUtils::DrawSingleImage(&aRenderingContext, mImageContainer,
  4483                                      graphicsFilter, aFill, aDirtyRect,
  4484                                      nullptr,
  4485                                      ConvertImageRendererToDrawFlags(mFlags));
  4486       return;
  4488     case eStyleImageType_Gradient:
  4490       nsCSSRendering::PaintGradient(aPresContext, aRenderingContext,
  4491                                     mGradientData, aDirtyRect,
  4492                                     aDest, aFill, aSrc, mSize);
  4493       return;
  4495     case eStyleImageType_Element:
  4497       nsRefPtr<gfxDrawable> drawable = DrawableForElement(aDest,
  4498                                                           aRenderingContext);
  4499       if (!drawable) {
  4500         NS_WARNING("Could not create drawable for element");
  4501         return;
  4503       nsLayoutUtils::DrawPixelSnapped(&aRenderingContext, drawable, graphicsFilter,
  4504                                       aDest, aFill, aDest.TopLeft(), aDirtyRect);
  4505       return;
  4507     case eStyleImageType_Null:
  4508     default:
  4509       return;
  4513 already_AddRefed<gfxDrawable>
  4514 nsImageRenderer::DrawableForElement(const nsRect& aImageRect,
  4515                                     nsRenderingContext&  aRenderingContext)
  4517   NS_ASSERTION(mType == eStyleImageType_Element,
  4518                "DrawableForElement only makes sense if backed by an element");
  4519   if (mPaintServerFrame) {
  4520     int32_t appUnitsPerDevPixel = mForFrame->PresContext()->AppUnitsPerDevPixel();
  4521     nsRect destRect = aImageRect - aImageRect.TopLeft();
  4522     nsIntSize roundedOut = destRect.ToOutsidePixels(appUnitsPerDevPixel).Size();
  4523     gfxIntSize imageSize(roundedOut.width, roundedOut.height);
  4524     nsRefPtr<gfxDrawable> drawable =
  4525       nsSVGIntegrationUtils::DrawableFromPaintServer(
  4526         mPaintServerFrame, mForFrame, mSize, imageSize,
  4527         aRenderingContext.ThebesContext()->CurrentMatrix(),
  4528         mFlags & FLAG_SYNC_DECODE_IMAGES
  4529           ? nsSVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES
  4530           : 0);
  4532     return drawable.forget();
  4534   NS_ASSERTION(mImageElementSurface.mSourceSurface, "Surface should be ready.");
  4535   nsRefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(
  4536                                 mImageElementSurface.mSourceSurface,
  4537                                 mImageElementSurface.mSize);
  4538   return drawable.forget();
  4541 void
  4542 nsImageRenderer::DrawBackground(nsPresContext*       aPresContext,
  4543                                 nsRenderingContext&  aRenderingContext,
  4544                                 const nsRect&        aDest,
  4545                                 const nsRect&        aFill,
  4546                                 const nsPoint&       aAnchor,
  4547                                 const nsRect&        aDirty)
  4549   if (!mIsReady) {
  4550     NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
  4551     return;
  4553   if (aDest.IsEmpty() || aFill.IsEmpty() ||
  4554       mSize.width <= 0 || mSize.height <= 0) {
  4555     return;
  4558   if (mType == eStyleImageType_Image) {
  4559     GraphicsFilter graphicsFilter =
  4560       nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame);
  4562     nsLayoutUtils::DrawBackgroundImage(&aRenderingContext, mImageContainer,
  4563                 nsIntSize(nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
  4564                           nsPresContext::AppUnitsToIntCSSPixels(mSize.height)),
  4565                 graphicsFilter,
  4566                 aDest, aFill, aAnchor, aDirty,
  4567                 ConvertImageRendererToDrawFlags(mFlags));
  4568     return;
  4571   Draw(aPresContext, aRenderingContext,
  4572        aDirty, aFill, aDest,
  4573        CSSIntRect(0, 0,
  4574                   nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
  4575                   nsPresContext::AppUnitsToIntCSSPixels(mSize.height)));
  4578 /**
  4579  * Compute the size and position of the master copy of the image. I.e., a single
  4580  * tile used to fill the dest rect.
  4581  * aFill The destination rect to be filled
  4582  * aHFill and aVFill are the repeat patterns for the component -
  4583  * NS_STYLE_BORDER_IMAGE_REPEAT_* - i.e., how a tiling unit is used to fill aFill
  4584  * aUnitSize The size of the source rect in dest coords.
  4585  */
  4586 static nsRect
  4587 ComputeTile(const nsRect&        aFill,
  4588             uint8_t              aHFill,
  4589             uint8_t              aVFill,
  4590             const nsSize&        aUnitSize)
  4592   nsRect tile;
  4593   switch (aHFill) {
  4594   case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH:
  4595     tile.x = aFill.x;
  4596     tile.width = aFill.width;
  4597     break;
  4598   case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT:
  4599     tile.x = aFill.x + aFill.width/2 - aUnitSize.width/2;
  4600     tile.width = aUnitSize.width;
  4601     break;
  4602   case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND:
  4603     tile.x = aFill.x;
  4604     tile.width = aFill.width / ceil(gfxFloat(aFill.width)/aUnitSize.width);
  4605     break;
  4606   default:
  4607     NS_NOTREACHED("unrecognized border-image fill style");
  4610   switch (aVFill) {
  4611   case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH:
  4612     tile.y = aFill.y;
  4613     tile.height = aFill.height;
  4614     break;
  4615   case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT:
  4616     tile.y = aFill.y + aFill.height/2 - aUnitSize.height/2;
  4617     tile.height = aUnitSize.height;
  4618     break;
  4619   case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND:
  4620     tile.y = aFill.y;
  4621     tile.height = aFill.height/ceil(gfxFloat(aFill.height)/aUnitSize.height);
  4622     break;
  4623   default:
  4624     NS_NOTREACHED("unrecognized border-image fill style");
  4627   return tile;
  4630 /**
  4631  * Returns true if the given set of arguments will require the tiles which fill
  4632  * the dest rect to be scaled from the source tile. See comment on ComputeTile
  4633  * for argument descriptions.
  4634  */
  4635 static bool
  4636 RequiresScaling(const nsRect&        aFill,
  4637                 uint8_t              aHFill,
  4638                 uint8_t              aVFill,
  4639                 const nsSize&        aUnitSize)
  4641   // If we have no tiling in either direction, we can skip the intermediate
  4642   // scaling step.
  4643   return (aHFill != NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH ||
  4644           aVFill != NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH) &&
  4645          (aUnitSize.width != aFill.width ||
  4646           aUnitSize.height != aFill.height);
  4649 void
  4650 nsImageRenderer::DrawBorderImageComponent(nsPresContext*       aPresContext,
  4651                                           nsRenderingContext&  aRenderingContext,
  4652                                           const nsRect&        aDirtyRect,
  4653                                           const nsRect&        aFill,
  4654                                           const CSSIntRect&    aSrc,
  4655                                           uint8_t              aHFill,
  4656                                           uint8_t              aVFill,
  4657                                           const nsSize&        aUnitSize,
  4658                                           uint8_t              aIndex)
  4660   if (!mIsReady) {
  4661     NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
  4662     return;
  4664   if (aFill.IsEmpty() || aSrc.IsEmpty()) {
  4665     return;
  4668   if (mType == eStyleImageType_Image) {
  4669     nsCOMPtr<imgIContainer> subImage;
  4670     if ((subImage = mImage->GetSubImage(aIndex)) == nullptr) {
  4671       subImage = ImageOps::Clip(mImageContainer, nsIntRect(aSrc.x,
  4672                                                            aSrc.y,
  4673                                                            aSrc.width,
  4674                                                            aSrc.height));
  4675       mImage->SetSubImage(aIndex, subImage);
  4678     GraphicsFilter graphicsFilter =
  4679       nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame);
  4681     if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) {
  4682       nsLayoutUtils::DrawSingleImage(&aRenderingContext,
  4683                                      subImage,
  4684                                      graphicsFilter,
  4685                                      aFill, aDirtyRect,
  4686                                      nullptr,
  4687                                      imgIContainer::FLAG_NONE);
  4688       return;
  4691     nsRect tile = ComputeTile(aFill, aHFill, aVFill, aUnitSize);
  4692     nsLayoutUtils::DrawImage(&aRenderingContext,
  4693                              subImage,
  4694                              graphicsFilter,
  4695                              tile, aFill, tile.TopLeft(), aDirtyRect,
  4696                              imgIContainer::FLAG_NONE);
  4697     return;
  4700   nsRect destTile = RequiresScaling(aFill, aHFill, aVFill, aUnitSize)
  4701                   ? ComputeTile(aFill, aHFill, aVFill, aUnitSize)
  4702                   : aFill;
  4704   if (mType == eStyleImageType_Element) {
  4705     // This path is horribly slow - we read and copy the source nine times(!)
  4706     // It could be easily optimised by only reading the source once and caching
  4707     // it. It could be further optimised by caching the sub-images between draws
  4708     // but that would be a bit harder because you would have to know when to
  4709     // invalidate the cache. A special case optimisation would be when
  4710     // border-image-slice is proportional to the border widths, in which case
  4711     // the subimages do not need to be independently scaled, then we don't need
  4712     // subimages at all.
  4713     // In any case, such optimisations are probably not worth doing because it
  4714     // seems unlikely anyone would use -moz-element as the source for a border
  4715     // image.
  4717     // draw the source image slice into an intermediate surface
  4718     nsPresContext* presContext = mForFrame->PresContext();
  4719     gfxRect srcRect = gfxRect(presContext->CSSPixelsToDevPixels(aSrc.x),
  4720                               presContext->CSSPixelsToDevPixels(aSrc.y),
  4721                               presContext->CSSPixelsToDevPixels(aSrc.width),
  4722                               presContext->CSSPixelsToDevPixels(aSrc.height));
  4723     RefPtr<DrawTarget> srcSlice = gfxPlatform::GetPlatform()->
  4724       CreateOffscreenContentDrawTarget(IntSize(srcRect.width, srcRect.height),
  4725                              SurfaceFormat::B8G8R8A8);
  4726     nsRefPtr<gfxContext> ctx = new gfxContext(srcSlice);
  4728     // grab the entire source
  4729     nsRefPtr<gfxDrawable> drawable = DrawableForElement(nsRect(nsPoint(), mSize),
  4730                                                         aRenderingContext);
  4731     if (!drawable) {
  4732       NS_WARNING("Could not create drawable for element");
  4733       return;
  4736     // draw the source into our intermediate surface
  4737     GraphicsFilter graphicsFilter =
  4738       nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame);
  4739     gfxMatrix transform;
  4740     transform.Translate(gfxPoint(srcRect.x, srcRect.y));
  4741     bool success = drawable->Draw(ctx,
  4742                                   gfxRect(0, 0, srcRect.width, srcRect.height),
  4743                                   false,
  4744                                   graphicsFilter,
  4745                                   transform);
  4746     if (!success) {
  4747       NS_WARNING("Could not copy element image");
  4748       return;
  4751     // Ensure that drawing the image gets flushed to the target.
  4752     ctx = nullptr;
  4754     // draw the slice
  4755     nsRefPtr<gfxSurfaceDrawable> srcSliceDrawable =
  4756       new gfxSurfaceDrawable(srcSlice,
  4757                              gfxIntSize(srcRect.width, srcRect.height));
  4758     nsPoint anchor(nsPresContext::CSSPixelsToAppUnits(aSrc.x),
  4759                    nsPresContext::CSSPixelsToAppUnits(aSrc.y));
  4760     nsLayoutUtils::DrawPixelSnapped(&aRenderingContext, srcSliceDrawable,
  4761                                     graphicsFilter, destTile, aFill,
  4762                                     anchor, aDirtyRect);
  4764     return;
  4767   Draw(aPresContext, aRenderingContext, aDirtyRect, aFill, destTile, aSrc);
  4770 bool
  4771 nsImageRenderer::IsRasterImage()
  4773   if (mType != eStyleImageType_Image || !mImageContainer)
  4774     return false;
  4775   return mImageContainer->GetType() == imgIContainer::TYPE_RASTER;
  4778 bool
  4779 nsImageRenderer::IsAnimatedImage()
  4781   if (mType != eStyleImageType_Image || !mImageContainer)
  4782     return false;
  4783   bool animated = false;
  4784   if (NS_SUCCEEDED(mImageContainer->GetAnimated(&animated)) && animated)
  4785     return true;
  4787   return false;
  4790 already_AddRefed<mozilla::layers::ImageContainer>
  4791 nsImageRenderer::GetContainer(LayerManager* aManager)
  4793   if (mType != eStyleImageType_Image || !mImageContainer)
  4794     return nullptr;
  4796   nsRefPtr<ImageContainer> container;
  4797   nsresult rv = mImageContainer->GetImageContainer(aManager, getter_AddRefs(container));
  4798   NS_ENSURE_SUCCESS(rv, nullptr);
  4799   return container.forget();
  4802 #define MAX_BLUR_RADIUS 300
  4803 #define MAX_SPREAD_RADIUS 50
  4805 static inline gfxPoint ComputeBlurStdDev(nscoord aBlurRadius,
  4806                                          int32_t aAppUnitsPerDevPixel,
  4807                                          gfxFloat aScaleX,
  4808                                          gfxFloat aScaleY)
  4810   // http://dev.w3.org/csswg/css3-background/#box-shadow says that the
  4811   // standard deviation of the blur should be half the given blur value.
  4812   gfxFloat blurStdDev = gfxFloat(aBlurRadius) / gfxFloat(aAppUnitsPerDevPixel);
  4814   return gfxPoint(std::min((blurStdDev * aScaleX),
  4815                            gfxFloat(MAX_BLUR_RADIUS)) / 2.0,
  4816                   std::min((blurStdDev * aScaleY),
  4817                            gfxFloat(MAX_BLUR_RADIUS)) / 2.0);
  4820 static inline gfxIntSize
  4821 ComputeBlurRadius(nscoord aBlurRadius,
  4822                   int32_t aAppUnitsPerDevPixel,
  4823                   gfxFloat aScaleX = 1.0,
  4824                   gfxFloat aScaleY = 1.0)
  4826   gfxPoint scaledBlurStdDev = ComputeBlurStdDev(aBlurRadius, aAppUnitsPerDevPixel,
  4827                                                 aScaleX, aScaleY);
  4828   return
  4829     gfxAlphaBoxBlur::CalculateBlurRadius(scaledBlurStdDev);
  4832 // -----
  4833 // nsContextBoxBlur
  4834 // -----
  4835 gfxContext*
  4836 nsContextBoxBlur::Init(const nsRect& aRect, nscoord aSpreadRadius,
  4837                        nscoord aBlurRadius,
  4838                        int32_t aAppUnitsPerDevPixel,
  4839                        gfxContext* aDestinationCtx,
  4840                        const nsRect& aDirtyRect,
  4841                        const gfxRect* aSkipRect,
  4842                        uint32_t aFlags)
  4844   if (aRect.IsEmpty()) {
  4845     mContext = nullptr;
  4846     return nullptr;
  4849   gfxFloat scaleX = 1;
  4850   gfxFloat scaleY = 1;
  4852   // Do blurs in device space when possible.
  4853   // Chrome/Skia always does the blurs in device space
  4854   // and will sometimes get incorrect results (e.g. rotated blurs)
  4855   gfxMatrix transform = aDestinationCtx->CurrentMatrix();
  4856   // XXX: we could probably handle negative scales but for now it's easier just to fallback
  4857   if (transform.HasNonAxisAlignedTransform() || transform.xx <= 0.0 || transform.yy <= 0.0) {
  4858     transform = gfxMatrix();
  4859   } else {
  4860     scaleX = transform.xx;
  4861     scaleY = transform.yy;
  4864   // compute a large or smaller blur radius
  4865   gfxIntSize blurRadius = ComputeBlurRadius(aBlurRadius, aAppUnitsPerDevPixel, scaleX, scaleY);
  4866   gfxIntSize spreadRadius = gfxIntSize(std::min(int32_t(aSpreadRadius * scaleX / aAppUnitsPerDevPixel),
  4867                                               int32_t(MAX_SPREAD_RADIUS)),
  4868                                        std::min(int32_t(aSpreadRadius * scaleY / aAppUnitsPerDevPixel),
  4869                                               int32_t(MAX_SPREAD_RADIUS)));
  4870   mDestinationCtx = aDestinationCtx;
  4872   // If not blurring, draw directly onto the destination device
  4873   if (blurRadius.width <= 0 && blurRadius.height <= 0 &&
  4874       spreadRadius.width <= 0 && spreadRadius.height <= 0 &&
  4875       !(aFlags & FORCE_MASK)) {
  4876     mContext = aDestinationCtx;
  4877     return mContext;
  4880   // Convert from app units to device pixels
  4881   gfxRect rect = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerDevPixel);
  4883   gfxRect dirtyRect =
  4884     nsLayoutUtils::RectToGfxRect(aDirtyRect, aAppUnitsPerDevPixel);
  4885   dirtyRect.RoundOut();
  4887   rect = transform.TransformBounds(rect);
  4889   mPreTransformed = !transform.IsIdentity();
  4891   // Create the temporary surface for blurring
  4892   dirtyRect = transform.TransformBounds(dirtyRect);
  4893   if (aSkipRect) {
  4894     gfxRect skipRect = transform.TransformBounds(*aSkipRect);
  4895     mContext = blur.Init(rect, spreadRadius,
  4896                          blurRadius, &dirtyRect, &skipRect);
  4897   } else {
  4898     mContext = blur.Init(rect, spreadRadius,
  4899                          blurRadius, &dirtyRect, nullptr);
  4902   if (mContext) {
  4903     // we don't need to blur if skipRect is equal to rect
  4904     // and mContext will be nullptr
  4905     mContext->Multiply(transform);
  4907   return mContext;
  4910 void
  4911 nsContextBoxBlur::DoPaint()
  4913   if (mContext == mDestinationCtx)
  4914     return;
  4916   gfxContextMatrixAutoSaveRestore saveMatrix(mDestinationCtx);
  4918   if (mPreTransformed) {
  4919     mDestinationCtx->IdentityMatrix();
  4922   blur.Paint(mDestinationCtx);
  4925 gfxContext*
  4926 nsContextBoxBlur::GetContext()
  4928   return mContext;
  4931 /* static */ nsMargin
  4932 nsContextBoxBlur::GetBlurRadiusMargin(nscoord aBlurRadius,
  4933                                       int32_t aAppUnitsPerDevPixel)
  4935   gfxIntSize blurRadius = ComputeBlurRadius(aBlurRadius, aAppUnitsPerDevPixel);
  4937   nsMargin result;
  4938   result.top    = blurRadius.height * aAppUnitsPerDevPixel;
  4939   result.right  = blurRadius.width  * aAppUnitsPerDevPixel;
  4940   result.bottom = blurRadius.height * aAppUnitsPerDevPixel;
  4941   result.left   = blurRadius.width  * aAppUnitsPerDevPixel;
  4942   return result;
  4945 /* static */ void
  4946 nsContextBoxBlur::BlurRectangle(gfxContext* aDestinationCtx,
  4947                                 const nsRect& aRect,
  4948                                 int32_t aAppUnitsPerDevPixel,
  4949                                 gfxCornerSizes* aCornerRadii,
  4950                                 nscoord aBlurRadius,
  4951                                 const gfxRGBA& aShadowColor,
  4952                                 const nsRect& aDirtyRect,
  4953                                 const gfxRect& aSkipRect)
  4955   if (aRect.IsEmpty()) {
  4956     return;
  4959   gfxRect shadowGfxRect =
  4960     nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerDevPixel);
  4962   if (aBlurRadius <= 0) {
  4963     aDestinationCtx->SetColor(aShadowColor);
  4964     aDestinationCtx->NewPath();
  4965     if (aCornerRadii) {
  4966       aDestinationCtx->RoundedRectangle(shadowGfxRect, *aCornerRadii);
  4967     } else {
  4968       aDestinationCtx->Rectangle(shadowGfxRect);
  4971     aDestinationCtx->Fill();
  4972     return;
  4975   gfxFloat scaleX = 1;
  4976   gfxFloat scaleY = 1;
  4978   // Do blurs in device space when possible.
  4979   // Chrome/Skia always does the blurs in device space
  4980   // and will sometimes get incorrect results (e.g. rotated blurs)
  4981   gfxMatrix transform = aDestinationCtx->CurrentMatrix();
  4982   // XXX: we could probably handle negative scales but for now it's easier just to fallback
  4983   if (!transform.HasNonAxisAlignedTransform() && transform.xx > 0.0 && transform.yy > 0.0) {
  4984     scaleX = transform.xx;
  4985     scaleY = transform.yy;
  4986     aDestinationCtx->IdentityMatrix();
  4987   } else {
  4988     transform = gfxMatrix();
  4991   gfxPoint blurStdDev = ComputeBlurStdDev(aBlurRadius, aAppUnitsPerDevPixel, scaleX, scaleY);
  4993   gfxRect dirtyRect =
  4994     nsLayoutUtils::RectToGfxRect(aDirtyRect, aAppUnitsPerDevPixel);
  4995   dirtyRect.RoundOut();
  4997   shadowGfxRect = transform.TransformBounds(shadowGfxRect);
  4998   dirtyRect = transform.TransformBounds(dirtyRect);
  4999   gfxRect skipRect = transform.TransformBounds(aSkipRect);
  5001   if (aCornerRadii) {
  5002     aCornerRadii->Scale(scaleX, scaleY);
  5005   gfxAlphaBoxBlur::BlurRectangle(aDestinationCtx,
  5006                                  shadowGfxRect,
  5007                                  aCornerRadii,
  5008                                  blurStdDev,
  5009                                  aShadowColor,
  5010                                  dirtyRect,
  5011                                  skipRect);

mercurial