Home | History | Annotate | Download | only in paint
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "config.h"
      6 #include "core/paint/BoxPainter.h"
      7 
      8 #include "core/HTMLNames.h"
      9 #include "core/frame/Settings.h"
     10 #include "core/html/HTMLFrameOwnerElement.h"
     11 #include "core/paint/BackgroundImageGeometry.h"
     12 #include "core/paint/BoxDecorationData.h"
     13 #include "core/rendering/ImageQualityController.h"
     14 #include "core/rendering/PaintInfo.h"
     15 #include "core/rendering/RenderBox.h"
     16 #include "core/rendering/RenderBoxModelObject.h"
     17 #include "core/rendering/RenderLayer.h"
     18 #include "core/rendering/RenderObject.h"
     19 #include "core/rendering/RenderTable.h"
     20 #include "core/rendering/RenderTheme.h"
     21 #include "core/rendering/RenderView.h"
     22 #include "core/rendering/compositing/CompositedLayerMapping.h"
     23 #include "core/rendering/style/BorderEdge.h"
     24 #include "core/rendering/style/ShadowList.h"
     25 #include "platform/LengthFunctions.h"
     26 #include "platform/geometry/LayoutPoint.h"
     27 #include "platform/graphics/GraphicsContextStateSaver.h"
     28 
     29 namespace blink {
     30 
     31 void BoxPainter::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
     32 {
     33     LayoutPoint adjustedPaintOffset = paintOffset + m_renderBox.location();
     34     // default implementation. Just pass paint through to the children
     35     PaintInfo childInfo(paintInfo);
     36     childInfo.updatePaintingRootForChildren(&m_renderBox);
     37     for (RenderObject* child = m_renderBox.slowFirstChild(); child; child = child->nextSibling())
     38         child->paint(childInfo, adjustedPaintOffset);
     39 }
     40 
     41 void BoxPainter::paintBoxDecorationBackground(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
     42 {
     43     if (!paintInfo.shouldPaintWithinRoot(&m_renderBox))
     44         return;
     45 
     46     LayoutRect paintRect = m_renderBox.borderBoxRect();
     47     paintRect.moveBy(paintOffset);
     48     paintBoxDecorationBackgroundWithRect(paintInfo, paintOffset, paintRect);
     49 }
     50 
     51 void BoxPainter::paintBoxDecorationBackgroundWithRect(PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutRect& paintRect)
     52 {
     53     RenderStyle* style = m_renderBox.style();
     54     BoxDecorationData boxDecorationData(*style, m_renderBox.canRenderBorderImage(), m_renderBox.backgroundHasOpaqueTopLayer(), paintInfo.context);
     55 
     56     // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have
     57     // custom shadows of their own.
     58     if (!m_renderBox.boxShadowShouldBeAppliedToBackground(boxDecorationData.bleedAvoidance()))
     59         paintBoxShadow(paintInfo, paintRect, style, Normal);
     60 
     61     GraphicsContextStateSaver stateSaver(*paintInfo.context, false);
     62     if (boxDecorationData.bleedAvoidance() == BackgroundBleedClipBackground) {
     63         stateSaver.save();
     64         RoundedRect border = style->getRoundedBorderFor(paintRect);
     65         paintInfo.context->clipRoundedRect(border);
     66     }
     67 
     68     // If we have a native theme appearance, paint that before painting our background.
     69     // The theme will tell us whether or not we should also paint the CSS background.
     70     IntRect snappedPaintRect(pixelSnappedIntRect(paintRect));
     71     bool themePainted = boxDecorationData.hasAppearance && !RenderTheme::theme().paint(&m_renderBox, paintInfo, snappedPaintRect);
     72     if (!themePainted) {
     73         if (boxDecorationData.bleedAvoidance() == BackgroundBleedBackgroundOverBorder)
     74             paintBorder(m_renderBox, paintInfo, paintRect, style, boxDecorationData.bleedAvoidance());
     75 
     76         paintBackground(paintInfo, paintRect, boxDecorationData.backgroundColor, boxDecorationData.bleedAvoidance());
     77 
     78         if (boxDecorationData.hasAppearance)
     79             RenderTheme::theme().paintDecorations(&m_renderBox, paintInfo, snappedPaintRect);
     80     }
     81     paintBoxShadow(paintInfo, paintRect, style, Inset);
     82 
     83     // The theme will tell us whether or not we should also paint the CSS border.
     84     if (boxDecorationData.hasBorder && boxDecorationData.bleedAvoidance() != BackgroundBleedBackgroundOverBorder
     85         && (!boxDecorationData.hasAppearance || (!themePainted && RenderTheme::theme().paintBorderOnly(&m_renderBox, paintInfo, snappedPaintRect)))
     86         && !(m_renderBox.isTable() && toRenderTable(&m_renderBox)->collapseBorders()))
     87         paintBorder(m_renderBox, paintInfo, paintRect, style, boxDecorationData.bleedAvoidance());
     88 }
     89 
     90 static bool skipBodyBackground(const RenderBox* bodyElementRenderer)
     91 {
     92     ASSERT(bodyElementRenderer->isBody());
     93     // The <body> only paints its background if the root element has defined a background independent of the body,
     94     // or if the <body>'s parent is not the document element's renderer (e.g. inside SVG foreignObject).
     95     RenderObject* documentElementRenderer = bodyElementRenderer->document().documentElement()->renderer();
     96     return documentElementRenderer
     97         && !documentElementRenderer->hasBackground()
     98         && (documentElementRenderer == bodyElementRenderer->parent());
     99 }
    100 
    101 void BoxPainter::paintBackground(const PaintInfo& paintInfo, const LayoutRect& paintRect, const Color& backgroundColor, BackgroundBleedAvoidance bleedAvoidance)
    102 {
    103     if (m_renderBox.isDocumentElement()) {
    104         paintRootBoxFillLayers(paintInfo);
    105         return;
    106     }
    107     if (m_renderBox.isBody() && skipBodyBackground(&m_renderBox))
    108         return;
    109     if (m_renderBox.boxDecorationBackgroundIsKnownToBeObscured())
    110         return;
    111     paintFillLayers(paintInfo, backgroundColor, m_renderBox.style()->backgroundLayers(), paintRect, bleedAvoidance);
    112 }
    113 
    114 void BoxPainter::paintRootBoxFillLayers(const PaintInfo& paintInfo)
    115 {
    116     if (paintInfo.skipRootBackground())
    117         return;
    118 
    119     RenderObject* rootBackgroundRenderer = m_renderBox.rendererForRootBackground();
    120 
    121     const FillLayer& bgLayer = rootBackgroundRenderer->style()->backgroundLayers();
    122     Color bgColor = rootBackgroundRenderer->resolveColor(CSSPropertyBackgroundColor);
    123 
    124     paintFillLayers(paintInfo, bgColor, bgLayer, m_renderBox.view()->backgroundRect(&m_renderBox), BackgroundBleedNone, CompositeSourceOver, rootBackgroundRenderer);
    125 }
    126 
    127 void BoxPainter::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect,
    128     BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject)
    129 {
    130     Vector<const FillLayer*, 8> layers;
    131     const FillLayer* curLayer = &fillLayer;
    132     bool shouldDrawBackgroundInSeparateBuffer = false;
    133     bool isBottomLayerOccluded = false;
    134     while (curLayer) {
    135         layers.append(curLayer);
    136         // Stop traversal when an opaque layer is encountered.
    137         // FIXME : It would be possible for the following occlusion culling test to be more aggressive
    138         // on layers with no repeat by testing whether the image covers the layout rect.
    139         // Testing that here would imply duplicating a lot of calculations that are currently done in
    140         // RenderBoxModelObject::paintFillLayerExtended. A more efficient solution might be to move
    141         // the layer recursion into paintFillLayerExtended, or to compute the layer geometry here
    142         // and pass it down.
    143 
    144         if (!shouldDrawBackgroundInSeparateBuffer && curLayer->blendMode() != WebBlendModeNormal)
    145             shouldDrawBackgroundInSeparateBuffer = true;
    146 
    147         // The clipOccludesNextLayers condition must be evaluated first to avoid short-circuiting.
    148         if (curLayer->clipOccludesNextLayers(curLayer == &fillLayer)
    149             && curLayer->hasOpaqueImage(&m_renderBox)
    150             && curLayer->image()->canRender(m_renderBox, m_renderBox.style()->effectiveZoom())
    151             && curLayer->hasRepeatXY()
    152             && curLayer->blendMode() == WebBlendModeNormal
    153             && !m_renderBox.boxShadowShouldBeAppliedToBackground(bleedAvoidance))
    154             break;
    155         curLayer = curLayer->next();
    156     }
    157 
    158     if (layers.size() > 0  && (**layers.rbegin()).next())
    159         isBottomLayerOccluded = true;
    160 
    161     GraphicsContext* context = paintInfo.context;
    162     if (!context)
    163         shouldDrawBackgroundInSeparateBuffer = false;
    164 
    165     bool skipBaseColor = false;
    166     if (shouldDrawBackgroundInSeparateBuffer) {
    167         bool isBaseColorVisible = !isBottomLayerOccluded && c.hasAlpha();
    168 
    169         // Paint the document's base background color outside the transparency layer,
    170         // so that the background images don't blend with this color: http://crbug.com/389039.
    171         if (isBaseColorVisible && isDocumentElementWithOpaqueBackground(m_renderBox)) {
    172             paintRootBackgroundColor(m_renderBox, paintInfo, rect, Color());
    173             skipBaseColor = true;
    174         }
    175         context->beginTransparencyLayer(1);
    176     }
    177 
    178     Vector<const FillLayer*>::const_reverse_iterator topLayer = layers.rend();
    179     for (Vector<const FillLayer*>::const_reverse_iterator it = layers.rbegin(); it != topLayer; ++it)
    180         paintFillLayer(paintInfo, c, **it, rect, bleedAvoidance, op, backgroundObject, skipBaseColor);
    181 
    182     if (shouldDrawBackgroundInSeparateBuffer)
    183         context->endLayer();
    184 }
    185 
    186 void BoxPainter::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect,
    187     BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject, bool skipBaseColor)
    188 {
    189     BoxPainter::paintFillLayerExtended(m_renderBox, paintInfo, c, fillLayer, rect, bleedAvoidance, 0, LayoutSize(), op, backgroundObject, skipBaseColor);
    190 }
    191 
    192 void BoxPainter::applyBoxShadowForBackground(GraphicsContext* context, RenderObject& obj)
    193 {
    194     const ShadowList* shadowList = obj.style()->boxShadow();
    195     ASSERT(shadowList);
    196     for (size_t i = shadowList->shadows().size(); i--; ) {
    197         const ShadowData& boxShadow = shadowList->shadows()[i];
    198         if (boxShadow.style() != Normal)
    199             continue;
    200         FloatSize shadowOffset(boxShadow.x(), boxShadow.y());
    201         context->setShadow(shadowOffset, boxShadow.blur(), boxShadow.color(),
    202             DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha);
    203         return;
    204     }
    205 }
    206 
    207 // FIXME: See crbug.com/382491. The use of getCTM in this context is incorrect because the matrix returned does not
    208 // include scales applied at raster time, such as the device zoom.
    209 static LayoutRect shrinkRectByOnePixel(GraphicsContext* context, const LayoutRect& rect)
    210 {
    211     LayoutRect shrunkRect = rect;
    212     AffineTransform transform = context->getCTM();
    213     shrunkRect.inflateX(-static_cast<LayoutUnit>(ceil(1 / transform.xScale())));
    214     shrunkRect.inflateY(-static_cast<LayoutUnit>(ceil(1 / transform.yScale())));
    215     return shrunkRect;
    216 }
    217 
    218 RoundedRect BoxPainter::getBackgroundRoundedRect(RenderObject& obj, const LayoutRect& borderRect, InlineFlowBox* box, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHeight,
    219     bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
    220 {
    221     RoundedRect border = obj.style()->getRoundedBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge);
    222     if (box && (box->nextLineBox() || box->prevLineBox())) {
    223         RoundedRect segmentBorder = obj.style()->getRoundedBorderFor(LayoutRect(0, 0, inlineBoxWidth, inlineBoxHeight), includeLogicalLeftEdge, includeLogicalRightEdge);
    224         border.setRadii(segmentBorder.radii());
    225     }
    226 
    227     return border;
    228 }
    229 
    230 RoundedRect BoxPainter::backgroundRoundedRectAdjustedForBleedAvoidance(RenderObject& obj, GraphicsContext* context, const LayoutRect& borderRect, BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
    231 {
    232     if (bleedAvoidance == BackgroundBleedShrinkBackground) {
    233         // We shrink the rectangle by one pixel on each side because the bleed is one pixel maximum.
    234         return BoxPainter::getBackgroundRoundedRect(obj, shrinkRectByOnePixel(context, borderRect), box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogicalRightEdge);
    235     }
    236     if (bleedAvoidance == BackgroundBleedBackgroundOverBorder)
    237         return obj.style()->getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge);
    238 
    239     return BoxPainter::getBackgroundRoundedRect(obj, borderRect, box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogicalRightEdge);
    240 }
    241 
    242 void BoxPainter::clipRoundedInnerRect(GraphicsContext * context, const LayoutRect& rect, const RoundedRect& clipRect)
    243 {
    244     if (clipRect.isRenderable()) {
    245         context->clipRoundedRect(clipRect);
    246     } else {
    247         // We create a rounded rect for each of the corners and clip it, while making sure we clip opposing corners together.
    248         if (!clipRect.radii().topLeft().isEmpty() || !clipRect.radii().bottomRight().isEmpty()) {
    249             IntRect topCorner(clipRect.rect().x(), clipRect.rect().y(), rect.maxX() - clipRect.rect().x(), rect.maxY() - clipRect.rect().y());
    250             RoundedRect::Radii topCornerRadii;
    251             topCornerRadii.setTopLeft(clipRect.radii().topLeft());
    252             context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii));
    253 
    254             IntRect bottomCorner(rect.x(), rect.y(), clipRect.rect().maxX() - rect.x(), clipRect.rect().maxY() - rect.y());
    255             RoundedRect::Radii bottomCornerRadii;
    256             bottomCornerRadii.setBottomRight(clipRect.radii().bottomRight());
    257             context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii));
    258         }
    259 
    260         if (!clipRect.radii().topRight().isEmpty() || !clipRect.radii().bottomLeft().isEmpty()) {
    261             IntRect topCorner(rect.x(), clipRect.rect().y(), clipRect.rect().maxX() - rect.x(), rect.maxY() - clipRect.rect().y());
    262             RoundedRect::Radii topCornerRadii;
    263             topCornerRadii.setTopRight(clipRect.radii().topRight());
    264             context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii));
    265 
    266             IntRect bottomCorner(clipRect.rect().x(), rect.y(), rect.maxX() - clipRect.rect().x(), clipRect.rect().maxY() - rect.y());
    267             RoundedRect::Radii bottomCornerRadii;
    268             bottomCornerRadii.setBottomLeft(clipRect.radii().bottomLeft());
    269             context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii));
    270         }
    271     }
    272 }
    273 
    274 void BoxPainter::paintFillLayerExtended(RenderBoxModelObject& obj, const PaintInfo& paintInfo, const Color& color, const FillLayer& bgLayer, const LayoutRect& rect,
    275     BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, CompositeOperator op, RenderObject* backgroundObject, bool skipBaseColor)
    276 {
    277     GraphicsContext* context = paintInfo.context;
    278     if (rect.isEmpty())
    279         return;
    280 
    281     bool includeLeftEdge = box ? box->includeLogicalLeftEdge() : true;
    282     bool includeRightEdge = box ? box->includeLogicalRightEdge() : true;
    283 
    284     bool hasRoundedBorder = obj.style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge);
    285     bool clippedWithLocalScrolling = obj.hasOverflowClip() && bgLayer.attachment() == LocalBackgroundAttachment;
    286     bool isBorderFill = bgLayer.clip() == BorderFillBox;
    287     bool isDocumentElementRenderer = obj.isDocumentElement();
    288     bool isBottomLayer = !bgLayer.next();
    289 
    290     Color bgColor = color;
    291     StyleImage* bgImage = bgLayer.image();
    292     bool shouldPaintBackgroundImage = bgImage && bgImage->canRender(obj, obj.style()->effectiveZoom());
    293 
    294     bool forceBackgroundToWhite = false;
    295     if (obj.document().printing()) {
    296         if (obj.style()->printColorAdjust() == PrintColorAdjustEconomy)
    297             forceBackgroundToWhite = true;
    298         if (obj.document().settings() && obj.document().settings()->shouldPrintBackgrounds())
    299             forceBackgroundToWhite = false;
    300     }
    301 
    302     // When printing backgrounds is disabled or using economy mode,
    303     // change existing background colors and images to a solid white background.
    304     // If there's no bg color or image, leave it untouched to avoid affecting transparency.
    305     // We don't try to avoid loading the background images, because this style flag is only set
    306     // when printing, and at that point we've already loaded the background images anyway. (To avoid
    307     // loading the background images we'd have to do this check when applying styles rather than
    308     // while rendering.)
    309     if (forceBackgroundToWhite) {
    310         // Note that we can't reuse this variable below because the bgColor might be changed
    311         bool shouldPaintBackgroundColor = isBottomLayer && bgColor.alpha();
    312         if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) {
    313             bgColor = Color::white;
    314             shouldPaintBackgroundImage = false;
    315         }
    316     }
    317 
    318     bool colorVisible = bgColor.alpha();
    319 
    320     // Fast path for drawing simple color backgrounds.
    321     if (!isDocumentElementRenderer && !clippedWithLocalScrolling && !shouldPaintBackgroundImage && isBorderFill && isBottomLayer) {
    322         if (!colorVisible)
    323             return;
    324 
    325         bool boxShadowShouldBeAppliedToBackground = obj.boxShadowShouldBeAppliedToBackground(bleedAvoidance, box);
    326         GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAppliedToBackground);
    327         if (boxShadowShouldBeAppliedToBackground)
    328             BoxPainter::applyBoxShadowForBackground(context, obj);
    329 
    330         if (hasRoundedBorder && bleedAvoidance != BackgroundBleedClipBackground) {
    331             RoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance(obj, context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge);
    332             if (border.isRenderable()) {
    333                 context->fillRoundedRect(border, bgColor);
    334             } else {
    335                 context->save();
    336                 clipRoundedInnerRect(context, rect, border);
    337                 context->fillRect(border.rect(), bgColor);
    338                 context->restore();
    339             }
    340         } else {
    341             context->fillRect(pixelSnappedIntRect(rect), bgColor);
    342         }
    343 
    344         return;
    345     }
    346 
    347     // BorderFillBox radius clipping is taken care of by BackgroundBleedClipBackground
    348     bool clipToBorderRadius = hasRoundedBorder && !(isBorderFill && bleedAvoidance == BackgroundBleedClipBackground);
    349     GraphicsContextStateSaver clipToBorderStateSaver(*context, clipToBorderRadius);
    350     if (clipToBorderRadius) {
    351         RoundedRect border = isBorderFill ? backgroundRoundedRectAdjustedForBleedAvoidance(obj, context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge) : getBackgroundRoundedRect(obj, rect, box, boxSize.width(), boxSize.height(), includeLeftEdge, includeRightEdge);
    352 
    353         // Clip to the padding or content boxes as necessary.
    354         if (bgLayer.clip() == ContentFillBox) {
    355             border = obj.style()->getRoundedInnerBorderFor(border.rect(),
    356                 obj.paddingTop() + obj.borderTop(), obj.paddingBottom() + obj.borderBottom(),
    357                 obj.paddingLeft() + obj.borderLeft(), obj.paddingRight() + obj.borderRight(), includeLeftEdge, includeRightEdge);
    358         } else if (bgLayer.clip() == PaddingFillBox) {
    359             border = obj.style()->getRoundedInnerBorderFor(border.rect(), includeLeftEdge, includeRightEdge);
    360         }
    361 
    362         clipRoundedInnerRect(context, rect, border);
    363     }
    364 
    365     int bLeft = includeLeftEdge ? obj.borderLeft() : 0;
    366     int bRight = includeRightEdge ? obj.borderRight() : 0;
    367     LayoutUnit pLeft = includeLeftEdge ? obj.paddingLeft() : LayoutUnit();
    368     LayoutUnit pRight = includeRightEdge ? obj.paddingRight() : LayoutUnit();
    369 
    370     GraphicsContextStateSaver clipWithScrollingStateSaver(*context, clippedWithLocalScrolling);
    371     LayoutRect scrolledPaintRect = rect;
    372     if (clippedWithLocalScrolling) {
    373         // Clip to the overflow area.
    374         RenderBox* thisBox = toRenderBox(&obj);
    375         context->clip(thisBox->overflowClipRect(rect.location()));
    376 
    377         // Adjust the paint rect to reflect a scrolled content box with borders at the ends.
    378         IntSize offset = thisBox->scrolledContentOffset();
    379         scrolledPaintRect.move(-offset);
    380         scrolledPaintRect.setWidth(bLeft + thisBox->scrollWidth() + bRight);
    381         scrolledPaintRect.setHeight(thisBox->borderTop() + thisBox->scrollHeight() + thisBox->borderBottom());
    382     }
    383 
    384     GraphicsContextStateSaver backgroundClipStateSaver(*context, false);
    385     IntRect maskRect;
    386 
    387     switch (bgLayer.clip()) {
    388     case PaddingFillBox:
    389     case ContentFillBox: {
    390         if (clipToBorderRadius)
    391             break;
    392 
    393         // Clip to the padding or content boxes as necessary.
    394         bool includePadding = bgLayer.clip() == ContentFillBox;
    395         LayoutRect clipRect = LayoutRect(scrolledPaintRect.x() + bLeft + (includePadding ? pLeft : LayoutUnit()),
    396             scrolledPaintRect.y() + obj.borderTop() + (includePadding ? obj.paddingTop() : LayoutUnit()),
    397             scrolledPaintRect.width() - bLeft - bRight - (includePadding ? pLeft + pRight : LayoutUnit()),
    398             scrolledPaintRect.height() - obj.borderTop() - obj.borderBottom() - (includePadding ? obj.paddingTop() + obj.paddingBottom() : LayoutUnit()));
    399         backgroundClipStateSaver.save();
    400         context->clip(clipRect);
    401 
    402         break;
    403     }
    404     case TextFillBox: {
    405         // First figure out how big the mask has to be. It should be no bigger than what we need
    406         // to actually render, so we should intersect the dirty rect with the border box of the background.
    407         maskRect = pixelSnappedIntRect(rect);
    408         maskRect.intersect(paintInfo.rect);
    409 
    410         // We draw the background into a separate layer, to be later masked with yet another layer
    411         // holding the text content.
    412         backgroundClipStateSaver.save();
    413         context->clip(maskRect);
    414         context->beginTransparencyLayer(1);
    415 
    416         break;
    417     }
    418     case BorderFillBox:
    419         break;
    420     default:
    421         ASSERT_NOT_REACHED();
    422         break;
    423     }
    424 
    425     // Paint the color first underneath all images, culled if background image occludes it.
    426     // FIXME: In the bgLayer->hasFiniteBounds() case, we could improve the culling test
    427     // by verifying whether the background image covers the entire layout rect.
    428     if (isBottomLayer) {
    429         IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect));
    430         bool boxShadowShouldBeAppliedToBackground = obj.boxShadowShouldBeAppliedToBackground(bleedAvoidance, box);
    431         bool isOpaqueRoot = (isDocumentElementRenderer && !bgColor.hasAlpha()) || isDocumentElementWithOpaqueBackground(obj);
    432         if (boxShadowShouldBeAppliedToBackground || !shouldPaintBackgroundImage || !bgLayer.hasOpaqueImage(&obj) || !bgLayer.hasRepeatXY() || (isOpaqueRoot && !toRenderBox(&obj)->height()))  {
    433             if (!boxShadowShouldBeAppliedToBackground)
    434                 backgroundRect.intersect(paintInfo.rect);
    435 
    436             GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAppliedToBackground);
    437             if (boxShadowShouldBeAppliedToBackground)
    438                 BoxPainter::applyBoxShadowForBackground(context, obj);
    439 
    440             if (isOpaqueRoot && !skipBaseColor) {
    441                 paintRootBackgroundColor(obj, paintInfo, rect, bgColor);
    442             } else if (bgColor.alpha()) {
    443                 context->fillRect(backgroundRect, bgColor, context->compositeOperation());
    444             }
    445         }
    446     }
    447 
    448     // no progressive loading of the background image
    449     if (shouldPaintBackgroundImage) {
    450         BackgroundImageGeometry geometry;
    451         calculateBackgroundImageGeometry(obj, paintInfo.paintContainer(), bgLayer, scrolledPaintRect, geometry, backgroundObject);
    452         if (!geometry.destRect().isEmpty()) {
    453             CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer.composite() : op;
    454             RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : &obj;
    455             RefPtr<Image> image = bgImage->image(clientForBackgroundImage, geometry.tileSize());
    456             InterpolationQuality interpolationQuality = chooseInterpolationQuality(obj, context, image.get(), &bgLayer, geometry.tileSize());
    457             if (bgLayer.maskSourceType() == MaskLuminance)
    458                 context->setColorFilter(ColorFilterLuminanceToAlpha);
    459             InterpolationQuality previousInterpolationQuality = context->imageInterpolationQuality();
    460             context->setImageInterpolationQuality(interpolationQuality);
    461             context->drawTiledImage(image.get(), geometry.destRect(), geometry.relativePhase(), geometry.tileSize(),
    462                 compositeOp, bgLayer.blendMode(), geometry.spaceSize());
    463             context->setImageInterpolationQuality(previousInterpolationQuality);
    464         }
    465     }
    466 
    467     if (bgLayer.clip() == TextFillBox) {
    468         // Create the text mask layer.
    469         context->setCompositeOperation(CompositeDestinationIn);
    470         context->beginTransparencyLayer(1);
    471 
    472         // FIXME: Workaround for https://code.google.com/p/skia/issues/detail?id=1291.
    473         context->clearRect(maskRect);
    474 
    475         // Now draw the text into the mask. We do this by painting using a special paint phase that signals to
    476         // InlineTextBoxes that they should just add their contents to the clip.
    477         PaintInfo info(context, maskRect, PaintPhaseTextClip, PaintBehaviorForceBlackText, 0);
    478         context->setCompositeOperation(CompositeSourceOver);
    479         if (box) {
    480             RootInlineBox& root = box->root();
    481             box->paint(info, LayoutPoint(scrolledPaintRect.x() - box->x(), scrolledPaintRect.y() - box->y()), root.lineTop(), root.lineBottom());
    482         } else {
    483             LayoutSize localOffset = obj.isBox() ? toRenderBox(&obj)->locationOffset() : LayoutSize();
    484             obj.paint(info, scrolledPaintRect.location() - localOffset);
    485         }
    486 
    487         context->endLayer();
    488         context->endLayer();
    489     }
    490 }
    491 
    492 void BoxPainter::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    493 {
    494     if (!paintInfo.shouldPaintWithinRoot(&m_renderBox) || m_renderBox.style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
    495         return;
    496 
    497     LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size());
    498     paintMaskImages(paintInfo, paintRect);
    499 }
    500 
    501 void BoxPainter::paintMaskImages(const PaintInfo& paintInfo, const LayoutRect& paintRect)
    502 {
    503     // Figure out if we need to push a transparency layer to render our mask.
    504     bool pushTransparencyLayer = false;
    505     bool compositedMask = m_renderBox.hasLayer() && m_renderBox.layer()->hasCompositedMask();
    506     bool flattenCompositingLayers = m_renderBox.view()->frameView() && m_renderBox.view()->frameView()->paintBehavior() & PaintBehaviorFlattenCompositingLayers;
    507     CompositeOperator compositeOp = CompositeSourceOver;
    508 
    509     bool allMaskImagesLoaded = true;
    510 
    511     if (!compositedMask || flattenCompositingLayers) {
    512         pushTransparencyLayer = true;
    513         StyleImage* maskBoxImage = m_renderBox.style()->maskBoxImage().image();
    514         const FillLayer& maskLayers = m_renderBox.style()->maskLayers();
    515 
    516         // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content.
    517         if (maskBoxImage)
    518             allMaskImagesLoaded &= maskBoxImage->isLoaded();
    519 
    520         allMaskImagesLoaded &= maskLayers.imagesAreLoaded();
    521 
    522         paintInfo.context->setCompositeOperation(CompositeDestinationIn);
    523         paintInfo.context->beginTransparencyLayer(1);
    524         compositeOp = CompositeSourceOver;
    525     }
    526 
    527     if (allMaskImagesLoaded) {
    528         paintFillLayers(paintInfo, Color::transparent, m_renderBox.style()->maskLayers(), paintRect, BackgroundBleedNone, compositeOp);
    529         paintNinePieceImage(m_renderBox, paintInfo.context, paintRect, m_renderBox.style(), m_renderBox.style()->maskBoxImage(), compositeOp);
    530     }
    531 
    532     if (pushTransparencyLayer)
    533         paintInfo.context->endLayer();
    534 }
    535 
    536 void BoxPainter::paintClippingMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    537 {
    538     if (!paintInfo.shouldPaintWithinRoot(&m_renderBox) || m_renderBox.style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseClippingMask)
    539         return;
    540 
    541     if (!m_renderBox.layer() || m_renderBox.layer()->compositingState() != PaintsIntoOwnBacking)
    542         return;
    543 
    544     // We should never have this state in this function. A layer with a mask
    545     // should have always created its own backing if it became composited.
    546     ASSERT(m_renderBox.layer()->compositingState() != HasOwnBackingButPaintsIntoAncestor);
    547 
    548     LayoutRect paintRect = LayoutRect(paintOffset, m_renderBox.size());
    549     paintInfo.context->fillRect(pixelSnappedIntRect(paintRect), Color::black);
    550 }
    551 
    552 void BoxPainter::paintRootBackgroundColor(RenderObject& obj, const PaintInfo& paintInfo, const LayoutRect& rect, const Color& bgColor)
    553 {
    554     GraphicsContext* context = paintInfo.context;
    555     if (rect.isEmpty())
    556         return;
    557 
    558     ASSERT(obj.isDocumentElement());
    559 
    560     IntRect backgroundRect(pixelSnappedIntRect(rect));
    561     backgroundRect.intersect(paintInfo.rect);
    562 
    563     Color baseColor = obj.view()->frameView()->baseBackgroundColor();
    564     bool shouldClearDocumentBackground = obj.document().settings() && obj.document().settings()->shouldClearDocumentBackground();
    565     CompositeOperator operation = shouldClearDocumentBackground ? CompositeCopy : context->compositeOperation();
    566 
    567     // If we have an alpha go ahead and blend with the base background color.
    568     if (baseColor.alpha()) {
    569         if (bgColor.alpha())
    570             baseColor = baseColor.blend(bgColor);
    571         context->fillRect(backgroundRect, baseColor, operation);
    572     } else if (bgColor.alpha()) {
    573         context->fillRect(backgroundRect, bgColor, operation);
    574     } else if (shouldClearDocumentBackground) {
    575         context->clearRect(backgroundRect);
    576     }
    577 }
    578 
    579 bool BoxPainter::isDocumentElementWithOpaqueBackground(RenderObject& obj)
    580 {
    581     if (!obj.isDocumentElement())
    582         return false;
    583 
    584     // The background is opaque only if we're the root document, since iframes with
    585     // no background in the child document should show the parent's background.
    586     bool isOpaque = true;
    587     Element* ownerElement = obj.document().ownerElement();
    588     if (ownerElement) {
    589         if (!isHTMLFrameElement(*ownerElement)) {
    590             // Locate the <body> element using the DOM. This is easier than trying
    591             // to crawl around a render tree with potential :before/:after content and
    592             // anonymous blocks created by inline <body> tags etc. We can locate the <body>
    593             // render object very easily via the DOM.
    594             HTMLElement* body = obj.document().body();
    595             if (body) {
    596                 // Can't scroll a frameset document anyway.
    597                 isOpaque = body->hasTagName(HTMLNames::framesetTag);
    598             } else {
    599                 // FIXME: SVG specific behavior should be in the SVG code.
    600                 // SVG documents and XML documents with SVG root nodes are transparent.
    601                 isOpaque = !obj.document().hasSVGRootNode();
    602             }
    603         }
    604     } else if (obj.view()->frameView()) {
    605         isOpaque = !obj.view()->frameView()->isTransparent();
    606     }
    607 
    608     return isOpaque;
    609 }
    610 
    611 static inline int getSpace(int areaSize, int tileSize)
    612 {
    613     int numberOfTiles = areaSize / tileSize;
    614     int space = -1;
    615 
    616     if (numberOfTiles > 1)
    617         space = lroundf((float)(areaSize - numberOfTiles * tileSize) / (numberOfTiles - 1));
    618 
    619     return space;
    620 }
    621 
    622 void BoxPainter::calculateBackgroundImageGeometry(RenderBoxModelObject& obj, const RenderLayerModelObject* paintContainer, const FillLayer& fillLayer, const LayoutRect& paintRect,
    623     BackgroundImageGeometry& geometry, RenderObject* backgroundObject)
    624 {
    625     LayoutUnit left = 0;
    626     LayoutUnit top = 0;
    627     IntSize positioningAreaSize;
    628     IntRect snappedPaintRect = pixelSnappedIntRect(paintRect);
    629 
    630     // Determine the background positioning area and set destRect to the background painting area.
    631     // destRect will be adjusted later if the background is non-repeating.
    632     // FIXME: transforms spec says that fixed backgrounds behave like scroll inside transforms.
    633     bool fixedAttachment = fillLayer.attachment() == FixedBackgroundAttachment;
    634 
    635     if (RuntimeEnabledFeatures::fastMobileScrollingEnabled()) {
    636         // As a side effect of an optimization to blit on scroll, we do not honor the CSS
    637         // property "background-attachment: fixed" because it may result in rendering
    638         // artifacts. Note, these artifacts only appear if we are blitting on scroll of
    639         // a page that has fixed background images.
    640         fixedAttachment = false;
    641     }
    642 
    643     if (!fixedAttachment) {
    644         geometry.setDestRect(snappedPaintRect);
    645 
    646         LayoutUnit right = 0;
    647         LayoutUnit bottom = 0;
    648         // Scroll and Local.
    649         if (fillLayer.origin() != BorderFillBox) {
    650             left = obj.borderLeft();
    651             right = obj.borderRight();
    652             top = obj.borderTop();
    653             bottom = obj.borderBottom();
    654             if (fillLayer.origin() == ContentFillBox) {
    655                 left += obj.paddingLeft();
    656                 right += obj.paddingRight();
    657                 top += obj.paddingTop();
    658                 bottom += obj.paddingBottom();
    659             }
    660         }
    661 
    662         // The background of the box generated by the root element covers the entire canvas including
    663         // its margins. Since those were added in already, we have to factor them out when computing
    664         // the background positioning area.
    665         if (obj.isDocumentElement()) {
    666             positioningAreaSize = pixelSnappedIntSize(toRenderBox(&obj)->size() - LayoutSize(left + right, top + bottom), toRenderBox(&obj)->location());
    667             left += obj.marginLeft();
    668             top += obj.marginTop();
    669         } else {
    670             positioningAreaSize = pixelSnappedIntSize(paintRect.size() - LayoutSize(left + right, top + bottom), paintRect.location());
    671         }
    672     } else {
    673         geometry.setHasNonLocalGeometry();
    674 
    675         IntRect viewportRect = pixelSnappedIntRect(obj.viewRect());
    676         if (fixedBackgroundPaintsInLocalCoordinates(obj))
    677             viewportRect.setLocation(IntPoint());
    678         else if (FrameView* frameView = obj.view()->frameView())
    679             viewportRect.setLocation(IntPoint(frameView->scrollOffsetForFixedPosition()));
    680 
    681         if (paintContainer) {
    682             IntPoint absoluteContainerOffset = roundedIntPoint(paintContainer->localToAbsolute(FloatPoint()));
    683             viewportRect.moveBy(-absoluteContainerOffset);
    684         }
    685 
    686         geometry.setDestRect(pixelSnappedIntRect(viewportRect));
    687         positioningAreaSize = geometry.destRect().size();
    688     }
    689 
    690     const RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : &obj;
    691     IntSize fillTileSize = calculateFillTileSize(obj, fillLayer, positioningAreaSize);
    692     fillLayer.image()->setContainerSizeForRenderer(clientForBackgroundImage, fillTileSize, obj.style()->effectiveZoom());
    693     geometry.setTileSize(fillTileSize);
    694 
    695     EFillRepeat backgroundRepeatX = fillLayer.repeatX();
    696     EFillRepeat backgroundRepeatY = fillLayer.repeatY();
    697     int availableWidth = positioningAreaSize.width() - geometry.tileSize().width();
    698     int availableHeight = positioningAreaSize.height() - geometry.tileSize().height();
    699 
    700     LayoutUnit computedXPosition = roundedMinimumValueForLength(fillLayer.xPosition(), availableWidth);
    701     if (backgroundRepeatX == RoundFill && positioningAreaSize.width() > 0 && fillTileSize.width() > 0) {
    702         long nrTiles = std::max(1l, lroundf((float)positioningAreaSize.width() / fillTileSize.width()));
    703 
    704         if (fillLayer.size().size.height().isAuto() && backgroundRepeatY != RoundFill) {
    705             fillTileSize.setHeight(fillTileSize.height() * positioningAreaSize.width() / (nrTiles * fillTileSize.width()));
    706         }
    707 
    708         fillTileSize.setWidth(positioningAreaSize.width() / nrTiles);
    709         geometry.setTileSize(fillTileSize);
    710         geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().width() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0);
    711         geometry.setSpaceSize(IntSize());
    712     }
    713 
    714     LayoutUnit computedYPosition = roundedMinimumValueForLength(fillLayer.yPosition(), availableHeight);
    715     if (backgroundRepeatY == RoundFill && positioningAreaSize.height() > 0 && fillTileSize.height() > 0) {
    716         long nrTiles = std::max(1l, lroundf((float)positioningAreaSize.height() / fillTileSize.height()));
    717 
    718         if (fillLayer.size().size.width().isAuto() && backgroundRepeatX != RoundFill) {
    719             fillTileSize.setWidth(fillTileSize.width() * positioningAreaSize.height() / (nrTiles * fillTileSize.height()));
    720         }
    721 
    722         fillTileSize.setHeight(positioningAreaSize.height() / nrTiles);
    723         geometry.setTileSize(fillTileSize);
    724         geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().height() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0);
    725         geometry.setSpaceSize(IntSize());
    726     }
    727 
    728     if (backgroundRepeatX == RepeatFill) {
    729         geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().width() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0);
    730         geometry.setSpaceSize(IntSize());
    731     } else if (backgroundRepeatX == SpaceFill && fillTileSize.width() > 0) {
    732         int space = getSpace(positioningAreaSize.width(), geometry.tileSize().width());
    733         int actualWidth = geometry.tileSize().width() + space;
    734 
    735         if (space >= 0) {
    736             computedXPosition = roundedMinimumValueForLength(Length(), availableWidth);
    737             geometry.setSpaceSize(IntSize(space, 0));
    738             geometry.setPhaseX(actualWidth ? actualWidth - roundToInt(computedXPosition + left) % actualWidth : 0);
    739         } else {
    740             backgroundRepeatX = NoRepeatFill;
    741         }
    742     }
    743     if (backgroundRepeatX == NoRepeatFill) {
    744         int xOffset = fillLayer.backgroundXOrigin() == RightEdge ? availableWidth - computedXPosition : computedXPosition;
    745         geometry.setNoRepeatX(left + xOffset);
    746         geometry.setSpaceSize(IntSize(0, geometry.spaceSize().height()));
    747     }
    748 
    749     if (backgroundRepeatY == RepeatFill) {
    750         geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().height() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0);
    751         geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), 0));
    752     } else if (backgroundRepeatY == SpaceFill && fillTileSize.height() > 0) {
    753         int space = getSpace(positioningAreaSize.height(), geometry.tileSize().height());
    754         int actualHeight = geometry.tileSize().height() + space;
    755 
    756         if (space >= 0) {
    757             computedYPosition = roundedMinimumValueForLength(Length(), availableHeight);
    758             geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), space));
    759             geometry.setPhaseY(actualHeight ? actualHeight - roundToInt(computedYPosition + top) % actualHeight : 0);
    760         } else {
    761             backgroundRepeatY = NoRepeatFill;
    762         }
    763     }
    764     if (backgroundRepeatY == NoRepeatFill) {
    765         int yOffset = fillLayer.backgroundYOrigin() == BottomEdge ? availableHeight - computedYPosition : computedYPosition;
    766         geometry.setNoRepeatY(top + yOffset);
    767         geometry.setSpaceSize(IntSize(geometry.spaceSize().width(), 0));
    768     }
    769 
    770     if (fixedAttachment)
    771         geometry.useFixedAttachment(snappedPaintRect.location());
    772 
    773     geometry.clip(snappedPaintRect);
    774     geometry.setDestOrigin(geometry.destRect().location());
    775 }
    776 
    777 InterpolationQuality BoxPainter::chooseInterpolationQuality(RenderBoxModelObject& obj, GraphicsContext* context, Image* image, const void* layer, const LayoutSize& size)
    778 {
    779     return ImageQualityController::imageQualityController()->chooseInterpolationQuality(context, &obj, image, layer, size);
    780 }
    781 
    782 bool BoxPainter::fixedBackgroundPaintsInLocalCoordinates(const RenderObject& obj)
    783 {
    784     if (!obj.isDocumentElement())
    785         return false;
    786 
    787     if (obj.view()->frameView() && obj.view()->frameView()->paintBehavior() & PaintBehaviorFlattenCompositingLayers)
    788         return false;
    789 
    790     RenderLayer* rootLayer = obj.view()->layer();
    791     if (!rootLayer || rootLayer->compositingState() == NotComposited)
    792         return false;
    793 
    794     return rootLayer->compositedLayerMapping()->backgroundLayerPaintsFixedRootBackground();
    795 }
    796 
    797 static inline void applySubPixelHeuristicForTileSize(LayoutSize& tileSize, const IntSize& positioningAreaSize)
    798 {
    799     tileSize.setWidth(positioningAreaSize.width() - tileSize.width() <= 1 ? tileSize.width().ceil() : tileSize.width().floor());
    800     tileSize.setHeight(positioningAreaSize.height() - tileSize.height() <= 1 ? tileSize.height().ceil() : tileSize.height().floor());
    801 }
    802 
    803 IntSize BoxPainter::calculateFillTileSize(const RenderBoxModelObject& obj, const FillLayer& fillLayer, const IntSize& positioningAreaSize)
    804 {
    805     StyleImage* image = fillLayer.image();
    806     EFillSizeType type = fillLayer.size().type;
    807 
    808     IntSize imageIntrinsicSize = obj.calculateImageIntrinsicDimensions(image, positioningAreaSize, RenderBoxModelObject::ScaleByEffectiveZoom);
    809     imageIntrinsicSize.scale(1 / image->imageScaleFactor(), 1 / image->imageScaleFactor());
    810     switch (type) {
    811     case SizeLength: {
    812         LayoutSize tileSize = positioningAreaSize;
    813 
    814         Length layerWidth = fillLayer.size().size.width();
    815         Length layerHeight = fillLayer.size().size.height();
    816 
    817         if (layerWidth.isFixed())
    818             tileSize.setWidth(layerWidth.value());
    819         else if (layerWidth.isPercent())
    820             tileSize.setWidth(valueForLength(layerWidth, positioningAreaSize.width()));
    821 
    822         if (layerHeight.isFixed())
    823             tileSize.setHeight(layerHeight.value());
    824         else if (layerHeight.isPercent())
    825             tileSize.setHeight(valueForLength(layerHeight, positioningAreaSize.height()));
    826 
    827         applySubPixelHeuristicForTileSize(tileSize, positioningAreaSize);
    828 
    829         // If one of the values is auto we have to use the appropriate
    830         // scale to maintain our aspect ratio.
    831         if (layerWidth.isAuto() && !layerHeight.isAuto()) {
    832             if (imageIntrinsicSize.height())
    833                 tileSize.setWidth(imageIntrinsicSize.width() * tileSize.height() / imageIntrinsicSize.height());
    834         } else if (!layerWidth.isAuto() && layerHeight.isAuto()) {
    835             if (imageIntrinsicSize.width())
    836                 tileSize.setHeight(imageIntrinsicSize.height() * tileSize.width() / imageIntrinsicSize.width());
    837         } else if (layerWidth.isAuto() && layerHeight.isAuto()) {
    838             // If both width and height are auto, use the image's intrinsic size.
    839             tileSize = imageIntrinsicSize;
    840         }
    841 
    842         tileSize.clampNegativeToZero();
    843         return flooredIntSize(tileSize);
    844     }
    845     case SizeNone: {
    846         // If both values are auto then the intrinsic width and/or height of the image should be used, if any.
    847         if (!imageIntrinsicSize.isEmpty())
    848             return imageIntrinsicSize;
    849 
    850         // If the image has neither an intrinsic width nor an intrinsic height, its size is determined as for contain.
    851         type = Contain;
    852     }
    853     case Contain:
    854     case Cover: {
    855         float horizontalScaleFactor = imageIntrinsicSize.width()
    856             ? static_cast<float>(positioningAreaSize.width()) / imageIntrinsicSize.width() : 1;
    857         float verticalScaleFactor = imageIntrinsicSize.height()
    858             ? static_cast<float>(positioningAreaSize.height()) / imageIntrinsicSize.height() : 1;
    859         float scaleFactor = type == Contain ? std::min(horizontalScaleFactor, verticalScaleFactor) : std::max(horizontalScaleFactor, verticalScaleFactor);
    860         return IntSize(std::max(1l, lround(imageIntrinsicSize.width() * scaleFactor)), std::max(1l, lround(imageIntrinsicSize.height() * scaleFactor)));
    861     }
    862     }
    863 
    864     ASSERT_NOT_REACHED();
    865     return IntSize();
    866 }
    867 
    868 static LayoutUnit computeBorderImageSide(const BorderImageLength& borderSlice, LayoutUnit borderSide, LayoutUnit imageSide, LayoutUnit boxExtent)
    869 {
    870     if (borderSlice.isNumber())
    871         return borderSlice.number() * borderSide;
    872     if (borderSlice.length().isAuto())
    873         return imageSide;
    874     return valueForLength(borderSlice.length(), boxExtent);
    875 }
    876 
    877 bool BoxPainter::paintNinePieceImage(RenderBoxModelObject& obj, GraphicsContext* graphicsContext, const LayoutRect& rect, const RenderStyle* style, const NinePieceImage& ninePieceImage, CompositeOperator op)
    878 {
    879     StyleImage* styleImage = ninePieceImage.image();
    880     if (!styleImage)
    881         return false;
    882 
    883     if (!styleImage->isLoaded())
    884         return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either.
    885 
    886     if (!styleImage->canRender(obj, style->effectiveZoom()))
    887         return false;
    888 
    889     // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function
    890     // doesn't have any understanding of the zoom that is in effect on the tile.
    891     LayoutRect rectWithOutsets = rect;
    892     rectWithOutsets.expand(style->imageOutsets(ninePieceImage));
    893     IntRect borderImageRect = pixelSnappedIntRect(rectWithOutsets);
    894 
    895     IntSize imageSize = obj.calculateImageIntrinsicDimensions(styleImage, borderImageRect.size(), RenderBoxModelObject::DoNotScaleByEffectiveZoom);
    896 
    897     // If both values are auto then the intrinsic width and/or height of the image should be used, if any.
    898     styleImage->setContainerSizeForRenderer(&obj, imageSize, style->effectiveZoom());
    899 
    900     int imageWidth = imageSize.width();
    901     int imageHeight = imageSize.height();
    902 
    903     float imageScaleFactor = styleImage->imageScaleFactor();
    904     int topSlice = std::min<int>(imageHeight, valueForLength(ninePieceImage.imageSlices().top(), imageHeight)) * imageScaleFactor;
    905     int rightSlice = std::min<int>(imageWidth, valueForLength(ninePieceImage.imageSlices().right(), imageWidth)) * imageScaleFactor;
    906     int bottomSlice = std::min<int>(imageHeight, valueForLength(ninePieceImage.imageSlices().bottom(), imageHeight)) * imageScaleFactor;
    907     int leftSlice = std::min<int>(imageWidth, valueForLength(ninePieceImage.imageSlices().left(), imageWidth)) * imageScaleFactor;
    908 
    909     ENinePieceImageRule hRule = ninePieceImage.horizontalRule();
    910     ENinePieceImageRule vRule = ninePieceImage.verticalRule();
    911 
    912     int topWidth = computeBorderImageSide(ninePieceImage.borderSlices().top(), style->borderTopWidth(), topSlice, borderImageRect.height());
    913     int rightWidth = computeBorderImageSide(ninePieceImage.borderSlices().right(), style->borderRightWidth(), rightSlice, borderImageRect.width());
    914     int bottomWidth = computeBorderImageSide(ninePieceImage.borderSlices().bottom(), style->borderBottomWidth(), bottomSlice, borderImageRect.height());
    915     int leftWidth = computeBorderImageSide(ninePieceImage.borderSlices().left(), style->borderLeftWidth(), leftSlice, borderImageRect.width());
    916 
    917     // Reduce the widths if they're too large.
    918     // The spec says: Given Lwidth as the width of the border image area, Lheight as its height, and Wside as the border image width
    919     // offset for the side, let f = min(Lwidth/(Wleft+Wright), Lheight/(Wtop+Wbottom)). If f < 1, then all W are reduced by
    920     // multiplying them by f.
    921     int borderSideWidth = std::max(1, leftWidth + rightWidth);
    922     int borderSideHeight = std::max(1, topWidth + bottomWidth);
    923     float borderSideScaleFactor = std::min((float)borderImageRect.width() / borderSideWidth, (float)borderImageRect.height() / borderSideHeight);
    924     if (borderSideScaleFactor < 1) {
    925         topWidth *= borderSideScaleFactor;
    926         rightWidth *= borderSideScaleFactor;
    927         bottomWidth *= borderSideScaleFactor;
    928         leftWidth *= borderSideScaleFactor;
    929     }
    930 
    931     bool drawLeft = leftSlice > 0 && leftWidth > 0;
    932     bool drawTop = topSlice > 0 && topWidth > 0;
    933     bool drawRight = rightSlice > 0 && rightWidth > 0;
    934     bool drawBottom = bottomSlice > 0 && bottomWidth > 0;
    935     bool drawMiddle = ninePieceImage.fill() && (imageWidth - leftSlice - rightSlice) > 0 && (borderImageRect.width() - leftWidth - rightWidth) > 0
    936         && (imageHeight - topSlice - bottomSlice) > 0 && (borderImageRect.height() - topWidth - bottomWidth) > 0;
    937 
    938     RefPtr<Image> image = styleImage->image(&obj, imageSize);
    939 
    940     float destinationWidth = borderImageRect.width() - leftWidth - rightWidth;
    941     float destinationHeight = borderImageRect.height() - topWidth - bottomWidth;
    942 
    943     float sourceWidth = imageWidth - leftSlice - rightSlice;
    944     float sourceHeight = imageHeight - topSlice - bottomSlice;
    945 
    946     float leftSideScale = drawLeft ? (float)leftWidth / leftSlice : 1;
    947     float rightSideScale = drawRight ? (float)rightWidth / rightSlice : 1;
    948     float topSideScale = drawTop ? (float)topWidth / topSlice : 1;
    949     float bottomSideScale = drawBottom ? (float)bottomWidth / bottomSlice : 1;
    950 
    951     if (drawLeft) {
    952         // Paint the top and bottom left corners.
    953 
    954         // The top left corner rect is (tx, ty, leftWidth, topWidth)
    955         // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice)
    956         if (drawTop) {
    957             graphicsContext->drawImage(image.get(), IntRect(borderImageRect.location(), IntSize(leftWidth, topWidth)),
    958                 LayoutRect(0, 0, leftSlice, topSlice), op);
    959         }
    960 
    961         // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth)
    962         // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice)
    963         if (drawBottom) {
    964             graphicsContext->drawImage(image.get(), IntRect(borderImageRect.x(), borderImageRect.maxY() - bottomWidth, leftWidth, bottomWidth),
    965                 LayoutRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op);
    966         }
    967 
    968         // Paint the left edge.
    969         // Have to scale and tile into the border rect.
    970         if (sourceHeight > 0) {
    971             graphicsContext->drawTiledImage(image.get(), IntRect(borderImageRect.x(), borderImageRect.y() + topWidth, leftWidth, destinationHeight),
    972                 IntRect(0, topSlice, leftSlice, sourceHeight), FloatSize(leftSideScale, leftSideScale), Image::StretchTile, (Image::TileRule)vRule, op);
    973         }
    974     }
    975 
    976     if (drawRight) {
    977         // Paint the top and bottom right corners
    978         // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth)
    979         // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice)
    980         if (drawTop) {
    981             graphicsContext->drawImage(image.get(), IntRect(borderImageRect.maxX() - rightWidth, borderImageRect.y(), rightWidth, topWidth),
    982                 LayoutRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op);
    983         }
    984 
    985         // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth)
    986         // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice)
    987         if (drawBottom) {
    988             graphicsContext->drawImage(image.get(), IntRect(borderImageRect.maxX() - rightWidth, borderImageRect.maxY() - bottomWidth, rightWidth, bottomWidth),
    989                 LayoutRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op);
    990         }
    991 
    992         // Paint the right edge.
    993         if (sourceHeight > 0) {
    994             graphicsContext->drawTiledImage(image.get(), IntRect(borderImageRect.maxX() - rightWidth, borderImageRect.y() + topWidth, rightWidth,
    995                 destinationHeight),
    996                 IntRect(imageWidth - rightSlice, topSlice, rightSlice, sourceHeight),
    997                 FloatSize(rightSideScale, rightSideScale),
    998                 Image::StretchTile, (Image::TileRule)vRule, op);
    999         }
   1000     }
   1001 
   1002     // Paint the top edge.
   1003     if (drawTop && sourceWidth > 0) {
   1004         graphicsContext->drawTiledImage(image.get(), IntRect(borderImageRect.x() + leftWidth, borderImageRect.y(), destinationWidth, topWidth),
   1005             IntRect(leftSlice, 0, sourceWidth, topSlice),
   1006             FloatSize(topSideScale, topSideScale), (Image::TileRule)hRule, Image::StretchTile, op);
   1007     }
   1008 
   1009     // Paint the bottom edge.
   1010     if (drawBottom && sourceWidth > 0) {
   1011         graphicsContext->drawTiledImage(image.get(), IntRect(borderImageRect.x() + leftWidth, borderImageRect.maxY() - bottomWidth,
   1012             destinationWidth, bottomWidth),
   1013             IntRect(leftSlice, imageHeight - bottomSlice, sourceWidth, bottomSlice),
   1014             FloatSize(bottomSideScale, bottomSideScale),
   1015             (Image::TileRule)hRule, Image::StretchTile, op);
   1016     }
   1017 
   1018     // Paint the middle.
   1019     if (drawMiddle) {
   1020         FloatSize middleScaleFactor(1, 1);
   1021         if (drawTop)
   1022             middleScaleFactor.setWidth(topSideScale);
   1023         else if (drawBottom)
   1024             middleScaleFactor.setWidth(bottomSideScale);
   1025         if (drawLeft)
   1026             middleScaleFactor.setHeight(leftSideScale);
   1027         else if (drawRight)
   1028             middleScaleFactor.setHeight(rightSideScale);
   1029 
   1030         // For "stretch" rules, just override the scale factor and replace. We only had to do this for the
   1031         // center tile, since sides don't even use the scale factor unless they have a rule other than "stretch".
   1032         // The middle however can have "stretch" specified in one axis but not the other, so we have to
   1033         // correct the scale here.
   1034         if (hRule == StretchImageRule)
   1035             middleScaleFactor.setWidth(destinationWidth / sourceWidth);
   1036 
   1037         if (vRule == StretchImageRule)
   1038             middleScaleFactor.setHeight(destinationHeight / sourceHeight);
   1039 
   1040         graphicsContext->drawTiledImage(image.get(),
   1041             IntRect(borderImageRect.x() + leftWidth, borderImageRect.y() + topWidth, destinationWidth, destinationHeight),
   1042             IntRect(leftSlice, topSlice, sourceWidth, sourceHeight),
   1043             middleScaleFactor, (Image::TileRule)hRule, (Image::TileRule)vRule, op);
   1044     }
   1045 
   1046     return true;
   1047 }
   1048 
   1049 static IntRect calculateSideRect(const RoundedRect& outerBorder, const BorderEdge edges[], int side)
   1050 {
   1051     IntRect sideRect = outerBorder.rect();
   1052     int width = edges[side].width;
   1053 
   1054     if (side == BSTop)
   1055         sideRect.setHeight(width);
   1056     else if (side == BSBottom)
   1057         sideRect.shiftYEdgeTo(sideRect.maxY() - width);
   1058     else if (side == BSLeft)
   1059         sideRect.setWidth(width);
   1060     else
   1061         sideRect.shiftXEdgeTo(sideRect.maxX() - width);
   1062 
   1063     return sideRect;
   1064 }
   1065 
   1066 enum BorderEdgeFlag {
   1067     TopBorderEdge = 1 << BSTop,
   1068     RightBorderEdge = 1 << BSRight,
   1069     BottomBorderEdge = 1 << BSBottom,
   1070     LeftBorderEdge = 1 << BSLeft,
   1071     AllBorderEdges = TopBorderEdge | BottomBorderEdge | LeftBorderEdge | RightBorderEdge
   1072 };
   1073 
   1074 static inline BorderEdgeFlag edgeFlagForSide(BoxSide side)
   1075 {
   1076     return static_cast<BorderEdgeFlag>(1 << side);
   1077 }
   1078 
   1079 static inline bool includesEdge(BorderEdgeFlags flags, BoxSide side)
   1080 {
   1081     return flags & edgeFlagForSide(side);
   1082 }
   1083 
   1084 inline bool styleRequiresClipPolygon(EBorderStyle style)
   1085 {
   1086     return style == DOTTED || style == DASHED; // These are drawn with a stroke, so we have to clip to get corner miters.
   1087 }
   1088 
   1089 static bool borderStyleFillsBorderArea(EBorderStyle style)
   1090 {
   1091     return !(style == DOTTED || style == DASHED || style == DOUBLE);
   1092 }
   1093 
   1094 static bool borderStyleHasInnerDetail(EBorderStyle style)
   1095 {
   1096     return style == GROOVE || style == RIDGE || style == DOUBLE;
   1097 }
   1098 
   1099 static bool borderStyleIsDottedOrDashed(EBorderStyle style)
   1100 {
   1101     return style == DOTTED || style == DASHED;
   1102 }
   1103 
   1104 // OUTSET darkens the bottom and right (and maybe lightens the top and left)
   1105 // INSET darkens the top and left (and maybe lightens the bottom and right)
   1106 static inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, BoxSide side, BoxSide adjacentSide)
   1107 {
   1108     // These styles match at the top/left and bottom/right.
   1109     if (style == INSET || style == GROOVE || style == RIDGE || style == OUTSET) {
   1110         const BorderEdgeFlags topRightFlags = edgeFlagForSide(BSTop) | edgeFlagForSide(BSRight);
   1111         const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edgeFlagForSide(BSLeft);
   1112 
   1113         BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacentSide);
   1114         return flags == topRightFlags || flags == bottomLeftFlags;
   1115     }
   1116     return false;
   1117 }
   1118 
   1119 static inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[])
   1120 {
   1121     if (edges[side].shouldRender() != edges[adjacentSide].shouldRender())
   1122         return false;
   1123 
   1124     if (!edges[side].sharesColorWith(edges[adjacentSide]))
   1125         return false;
   1126 
   1127     return !borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), side, adjacentSide);
   1128 }
   1129 
   1130 static inline bool colorNeedsAntiAliasAtCorner(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[])
   1131 {
   1132     if (!edges[side].color.hasAlpha())
   1133         return false;
   1134 
   1135     if (edges[side].shouldRender() != edges[adjacentSide].shouldRender())
   1136         return false;
   1137 
   1138     if (!edges[side].sharesColorWith(edges[adjacentSide]))
   1139         return true;
   1140 
   1141     return borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), side, adjacentSide);
   1142 }
   1143 
   1144 bool BoxPainter::shouldAntialiasLines(GraphicsContext* context)
   1145 {
   1146     // FIXME: We may want to not antialias when scaled by an integral value,
   1147     // and we may want to antialias when translated by a non-integral value.
   1148     // FIXME: See crbug.com/382491. getCTM does not include scale factors applied at raster time, such
   1149     // as device zoom.
   1150     return !context->getCTM().isIdentityOrTranslationOrFlipped();
   1151 }
   1152 
   1153 static bool borderWillArcInnerEdge(const LayoutSize& firstRadius, const FloatSize& secondRadius)
   1154 {
   1155     return !firstRadius.isZero() || !secondRadius.isZero();
   1156 }
   1157 
   1158 // This assumes that we draw in order: top, bottom, left, right.
   1159 static inline bool willBeOverdrawn(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[])
   1160 {
   1161     switch (side) {
   1162     case BSTop:
   1163     case BSBottom:
   1164         if (edges[adjacentSide].presentButInvisible())
   1165             return false;
   1166 
   1167         if (!edges[side].sharesColorWith(edges[adjacentSide]) && edges[adjacentSide].color.hasAlpha())
   1168             return false;
   1169 
   1170         if (!borderStyleFillsBorderArea(edges[adjacentSide].borderStyle()))
   1171             return false;
   1172 
   1173         return true;
   1174 
   1175     case BSLeft:
   1176     case BSRight:
   1177         // These draw last, so are never overdrawn.
   1178         return false;
   1179     }
   1180     return false;
   1181 }
   1182 
   1183 static inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorderStyle style, EBorderStyle adjacentStyle)
   1184 {
   1185     if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE || adjacentStyle == RIDGE)
   1186         return true;
   1187 
   1188     if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjacentStyle))
   1189         return true;
   1190 
   1191     if (style != adjacentStyle)
   1192         return true;
   1193 
   1194     return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide);
   1195 }
   1196 
   1197 static bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[], bool allowOverdraw)
   1198 {
   1199     if ((edges[side].isTransparent && edges[adjacentSide].isTransparent) || !edges[adjacentSide].isPresent)
   1200         return false;
   1201 
   1202     if (allowOverdraw && willBeOverdrawn(side, adjacentSide, edges))
   1203         return false;
   1204 
   1205     if (!edges[side].sharesColorWith(edges[adjacentSide]))
   1206         return true;
   1207 
   1208     if (borderStylesRequireMitre(side, adjacentSide, edges[side].borderStyle(), edges[adjacentSide].borderStyle()))
   1209         return true;
   1210 
   1211     return false;
   1212 }
   1213 
   1214 static IntRect calculateSideRectIncludingInner(const RoundedRect& outerBorder, const BorderEdge edges[], BoxSide side)
   1215 {
   1216     IntRect sideRect = outerBorder.rect();
   1217     int width;
   1218 
   1219     switch (side) {
   1220     case BSTop:
   1221         width = sideRect.height() - edges[BSBottom].width;
   1222         sideRect.setHeight(width);
   1223         break;
   1224     case BSBottom:
   1225         width = sideRect.height() - edges[BSTop].width;
   1226         sideRect.shiftYEdgeTo(sideRect.maxY() - width);
   1227         break;
   1228     case BSLeft:
   1229         width = sideRect.width() - edges[BSRight].width;
   1230         sideRect.setWidth(width);
   1231         break;
   1232     case BSRight:
   1233         width = sideRect.width() - edges[BSLeft].width;
   1234         sideRect.shiftXEdgeTo(sideRect.maxX() - width);
   1235         break;
   1236     }
   1237 
   1238     return sideRect;
   1239 }
   1240 
   1241 static RoundedRect calculateAdjustedInnerBorder(const RoundedRect&innerBorder, BoxSide side)
   1242 {
   1243     // Expand the inner border as necessary to make it a rounded rect (i.e. radii contained within each edge).
   1244     // This function relies on the fact we only get radii not contained within each edge if one of the radii
   1245     // for an edge is zero, so we can shift the arc towards the zero radius corner.
   1246     RoundedRect::Radii newRadii = innerBorder.radii();
   1247     IntRect newRect = innerBorder.rect();
   1248 
   1249     float overshoot;
   1250     float maxRadii;
   1251 
   1252     switch (side) {
   1253     case BSTop:
   1254         overshoot = newRadii.topLeft().width() + newRadii.topRight().width() - newRect.width();
   1255         if (overshoot > 0) {
   1256             ASSERT(!(newRadii.topLeft().width() && newRadii.topRight().width()));
   1257             newRect.setWidth(newRect.width() + overshoot);
   1258             if (!newRadii.topLeft().width())
   1259                 newRect.move(-overshoot, 0);
   1260         }
   1261         newRadii.setBottomLeft(IntSize(0, 0));
   1262         newRadii.setBottomRight(IntSize(0, 0));
   1263         maxRadii = std::max(newRadii.topLeft().height(), newRadii.topRight().height());
   1264         if (maxRadii > newRect.height())
   1265             newRect.setHeight(maxRadii);
   1266         break;
   1267 
   1268     case BSBottom:
   1269         overshoot = newRadii.bottomLeft().width() + newRadii.bottomRight().width() - newRect.width();
   1270         if (overshoot > 0) {
   1271             ASSERT(!(newRadii.bottomLeft().width() && newRadii.bottomRight().width()));
   1272             newRect.setWidth(newRect.width() + overshoot);
   1273             if (!newRadii.bottomLeft().width())
   1274                 newRect.move(-overshoot, 0);
   1275         }
   1276         newRadii.setTopLeft(IntSize(0, 0));
   1277         newRadii.setTopRight(IntSize(0, 0));
   1278         maxRadii = std::max(newRadii.bottomLeft().height(), newRadii.bottomRight().height());
   1279         if (maxRadii > newRect.height()) {
   1280             newRect.move(0, newRect.height() - maxRadii);
   1281             newRect.setHeight(maxRadii);
   1282         }
   1283         break;
   1284 
   1285     case BSLeft:
   1286         overshoot = newRadii.topLeft().height() + newRadii.bottomLeft().height() - newRect.height();
   1287         if (overshoot > 0) {
   1288             ASSERT(!(newRadii.topLeft().height() && newRadii.bottomLeft().height()));
   1289             newRect.setHeight(newRect.height() + overshoot);
   1290             if (!newRadii.topLeft().height())
   1291                 newRect.move(0, -overshoot);
   1292         }
   1293         newRadii.setTopRight(IntSize(0, 0));
   1294         newRadii.setBottomRight(IntSize(0, 0));
   1295         maxRadii = std::max(newRadii.topLeft().width(), newRadii.bottomLeft().width());
   1296         if (maxRadii > newRect.width())
   1297             newRect.setWidth(maxRadii);
   1298         break;
   1299 
   1300     case BSRight:
   1301         overshoot = newRadii.topRight().height() + newRadii.bottomRight().height() - newRect.height();
   1302         if (overshoot > 0) {
   1303             ASSERT(!(newRadii.topRight().height() && newRadii.bottomRight().height()));
   1304             newRect.setHeight(newRect.height() + overshoot);
   1305             if (!newRadii.topRight().height())
   1306                 newRect.move(0, -overshoot);
   1307         }
   1308         newRadii.setTopLeft(IntSize(0, 0));
   1309         newRadii.setBottomLeft(IntSize(0, 0));
   1310         maxRadii = std::max(newRadii.topRight().width(), newRadii.bottomRight().width());
   1311         if (maxRadii > newRect.width()) {
   1312             newRect.move(newRect.width() - maxRadii, 0);
   1313             newRect.setWidth(maxRadii);
   1314         }
   1315         break;
   1316     }
   1317 
   1318     return RoundedRect(newRect, newRadii);
   1319 }
   1320 
   1321 void BoxPainter::clipBorderSideForComplexInnerPath(GraphicsContext* graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
   1322     BoxSide side, const BorderEdge edges[])
   1323 {
   1324     graphicsContext->clip(calculateSideRectIncludingInner(outerBorder, edges, side));
   1325     RoundedRect adjustedInnerRect = calculateAdjustedInnerBorder(innerBorder, side);
   1326     if (!adjustedInnerRect.isEmpty())
   1327         graphicsContext->clipOutRoundedRect(adjustedInnerRect);
   1328 }
   1329 
   1330 static bool allCornersClippedOut(const RoundedRect& border, const LayoutRect& clipRect)
   1331 {
   1332     LayoutRect boundingRect = border.rect();
   1333     if (clipRect.contains(boundingRect))
   1334         return false;
   1335 
   1336     RoundedRect::Radii radii = border.radii();
   1337 
   1338     LayoutRect topLeftRect(boundingRect.location(), radii.topLeft());
   1339     if (clipRect.intersects(topLeftRect))
   1340         return false;
   1341 
   1342     LayoutRect topRightRect(boundingRect.location(), radii.topRight());
   1343     topRightRect.setX(boundingRect.maxX() - topRightRect.width());
   1344     if (clipRect.intersects(topRightRect))
   1345         return false;
   1346 
   1347     LayoutRect bottomLeftRect(boundingRect.location(), radii.bottomLeft());
   1348     bottomLeftRect.setY(boundingRect.maxY() - bottomLeftRect.height());
   1349     if (clipRect.intersects(bottomLeftRect))
   1350         return false;
   1351 
   1352     LayoutRect bottomRightRect(boundingRect.location(), radii.bottomRight());
   1353     bottomRightRect.setX(boundingRect.maxX() - bottomRightRect.width());
   1354     bottomRightRect.setY(boundingRect.maxY() - bottomRightRect.height());
   1355     if (clipRect.intersects(bottomRightRect))
   1356         return false;
   1357 
   1358     return true;
   1359 }
   1360 
   1361 void BoxPainter::paintBorder(RenderBoxModelObject& obj, const PaintInfo& info, const LayoutRect& rect, const RenderStyle* style, BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
   1362 {
   1363     GraphicsContext* graphicsContext = info.context;
   1364     // border-image is not affected by border-radius.
   1365     if (paintNinePieceImage(obj, graphicsContext, rect, style, style->borderImage()))
   1366         return;
   1367 
   1368     BorderEdge edges[4];
   1369     style->getBorderEdgeInfo(edges, includeLogicalLeftEdge, includeLogicalRightEdge);
   1370     RoundedRect outerBorder = style->getRoundedBorderFor(rect, includeLogicalLeftEdge, includeLogicalRightEdge);
   1371     RoundedRect innerBorder = style->getRoundedInnerBorderFor(borderInnerRectAdjustedForBleedAvoidance(graphicsContext, rect, bleedAvoidance), includeLogicalLeftEdge, includeLogicalRightEdge);
   1372 
   1373     if (outerBorder.rect().isEmpty())
   1374         return;
   1375 
   1376     bool haveAlphaColor = false;
   1377     bool haveAllSolidEdges = true;
   1378     bool haveAllDoubleEdges = true;
   1379     int numEdgesVisible = 4;
   1380     bool allEdgesShareColor = true;
   1381     bool allEdgesShareWidth = true;
   1382     int firstVisibleEdge = -1;
   1383     BorderEdgeFlags edgesToDraw = 0;
   1384 
   1385     for (int i = BSTop; i <= BSLeft; ++i) {
   1386         const BorderEdge& currEdge = edges[i];
   1387 
   1388         if (edges[i].shouldRender())
   1389             edgesToDraw |= edgeFlagForSide(static_cast<BoxSide>(i));
   1390 
   1391         if (currEdge.presentButInvisible()) {
   1392             --numEdgesVisible;
   1393             allEdgesShareColor = false;
   1394             allEdgesShareWidth = false;
   1395             continue;
   1396         }
   1397 
   1398         if (!currEdge.shouldRender()) {
   1399             --numEdgesVisible;
   1400             continue;
   1401         }
   1402 
   1403         if (firstVisibleEdge == -1) {
   1404             firstVisibleEdge = i;
   1405         } else {
   1406             if (currEdge.color != edges[firstVisibleEdge].color)
   1407                 allEdgesShareColor = false;
   1408             if (currEdge.width != edges[firstVisibleEdge].width)
   1409                 allEdgesShareWidth = false;
   1410         }
   1411 
   1412         if (currEdge.color.hasAlpha())
   1413             haveAlphaColor = true;
   1414 
   1415         if (currEdge.borderStyle() != SOLID)
   1416             haveAllSolidEdges = false;
   1417 
   1418         if (currEdge.borderStyle() != DOUBLE)
   1419             haveAllDoubleEdges = false;
   1420     }
   1421 
   1422     // If no corner intersects the clip region, we can pretend outerBorder is
   1423     // rectangular to improve performance.
   1424     if (haveAllSolidEdges && outerBorder.isRounded() && allCornersClippedOut(outerBorder, info.rect))
   1425         outerBorder.setRadii(RoundedRect::Radii());
   1426 
   1427     // isRenderable() check avoids issue described in https://bugs.webkit.org/show_bug.cgi?id=38787
   1428     if ((haveAllSolidEdges || haveAllDoubleEdges) && allEdgesShareColor && innerBorder.isRenderable()) {
   1429         // Fast path for drawing all solid edges and all unrounded double edges
   1430 
   1431         if (numEdgesVisible == 4 && (outerBorder.isRounded() || haveAlphaColor)
   1432             && (haveAllSolidEdges || (!outerBorder.isRounded() && !innerBorder.isRounded()))) {
   1433             Path path;
   1434 
   1435             if (outerBorder.isRounded() && allEdgesShareWidth) {
   1436 
   1437                 // Very fast path for single stroked round rect with circular corners
   1438 
   1439                 graphicsContext->fillBetweenRoundedRects(outerBorder, innerBorder, edges[firstVisibleEdge].color);
   1440                 return;
   1441             }
   1442             if (outerBorder.isRounded() && bleedAvoidance != BackgroundBleedClipBackground)
   1443                 path.addRoundedRect(outerBorder);
   1444             else
   1445                 path.addRect(outerBorder.rect());
   1446 
   1447             if (haveAllDoubleEdges) {
   1448                 IntRect innerThirdRect = outerBorder.rect();
   1449                 IntRect outerThirdRect = outerBorder.rect();
   1450                 for (int side = BSTop; side <= BSLeft; ++side) {
   1451                     int outerWidth;
   1452                     int innerWidth;
   1453                     edges[side].getDoubleBorderStripeWidths(outerWidth, innerWidth);
   1454 
   1455                     if (side == BSTop) {
   1456                         innerThirdRect.shiftYEdgeTo(innerThirdRect.y() + innerWidth);
   1457                         outerThirdRect.shiftYEdgeTo(outerThirdRect.y() + outerWidth);
   1458                     } else if (side == BSBottom) {
   1459                         innerThirdRect.setHeight(innerThirdRect.height() - innerWidth);
   1460                         outerThirdRect.setHeight(outerThirdRect.height() - outerWidth);
   1461                     } else if (side == BSLeft) {
   1462                         innerThirdRect.shiftXEdgeTo(innerThirdRect.x() + innerWidth);
   1463                         outerThirdRect.shiftXEdgeTo(outerThirdRect.x() + outerWidth);
   1464                     } else {
   1465                         innerThirdRect.setWidth(innerThirdRect.width() - innerWidth);
   1466                         outerThirdRect.setWidth(outerThirdRect.width() - outerWidth);
   1467                     }
   1468                 }
   1469 
   1470                 RoundedRect outerThird = outerBorder;
   1471                 RoundedRect innerThird = innerBorder;
   1472                 innerThird.setRect(innerThirdRect);
   1473                 outerThird.setRect(outerThirdRect);
   1474 
   1475                 if (outerThird.isRounded() && bleedAvoidance != BackgroundBleedClipBackground)
   1476                     path.addRoundedRect(outerThird);
   1477                 else
   1478                     path.addRect(outerThird.rect());
   1479 
   1480                 if (innerThird.isRounded() && bleedAvoidance != BackgroundBleedClipBackground)
   1481                     path.addRoundedRect(innerThird);
   1482                 else
   1483                     path.addRect(innerThird.rect());
   1484             }
   1485 
   1486             if (innerBorder.isRounded())
   1487                 path.addRoundedRect(innerBorder);
   1488             else
   1489                 path.addRect(innerBorder.rect());
   1490 
   1491             graphicsContext->setFillRule(RULE_EVENODD);
   1492             graphicsContext->setFillColor(edges[firstVisibleEdge].color);
   1493             graphicsContext->fillPath(path);
   1494             return;
   1495         }
   1496         // Avoid creating transparent layers
   1497         if (haveAllSolidEdges && numEdgesVisible != 4 && !outerBorder.isRounded() && haveAlphaColor) {
   1498             Path path;
   1499 
   1500             for (int i = BSTop; i <= BSLeft; ++i) {
   1501                 const BorderEdge& currEdge = edges[i];
   1502                 if (currEdge.shouldRender()) {
   1503                     IntRect sideRect = calculateSideRect(outerBorder, edges, i);
   1504                     path.addRect(sideRect);
   1505                 }
   1506             }
   1507 
   1508             graphicsContext->setFillRule(RULE_NONZERO);
   1509             graphicsContext->setFillColor(edges[firstVisibleEdge].color);
   1510             graphicsContext->fillPath(path);
   1511             return;
   1512         }
   1513     }
   1514 
   1515     bool clipToOuterBorder = outerBorder.isRounded();
   1516     GraphicsContextStateSaver stateSaver(*graphicsContext, clipToOuterBorder);
   1517     if (clipToOuterBorder) {
   1518         // Clip to the inner and outer radii rects.
   1519         if (bleedAvoidance != BackgroundBleedClipBackground)
   1520             graphicsContext->clipRoundedRect(outerBorder);
   1521         // isRenderable() check avoids issue described in https://bugs.webkit.org/show_bug.cgi?id=38787
   1522         // The inside will be clipped out later (in clipBorderSideForComplexInnerPath)
   1523         if (innerBorder.isRenderable() && !innerBorder.isEmpty())
   1524             graphicsContext->clipOutRoundedRect(innerBorder);
   1525     }
   1526 
   1527     // If only one edge visible antialiasing doesn't create seams
   1528     bool antialias = shouldAntialiasLines(graphicsContext) || numEdgesVisible == 1;
   1529     RoundedRect unadjustedInnerBorder = (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? style->getRoundedInnerBorderFor(rect, includeLogicalLeftEdge, includeLogicalRightEdge) : innerBorder;
   1530     IntPoint innerBorderAdjustment(innerBorder.rect().x() - unadjustedInnerBorder.rect().x(), innerBorder.rect().y() - unadjustedInnerBorder.rect().y());
   1531     if (haveAlphaColor)
   1532         paintTranslucentBorderSides(obj, graphicsContext, style, outerBorder, unadjustedInnerBorder, innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias);
   1533     else
   1534         paintBorderSides(obj, graphicsContext, style, outerBorder, unadjustedInnerBorder, innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias);
   1535 }
   1536 
   1537 static inline bool includesAdjacentEdges(BorderEdgeFlags flags)
   1538 {
   1539     return (flags & (TopBorderEdge | RightBorderEdge)) == (TopBorderEdge | RightBorderEdge)
   1540         || (flags & (RightBorderEdge | BottomBorderEdge)) == (RightBorderEdge | BottomBorderEdge)
   1541         || (flags & (BottomBorderEdge | LeftBorderEdge)) == (BottomBorderEdge | LeftBorderEdge)
   1542         || (flags & (LeftBorderEdge | TopBorderEdge)) == (LeftBorderEdge | TopBorderEdge);
   1543 }
   1544 
   1545 void BoxPainter::paintTranslucentBorderSides(RenderObject& obj, GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, const IntPoint& innerBorderAdjustment,
   1546     const BorderEdge edges[], BorderEdgeFlags edgesToDraw, BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias)
   1547 {
   1548     // willBeOverdrawn assumes that we draw in order: top, bottom, left, right.
   1549     // This is different from BoxSide enum order.
   1550     static const BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight };
   1551 
   1552     while (edgesToDraw) {
   1553         // Find undrawn edges sharing a color.
   1554         Color commonColor;
   1555 
   1556         BorderEdgeFlags commonColorEdgeSet = 0;
   1557         for (size_t i = 0; i < sizeof(paintOrder) / sizeof(paintOrder[0]); ++i) {
   1558             BoxSide currSide = paintOrder[i];
   1559             if (!includesEdge(edgesToDraw, currSide))
   1560                 continue;
   1561 
   1562             bool includeEdge;
   1563             if (!commonColorEdgeSet) {
   1564                 commonColor = edges[currSide].color;
   1565                 includeEdge = true;
   1566             } else {
   1567                 includeEdge = edges[currSide].color == commonColor;
   1568             }
   1569 
   1570             if (includeEdge)
   1571                 commonColorEdgeSet |= edgeFlagForSide(currSide);
   1572         }
   1573 
   1574         bool useTransparencyLayer = includesAdjacentEdges(commonColorEdgeSet) && commonColor.hasAlpha();
   1575         if (useTransparencyLayer) {
   1576             graphicsContext->beginTransparencyLayer(static_cast<float>(commonColor.alpha()) / 255);
   1577             commonColor = Color(commonColor.red(), commonColor.green(), commonColor.blue());
   1578         }
   1579 
   1580         paintBorderSides(obj, graphicsContext, style, outerBorder, innerBorder, innerBorderAdjustment, edges, commonColorEdgeSet, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, &commonColor);
   1581 
   1582         if (useTransparencyLayer)
   1583             graphicsContext->endLayer();
   1584 
   1585         edgesToDraw &= ~commonColorEdgeSet;
   1586     }
   1587 }
   1588 
   1589 LayoutRect BoxPainter::borderInnerRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& rect, BackgroundBleedAvoidance bleedAvoidance)
   1590 {
   1591     // We shrink the rectangle by one pixel on each side to make it fully overlap the anti-aliased background border
   1592     return (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? shrinkRectByOnePixel(context, rect) : rect;
   1593 }
   1594 
   1595 void BoxPainter::paintOneBorderSide(RenderObject& obj, GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
   1596     const IntRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adjacentSide2, const BorderEdge edges[], const Path* path,
   1597     BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor)
   1598 {
   1599     const BorderEdge& edgeToRender = edges[side];
   1600     ASSERT(edgeToRender.width);
   1601     const BorderEdge& adjacentEdge1 = edges[adjacentSide1];
   1602     const BorderEdge& adjacentEdge2 = edges[adjacentSide2];
   1603 
   1604     bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, edges, !antialias);
   1605     bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, edges, !antialias);
   1606 
   1607     bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1, edges);
   1608     bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2, edges);
   1609 
   1610     const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.color;
   1611 
   1612     if (path) {
   1613         GraphicsContextStateSaver stateSaver(*graphicsContext);
   1614         if (innerBorder.isRenderable())
   1615             clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, adjacentSide1StylesMatch, adjacentSide2StylesMatch);
   1616         else
   1617             clipBorderSideForComplexInnerPath(graphicsContext, outerBorder, innerBorder, side, edges);
   1618         float thickness = std::max(std::max(edgeToRender.width, adjacentEdge1.width), adjacentEdge2.width);
   1619         drawBoxSideFromPath(graphicsContext, outerBorder.rect(), *path, edges, edgeToRender.width, thickness, side, style,
   1620             colorToPaint, edgeToRender.borderStyle(), bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
   1621     } else {
   1622         bool clipForStyle = styleRequiresClipPolygon(edgeToRender.borderStyle()) && (mitreAdjacentSide1 || mitreAdjacentSide2);
   1623         bool clipAdjacentSide1 = colorNeedsAntiAliasAtCorner(side, adjacentSide1, edges) && mitreAdjacentSide1;
   1624         bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSide2, edges) && mitreAdjacentSide2;
   1625         bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2;
   1626 
   1627         GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip);
   1628         if (shouldClip) {
   1629             bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitreAdjacentSide1);
   1630             bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitreAdjacentSide2);
   1631             clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, !aliasAdjacentSide1, !aliasAdjacentSide2);
   1632             // Since we clipped, no need to draw with a mitre.
   1633             mitreAdjacentSide1 = false;
   1634             mitreAdjacentSide2 = false;
   1635         }
   1636 
   1637         ObjectPainter::drawLineForBoxSide(graphicsContext, sideRect.x(), sideRect.y(), sideRect.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.borderStyle(),
   1638             mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? adjacentEdge2.width : 0, antialias);
   1639     }
   1640 }
   1641 
   1642 void BoxPainter::paintBorderSides(RenderObject& obj, GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
   1643     const IntPoint& innerBorderAdjustment, const BorderEdge edges[], BorderEdgeFlags edgeSet, BackgroundBleedAvoidance bleedAvoidance,
   1644     bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor)
   1645 {
   1646     bool renderRadii = outerBorder.isRounded();
   1647 
   1648     Path roundedPath;
   1649     if (renderRadii)
   1650         roundedPath.addRoundedRect(outerBorder);
   1651 
   1652     // The inner border adjustment for bleed avoidance mode BackgroundBleedBackgroundOverBorder
   1653     // is only applied to sideRect, which is okay since BackgroundBleedBackgroundOverBorder
   1654     // is only to be used for solid borders and the shape of the border painted by drawBoxSideFromPath
   1655     // only depends on sideRect when painting solid borders.
   1656 
   1657     if (edges[BSTop].shouldRender() && includesEdge(edgeSet, BSTop)) {
   1658         IntRect sideRect = outerBorder.rect();
   1659         sideRect.setHeight(edges[BSTop].width + innerBorderAdjustment.y());
   1660 
   1661         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSTop].borderStyle()) || borderWillArcInnerEdge(innerBorder.radii().topLeft(), innerBorder.radii().topRight()));
   1662         paintOneBorderSide(obj, graphicsContext, style, outerBorder, innerBorder, sideRect, BSTop, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
   1663     }
   1664 
   1665     if (edges[BSBottom].shouldRender() && includesEdge(edgeSet, BSBottom)) {
   1666         IntRect sideRect = outerBorder.rect();
   1667         sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].width - innerBorderAdjustment.y());
   1668 
   1669         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSBottom].borderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().bottomRight()));
   1670         paintOneBorderSide(obj, graphicsContext, style, outerBorder, innerBorder, sideRect, BSBottom, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
   1671     }
   1672 
   1673     if (edges[BSLeft].shouldRender() && includesEdge(edgeSet, BSLeft)) {
   1674         IntRect sideRect = outerBorder.rect();
   1675         sideRect.setWidth(edges[BSLeft].width + innerBorderAdjustment.x());
   1676 
   1677         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSLeft].borderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().topLeft()));
   1678         paintOneBorderSide(obj, graphicsContext, style, outerBorder, innerBorder, sideRect, BSLeft, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
   1679     }
   1680 
   1681     if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) {
   1682         IntRect sideRect = outerBorder.rect();
   1683         sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width - innerBorderAdjustment.x());
   1684 
   1685         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSRight].borderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomRight(), innerBorder.radii().topRight()));
   1686         paintOneBorderSide(obj, graphicsContext, style, outerBorder, innerBorder, sideRect, BSRight, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
   1687     }
   1688 }
   1689 
   1690 void BoxPainter::drawBoxSideFromPath(GraphicsContext* graphicsContext, const LayoutRect& borderRect, const Path& borderPath, const BorderEdge edges[],
   1691     float thickness, float drawThickness, BoxSide side, const RenderStyle* style, Color color, EBorderStyle borderStyle, BackgroundBleedAvoidance bleedAvoidance,
   1692     bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
   1693 {
   1694     if (thickness <= 0)
   1695         return;
   1696 
   1697     if (borderStyle == DOUBLE && thickness < 3)
   1698         borderStyle = SOLID;
   1699 
   1700     switch (borderStyle) {
   1701     case BNONE:
   1702     case BHIDDEN:
   1703         return;
   1704     case DOTTED:
   1705     case DASHED: {
   1706         graphicsContext->setStrokeColor(color);
   1707 
   1708         // The stroke is doubled here because the provided path is the
   1709         // outside edge of the border so half the stroke is clipped off.
   1710         // The extra multiplier is so that the clipping mask can antialias
   1711         // the edges to prevent jaggies.
   1712         graphicsContext->setStrokeThickness(drawThickness * 2 * 1.1f);
   1713         graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke : DottedStroke);
   1714 
   1715         // If the number of dashes that fit in the path is odd and non-integral then we
   1716         // will have an awkwardly-sized dash at the end of the path. To try to avoid that
   1717         // here, we simply make the whitespace dashes ever so slightly bigger.
   1718         // FIXME: This could be even better if we tried to manipulate the dash offset
   1719         // and possibly the gapLength to get the corners dash-symmetrical.
   1720         float dashLength = thickness * ((borderStyle == DASHED) ? 3.0f : 1.0f);
   1721         float gapLength = dashLength;
   1722         float numberOfDashes = borderPath.length() / dashLength;
   1723         // Don't try to show dashes if we have less than 2 dashes + 2 gaps.
   1724         // FIXME: should do this test per side.
   1725         if (numberOfDashes >= 4) {
   1726             bool evenNumberOfFullDashes = !((int)numberOfDashes % 2);
   1727             bool integralNumberOfDashes = !(numberOfDashes - (int)numberOfDashes);
   1728             if (!evenNumberOfFullDashes && !integralNumberOfDashes) {
   1729                 float numberOfGaps = numberOfDashes / 2;
   1730                 gapLength += (dashLength  / numberOfGaps);
   1731             }
   1732 
   1733             DashArray lineDash;
   1734             lineDash.append(dashLength);
   1735             lineDash.append(gapLength);
   1736             graphicsContext->setLineDash(lineDash, dashLength);
   1737         }
   1738 
   1739         // FIXME: stroking the border path causes issues with tight corners:
   1740         // https://bugs.webkit.org/show_bug.cgi?id=58711
   1741         // Also, to get the best appearance we should stroke a path between the two borders.
   1742         graphicsContext->strokePath(borderPath);
   1743         return;
   1744     }
   1745     case DOUBLE: {
   1746         // Get the inner border rects for both the outer border line and the inner border line
   1747         int outerBorderTopWidth;
   1748         int innerBorderTopWidth;
   1749         edges[BSTop].getDoubleBorderStripeWidths(outerBorderTopWidth, innerBorderTopWidth);
   1750 
   1751         int outerBorderRightWidth;
   1752         int innerBorderRightWidth;
   1753         edges[BSRight].getDoubleBorderStripeWidths(outerBorderRightWidth, innerBorderRightWidth);
   1754 
   1755         int outerBorderBottomWidth;
   1756         int innerBorderBottomWidth;
   1757         edges[BSBottom].getDoubleBorderStripeWidths(outerBorderBottomWidth, innerBorderBottomWidth);
   1758 
   1759         int outerBorderLeftWidth;
   1760         int innerBorderLeftWidth;
   1761         edges[BSLeft].getDoubleBorderStripeWidths(outerBorderLeftWidth, innerBorderLeftWidth);
   1762 
   1763         // Draw inner border line
   1764         {
   1765             GraphicsContextStateSaver stateSaver(*graphicsContext);
   1766             RoundedRect innerClip = style->getRoundedInnerBorderFor(borderRect,
   1767                 innerBorderTopWidth, innerBorderBottomWidth, innerBorderLeftWidth, innerBorderRightWidth,
   1768                 includeLogicalLeftEdge, includeLogicalRightEdge);
   1769 
   1770             graphicsContext->clipRoundedRect(innerClip);
   1771             drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
   1772         }
   1773 
   1774         // Draw outer border line
   1775         {
   1776             GraphicsContextStateSaver stateSaver(*graphicsContext);
   1777             LayoutRect outerRect = borderRect;
   1778             if (bleedAvoidance == BackgroundBleedClipBackground) {
   1779                 outerRect.inflate(1);
   1780                 ++outerBorderTopWidth;
   1781                 ++outerBorderBottomWidth;
   1782                 ++outerBorderLeftWidth;
   1783                 ++outerBorderRightWidth;
   1784             }
   1785 
   1786             RoundedRect outerClip = style->getRoundedInnerBorderFor(outerRect,
   1787                 outerBorderTopWidth, outerBorderBottomWidth, outerBorderLeftWidth, outerBorderRightWidth,
   1788                 includeLogicalLeftEdge, includeLogicalRightEdge);
   1789             graphicsContext->clipOutRoundedRect(outerClip);
   1790             drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
   1791         }
   1792         return;
   1793     }
   1794     case RIDGE:
   1795     case GROOVE:
   1796     {
   1797         EBorderStyle s1;
   1798         EBorderStyle s2;
   1799         if (borderStyle == GROOVE) {
   1800             s1 = INSET;
   1801             s2 = OUTSET;
   1802         } else {
   1803             s1 = OUTSET;
   1804             s2 = INSET;
   1805         }
   1806 
   1807         // Paint full border
   1808         drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s1, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
   1809 
   1810         // Paint inner only
   1811         GraphicsContextStateSaver stateSaver(*graphicsContext);
   1812         LayoutUnit topWidth = edges[BSTop].usedWidth() / 2;
   1813         LayoutUnit bottomWidth = edges[BSBottom].usedWidth() / 2;
   1814         LayoutUnit leftWidth = edges[BSLeft].usedWidth() / 2;
   1815         LayoutUnit rightWidth = edges[BSRight].usedWidth() / 2;
   1816 
   1817         RoundedRect clipRect = style->getRoundedInnerBorderFor(borderRect,
   1818             topWidth, bottomWidth, leftWidth, rightWidth,
   1819             includeLogicalLeftEdge, includeLogicalRightEdge);
   1820 
   1821         graphicsContext->clipRoundedRect(clipRect);
   1822         drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s2, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
   1823         return;
   1824     }
   1825     case INSET:
   1826         if (side == BSTop || side == BSLeft)
   1827             color = color.dark();
   1828         break;
   1829     case OUTSET:
   1830         if (side == BSBottom || side == BSRight)
   1831             color = color.dark();
   1832         break;
   1833     default:
   1834         break;
   1835     }
   1836 
   1837     graphicsContext->setStrokeStyle(NoStroke);
   1838     graphicsContext->setFillColor(color);
   1839     graphicsContext->drawRect(pixelSnappedIntRect(borderRect));
   1840 }
   1841 
   1842 void BoxPainter::paintBoxShadow(const PaintInfo& info, const LayoutRect& paintRect, const RenderStyle* s, ShadowStyle shadowStyle, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
   1843 {
   1844     // FIXME: Deal with border-image. Would be great to use border-image as a mask.
   1845     GraphicsContext* context = info.context;
   1846     if (!s->boxShadow())
   1847         return;
   1848 
   1849     RoundedRect border = (shadowStyle == Inset) ? s->getRoundedInnerBorderFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge)
   1850         : s->getRoundedBorderFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge);
   1851 
   1852     bool hasBorderRadius = s->hasBorderRadius();
   1853     bool isHorizontal = s->isHorizontalWritingMode();
   1854     bool hasOpaqueBackground = s->visitedDependentColor(CSSPropertyBackgroundColor).alpha() == 255;
   1855 
   1856     GraphicsContextStateSaver stateSaver(*context, false);
   1857 
   1858     const ShadowList* shadowList = s->boxShadow();
   1859     for (size_t i = shadowList->shadows().size(); i--; ) {
   1860         const ShadowData& shadow = shadowList->shadows()[i];
   1861         if (shadow.style() != shadowStyle)
   1862             continue;
   1863 
   1864         FloatSize shadowOffset(shadow.x(), shadow.y());
   1865         float shadowBlur = shadow.blur();
   1866         float shadowSpread = shadow.spread();
   1867 
   1868         if (shadowOffset.isZero() && !shadowBlur && !shadowSpread)
   1869             continue;
   1870 
   1871         const Color& shadowColor = shadow.color();
   1872 
   1873         if (shadow.style() == Normal) {
   1874             FloatRect fillRect = border.rect();
   1875             fillRect.inflate(shadowSpread);
   1876             if (fillRect.isEmpty())
   1877                 continue;
   1878 
   1879             FloatRect shadowRect(border.rect());
   1880             shadowRect.inflate(shadowBlur + shadowSpread);
   1881             shadowRect.move(shadowOffset);
   1882 
   1883             // Save the state and clip, if not already done.
   1884             // The clip does not depend on any shadow-specific properties.
   1885             if (!stateSaver.saved()) {
   1886                 stateSaver.save();
   1887                 if (hasBorderRadius) {
   1888                     RoundedRect rectToClipOut = border;
   1889 
   1890                     // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
   1891                     // when painting the shadow. On the other hand, it introduces subpixel gaps along the
   1892                     // corners. Those are avoided by insetting the clipping path by one pixel.
   1893                     if (hasOpaqueBackground)
   1894                         rectToClipOut.inflateWithRadii(-1);
   1895 
   1896                     if (!rectToClipOut.isEmpty()) {
   1897                         context->clipOutRoundedRect(rectToClipOut);
   1898                     }
   1899                 } else {
   1900                     // This IntRect is correct even with fractional shadows, because it is used for the rectangle
   1901                     // of the box itself, which is always pixel-aligned.
   1902                     IntRect rectToClipOut = border.rect();
   1903 
   1904                     // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
   1905                     // when painting the shadow. On the other hand, it introduces subpixel gaps along the
   1906                     // edges if they are not pixel-aligned. Those are avoided by insetting the clipping path
   1907                     // by one pixel.
   1908                     if (hasOpaqueBackground) {
   1909                         // FIXME: The function to decide on the policy based on the transform should be a named function.
   1910                         // FIXME: It's not clear if this check is right. What about integral scale factors?
   1911                         // FIXME: See crbug.com/382491. The use of getCTM may also be wrong because it does not include
   1912                         // device zoom applied at raster time.
   1913                         AffineTransform transform = context->getCTM();
   1914                         if (transform.a() != 1 || (transform.d() != 1 && transform.d() != -1) || transform.b() || transform.c())
   1915                             rectToClipOut.inflate(-1);
   1916                     }
   1917 
   1918                     if (!rectToClipOut.isEmpty()) {
   1919                         context->clipOut(rectToClipOut);
   1920                     }
   1921                 }
   1922             }
   1923 
   1924             // Draw only the shadow.
   1925             OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create();
   1926             drawLooperBuilder->addShadow(shadowOffset, shadowBlur, shadowColor,
   1927                 DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha);
   1928             context->setDrawLooper(drawLooperBuilder.release());
   1929 
   1930             if (hasBorderRadius) {
   1931                 RoundedRect influenceRect(pixelSnappedIntRect(LayoutRect(shadowRect)), border.radii());
   1932                 influenceRect.expandRadii(2 * shadowBlur + shadowSpread);
   1933                 if (allCornersClippedOut(influenceRect, info.rect)) {
   1934                     context->fillRect(fillRect, Color::black);
   1935                 } else {
   1936                     // TODO: support non-integer shadows - crbug.com/334829
   1937                     RoundedRect roundedFillRect = border;
   1938                     roundedFillRect.inflate(shadowSpread);
   1939 
   1940                     roundedFillRect.expandRadii(shadowSpread);
   1941                     if (!roundedFillRect.isRenderable())
   1942                         roundedFillRect.adjustRadii();
   1943                     context->fillRoundedRect(roundedFillRect, Color::black);
   1944                 }
   1945             } else {
   1946                 context->fillRect(fillRect, Color::black);
   1947             }
   1948         } else {
   1949             // The inset shadow case.
   1950             GraphicsContext::Edges clippedEdges = GraphicsContext::NoEdge;
   1951             if (!includeLogicalLeftEdge) {
   1952                 if (isHorizontal)
   1953                     clippedEdges |= GraphicsContext::LeftEdge;
   1954                 else
   1955                     clippedEdges |= GraphicsContext::TopEdge;
   1956             }
   1957             if (!includeLogicalRightEdge) {
   1958                 if (isHorizontal)
   1959                     clippedEdges |= GraphicsContext::RightEdge;
   1960                 else
   1961                     clippedEdges |= GraphicsContext::BottomEdge;
   1962             }
   1963             // TODO: support non-integer shadows - crbug.com/334828
   1964             context->drawInnerShadow(border, shadowColor, flooredIntSize(shadowOffset), shadowBlur, shadowSpread, clippedEdges);
   1965         }
   1966     }
   1967 }
   1968 
   1969 void BoxPainter::clipBorderSidePolygon(GraphicsContext* graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder, BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches)
   1970 {
   1971     FloatPoint quad[4];
   1972 
   1973     const LayoutRect& outerRect = outerBorder.rect();
   1974     const LayoutRect& innerRect = innerBorder.rect();
   1975 
   1976     FloatPoint centerPoint(innerRect.location().x().toFloat() + innerRect.width().toFloat() / 2, innerRect.location().y().toFloat() + innerRect.height().toFloat() / 2);
   1977 
   1978     // For each side, create a quad that encompasses all parts of that side that may draw,
   1979     // including areas inside the innerBorder.
   1980     //
   1981     //         0----------------3
   1982     //       0  \              /  0
   1983     //       |\  1----------- 2  /|
   1984     //       | 1                1 |
   1985     //       | |                | |
   1986     //       | |                | |
   1987     //       | 2                2 |
   1988     //       |/  1------------2  \|
   1989     //       3  /              \  3
   1990     //         0----------------3
   1991     //
   1992     switch (side) {
   1993     case BSTop:
   1994         quad[0] = outerRect.minXMinYCorner();
   1995         quad[1] = innerRect.minXMinYCorner();
   1996         quad[2] = innerRect.maxXMinYCorner();
   1997         quad[3] = outerRect.maxXMinYCorner();
   1998 
   1999         if (!innerBorder.radii().topLeft().isZero()) {
   2000             findIntersection(quad[0], quad[1],
   2001                 FloatPoint(
   2002                     quad[1].x() + innerBorder.radii().topLeft().width(),
   2003                     quad[1].y()),
   2004                 FloatPoint(
   2005                     quad[1].x(),
   2006                     quad[1].y() + innerBorder.radii().topLeft().height()),
   2007                 quad[1]);
   2008         }
   2009 
   2010         if (!innerBorder.radii().topRight().isZero()) {
   2011             findIntersection(quad[3], quad[2],
   2012                 FloatPoint(
   2013                     quad[2].x() - innerBorder.radii().topRight().width(),
   2014                     quad[2].y()),
   2015                 FloatPoint(
   2016                     quad[2].x(),
   2017                     quad[2].y() + innerBorder.radii().topRight().height()),
   2018                 quad[2]);
   2019         }
   2020         break;
   2021 
   2022     case BSLeft:
   2023         quad[0] = outerRect.minXMinYCorner();
   2024         quad[1] = innerRect.minXMinYCorner();
   2025         quad[2] = innerRect.minXMaxYCorner();
   2026         quad[3] = outerRect.minXMaxYCorner();
   2027 
   2028         if (!innerBorder.radii().topLeft().isZero()) {
   2029             findIntersection(quad[0], quad[1],
   2030                 FloatPoint(
   2031                     quad[1].x() + innerBorder.radii().topLeft().width(),
   2032                     quad[1].y()),
   2033                 FloatPoint(
   2034                     quad[1].x(),
   2035                     quad[1].y() + innerBorder.radii().topLeft().height()),
   2036                 quad[1]);
   2037         }
   2038 
   2039         if (!innerBorder.radii().bottomLeft().isZero()) {
   2040             findIntersection(quad[3], quad[2],
   2041                 FloatPoint(
   2042                     quad[2].x() + innerBorder.radii().bottomLeft().width(),
   2043                     quad[2].y()),
   2044                 FloatPoint(
   2045                     quad[2].x(),
   2046                     quad[2].y() - innerBorder.radii().bottomLeft().height()),
   2047                 quad[2]);
   2048         }
   2049         break;
   2050 
   2051     case BSBottom:
   2052         quad[0] = outerRect.minXMaxYCorner();
   2053         quad[1] = innerRect.minXMaxYCorner();
   2054         quad[2] = innerRect.maxXMaxYCorner();
   2055         quad[3] = outerRect.maxXMaxYCorner();
   2056 
   2057         if (!innerBorder.radii().bottomLeft().isZero()) {
   2058             findIntersection(quad[0], quad[1],
   2059                 FloatPoint(
   2060                     quad[1].x() + innerBorder.radii().bottomLeft().width(),
   2061                     quad[1].y()),
   2062                 FloatPoint(
   2063                     quad[1].x(),
   2064                     quad[1].y() - innerBorder.radii().bottomLeft().height()),
   2065                 quad[1]);
   2066         }
   2067 
   2068         if (!innerBorder.radii().bottomRight().isZero()) {
   2069             findIntersection(quad[3], quad[2],
   2070                 FloatPoint(
   2071                     quad[2].x() - innerBorder.radii().bottomRight().width(),
   2072                     quad[2].y()),
   2073                 FloatPoint(
   2074                     quad[2].x(),
   2075                     quad[2].y() - innerBorder.radii().bottomRight().height()),
   2076                 quad[2]);
   2077         }
   2078         break;
   2079 
   2080     case BSRight:
   2081         quad[0] = outerRect.maxXMinYCorner();
   2082         quad[1] = innerRect.maxXMinYCorner();
   2083         quad[2] = innerRect.maxXMaxYCorner();
   2084         quad[3] = outerRect.maxXMaxYCorner();
   2085 
   2086         if (!innerBorder.radii().topRight().isZero()) {
   2087             findIntersection(quad[0], quad[1],
   2088                 FloatPoint(
   2089                     quad[1].x() - innerBorder.radii().topRight().width(),
   2090                     quad[1].y()),
   2091                 FloatPoint(
   2092                     quad[1].x(),
   2093                     quad[1].y() + innerBorder.radii().topRight().height()),
   2094                 quad[1]);
   2095         }
   2096 
   2097         if (!innerBorder.radii().bottomRight().isZero()) {
   2098             findIntersection(quad[3], quad[2],
   2099                 FloatPoint(
   2100                     quad[2].x() - innerBorder.radii().bottomRight().width(),
   2101                     quad[2].y()),
   2102                 FloatPoint(
   2103                     quad[2].x(),
   2104                     quad[2].y() - innerBorder.radii().bottomRight().height()),
   2105                 quad[2]);
   2106         }
   2107         break;
   2108     }
   2109 
   2110     // If the border matches both of its adjacent sides, don't anti-alias the clip, and
   2111     // if neither side matches, anti-alias the clip.
   2112     if (firstEdgeMatches == secondEdgeMatches) {
   2113         graphicsContext->clipConvexPolygon(4, quad, !firstEdgeMatches);
   2114         return;
   2115     }
   2116 
   2117     // If antialiasing settings for the first edge and second edge is different,
   2118     // they have to be addressed separately. We do this by breaking the quad into
   2119     // two parallelograms, made by moving quad[1] and quad[2].
   2120     float ax = quad[1].x() - quad[0].x();
   2121     float ay = quad[1].y() - quad[0].y();
   2122     float bx = quad[2].x() - quad[1].x();
   2123     float by = quad[2].y() - quad[1].y();
   2124     float cx = quad[3].x() - quad[2].x();
   2125     float cy = quad[3].y() - quad[2].y();
   2126 
   2127     const static float kEpsilon = 1e-2f;
   2128     float r1, r2;
   2129     if (fabsf(bx) < kEpsilon && fabsf(by) < kEpsilon) {
   2130         // The quad was actually a triangle.
   2131         r1 = r2 = 1.0f;
   2132     } else {
   2133         // Extend parallelogram a bit to hide calculation error
   2134         const static float kExtendFill = 1e-2f;
   2135 
   2136         r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill;
   2137         r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill;
   2138     }
   2139 
   2140     FloatPoint firstQuad[4];
   2141     firstQuad[0] = quad[0];
   2142     firstQuad[1] = quad[1];
   2143     firstQuad[2] = FloatPoint(quad[3].x() + r2 * ax, quad[3].y() + r2 * ay);
   2144     firstQuad[3] = quad[3];
   2145     graphicsContext->clipConvexPolygon(4, firstQuad, !firstEdgeMatches);
   2146 
   2147     FloatPoint secondQuad[4];
   2148     secondQuad[0] = quad[0];
   2149     secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy);
   2150     secondQuad[2] = quad[2];
   2151     secondQuad[3] = quad[3];
   2152     graphicsContext->clipConvexPolygon(4, secondQuad, !secondEdgeMatches);
   2153 }
   2154 
   2155 } // namespace blink
   2156