Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  *           (C) 2005 Allan Sandfeld Jensen (kde (at) carewolf.com)
      5  *           (C) 2005, 2006 Samuel Weinig (sam.weinig (at) gmail.com)
      6  * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
      7  * Copyright (C) 2010 Google Inc. All rights reserved.
      8  *
      9  * This library is free software; you can redistribute it and/or
     10  * modify it under the terms of the GNU Library General Public
     11  * License as published by the Free Software Foundation; either
     12  * version 2 of the License, or (at your option) any later version.
     13  *
     14  * This library is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     17  * Library General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU Library General Public License
     20  * along with this library; see the file COPYING.LIB.  If not, write to
     21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     22  * Boston, MA 02110-1301, USA.
     23  *
     24  */
     25 
     26 #include "config.h"
     27 #include "core/rendering/RenderBoxModelObject.h"
     28 
     29 #include "HTMLNames.h"
     30 #include "core/html/HTMLFrameOwnerElement.h"
     31 #include "core/page/Settings.h"
     32 #include "core/page/scrolling/ScrollingConstraints.h"
     33 #include "core/platform/graphics/DrawLooper.h"
     34 #include "core/platform/graphics/GraphicsContextStateSaver.h"
     35 #include "core/platform/graphics/Path.h"
     36 #include "core/platform/graphics/transforms/TransformState.h"
     37 #include "core/rendering/ImageQualityController.h"
     38 #include "core/rendering/RenderBlock.h"
     39 #include "core/rendering/RenderGeometryMap.h"
     40 #include "core/rendering/RenderInline.h"
     41 #include "core/rendering/RenderLayer.h"
     42 #include "core/rendering/RenderLayerBacking.h"
     43 #include "core/rendering/RenderLayerCompositor.h"
     44 #include "core/rendering/RenderNamedFlowThread.h"
     45 #include "core/rendering/RenderRegion.h"
     46 #include "core/rendering/RenderView.h"
     47 #include "wtf/CurrentTime.h"
     48 
     49 using namespace std;
     50 
     51 namespace WebCore {
     52 
     53 using namespace HTMLNames;
     54 
     55 // The HashMap for storing continuation pointers.
     56 // An inline can be split with blocks occuring in between the inline content.
     57 // When this occurs we need a pointer to the next object. We can basically be
     58 // split into a sequence of inlines and blocks. The continuation will either be
     59 // an anonymous block (that houses other blocks) or it will be an inline flow.
     60 // <b><i><p>Hello</p></i></b>. In this example the <i> will have a block as
     61 // its continuation but the <b> will just have an inline as its continuation.
     62 typedef HashMap<const RenderBoxModelObject*, RenderBoxModelObject*> ContinuationMap;
     63 static ContinuationMap* continuationMap = 0;
     64 
     65 // This HashMap is similar to the continuation map, but connects first-letter
     66 // renderers to their remaining text fragments.
     67 typedef HashMap<const RenderBoxModelObject*, RenderObject*> FirstLetterRemainingTextMap;
     68 static FirstLetterRemainingTextMap* firstLetterRemainingTextMap = 0;
     69 
     70 void RenderBoxModelObject::setSelectionState(SelectionState state)
     71 {
     72     if (state == SelectionInside && selectionState() != SelectionNone)
     73         return;
     74 
     75     if ((state == SelectionStart && selectionState() == SelectionEnd)
     76         || (state == SelectionEnd && selectionState() == SelectionStart))
     77         RenderObject::setSelectionState(SelectionBoth);
     78     else
     79         RenderObject::setSelectionState(state);
     80 
     81     // FIXME: We should consider whether it is OK propagating to ancestor RenderInlines.
     82     // This is a workaround for http://webkit.org/b/32123
     83     // The containing block can be null in case of an orphaned tree.
     84     RenderBlock* containingBlock = this->containingBlock();
     85     if (containingBlock && !containingBlock->isRenderView())
     86         containingBlock->setSelectionState(state);
     87 }
     88 
     89 void RenderBoxModelObject::contentChanged(ContentChangeType changeType)
     90 {
     91     if (!hasLayer())
     92         return;
     93 
     94     layer()->contentChanged(changeType);
     95 }
     96 
     97 bool RenderBoxModelObject::hasAcceleratedCompositing() const
     98 {
     99     return view()->compositor()->hasAcceleratedCompositing();
    100 }
    101 
    102 bool RenderBoxModelObject::startTransition(double timeOffset, CSSPropertyID propertyId, const RenderStyle* fromStyle, const RenderStyle* toStyle)
    103 {
    104     ASSERT(hasLayer());
    105     ASSERT(isComposited());
    106     return layer()->backing()->startTransition(timeOffset, propertyId, fromStyle, toStyle);
    107 }
    108 
    109 void RenderBoxModelObject::transitionPaused(double timeOffset, CSSPropertyID propertyId)
    110 {
    111     ASSERT(hasLayer());
    112     ASSERT(isComposited());
    113     layer()->backing()->transitionPaused(timeOffset, propertyId);
    114 }
    115 
    116 void RenderBoxModelObject::transitionFinished(CSSPropertyID propertyId)
    117 {
    118     ASSERT(hasLayer());
    119     ASSERT(isComposited());
    120     layer()->backing()->transitionFinished(propertyId);
    121 }
    122 
    123 bool RenderBoxModelObject::startAnimation(double timeOffset, const CSSAnimationData* animation, const KeyframeList& keyframes)
    124 {
    125     ASSERT(hasLayer());
    126     ASSERT(isComposited());
    127     return layer()->backing()->startAnimation(timeOffset, animation, keyframes);
    128 }
    129 
    130 void RenderBoxModelObject::animationPaused(double timeOffset, const String& name)
    131 {
    132     ASSERT(hasLayer());
    133     ASSERT(isComposited());
    134     layer()->backing()->animationPaused(timeOffset, name);
    135 }
    136 
    137 void RenderBoxModelObject::animationFinished(const String& name)
    138 {
    139     ASSERT(hasLayer());
    140     ASSERT(isComposited());
    141     layer()->backing()->animationFinished(name);
    142 }
    143 
    144 void RenderBoxModelObject::suspendAnimations(double time)
    145 {
    146     ASSERT(hasLayer());
    147     ASSERT(isComposited());
    148     layer()->backing()->suspendAnimations(time);
    149 }
    150 
    151 bool RenderBoxModelObject::shouldPaintAtLowQuality(GraphicsContext* context, Image* image, const void* layer, const LayoutSize& size)
    152 {
    153     return ImageQualityController::imageQualityController()->shouldPaintAtLowQuality(context, this, image, layer, size);
    154 }
    155 
    156 RenderBoxModelObject::RenderBoxModelObject(ContainerNode* node)
    157     : RenderLayerModelObject(node)
    158 {
    159 }
    160 
    161 RenderBoxModelObject::~RenderBoxModelObject()
    162 {
    163     ImageQualityController::remove(this);
    164 }
    165 
    166 void RenderBoxModelObject::willBeDestroyed()
    167 {
    168     // A continuation of this RenderObject should be destroyed at subclasses.
    169     ASSERT(!continuation());
    170 
    171     // If this is a first-letter object with a remaining text fragment then the
    172     // entry needs to be cleared from the map.
    173     if (firstLetterRemainingText())
    174         setFirstLetterRemainingText(0);
    175 
    176     RenderLayerModelObject::willBeDestroyed();
    177 }
    178 
    179 void RenderBoxModelObject::updateFromStyle()
    180 {
    181     RenderLayerModelObject::updateFromStyle();
    182 
    183     // Set the appropriate bits for a box model object.  Since all bits are cleared in styleWillChange,
    184     // we only check for bits that could possibly be set to true.
    185     RenderStyle* styleToUse = style();
    186     setHasBoxDecorations(hasBackground() || styleToUse->hasBorder() || styleToUse->hasAppearance() || styleToUse->boxShadow());
    187     setInline(styleToUse->isDisplayInlineType());
    188     setPositionState(styleToUse->position());
    189     setHorizontalWritingMode(styleToUse->isHorizontalWritingMode());
    190 }
    191 
    192 static LayoutSize accumulateInFlowPositionOffsets(const RenderObject* child)
    193 {
    194     if (!child->isAnonymousBlock() || !child->isInFlowPositioned())
    195         return LayoutSize();
    196     LayoutSize offset;
    197     RenderObject* p = toRenderBlock(child)->inlineElementContinuation();
    198     while (p && p->isRenderInline()) {
    199         if (p->isInFlowPositioned()) {
    200             RenderInline* renderInline = toRenderInline(p);
    201             offset += renderInline->offsetForInFlowPosition();
    202         }
    203         p = p->parent();
    204     }
    205     return offset;
    206 }
    207 
    208 bool RenderBoxModelObject::hasAutoHeightOrContainingBlockWithAutoHeight() const
    209 {
    210     Length logicalHeightLength = style()->logicalHeight();
    211     if (logicalHeightLength.isAuto())
    212         return true;
    213 
    214     // For percentage heights: The percentage is calculated with respect to the height of the generated box's
    215     // containing block. If the height of the containing block is not specified explicitly (i.e., it depends
    216     // on content height), and this element is not absolutely positioned, the value computes to 'auto'.
    217     if (!logicalHeightLength.isPercent() || isOutOfFlowPositioned() || document()->inQuirksMode())
    218         return false;
    219 
    220     // Anonymous block boxes are ignored when resolving percentage values that would refer to it:
    221     // the closest non-anonymous ancestor box is used instead.
    222     RenderBlock* cb = containingBlock();
    223     while (cb->isAnonymous())
    224         cb = cb->containingBlock();
    225 
    226     // Matching RenderBox::percentageLogicalHeightIsResolvableFromBlock() by
    227     // ignoring table cell's attribute value, where it says that table cells violate
    228     // what the CSS spec says to do with heights. Basically we
    229     // don't care if the cell specified a height or not.
    230     if (cb->isTableCell())
    231         return false;
    232 
    233     if (!cb->style()->logicalHeight().isAuto() || (!cb->style()->logicalTop().isAuto() && !cb->style()->logicalBottom().isAuto()))
    234         return false;
    235 
    236     return true;
    237 }
    238 
    239 LayoutSize RenderBoxModelObject::relativePositionOffset() const
    240 {
    241     LayoutSize offset = accumulateInFlowPositionOffsets(this);
    242 
    243     RenderBlock* containingBlock = this->containingBlock();
    244 
    245     // Objects that shrink to avoid floats normally use available line width when computing containing block width.  However
    246     // in the case of relative positioning using percentages, we can't do this.  The offset should always be resolved using the
    247     // available width of the containing block.  Therefore we don't use containingBlockLogicalWidthForContent() here, but instead explicitly
    248     // call availableWidth on our containing block.
    249     if (!style()->left().isAuto()) {
    250         if (!style()->right().isAuto() && !containingBlock->style()->isLeftToRightDirection())
    251             offset.setWidth(-valueForLength(style()->right(), containingBlock->availableWidth(), view()));
    252         else
    253             offset.expand(valueForLength(style()->left(), containingBlock->availableWidth(), view()), 0);
    254     } else if (!style()->right().isAuto()) {
    255         offset.expand(-valueForLength(style()->right(), containingBlock->availableWidth(), view()), 0);
    256     }
    257 
    258     // If the containing block of a relatively positioned element does not
    259     // specify a height, a percentage top or bottom offset should be resolved as
    260     // auto. An exception to this is if the containing block has the WinIE quirk
    261     // where <html> and <body> assume the size of the viewport. In this case,
    262     // calculate the percent offset based on this height.
    263     // See <https://bugs.webkit.org/show_bug.cgi?id=26396>.
    264     if (!style()->top().isAuto()
    265         && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight()
    266             || !style()->top().isPercent()
    267             || containingBlock->stretchesToViewport()))
    268         offset.expand(0, valueForLength(style()->top(), containingBlock->availableHeight(), view()));
    269 
    270     else if (!style()->bottom().isAuto()
    271         && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight()
    272             || !style()->bottom().isPercent()
    273             || containingBlock->stretchesToViewport()))
    274         offset.expand(0, -valueForLength(style()->bottom(), containingBlock->availableHeight(), view()));
    275 
    276     return offset;
    277 }
    278 
    279 LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const LayoutPoint& startPoint) const
    280 {
    281     // If the element is the HTML body element or doesn't have a parent
    282     // return 0 and stop this algorithm.
    283     if (isBody() || !parent())
    284         return LayoutPoint();
    285 
    286     LayoutPoint referencePoint = startPoint;
    287     referencePoint.move(parent()->offsetForColumns(referencePoint));
    288 
    289     // If the offsetParent of the element is null, or is the HTML body element,
    290     // return the distance between the canvas origin and the left border edge
    291     // of the element and stop this algorithm.
    292     Element* element = offsetParent();
    293     if (!element)
    294         return referencePoint;
    295 
    296     if (const RenderBoxModelObject* offsetParent = element->renderBoxModelObject()) {
    297         if (offsetParent->isBox() && !offsetParent->isBody())
    298             referencePoint.move(-toRenderBox(offsetParent)->borderLeft(), -toRenderBox(offsetParent)->borderTop());
    299         if (!isOutOfFlowPositioned() || flowThreadContainingBlock()) {
    300             if (isRelPositioned())
    301                 referencePoint.move(relativePositionOffset());
    302             else if (isStickyPositioned())
    303                 referencePoint.move(stickyPositionOffset());
    304 
    305             // CSS regions specification says that region flows should return the body element as their offsetParent.
    306             // Since we will bypass the bodys renderer anyway, just end the loop if we encounter a region flow (named flow thread).
    307             // See http://dev.w3.org/csswg/css-regions/#cssomview-offset-attributes
    308             RenderObject* current;
    309             for (current = parent(); current != offsetParent && !current->isRenderNamedFlowThread() && current->parent(); current = current->parent()) {
    310                 // FIXME: What are we supposed to do inside SVG content?
    311                 if (!isOutOfFlowPositioned()) {
    312                     if (current->isBox() && !current->isTableRow())
    313                         referencePoint.moveBy(toRenderBox(current)->topLeftLocation());
    314                     referencePoint.move(current->parent()->offsetForColumns(referencePoint));
    315                 }
    316             }
    317 
    318             // Compute the offset position for elements inside named flow threads for which the offsetParent was the body.
    319             // See https://code.google.com/p/chromium/issues/detail?id=242168
    320             if (current->isRenderNamedFlowThread())
    321                 referencePoint = toRenderNamedFlowThread(current)->adjustedPositionRelativeToOffsetParent(*this, referencePoint);
    322             else if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent->isPositioned())
    323                 referencePoint.moveBy(toRenderBox(offsetParent)->topLeftLocation());
    324         }
    325     }
    326 
    327     return referencePoint;
    328 }
    329 
    330 void RenderBoxModelObject::computeStickyPositionConstraints(StickyPositionViewportConstraints& constraints, const FloatRect& viewportRect) const
    331 {
    332     RenderBlock* containingBlock = this->containingBlock();
    333 
    334     LayoutRect containerContentRect = containingBlock->contentBoxRect();
    335     LayoutUnit maxWidth = containingBlock->availableLogicalWidth();
    336 
    337     // Sticky positioned element ignore any override logical width on the containing block (as they don't call
    338     // containingBlockLogicalWidthForContent). It's unclear whether this is totally fine.
    339     LayoutBoxExtent minMargin(minimumValueForLength(style()->marginTop(), maxWidth, view()),
    340         minimumValueForLength(style()->marginRight(), maxWidth, view()),
    341         minimumValueForLength(style()->marginBottom(), maxWidth, view()),
    342         minimumValueForLength(style()->marginLeft(), maxWidth, view()));
    343 
    344     // Compute the container-relative area within which the sticky element is allowed to move.
    345     containerContentRect.contract(minMargin);
    346     // Map to the view to avoid including page scale factor.
    347     constraints.setAbsoluteContainingBlockRect(containingBlock->localToContainerQuad(FloatRect(containerContentRect), view()).boundingBox());
    348 
    349     LayoutRect stickyBoxRect = frameRectForStickyPositioning();
    350     LayoutRect flippedStickyBoxRect = stickyBoxRect;
    351     containingBlock->flipForWritingMode(flippedStickyBoxRect);
    352     LayoutPoint stickyLocation = flippedStickyBoxRect.location();
    353 
    354     // FIXME: sucks to call localToAbsolute again, but we can't just offset from the previously computed rect if there are transforms.
    355     // Map to the view to avoid including page scale factor.
    356     FloatRect absContainerFrame = containingBlock->localToContainerQuad(FloatRect(FloatPoint(), containingBlock->size()), view()).boundingBox();
    357 
    358     // We can't call localToAbsolute on |this| because that will recur. FIXME: For now, assume that |this| is not transformed.
    359     FloatRect absoluteStickyBoxRect(absContainerFrame.location() + stickyLocation, flippedStickyBoxRect.size());
    360     constraints.setAbsoluteStickyBoxRect(absoluteStickyBoxRect);
    361 
    362     if (!style()->left().isAuto()) {
    363         constraints.setLeftOffset(valueForLength(style()->left(), viewportRect.width(), view()));
    364         constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeLeft);
    365     }
    366 
    367     if (!style()->right().isAuto()) {
    368         constraints.setRightOffset(valueForLength(style()->right(), viewportRect.width(), view()));
    369         constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeRight);
    370     }
    371 
    372     if (!style()->top().isAuto()) {
    373         constraints.setTopOffset(valueForLength(style()->top(), viewportRect.height(), view()));
    374         constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeTop);
    375     }
    376 
    377     if (!style()->bottom().isAuto()) {
    378         constraints.setBottomOffset(valueForLength(style()->bottom(), viewportRect.height(), view()));
    379         constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeBottom);
    380     }
    381 }
    382 
    383 LayoutSize RenderBoxModelObject::stickyPositionOffset() const
    384 {
    385     LayoutRect viewportRect = view()->frameView()->viewportConstrainedVisibleContentRect();
    386 
    387     StickyPositionViewportConstraints constraints;
    388     computeStickyPositionConstraints(constraints, viewportRect);
    389 
    390     // The sticky offset is physical, so we can just return the delta computed in absolute coords (though it may be wrong with transforms).
    391     return LayoutSize(constraints.computeStickyOffset(viewportRect));
    392 }
    393 
    394 LayoutSize RenderBoxModelObject::offsetForInFlowPosition() const
    395 {
    396     if (isRelPositioned())
    397         return relativePositionOffset();
    398 
    399     if (isStickyPositioned())
    400         return stickyPositionOffset();
    401 
    402     return LayoutSize();
    403 }
    404 
    405 LayoutUnit RenderBoxModelObject::offsetLeft() const
    406 {
    407     // Note that RenderInline and RenderBox override this to pass a different
    408     // startPoint to adjustedPositionRelativeToOffsetParent.
    409     return adjustedPositionRelativeToOffsetParent(LayoutPoint()).x();
    410 }
    411 
    412 LayoutUnit RenderBoxModelObject::offsetTop() const
    413 {
    414     // Note that RenderInline and RenderBox override this to pass a different
    415     // startPoint to adjustedPositionRelativeToOffsetParent.
    416     return adjustedPositionRelativeToOffsetParent(LayoutPoint()).y();
    417 }
    418 
    419 int RenderBoxModelObject::pixelSnappedOffsetWidth() const
    420 {
    421     return snapSizeToPixel(offsetWidth(), offsetLeft());
    422 }
    423 
    424 int RenderBoxModelObject::pixelSnappedOffsetHeight() const
    425 {
    426     return snapSizeToPixel(offsetHeight(), offsetTop());
    427 }
    428 
    429 LayoutUnit RenderBoxModelObject::computedCSSPadding(Length padding) const
    430 {
    431     LayoutUnit w = 0;
    432     RenderView* renderView = 0;
    433     if (padding.isPercent())
    434         w = containingBlockLogicalWidthForContent();
    435     else if (padding.isViewportPercentage())
    436         renderView = view();
    437     return minimumValueForLength(padding, w, renderView);
    438 }
    439 
    440 RoundedRect RenderBoxModelObject::getBackgroundRoundedRect(const LayoutRect& borderRect, InlineFlowBox* box, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHeight,
    441     bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const
    442 {
    443     RenderView* renderView = view();
    444     RoundedRect border = style()->getRoundedBorderFor(borderRect, renderView, includeLogicalLeftEdge, includeLogicalRightEdge);
    445     if (box && (box->nextLineBox() || box->prevLineBox())) {
    446         RoundedRect segmentBorder = style()->getRoundedBorderFor(LayoutRect(0, 0, inlineBoxWidth, inlineBoxHeight), renderView, includeLogicalLeftEdge, includeLogicalRightEdge);
    447         border.setRadii(segmentBorder.radii());
    448     }
    449 
    450     return border;
    451 }
    452 
    453 void RenderBoxModelObject::clipRoundedInnerRect(GraphicsContext * context, const LayoutRect& rect, const RoundedRect& clipRect)
    454 {
    455     if (clipRect.isRenderable())
    456         context->clipRoundedRect(clipRect);
    457     else {
    458         // We create a rounded rect for each of the corners and clip it, while making sure we clip opposing corners together.
    459         if (!clipRect.radii().topLeft().isEmpty() || !clipRect.radii().bottomRight().isEmpty()) {
    460             IntRect topCorner(clipRect.rect().x(), clipRect.rect().y(), rect.maxX() - clipRect.rect().x(), rect.maxY() - clipRect.rect().y());
    461             RoundedRect::Radii topCornerRadii;
    462             topCornerRadii.setTopLeft(clipRect.radii().topLeft());
    463             context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii));
    464 
    465             IntRect bottomCorner(rect.x(), rect.y(), clipRect.rect().maxX() - rect.x(), clipRect.rect().maxY() - rect.y());
    466             RoundedRect::Radii bottomCornerRadii;
    467             bottomCornerRadii.setBottomRight(clipRect.radii().bottomRight());
    468             context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii));
    469         }
    470 
    471         if (!clipRect.radii().topRight().isEmpty() || !clipRect.radii().bottomLeft().isEmpty()) {
    472             IntRect topCorner(rect.x(), clipRect.rect().y(), clipRect.rect().maxX() - rect.x(), rect.maxY() - clipRect.rect().y());
    473             RoundedRect::Radii topCornerRadii;
    474             topCornerRadii.setTopRight(clipRect.radii().topRight());
    475             context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii));
    476 
    477             IntRect bottomCorner(clipRect.rect().x(), rect.y(), rect.maxX() - clipRect.rect().x(), clipRect.rect().maxY() - rect.y());
    478             RoundedRect::Radii bottomCornerRadii;
    479             bottomCornerRadii.setBottomLeft(clipRect.radii().bottomLeft());
    480             context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii));
    481         }
    482     }
    483 }
    484 
    485 static LayoutRect shrinkRectByOnePixel(GraphicsContext* context, const LayoutRect& rect)
    486 {
    487     LayoutRect shrunkRect = rect;
    488     AffineTransform transform = context->getCTM();
    489     shrunkRect.inflateX(-static_cast<LayoutUnit>(ceil(1 / transform.xScale())));
    490     shrunkRect.inflateY(-static_cast<LayoutUnit>(ceil(1 / transform.yScale())));
    491     return shrunkRect;
    492 }
    493 
    494 LayoutRect RenderBoxModelObject::borderInnerRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& rect, BackgroundBleedAvoidance bleedAvoidance) const
    495 {
    496     // We shrink the rectangle by one pixel on each side to make it fully overlap the anti-aliased background border
    497     return (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? shrinkRectByOnePixel(context, rect) : rect;
    498 }
    499 
    500 RoundedRect RenderBoxModelObject::backgroundRoundedRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& borderRect, BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const
    501 {
    502     if (bleedAvoidance == BackgroundBleedShrinkBackground) {
    503         // We shrink the rectangle by one pixel on each side because the bleed is one pixel maximum.
    504         return getBackgroundRoundedRect(shrinkRectByOnePixel(context, borderRect), box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogicalRightEdge);
    505     }
    506     if (bleedAvoidance == BackgroundBleedBackgroundOverBorder)
    507         return style()->getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge);
    508 
    509     return getBackgroundRoundedRect(borderRect, box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogicalRightEdge);
    510 }
    511 
    512 static void applyBoxShadowForBackground(GraphicsContext* context, const RenderObject* renderer)
    513 {
    514     const ShadowData* boxShadow = renderer->style()->boxShadow();
    515     while (boxShadow->style() != Normal)
    516         boxShadow = boxShadow->next();
    517 
    518     FloatSize shadowOffset(boxShadow->x(), boxShadow->y());
    519     context->setShadow(shadowOffset, boxShadow->blur(), renderer->resolveColor(boxShadow->color()),
    520         DrawLooper::ShadowRespectsTransforms, DrawLooper::ShadowIgnoresAlpha);
    521 }
    522 
    523 void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& color, const FillLayer* bgLayer, const LayoutRect& rect,
    524     BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, CompositeOperator op, RenderObject* backgroundObject)
    525 {
    526     GraphicsContext* context = paintInfo.context;
    527     if (context->paintingDisabled() || rect.isEmpty())
    528         return;
    529 
    530     bool includeLeftEdge = box ? box->includeLogicalLeftEdge() : true;
    531     bool includeRightEdge = box ? box->includeLogicalRightEdge() : true;
    532 
    533     bool hasRoundedBorder = style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge);
    534     bool clippedWithLocalScrolling = hasOverflowClip() && bgLayer->attachment() == LocalBackgroundAttachment;
    535     bool isBorderFill = bgLayer->clip() == BorderFillBox;
    536     bool isRoot = this->isRoot();
    537 
    538     Color bgColor = color;
    539     StyleImage* bgImage = bgLayer->image();
    540     bool shouldPaintBackgroundImage = bgImage && bgImage->canRender(this, style()->effectiveZoom());
    541 
    542     bool forceBackgroundToWhite = false;
    543     if (document()->printing()) {
    544         if (style()->printColorAdjust() == PrintColorAdjustEconomy)
    545             forceBackgroundToWhite = true;
    546         if (document()->settings() && document()->settings()->shouldPrintBackgrounds())
    547             forceBackgroundToWhite = false;
    548     }
    549 
    550     // When printing backgrounds is disabled or using economy mode,
    551     // change existing background colors and images to a solid white background.
    552     // If there's no bg color or image, leave it untouched to avoid affecting transparency.
    553     // We don't try to avoid loading the background images, because this style flag is only set
    554     // when printing, and at that point we've already loaded the background images anyway. (To avoid
    555     // loading the background images we'd have to do this check when applying styles rather than
    556     // while rendering.)
    557     if (forceBackgroundToWhite) {
    558         // Note that we can't reuse this variable below because the bgColor might be changed
    559         bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.alpha();
    560         if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) {
    561             bgColor = Color::white;
    562             shouldPaintBackgroundImage = false;
    563         }
    564     }
    565 
    566     bool colorVisible = bgColor.alpha();
    567 
    568     // Fast path for drawing simple color backgrounds.
    569     if (!isRoot && !clippedWithLocalScrolling && !shouldPaintBackgroundImage && isBorderFill && !bgLayer->next()) {
    570         if (!colorVisible)
    571             return;
    572 
    573         bool boxShadowShouldBeAppliedToBackground = this->boxShadowShouldBeAppliedToBackground(bleedAvoidance, box);
    574         GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAppliedToBackground);
    575         if (boxShadowShouldBeAppliedToBackground)
    576             applyBoxShadowForBackground(context, this);
    577 
    578         if (hasRoundedBorder && bleedAvoidance != BackgroundBleedUseTransparencyLayer) {
    579             RoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge);
    580             if (border.isRenderable())
    581                 context->fillRoundedRect(border, bgColor);
    582             else {
    583                 context->save();
    584                 clipRoundedInnerRect(context, rect, border);
    585                 context->fillRect(border.rect(), bgColor);
    586                 context->restore();
    587             }
    588         } else {
    589             context->fillRect(pixelSnappedIntRect(rect), bgColor);
    590         }
    591 
    592         return;
    593     }
    594 
    595     // BorderFillBox radius clipping is taken care of by BackgroundBleedUseTransparencyLayer
    596     bool clipToBorderRadius = hasRoundedBorder && !(isBorderFill && bleedAvoidance == BackgroundBleedUseTransparencyLayer);
    597     GraphicsContextStateSaver clipToBorderStateSaver(*context, clipToBorderRadius);
    598     if (clipToBorderRadius) {
    599         RoundedRect border = isBorderFill ? backgroundRoundedRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge) : getBackgroundRoundedRect(rect, box, boxSize.width(), boxSize.height(), includeLeftEdge, includeRightEdge);
    600 
    601         // Clip to the padding or content boxes as necessary.
    602         if (bgLayer->clip() == ContentFillBox) {
    603             border = style()->getRoundedInnerBorderFor(border.rect(),
    604                 paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), includeLeftEdge, includeRightEdge);
    605         } else if (bgLayer->clip() == PaddingFillBox)
    606             border = style()->getRoundedInnerBorderFor(border.rect(), includeLeftEdge, includeRightEdge);
    607 
    608         clipRoundedInnerRect(context, rect, border);
    609     }
    610 
    611     int bLeft = includeLeftEdge ? borderLeft() : 0;
    612     int bRight = includeRightEdge ? borderRight() : 0;
    613     LayoutUnit pLeft = includeLeftEdge ? paddingLeft() : LayoutUnit();
    614     LayoutUnit pRight = includeRightEdge ? paddingRight() : LayoutUnit();
    615 
    616     GraphicsContextStateSaver clipWithScrollingStateSaver(*context, clippedWithLocalScrolling);
    617     LayoutRect scrolledPaintRect = rect;
    618     if (clippedWithLocalScrolling) {
    619         // Clip to the overflow area.
    620         RenderBox* thisBox = toRenderBox(this);
    621         context->clip(thisBox->overflowClipRect(rect.location(), paintInfo.renderRegion));
    622 
    623         // Adjust the paint rect to reflect a scrolled content box with borders at the ends.
    624         IntSize offset = thisBox->scrolledContentOffset();
    625         scrolledPaintRect.move(-offset);
    626         scrolledPaintRect.setWidth(bLeft + layer()->scrollWidth() + bRight);
    627         scrolledPaintRect.setHeight(borderTop() + layer()->scrollHeight() + borderBottom());
    628     }
    629 
    630     GraphicsContextStateSaver backgroundClipStateSaver(*context, false);
    631     IntRect maskRect;
    632 
    633     switch (bgLayer->clip()) {
    634     case PaddingFillBox:
    635     case ContentFillBox: {
    636         if (clipToBorderRadius)
    637             break;
    638 
    639         // Clip to the padding or content boxes as necessary.
    640         bool includePadding = bgLayer->clip() == ContentFillBox;
    641         LayoutRect clipRect = LayoutRect(scrolledPaintRect.x() + bLeft + (includePadding ? pLeft : LayoutUnit()),
    642             scrolledPaintRect.y() + borderTop() + (includePadding ? paddingTop() : LayoutUnit()),
    643             scrolledPaintRect.width() - bLeft - bRight - (includePadding ? pLeft + pRight : LayoutUnit()),
    644             scrolledPaintRect.height() - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : LayoutUnit()));
    645         backgroundClipStateSaver.save();
    646         context->clip(clipRect);
    647 
    648         break;
    649     }
    650     case TextFillBox: {
    651         // First figure out how big the mask has to be.  It should be no bigger than what we need
    652         // to actually render, so we should intersect the dirty rect with the border box of the background.
    653         maskRect = pixelSnappedIntRect(rect);
    654         maskRect.intersect(paintInfo.rect);
    655 
    656         // We draw the background into a separate layer, to be later masked with yet another layer
    657         // holding the text content.
    658         backgroundClipStateSaver.save();
    659         context->clip(maskRect);
    660         context->beginTransparencyLayer(1);
    661 
    662         break;
    663     }
    664     case BorderFillBox:
    665         break;
    666     default:
    667         ASSERT_NOT_REACHED();
    668         break;
    669     }
    670 
    671     // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with
    672     // no background in the child document should show the parent's background.
    673     bool isOpaqueRoot = false;
    674     if (isRoot) {
    675         isOpaqueRoot = true;
    676         if (!bgLayer->next() && !(bgColor.alpha() == 255) && view()->frameView()) {
    677             Element* ownerElement = document()->ownerElement();
    678             if (ownerElement) {
    679                 if (!ownerElement->hasTagName(frameTag)) {
    680                     // Locate the <body> element using the DOM.  This is easier than trying
    681                     // to crawl around a render tree with potential :before/:after content and
    682                     // anonymous blocks created by inline <body> tags etc.  We can locate the <body>
    683                     // render object very easily via the DOM.
    684                     HTMLElement* body = document()->body();
    685                     if (body) {
    686                         // Can't scroll a frameset document anyway.
    687                         isOpaqueRoot = body->hasLocalName(framesetTag);
    688                     } else {
    689                         // SVG documents and XML documents with SVG root nodes are transparent.
    690                         isOpaqueRoot = !document()->hasSVGRootNode();
    691                     }
    692                 }
    693             } else
    694                 isOpaqueRoot = !view()->frameView()->isTransparent();
    695         }
    696         view()->frameView()->setContentIsOpaque(isOpaqueRoot);
    697     }
    698 
    699     // Paint the color first underneath all images, culled if background image occludes it.
    700     // FIXME: In the bgLayer->hasFiniteBounds() case, we could improve the culling test
    701     // by verifying whether the background image covers the entire layout rect.
    702     if (!bgLayer->next()) {
    703         IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect));
    704         bool boxShadowShouldBeAppliedToBackground = this->boxShadowShouldBeAppliedToBackground(bleedAvoidance, box);
    705         if (boxShadowShouldBeAppliedToBackground || !shouldPaintBackgroundImage || !bgLayer->hasOpaqueImage(this) || !bgLayer->hasRepeatXY()) {
    706             if (!boxShadowShouldBeAppliedToBackground)
    707                 backgroundRect.intersect(paintInfo.rect);
    708 
    709             GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAppliedToBackground);
    710             if (boxShadowShouldBeAppliedToBackground)
    711                 applyBoxShadowForBackground(context, this);
    712 
    713             if (isOpaqueRoot) {
    714                 // If we have an alpha and we are painting the root element, go ahead and blend with the base background color.
    715                 Color baseColor = view()->frameView()->baseBackgroundColor();
    716                 bool shouldClearDocumentBackground = document()->settings() && document()->settings()->shouldClearDocumentBackground();
    717                 CompositeOperator operation = shouldClearDocumentBackground ? CompositeCopy : context->compositeOperation();
    718 
    719                 if (baseColor.alpha()) {
    720                     if (bgColor.alpha())
    721                         baseColor = baseColor.blend(bgColor);
    722                     context->fillRect(backgroundRect, baseColor, operation);
    723                 } else if (bgColor.alpha()) {
    724                     context->fillRect(backgroundRect, bgColor, operation);
    725                 } else if (shouldClearDocumentBackground) {
    726                     context->clearRect(backgroundRect);
    727                 }
    728             } else if (bgColor.alpha()) {
    729                 context->fillRect(backgroundRect, bgColor, context->compositeOperation());
    730             }
    731         }
    732     }
    733 
    734     // no progressive loading of the background image
    735     if (shouldPaintBackgroundImage) {
    736         BackgroundImageGeometry geometry;
    737         calculateBackgroundImageGeometry(bgLayer, scrolledPaintRect, geometry, backgroundObject);
    738         geometry.clip(paintInfo.rect);
    739         if (!geometry.destRect().isEmpty()) {
    740             CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op;
    741             RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this;
    742             RefPtr<Image> image = bgImage->image(clientForBackgroundImage, geometry.tileSize());
    743             bool useLowQualityScaling = shouldPaintAtLowQuality(context, image.get(), bgLayer, geometry.tileSize());
    744             context->drawTiledImage(image.get(), geometry.destRect(), geometry.relativePhase(), geometry.tileSize(),
    745                 compositeOp, useLowQualityScaling, bgLayer->blendMode());
    746         }
    747     }
    748 
    749     if (bgLayer->clip() == TextFillBox) {
    750         // Create the text mask layer.
    751         context->setCompositeOperation(CompositeDestinationIn);
    752         context->beginTransparencyLayer(1);
    753 
    754         // FIXME: Workaround for https://code.google.com/p/skia/issues/detail?id=1291.
    755         context->clearRect(maskRect);
    756 
    757         // Now draw the text into the mask. We do this by painting using a special paint phase that signals to
    758         // InlineTextBoxes that they should just add their contents to the clip.
    759         PaintInfo info(context, maskRect, PaintPhaseTextClip, PaintBehaviorForceBlackText, 0, paintInfo.renderRegion);
    760         context->setCompositeOperation(CompositeSourceOver);
    761         if (box) {
    762             RootInlineBox* root = box->root();
    763             box->paint(info, LayoutPoint(scrolledPaintRect.x() - box->x(), scrolledPaintRect.y() - box->y()), root->lineTop(), root->lineBottom());
    764         } else {
    765             LayoutSize localOffset = isBox() ? toRenderBox(this)->locationOffset() : LayoutSize();
    766             paint(info, scrolledPaintRect.location() - localOffset);
    767         }
    768 
    769         context->endTransparencyLayer();
    770         context->endTransparencyLayer();
    771     }
    772 }
    773 
    774 static inline int resolveWidthForRatio(int height, const FloatSize& intrinsicRatio)
    775 {
    776     return ceilf(height * intrinsicRatio.width() / intrinsicRatio.height());
    777 }
    778 
    779 static inline int resolveHeightForRatio(int width, const FloatSize& intrinsicRatio)
    780 {
    781     return ceilf(width * intrinsicRatio.height() / intrinsicRatio.width());
    782 }
    783 
    784 static inline IntSize resolveAgainstIntrinsicWidthOrHeightAndRatio(const IntSize& size, const FloatSize& intrinsicRatio, int useWidth, int useHeight)
    785 {
    786     if (intrinsicRatio.isEmpty()) {
    787         if (useWidth)
    788             return IntSize(useWidth, size.height());
    789         return IntSize(size.width(), useHeight);
    790     }
    791 
    792     if (useWidth)
    793         return IntSize(useWidth, resolveHeightForRatio(useWidth, intrinsicRatio));
    794     return IntSize(resolveWidthForRatio(useHeight, intrinsicRatio), useHeight);
    795 }
    796 
    797 static inline IntSize resolveAgainstIntrinsicRatio(const IntSize& size, const FloatSize& intrinsicRatio)
    798 {
    799     // Two possible solutions: (size.width(), solutionHeight) or (solutionWidth, size.height())
    800     // "... must be assumed to be the largest dimensions..." = easiest answer: the rect with the largest surface area.
    801 
    802     int solutionWidth = resolveWidthForRatio(size.height(), intrinsicRatio);
    803     int solutionHeight = resolveHeightForRatio(size.width(), intrinsicRatio);
    804     if (solutionWidth <= size.width()) {
    805         if (solutionHeight <= size.height()) {
    806             // If both solutions fit, choose the one covering the larger area.
    807             int areaOne = solutionWidth * size.height();
    808             int areaTwo = size.width() * solutionHeight;
    809             if (areaOne < areaTwo)
    810                 return IntSize(size.width(), solutionHeight);
    811             return IntSize(solutionWidth, size.height());
    812         }
    813 
    814         // Only the first solution fits.
    815         return IntSize(solutionWidth, size.height());
    816     }
    817 
    818     // Only the second solution fits, assert that.
    819     ASSERT(solutionHeight <= size.height());
    820     return IntSize(size.width(), solutionHeight);
    821 }
    822 
    823 IntSize RenderBoxModelObject::calculateImageIntrinsicDimensions(StyleImage* image, const IntSize& positioningAreaSize, ScaleByEffectiveZoomOrNot shouldScaleOrNot) const
    824 {
    825     // A generated image without a fixed size, will always return the container size as intrinsic size.
    826     if (image->isGeneratedImage() && image->usesImageContainerSize())
    827         return IntSize(positioningAreaSize.width(), positioningAreaSize.height());
    828 
    829     Length intrinsicWidth;
    830     Length intrinsicHeight;
    831     FloatSize intrinsicRatio;
    832     image->computeIntrinsicDimensions(this, intrinsicWidth, intrinsicHeight, intrinsicRatio);
    833 
    834     // Intrinsic dimensions expressed as percentages must be resolved relative to the dimensions of the rectangle
    835     // that establishes the coordinate system for the 'background-position' property.
    836 
    837     // FIXME: Remove unnecessary rounding when layout is off ints: webkit.org/b/63656
    838     if (intrinsicWidth.isPercent() && intrinsicHeight.isPercent() && intrinsicRatio.isEmpty()) {
    839         // Resolve width/height percentages against positioningAreaSize, only if no intrinsic ratio is provided.
    840         int resolvedWidth = static_cast<int>(round(positioningAreaSize.width() * intrinsicWidth.percent() / 100));
    841         int resolvedHeight = static_cast<int>(round(positioningAreaSize.height() * intrinsicHeight.percent() / 100));
    842         return IntSize(resolvedWidth, resolvedHeight);
    843     }
    844 
    845     IntSize resolvedSize(intrinsicWidth.isFixed() ? intrinsicWidth.value() : 0, intrinsicHeight.isFixed() ? intrinsicHeight.value() : 0);
    846     IntSize minimumSize(resolvedSize.width() > 0 ? 1 : 0, resolvedSize.height() > 0 ? 1 : 0);
    847     if (shouldScaleOrNot == ScaleByEffectiveZoom)
    848         resolvedSize.scale(style()->effectiveZoom());
    849     resolvedSize.clampToMinimumSize(minimumSize);
    850 
    851     if (!resolvedSize.isEmpty())
    852         return resolvedSize;
    853 
    854     // If the image has one of either an intrinsic width or an intrinsic height:
    855     // * and an intrinsic aspect ratio, then the missing dimension is calculated from the given dimension and the ratio.
    856     // * and no intrinsic aspect ratio, then the missing dimension is assumed to be the size of the rectangle that
    857     //   establishes the coordinate system for the 'background-position' property.
    858     if (resolvedSize.width() > 0 || resolvedSize.height() > 0)
    859         return resolveAgainstIntrinsicWidthOrHeightAndRatio(positioningAreaSize, intrinsicRatio, resolvedSize.width(), resolvedSize.height());
    860 
    861     // If the image has no intrinsic dimensions and has an intrinsic ratio the dimensions must be assumed to be the
    862     // largest dimensions at that ratio such that neither dimension exceeds the dimensions of the rectangle that
    863     // establishes the coordinate system for the 'background-position' property.
    864     if (!intrinsicRatio.isEmpty())
    865         return resolveAgainstIntrinsicRatio(positioningAreaSize, intrinsicRatio);
    866 
    867     // If the image has no intrinsic ratio either, then the dimensions must be assumed to be the rectangle that
    868     // establishes the coordinate system for the 'background-position' property.
    869     return positioningAreaSize;
    870 }
    871 
    872 static inline void applySubPixelHeuristicForTileSize(LayoutSize& tileSize, const IntSize& positioningAreaSize)
    873 {
    874     tileSize.setWidth(positioningAreaSize.width() - tileSize.width() <= 1 ? tileSize.width().ceil() : tileSize.width().floor());
    875     tileSize.setHeight(positioningAreaSize.height() - tileSize.height() <= 1 ? tileSize.height().ceil() : tileSize.height().floor());
    876 }
    877 
    878 IntSize RenderBoxModelObject::calculateFillTileSize(const FillLayer* fillLayer, const IntSize& positioningAreaSize) const
    879 {
    880     StyleImage* image = fillLayer->image();
    881     EFillSizeType type = fillLayer->size().type;
    882 
    883     IntSize imageIntrinsicSize = calculateImageIntrinsicDimensions(image, positioningAreaSize, ScaleByEffectiveZoom);
    884     imageIntrinsicSize.scale(1 / image->imageScaleFactor(), 1 / image->imageScaleFactor());
    885     RenderView* renderView = view();
    886     switch (type) {
    887         case SizeLength: {
    888             LayoutSize tileSize = positioningAreaSize;
    889 
    890             Length layerWidth = fillLayer->size().size.width();
    891             Length layerHeight = fillLayer->size().size.height();
    892 
    893             if (layerWidth.isFixed())
    894                 tileSize.setWidth(layerWidth.value());
    895             else if (layerWidth.isPercent() || layerWidth.isViewportPercentage())
    896                 tileSize.setWidth(valueForLength(layerWidth, positioningAreaSize.width(), renderView));
    897 
    898             if (layerHeight.isFixed())
    899                 tileSize.setHeight(layerHeight.value());
    900             else if (layerHeight.isPercent() || layerHeight.isViewportPercentage())
    901                 tileSize.setHeight(valueForLength(layerHeight, positioningAreaSize.height(), renderView));
    902 
    903             applySubPixelHeuristicForTileSize(tileSize, positioningAreaSize);
    904 
    905             // If one of the values is auto we have to use the appropriate
    906             // scale to maintain our aspect ratio.
    907             if (layerWidth.isAuto() && !layerHeight.isAuto()) {
    908                 if (imageIntrinsicSize.height())
    909                     tileSize.setWidth(imageIntrinsicSize.width() * tileSize.height() / imageIntrinsicSize.height());
    910             } else if (!layerWidth.isAuto() && layerHeight.isAuto()) {
    911                 if (imageIntrinsicSize.width())
    912                     tileSize.setHeight(imageIntrinsicSize.height() * tileSize.width() / imageIntrinsicSize.width());
    913             } else if (layerWidth.isAuto() && layerHeight.isAuto()) {
    914                 // If both width and height are auto, use the image's intrinsic size.
    915                 tileSize = imageIntrinsicSize;
    916             }
    917 
    918             tileSize.clampNegativeToZero();
    919             return flooredIntSize(tileSize);
    920         }
    921         case SizeNone: {
    922             // If both values are auto then the intrinsic width and/or height of the image should be used, if any.
    923             if (!imageIntrinsicSize.isEmpty())
    924                 return imageIntrinsicSize;
    925 
    926             // If the image has neither an intrinsic width nor an intrinsic height, its size is determined as for contain.
    927             type = Contain;
    928         }
    929         case Contain:
    930         case Cover: {
    931             float horizontalScaleFactor = imageIntrinsicSize.width()
    932                 ? static_cast<float>(positioningAreaSize.width()) / imageIntrinsicSize.width() : 1;
    933             float verticalScaleFactor = imageIntrinsicSize.height()
    934                 ? static_cast<float>(positioningAreaSize.height()) / imageIntrinsicSize.height() : 1;
    935             float scaleFactor = type == Contain ? min(horizontalScaleFactor, verticalScaleFactor) : max(horizontalScaleFactor, verticalScaleFactor);
    936             return IntSize(max(1, static_cast<int>(imageIntrinsicSize.width() * scaleFactor)), max(1, static_cast<int>(imageIntrinsicSize.height() * scaleFactor)));
    937        }
    938     }
    939 
    940     ASSERT_NOT_REACHED();
    941     return IntSize();
    942 }
    943 
    944 void RenderBoxModelObject::BackgroundImageGeometry::setNoRepeatX(int xOffset)
    945 {
    946     m_destRect.move(max(xOffset, 0), 0);
    947     m_phase.setX(-min(xOffset, 0));
    948     m_destRect.setWidth(m_tileSize.width() + min(xOffset, 0));
    949 }
    950 void RenderBoxModelObject::BackgroundImageGeometry::setNoRepeatY(int yOffset)
    951 {
    952     m_destRect.move(0, max(yOffset, 0));
    953     m_phase.setY(-min(yOffset, 0));
    954     m_destRect.setHeight(m_tileSize.height() + min(yOffset, 0));
    955 }
    956 
    957 void RenderBoxModelObject::BackgroundImageGeometry::useFixedAttachment(const IntPoint& attachmentPoint)
    958 {
    959     IntPoint alignedPoint = attachmentPoint;
    960     m_phase.move(max(alignedPoint.x() - m_destRect.x(), 0), max(alignedPoint.y() - m_destRect.y(), 0));
    961 }
    962 
    963 void RenderBoxModelObject::BackgroundImageGeometry::clip(const IntRect& clipRect)
    964 {
    965     m_destRect.intersect(clipRect);
    966 }
    967 
    968 IntPoint RenderBoxModelObject::BackgroundImageGeometry::relativePhase() const
    969 {
    970     IntPoint phase = m_phase;
    971     phase += m_destRect.location() - m_destOrigin;
    972     return phase;
    973 }
    974 
    975 bool RenderBoxModelObject::fixedBackgroundPaintsInLocalCoordinates() const
    976 {
    977     if (!isRoot())
    978         return false;
    979 
    980     if (view()->frameView() && view()->frameView()->paintBehavior() & PaintBehaviorFlattenCompositingLayers)
    981         return false;
    982 
    983     RenderLayer* rootLayer = view()->layer();
    984     if (!rootLayer || !rootLayer->isComposited())
    985         return false;
    986 
    987     return rootLayer->backing()->backgroundLayerPaintsFixedRootBackground();
    988 }
    989 
    990 void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* fillLayer, const LayoutRect& paintRect,
    991     BackgroundImageGeometry& geometry, RenderObject* backgroundObject)
    992 {
    993     LayoutUnit left = 0;
    994     LayoutUnit top = 0;
    995     IntSize positioningAreaSize;
    996     IntRect snappedPaintRect = pixelSnappedIntRect(paintRect);
    997 
    998     // Determine the background positioning area and set destRect to the background painting area.
    999     // destRect will be adjusted later if the background is non-repeating.
   1000     bool fixedAttachment = fillLayer->attachment() == FixedBackgroundAttachment;
   1001 
   1002 #if ENABLE(FAST_MOBILE_SCROLLING)
   1003     if (view()->frameView() && view()->frameView()->canBlitOnScroll()) {
   1004         // As a side effect of an optimization to blit on scroll, we do not honor the CSS
   1005         // property "background-attachment: fixed" because it may result in rendering
   1006         // artifacts. Note, these artifacts only appear if we are blitting on scroll of
   1007         // a page that has fixed background images.
   1008         fixedAttachment = false;
   1009     }
   1010 #endif
   1011 
   1012     if (!fixedAttachment) {
   1013         geometry.setDestRect(snappedPaintRect);
   1014 
   1015         LayoutUnit right = 0;
   1016         LayoutUnit bottom = 0;
   1017         // Scroll and Local.
   1018         if (fillLayer->origin() != BorderFillBox) {
   1019             left = borderLeft();
   1020             right = borderRight();
   1021             top = borderTop();
   1022             bottom = borderBottom();
   1023             if (fillLayer->origin() == ContentFillBox) {
   1024                 left += paddingLeft();
   1025                 right += paddingRight();
   1026                 top += paddingTop();
   1027                 bottom += paddingBottom();
   1028             }
   1029         }
   1030 
   1031         // The background of the box generated by the root element covers the entire canvas including
   1032         // its margins. Since those were added in already, we have to factor them out when computing
   1033         // the background positioning area.
   1034         if (isRoot()) {
   1035             positioningAreaSize = pixelSnappedIntSize(toRenderBox(this)->size() - LayoutSize(left + right, top + bottom), toRenderBox(this)->location());
   1036             left += marginLeft();
   1037             top += marginTop();
   1038         } else
   1039             positioningAreaSize = pixelSnappedIntSize(paintRect.size() - LayoutSize(left + right, top + bottom), paintRect.location());
   1040     } else {
   1041         IntRect viewportRect = pixelSnappedIntRect(viewRect());
   1042         if (fixedBackgroundPaintsInLocalCoordinates())
   1043             viewportRect.setLocation(IntPoint());
   1044         else if (FrameView* frameView = view()->frameView())
   1045             viewportRect.setLocation(IntPoint(frameView->scrollOffsetForFixedPosition()));
   1046 
   1047         geometry.setDestRect(pixelSnappedIntRect(viewportRect));
   1048         positioningAreaSize = geometry.destRect().size();
   1049     }
   1050 
   1051     RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this;
   1052     IntSize fillTileSize = calculateFillTileSize(fillLayer, positioningAreaSize);
   1053     fillLayer->image()->setContainerSizeForRenderer(clientForBackgroundImage, fillTileSize, style()->effectiveZoom());
   1054     geometry.setTileSize(fillTileSize);
   1055 
   1056     EFillRepeat backgroundRepeatX = fillLayer->repeatX();
   1057     EFillRepeat backgroundRepeatY = fillLayer->repeatY();
   1058     RenderView* renderView = view();
   1059     int availableWidth = positioningAreaSize.width() - geometry.tileSize().width();
   1060     int availableHeight = positioningAreaSize.height() - geometry.tileSize().height();
   1061 
   1062     LayoutUnit computedXPosition = minimumValueForLength(fillLayer->xPosition(), availableWidth, renderView, true);
   1063     if (backgroundRepeatX == RepeatFill)
   1064         geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().width() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0);
   1065     else {
   1066         int xOffset = fillLayer->backgroundXOrigin() == RightEdge ? availableWidth - computedXPosition : computedXPosition;
   1067         geometry.setNoRepeatX(left + xOffset);
   1068     }
   1069     LayoutUnit computedYPosition = minimumValueForLength(fillLayer->yPosition(), availableHeight, renderView, true);
   1070     if (backgroundRepeatY == RepeatFill)
   1071         geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().height() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0);
   1072     else {
   1073         int yOffset = fillLayer->backgroundYOrigin() == BottomEdge ? availableHeight - computedYPosition : computedYPosition;
   1074         geometry.setNoRepeatY(top + yOffset);
   1075     }
   1076 
   1077     if (fixedAttachment)
   1078         geometry.useFixedAttachment(snappedPaintRect.location());
   1079 
   1080     geometry.clip(snappedPaintRect);
   1081     geometry.setDestOrigin(geometry.destRect().location());
   1082 }
   1083 
   1084 static LayoutUnit computeBorderImageSide(Length borderSlice, LayoutUnit borderSide, LayoutUnit imageSide, LayoutUnit boxExtent, RenderView* renderView)
   1085 {
   1086     if (borderSlice.isRelative())
   1087         return borderSlice.value() * borderSide;
   1088     if (borderSlice.isAuto())
   1089         return imageSide;
   1090     return valueForLength(borderSlice, boxExtent, renderView);
   1091 }
   1092 
   1093 bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, const LayoutRect& rect, const RenderStyle* style,
   1094                                                const NinePieceImage& ninePieceImage, CompositeOperator op)
   1095 {
   1096     StyleImage* styleImage = ninePieceImage.image();
   1097     if (!styleImage)
   1098         return false;
   1099 
   1100     if (!styleImage->isLoaded())
   1101         return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either.
   1102 
   1103     if (!styleImage->canRender(this, style->effectiveZoom()))
   1104         return false;
   1105 
   1106     // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function
   1107     // doesn't have any understanding of the zoom that is in effect on the tile.
   1108     LayoutRect rectWithOutsets = rect;
   1109     rectWithOutsets.expand(style->imageOutsets(ninePieceImage));
   1110     IntRect borderImageRect = pixelSnappedIntRect(rectWithOutsets);
   1111 
   1112     IntSize imageSize = calculateImageIntrinsicDimensions(styleImage, borderImageRect.size(), DoNotScaleByEffectiveZoom);
   1113 
   1114     // If both values are auto then the intrinsic width and/or height of the image should be used, if any.
   1115     styleImage->setContainerSizeForRenderer(this, imageSize, style->effectiveZoom());
   1116 
   1117     int imageWidth = imageSize.width();
   1118     int imageHeight = imageSize.height();
   1119     RenderView* renderView = view();
   1120 
   1121     float imageScaleFactor = styleImage->imageScaleFactor();
   1122     int topSlice = min<int>(imageHeight, valueForLength(ninePieceImage.imageSlices().top(), imageHeight, renderView)) * imageScaleFactor;
   1123     int rightSlice = min<int>(imageWidth, valueForLength(ninePieceImage.imageSlices().right(), imageWidth, renderView)) * imageScaleFactor;
   1124     int bottomSlice = min<int>(imageHeight, valueForLength(ninePieceImage.imageSlices().bottom(), imageHeight, renderView)) * imageScaleFactor;
   1125     int leftSlice = min<int>(imageWidth, valueForLength(ninePieceImage.imageSlices().left(), imageWidth, renderView)) * imageScaleFactor;
   1126 
   1127     ENinePieceImageRule hRule = ninePieceImage.horizontalRule();
   1128     ENinePieceImageRule vRule = ninePieceImage.verticalRule();
   1129 
   1130     int topWidth = computeBorderImageSide(ninePieceImage.borderSlices().top(), style->borderTopWidth(), topSlice, borderImageRect.height(), renderView);
   1131     int rightWidth = computeBorderImageSide(ninePieceImage.borderSlices().right(), style->borderRightWidth(), rightSlice, borderImageRect.width(), renderView);
   1132     int bottomWidth = computeBorderImageSide(ninePieceImage.borderSlices().bottom(), style->borderBottomWidth(), bottomSlice, borderImageRect.height(), renderView);
   1133     int leftWidth = computeBorderImageSide(ninePieceImage.borderSlices().left(), style->borderLeftWidth(), leftSlice, borderImageRect.width(), renderView);
   1134 
   1135     // Reduce the widths if they're too large.
   1136     // The spec says: Given Lwidth as the width of the border image area, Lheight as its height, and Wside as the border image width
   1137     // offset for the side, let f = min(Lwidth/(Wleft+Wright), Lheight/(Wtop+Wbottom)). If f < 1, then all W are reduced by
   1138     // multiplying them by f.
   1139     int borderSideWidth = max(1, leftWidth + rightWidth);
   1140     int borderSideHeight = max(1, topWidth + bottomWidth);
   1141     float borderSideScaleFactor = min((float)borderImageRect.width() / borderSideWidth, (float)borderImageRect.height() / borderSideHeight);
   1142     if (borderSideScaleFactor < 1) {
   1143         topWidth *= borderSideScaleFactor;
   1144         rightWidth *= borderSideScaleFactor;
   1145         bottomWidth *= borderSideScaleFactor;
   1146         leftWidth *= borderSideScaleFactor;
   1147     }
   1148 
   1149     bool drawLeft = leftSlice > 0 && leftWidth > 0;
   1150     bool drawTop = topSlice > 0 && topWidth > 0;
   1151     bool drawRight = rightSlice > 0 && rightWidth > 0;
   1152     bool drawBottom = bottomSlice > 0 && bottomWidth > 0;
   1153     bool drawMiddle = ninePieceImage.fill() && (imageWidth - leftSlice - rightSlice) > 0 && (borderImageRect.width() - leftWidth - rightWidth) > 0
   1154                       && (imageHeight - topSlice - bottomSlice) > 0 && (borderImageRect.height() - topWidth - bottomWidth) > 0;
   1155 
   1156     RefPtr<Image> image = styleImage->image(this, imageSize);
   1157 
   1158     float destinationWidth = borderImageRect.width() - leftWidth - rightWidth;
   1159     float destinationHeight = borderImageRect.height() - topWidth - bottomWidth;
   1160 
   1161     float sourceWidth = imageWidth - leftSlice - rightSlice;
   1162     float sourceHeight = imageHeight - topSlice - bottomSlice;
   1163 
   1164     float leftSideScale = drawLeft ? (float)leftWidth / leftSlice : 1;
   1165     float rightSideScale = drawRight ? (float)rightWidth / rightSlice : 1;
   1166     float topSideScale = drawTop ? (float)topWidth / topSlice : 1;
   1167     float bottomSideScale = drawBottom ? (float)bottomWidth / bottomSlice : 1;
   1168 
   1169     if (drawLeft) {
   1170         // Paint the top and bottom left corners.
   1171 
   1172         // The top left corner rect is (tx, ty, leftWidth, topWidth)
   1173         // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice)
   1174         if (drawTop)
   1175             graphicsContext->drawImage(image.get(), IntRect(borderImageRect.location(), IntSize(leftWidth, topWidth)),
   1176                                        LayoutRect(0, 0, leftSlice, topSlice), op);
   1177 
   1178         // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth)
   1179         // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice)
   1180         if (drawBottom)
   1181             graphicsContext->drawImage(image.get(), IntRect(borderImageRect.x(), borderImageRect.maxY() - bottomWidth, leftWidth, bottomWidth),
   1182                                        LayoutRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op);
   1183 
   1184         // Paint the left edge.
   1185         // Have to scale and tile into the border rect.
   1186         if (sourceHeight > 0)
   1187             graphicsContext->drawTiledImage(image.get(), IntRect(borderImageRect.x(), borderImageRect.y() + topWidth, leftWidth, destinationHeight),
   1188                                             IntRect(0, topSlice, leftSlice, sourceHeight),
   1189                                             FloatSize(leftSideScale, leftSideScale), Image::StretchTile, (Image::TileRule)vRule, op);
   1190     }
   1191 
   1192     if (drawRight) {
   1193         // Paint the top and bottom right corners
   1194         // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth)
   1195         // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice)
   1196         if (drawTop)
   1197             graphicsContext->drawImage(image.get(), IntRect(borderImageRect.maxX() - rightWidth, borderImageRect.y(), rightWidth, topWidth),
   1198                                        LayoutRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op);
   1199 
   1200         // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth)
   1201         // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice)
   1202         if (drawBottom)
   1203             graphicsContext->drawImage(image.get(), IntRect(borderImageRect.maxX() - rightWidth, borderImageRect.maxY() - bottomWidth, rightWidth, bottomWidth),
   1204                                        LayoutRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op);
   1205 
   1206         // Paint the right edge.
   1207         if (sourceHeight > 0)
   1208             graphicsContext->drawTiledImage(image.get(), IntRect(borderImageRect.maxX() - rightWidth, borderImageRect.y() + topWidth, rightWidth,
   1209                                             destinationHeight),
   1210                                             IntRect(imageWidth - rightSlice, topSlice, rightSlice, sourceHeight),
   1211                                             FloatSize(rightSideScale, rightSideScale),
   1212                                             Image::StretchTile, (Image::TileRule)vRule, op);
   1213     }
   1214 
   1215     // Paint the top edge.
   1216     if (drawTop && sourceWidth > 0)
   1217         graphicsContext->drawTiledImage(image.get(), IntRect(borderImageRect.x() + leftWidth, borderImageRect.y(), destinationWidth, topWidth),
   1218                                         IntRect(leftSlice, 0, sourceWidth, topSlice),
   1219                                         FloatSize(topSideScale, topSideScale), (Image::TileRule)hRule, Image::StretchTile, op);
   1220 
   1221     // Paint the bottom edge.
   1222     if (drawBottom && sourceWidth > 0)
   1223         graphicsContext->drawTiledImage(image.get(), IntRect(borderImageRect.x() + leftWidth, borderImageRect.maxY() - bottomWidth,
   1224                                         destinationWidth, bottomWidth),
   1225                                         IntRect(leftSlice, imageHeight - bottomSlice, sourceWidth, bottomSlice),
   1226                                         FloatSize(bottomSideScale, bottomSideScale),
   1227                                         (Image::TileRule)hRule, Image::StretchTile, op);
   1228 
   1229     // Paint the middle.
   1230     if (drawMiddle) {
   1231         FloatSize middleScaleFactor(1, 1);
   1232         if (drawTop)
   1233             middleScaleFactor.setWidth(topSideScale);
   1234         else if (drawBottom)
   1235             middleScaleFactor.setWidth(bottomSideScale);
   1236         if (drawLeft)
   1237             middleScaleFactor.setHeight(leftSideScale);
   1238         else if (drawRight)
   1239             middleScaleFactor.setHeight(rightSideScale);
   1240 
   1241         // For "stretch" rules, just override the scale factor and replace. We only had to do this for the
   1242         // center tile, since sides don't even use the scale factor unless they have a rule other than "stretch".
   1243         // The middle however can have "stretch" specified in one axis but not the other, so we have to
   1244         // correct the scale here.
   1245         if (hRule == StretchImageRule)
   1246             middleScaleFactor.setWidth(destinationWidth / sourceWidth);
   1247 
   1248         if (vRule == StretchImageRule)
   1249             middleScaleFactor.setHeight(destinationHeight / sourceHeight);
   1250 
   1251         graphicsContext->drawTiledImage(image.get(),
   1252             IntRect(borderImageRect.x() + leftWidth, borderImageRect.y() + topWidth, destinationWidth, destinationHeight),
   1253             IntRect(leftSlice, topSlice, sourceWidth, sourceHeight),
   1254             middleScaleFactor, (Image::TileRule)hRule, (Image::TileRule)vRule, op);
   1255     }
   1256 
   1257     return true;
   1258 }
   1259 
   1260 class BorderEdge {
   1261 public:
   1262     BorderEdge(int edgeWidth, const Color& edgeColor, EBorderStyle edgeStyle, bool edgeIsTransparent, bool edgeIsPresent = true)
   1263         : width(edgeWidth)
   1264         , color(edgeColor)
   1265         , style(edgeStyle)
   1266         , isTransparent(edgeIsTransparent)
   1267         , isPresent(edgeIsPresent)
   1268     {
   1269         if (style == DOUBLE && edgeWidth < 3)
   1270             style = SOLID;
   1271     }
   1272 
   1273     BorderEdge()
   1274         : width(0)
   1275         , style(BHIDDEN)
   1276         , isTransparent(false)
   1277         , isPresent(false)
   1278     {
   1279     }
   1280 
   1281     bool hasVisibleColorAndStyle() const { return style > BHIDDEN && !isTransparent; }
   1282     bool shouldRender() const { return isPresent && width && hasVisibleColorAndStyle(); }
   1283     bool presentButInvisible() const { return usedWidth() && !hasVisibleColorAndStyle(); }
   1284     bool obscuresBackgroundEdge(float scale) const
   1285     {
   1286         if (!isPresent || isTransparent || (width * scale) < 2 || color.hasAlpha() || style == BHIDDEN)
   1287             return false;
   1288 
   1289         if (style == DOTTED || style == DASHED)
   1290             return false;
   1291 
   1292         if (style == DOUBLE)
   1293             return width >= 5 * scale; // The outer band needs to be >= 2px wide at unit scale.
   1294 
   1295         return true;
   1296     }
   1297     bool obscuresBackground() const
   1298     {
   1299         if (!isPresent || isTransparent || color.hasAlpha() || style == BHIDDEN)
   1300             return false;
   1301 
   1302         if (style == DOTTED || style == DASHED || style == DOUBLE)
   1303             return false;
   1304 
   1305         return true;
   1306     }
   1307 
   1308     int usedWidth() const { return isPresent ? width : 0; }
   1309 
   1310     void getDoubleBorderStripeWidths(int& outerWidth, int& innerWidth) const
   1311     {
   1312         int fullWidth = usedWidth();
   1313         outerWidth = fullWidth / 3;
   1314         innerWidth = fullWidth * 2 / 3;
   1315 
   1316         // We need certain integer rounding results
   1317         if (fullWidth % 3 == 2)
   1318             outerWidth += 1;
   1319 
   1320         if (fullWidth % 3 == 1)
   1321             innerWidth += 1;
   1322     }
   1323 
   1324     int width;
   1325     Color color;
   1326     EBorderStyle style;
   1327     bool isTransparent;
   1328     bool isPresent;
   1329 };
   1330 
   1331 static bool allCornersClippedOut(const RoundedRect& border, const LayoutRect& clipRect)
   1332 {
   1333     LayoutRect boundingRect = border.rect();
   1334     if (clipRect.contains(boundingRect))
   1335         return false;
   1336 
   1337     RoundedRect::Radii radii = border.radii();
   1338 
   1339     LayoutRect topLeftRect(boundingRect.location(), radii.topLeft());
   1340     if (clipRect.intersects(topLeftRect))
   1341         return false;
   1342 
   1343     LayoutRect topRightRect(boundingRect.location(), radii.topRight());
   1344     topRightRect.setX(boundingRect.maxX() - topRightRect.width());
   1345     if (clipRect.intersects(topRightRect))
   1346         return false;
   1347 
   1348     LayoutRect bottomLeftRect(boundingRect.location(), radii.bottomLeft());
   1349     bottomLeftRect.setY(boundingRect.maxY() - bottomLeftRect.height());
   1350     if (clipRect.intersects(bottomLeftRect))
   1351         return false;
   1352 
   1353     LayoutRect bottomRightRect(boundingRect.location(), radii.bottomRight());
   1354     bottomRightRect.setX(boundingRect.maxX() - bottomRightRect.width());
   1355     bottomRightRect.setY(boundingRect.maxY() - bottomRightRect.height());
   1356     if (clipRect.intersects(bottomRightRect))
   1357         return false;
   1358 
   1359     return true;
   1360 }
   1361 
   1362 static bool borderWillArcInnerEdge(const LayoutSize& firstRadius, const FloatSize& secondRadius)
   1363 {
   1364     return !firstRadius.isZero() || !secondRadius.isZero();
   1365 }
   1366 
   1367 enum BorderEdgeFlag {
   1368     TopBorderEdge = 1 << BSTop,
   1369     RightBorderEdge = 1 << BSRight,
   1370     BottomBorderEdge = 1 << BSBottom,
   1371     LeftBorderEdge = 1 << BSLeft,
   1372     AllBorderEdges = TopBorderEdge | BottomBorderEdge | LeftBorderEdge | RightBorderEdge
   1373 };
   1374 
   1375 static inline BorderEdgeFlag edgeFlagForSide(BoxSide side)
   1376 {
   1377     return static_cast<BorderEdgeFlag>(1 << side);
   1378 }
   1379 
   1380 static inline bool includesEdge(BorderEdgeFlags flags, BoxSide side)
   1381 {
   1382     return flags & edgeFlagForSide(side);
   1383 }
   1384 
   1385 static inline bool includesAdjacentEdges(BorderEdgeFlags flags)
   1386 {
   1387     return (flags & (TopBorderEdge | RightBorderEdge)) == (TopBorderEdge | RightBorderEdge)
   1388         || (flags & (RightBorderEdge | BottomBorderEdge)) == (RightBorderEdge | BottomBorderEdge)
   1389         || (flags & (BottomBorderEdge | LeftBorderEdge)) == (BottomBorderEdge | LeftBorderEdge)
   1390         || (flags & (LeftBorderEdge | TopBorderEdge)) == (LeftBorderEdge | TopBorderEdge);
   1391 }
   1392 
   1393 inline bool edgesShareColor(const BorderEdge& firstEdge, const BorderEdge& secondEdge)
   1394 {
   1395     return firstEdge.color == secondEdge.color;
   1396 }
   1397 
   1398 inline bool styleRequiresClipPolygon(EBorderStyle style)
   1399 {
   1400     return style == DOTTED || style == DASHED; // These are drawn with a stroke, so we have to clip to get corner miters.
   1401 }
   1402 
   1403 static bool borderStyleFillsBorderArea(EBorderStyle style)
   1404 {
   1405     return !(style == DOTTED || style == DASHED || style == DOUBLE);
   1406 }
   1407 
   1408 static bool borderStyleHasInnerDetail(EBorderStyle style)
   1409 {
   1410     return style == GROOVE || style == RIDGE || style == DOUBLE;
   1411 }
   1412 
   1413 static bool borderStyleIsDottedOrDashed(EBorderStyle style)
   1414 {
   1415     return style == DOTTED || style == DASHED;
   1416 }
   1417 
   1418 // OUTSET darkens the bottom and right (and maybe lightens the top and left)
   1419 // INSET darkens the top and left (and maybe lightens the bottom and right)
   1420 static inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, BoxSide side, BoxSide adjacentSide)
   1421 {
   1422     // These styles match at the top/left and bottom/right.
   1423     if (style == INSET || style == GROOVE || style == RIDGE || style == OUTSET) {
   1424         const BorderEdgeFlags topRightFlags = edgeFlagForSide(BSTop) | edgeFlagForSide(BSRight);
   1425         const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edgeFlagForSide(BSLeft);
   1426 
   1427         BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacentSide);
   1428         return flags == topRightFlags || flags == bottomLeftFlags;
   1429     }
   1430     return false;
   1431 }
   1432 
   1433 static inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[])
   1434 {
   1435     if (edges[side].shouldRender() != edges[adjacentSide].shouldRender())
   1436         return false;
   1437 
   1438     if (!edgesShareColor(edges[side], edges[adjacentSide]))
   1439         return false;
   1440 
   1441     return !borderStyleHasUnmatchedColorsAtCorner(edges[side].style, side, adjacentSide);
   1442 }
   1443 
   1444 
   1445 static inline bool colorNeedsAntiAliasAtCorner(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[])
   1446 {
   1447     if (!edges[side].color.hasAlpha())
   1448         return false;
   1449 
   1450     if (edges[side].shouldRender() != edges[adjacentSide].shouldRender())
   1451         return false;
   1452 
   1453     if (!edgesShareColor(edges[side], edges[adjacentSide]))
   1454         return true;
   1455 
   1456     return borderStyleHasUnmatchedColorsAtCorner(edges[side].style, side, adjacentSide);
   1457 }
   1458 
   1459 // This assumes that we draw in order: top, bottom, left, right.
   1460 static inline bool willBeOverdrawn(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[])
   1461 {
   1462     switch (side) {
   1463     case BSTop:
   1464     case BSBottom:
   1465         if (edges[adjacentSide].presentButInvisible())
   1466             return false;
   1467 
   1468         if (!edgesShareColor(edges[side], edges[adjacentSide]) && edges[adjacentSide].color.hasAlpha())
   1469             return false;
   1470 
   1471         if (!borderStyleFillsBorderArea(edges[adjacentSide].style))
   1472             return false;
   1473 
   1474         return true;
   1475 
   1476     case BSLeft:
   1477     case BSRight:
   1478         // These draw last, so are never overdrawn.
   1479         return false;
   1480     }
   1481     return false;
   1482 }
   1483 
   1484 static inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorderStyle style, EBorderStyle adjacentStyle)
   1485 {
   1486     if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE || adjacentStyle == RIDGE)
   1487         return true;
   1488 
   1489     if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjacentStyle))
   1490         return true;
   1491 
   1492     if (style != adjacentStyle)
   1493         return true;
   1494 
   1495     return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide);
   1496 }
   1497 
   1498 static bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[], bool allowOverdraw)
   1499 {
   1500     if ((edges[side].isTransparent && edges[adjacentSide].isTransparent) || !edges[adjacentSide].isPresent)
   1501         return false;
   1502 
   1503     if (allowOverdraw && willBeOverdrawn(side, adjacentSide, edges))
   1504         return false;
   1505 
   1506     if (!edgesShareColor(edges[side], edges[adjacentSide]))
   1507         return true;
   1508 
   1509     if (borderStylesRequireMitre(side, adjacentSide, edges[side].style, edges[adjacentSide].style))
   1510         return true;
   1511 
   1512     return false;
   1513 }
   1514 
   1515 void RenderBoxModelObject::paintOneBorderSide(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
   1516     const IntRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adjacentSide2, const BorderEdge edges[], const Path* path,
   1517     BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor)
   1518 {
   1519     const BorderEdge& edgeToRender = edges[side];
   1520     ASSERT(edgeToRender.width);
   1521     const BorderEdge& adjacentEdge1 = edges[adjacentSide1];
   1522     const BorderEdge& adjacentEdge2 = edges[adjacentSide2];
   1523 
   1524     bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, edges, !antialias);
   1525     bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, edges, !antialias);
   1526 
   1527     bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1, edges);
   1528     bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2, edges);
   1529 
   1530     const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.color;
   1531 
   1532     if (path) {
   1533         GraphicsContextStateSaver stateSaver(*graphicsContext);
   1534         if (innerBorder.isRenderable())
   1535             clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, adjacentSide1StylesMatch, adjacentSide2StylesMatch);
   1536         else
   1537             clipBorderSideForComplexInnerPath(graphicsContext, outerBorder, innerBorder, side, edges);
   1538         float thickness = max(max(edgeToRender.width, adjacentEdge1.width), adjacentEdge2.width);
   1539         drawBoxSideFromPath(graphicsContext, outerBorder.rect(), *path, edges, edgeToRender.width, thickness, side, style,
   1540             colorToPaint, edgeToRender.style, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
   1541     } else {
   1542         bool clipForStyle = styleRequiresClipPolygon(edgeToRender.style) && (mitreAdjacentSide1 || mitreAdjacentSide2);
   1543         bool clipAdjacentSide1 = colorNeedsAntiAliasAtCorner(side, adjacentSide1, edges) && mitreAdjacentSide1;
   1544         bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSide2, edges) && mitreAdjacentSide2;
   1545         bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2;
   1546 
   1547         GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip);
   1548         if (shouldClip) {
   1549             bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitreAdjacentSide1);
   1550             bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitreAdjacentSide2);
   1551             clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, !aliasAdjacentSide1, !aliasAdjacentSide2);
   1552             // Since we clipped, no need to draw with a mitre.
   1553             mitreAdjacentSide1 = false;
   1554             mitreAdjacentSide2 = false;
   1555         }
   1556 
   1557         drawLineForBoxSide(graphicsContext, sideRect.x(), sideRect.y(), sideRect.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.style,
   1558                 mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? adjacentEdge2.width : 0, antialias);
   1559     }
   1560 }
   1561 
   1562 static IntRect calculateSideRect(const RoundedRect& outerBorder, const BorderEdge edges[], int side)
   1563 {
   1564     IntRect sideRect = outerBorder.rect();
   1565     int width = edges[side].width;
   1566 
   1567     if (side == BSTop)
   1568         sideRect.setHeight(width);
   1569     else if (side == BSBottom)
   1570         sideRect.shiftYEdgeTo(sideRect.maxY() - width);
   1571     else if (side == BSLeft)
   1572         sideRect.setWidth(width);
   1573     else
   1574         sideRect.shiftXEdgeTo(sideRect.maxX() - width);
   1575 
   1576     return sideRect;
   1577 }
   1578 
   1579 void RenderBoxModelObject::paintBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
   1580     const IntPoint& innerBorderAdjustment, const BorderEdge edges[], BorderEdgeFlags edgeSet, BackgroundBleedAvoidance bleedAvoidance,
   1581     bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor)
   1582 {
   1583     bool renderRadii = outerBorder.isRounded();
   1584 
   1585     Path roundedPath;
   1586     if (renderRadii)
   1587         roundedPath.addRoundedRect(outerBorder);
   1588 
   1589     // The inner border adjustment for bleed avoidance mode BackgroundBleedBackgroundOverBorder
   1590     // is only applied to sideRect, which is okay since BackgroundBleedBackgroundOverBorder
   1591     // is only to be used for solid borders and the shape of the border painted by drawBoxSideFromPath
   1592     // only depends on sideRect when painting solid borders.
   1593 
   1594     if (edges[BSTop].shouldRender() && includesEdge(edgeSet, BSTop)) {
   1595         IntRect sideRect = outerBorder.rect();
   1596         sideRect.setHeight(edges[BSTop].width + innerBorderAdjustment.y());
   1597 
   1598         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSTop].style) || borderWillArcInnerEdge(innerBorder.radii().topLeft(), innerBorder.radii().topRight()));
   1599         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSTop, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
   1600     }
   1601 
   1602     if (edges[BSBottom].shouldRender() && includesEdge(edgeSet, BSBottom)) {
   1603         IntRect sideRect = outerBorder.rect();
   1604         sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].width - innerBorderAdjustment.y());
   1605 
   1606         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSBottom].style) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().bottomRight()));
   1607         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSBottom, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
   1608     }
   1609 
   1610     if (edges[BSLeft].shouldRender() && includesEdge(edgeSet, BSLeft)) {
   1611         IntRect sideRect = outerBorder.rect();
   1612         sideRect.setWidth(edges[BSLeft].width + innerBorderAdjustment.x());
   1613 
   1614         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSLeft].style) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().topLeft()));
   1615         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSLeft, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
   1616     }
   1617 
   1618     if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) {
   1619         IntRect sideRect = outerBorder.rect();
   1620         sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width - innerBorderAdjustment.x());
   1621 
   1622         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSRight].style) || borderWillArcInnerEdge(innerBorder.radii().bottomRight(), innerBorder.radii().topRight()));
   1623         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSRight, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
   1624     }
   1625 }
   1626 
   1627 void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, const IntPoint& innerBorderAdjustment,
   1628     const BorderEdge edges[], BorderEdgeFlags edgesToDraw, BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias)
   1629 {
   1630     // willBeOverdrawn assumes that we draw in order: top, bottom, left, right.
   1631     // This is different from BoxSide enum order.
   1632     static const BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight };
   1633 
   1634     while (edgesToDraw) {
   1635         // Find undrawn edges sharing a color.
   1636         Color commonColor;
   1637 
   1638         BorderEdgeFlags commonColorEdgeSet = 0;
   1639         for (size_t i = 0; i < sizeof(paintOrder) / sizeof(paintOrder[0]); ++i) {
   1640             BoxSide currSide = paintOrder[i];
   1641             if (!includesEdge(edgesToDraw, currSide))
   1642                 continue;
   1643 
   1644             bool includeEdge;
   1645             if (!commonColorEdgeSet) {
   1646                 commonColor = edges[currSide].color;
   1647                 includeEdge = true;
   1648             } else
   1649                 includeEdge = edges[currSide].color == commonColor;
   1650 
   1651             if (includeEdge)
   1652                 commonColorEdgeSet |= edgeFlagForSide(currSide);
   1653         }
   1654 
   1655         bool useTransparencyLayer = includesAdjacentEdges(commonColorEdgeSet) && commonColor.hasAlpha();
   1656         if (useTransparencyLayer) {
   1657             graphicsContext->beginTransparencyLayer(static_cast<float>(commonColor.alpha()) / 255);
   1658             commonColor = Color(commonColor.red(), commonColor.green(), commonColor.blue());
   1659         }
   1660 
   1661         paintBorderSides(graphicsContext, style, outerBorder, innerBorder, innerBorderAdjustment, edges, commonColorEdgeSet, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, &commonColor);
   1662 
   1663         if (useTransparencyLayer)
   1664             graphicsContext->endTransparencyLayer();
   1665 
   1666         edgesToDraw &= ~commonColorEdgeSet;
   1667     }
   1668 }
   1669 
   1670 void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect& rect, const RenderStyle* style,
   1671                                        BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
   1672 {
   1673     GraphicsContext* graphicsContext = info.context;
   1674     // border-image is not affected by border-radius.
   1675     if (paintNinePieceImage(graphicsContext, rect, style, style->borderImage()))
   1676         return;
   1677 
   1678     if (graphicsContext->paintingDisabled())
   1679         return;
   1680 
   1681     BorderEdge edges[4];
   1682     getBorderEdgeInfo(edges, style, includeLogicalLeftEdge, includeLogicalRightEdge);
   1683     RoundedRect outerBorder = style->getRoundedBorderFor(rect, view(), includeLogicalLeftEdge, includeLogicalRightEdge);
   1684     RoundedRect innerBorder = style->getRoundedInnerBorderFor(borderInnerRectAdjustedForBleedAvoidance(graphicsContext, rect, bleedAvoidance), includeLogicalLeftEdge, includeLogicalRightEdge);
   1685 
   1686     bool haveAlphaColor = false;
   1687     bool haveAllSolidEdges = true;
   1688     bool haveAllDoubleEdges = true;
   1689     int numEdgesVisible = 4;
   1690     bool allEdgesShareColor = true;
   1691     int firstVisibleEdge = -1;
   1692     BorderEdgeFlags edgesToDraw = 0;
   1693 
   1694     for (int i = BSTop; i <= BSLeft; ++i) {
   1695         const BorderEdge& currEdge = edges[i];
   1696 
   1697         if (edges[i].shouldRender())
   1698             edgesToDraw |= edgeFlagForSide(static_cast<BoxSide>(i));
   1699 
   1700         if (currEdge.presentButInvisible()) {
   1701             --numEdgesVisible;
   1702             allEdgesShareColor = false;
   1703             continue;
   1704         }
   1705 
   1706         if (!currEdge.width) {
   1707             --numEdgesVisible;
   1708             continue;
   1709         }
   1710 
   1711         if (firstVisibleEdge == -1)
   1712             firstVisibleEdge = i;
   1713         else if (currEdge.color != edges[firstVisibleEdge].color)
   1714             allEdgesShareColor = false;
   1715 
   1716         if (currEdge.color.hasAlpha())
   1717             haveAlphaColor = true;
   1718 
   1719         if (currEdge.style != SOLID)
   1720             haveAllSolidEdges = false;
   1721 
   1722         if (currEdge.style != DOUBLE)
   1723             haveAllDoubleEdges = false;
   1724     }
   1725 
   1726     // If no corner intersects the clip region, we can pretend outerBorder is
   1727     // rectangular to improve performance.
   1728     if (haveAllSolidEdges && outerBorder.isRounded() && allCornersClippedOut(outerBorder, info.rect))
   1729         outerBorder.setRadii(RoundedRect::Radii());
   1730 
   1731     // isRenderable() check avoids issue described in https://bugs.webkit.org/show_bug.cgi?id=38787
   1732     if ((haveAllSolidEdges || haveAllDoubleEdges) && allEdgesShareColor && innerBorder.isRenderable()) {
   1733         // Fast path for drawing all solid edges and all unrounded double edges
   1734         if (numEdgesVisible == 4 && (outerBorder.isRounded() || haveAlphaColor)
   1735             && (haveAllSolidEdges || (!outerBorder.isRounded() && !innerBorder.isRounded()))) {
   1736             Path path;
   1737 
   1738             if (outerBorder.isRounded() && bleedAvoidance != BackgroundBleedUseTransparencyLayer)
   1739                 path.addRoundedRect(outerBorder);
   1740             else
   1741                 path.addRect(outerBorder.rect());
   1742 
   1743             if (haveAllDoubleEdges) {
   1744                 IntRect innerThirdRect = outerBorder.rect();
   1745                 IntRect outerThirdRect = outerBorder.rect();
   1746                 for (int side = BSTop; side <= BSLeft; ++side) {
   1747                     int outerWidth;
   1748                     int innerWidth;
   1749                     edges[side].getDoubleBorderStripeWidths(outerWidth, innerWidth);
   1750 
   1751                     if (side == BSTop) {
   1752                         innerThirdRect.shiftYEdgeTo(innerThirdRect.y() + innerWidth);
   1753                         outerThirdRect.shiftYEdgeTo(outerThirdRect.y() + outerWidth);
   1754                     } else if (side == BSBottom) {
   1755                         innerThirdRect.setHeight(innerThirdRect.height() - innerWidth);
   1756                         outerThirdRect.setHeight(outerThirdRect.height() - outerWidth);
   1757                     } else if (side == BSLeft) {
   1758                         innerThirdRect.shiftXEdgeTo(innerThirdRect.x() + innerWidth);
   1759                         outerThirdRect.shiftXEdgeTo(outerThirdRect.x() + outerWidth);
   1760                     } else {
   1761                         innerThirdRect.setWidth(innerThirdRect.width() - innerWidth);
   1762                         outerThirdRect.setWidth(outerThirdRect.width() - outerWidth);
   1763                     }
   1764                 }
   1765 
   1766                 RoundedRect outerThird = outerBorder;
   1767                 RoundedRect innerThird = innerBorder;
   1768                 innerThird.setRect(innerThirdRect);
   1769                 outerThird.setRect(outerThirdRect);
   1770 
   1771                 if (outerThird.isRounded() && bleedAvoidance != BackgroundBleedUseTransparencyLayer)
   1772                     path.addRoundedRect(outerThird);
   1773                 else
   1774                     path.addRect(outerThird.rect());
   1775 
   1776                 if (innerThird.isRounded() && bleedAvoidance != BackgroundBleedUseTransparencyLayer)
   1777                     path.addRoundedRect(innerThird);
   1778                 else
   1779                     path.addRect(innerThird.rect());
   1780             }
   1781 
   1782             if (innerBorder.isRounded())
   1783                 path.addRoundedRect(innerBorder);
   1784             else
   1785                 path.addRect(innerBorder.rect());
   1786 
   1787             graphicsContext->setFillRule(RULE_EVENODD);
   1788             graphicsContext->setFillColor(edges[firstVisibleEdge].color);
   1789             graphicsContext->fillPath(path);
   1790             return;
   1791         }
   1792         // Avoid creating transparent layers
   1793         if (haveAllSolidEdges && numEdgesVisible != 4 && !outerBorder.isRounded() && haveAlphaColor) {
   1794             Path path;
   1795 
   1796             for (int i = BSTop; i <= BSLeft; ++i) {
   1797                 const BorderEdge& currEdge = edges[i];
   1798                 if (currEdge.shouldRender()) {
   1799                     IntRect sideRect = calculateSideRect(outerBorder, edges, i);
   1800                     path.addRect(sideRect);
   1801                 }
   1802             }
   1803 
   1804             graphicsContext->setFillRule(RULE_NONZERO);
   1805             graphicsContext->setFillColor(edges[firstVisibleEdge].color);
   1806             graphicsContext->fillPath(path);
   1807             return;
   1808         }
   1809     }
   1810 
   1811     bool clipToOuterBorder = outerBorder.isRounded();
   1812     GraphicsContextStateSaver stateSaver(*graphicsContext, clipToOuterBorder);
   1813     if (clipToOuterBorder) {
   1814         // Clip to the inner and outer radii rects.
   1815         if (bleedAvoidance != BackgroundBleedUseTransparencyLayer)
   1816             graphicsContext->clipRoundedRect(outerBorder);
   1817         // isRenderable() check avoids issue described in https://bugs.webkit.org/show_bug.cgi?id=38787
   1818         // The inside will be clipped out later (in clipBorderSideForComplexInnerPath)
   1819         if (innerBorder.isRenderable())
   1820             graphicsContext->clipOutRoundedRect(innerBorder);
   1821     }
   1822 
   1823     // If only one edge visible antialiasing doesn't create seams
   1824     bool antialias = shouldAntialiasLines(graphicsContext) || numEdgesVisible == 1;
   1825     RoundedRect unadjustedInnerBorder = (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? style->getRoundedInnerBorderFor(rect, includeLogicalLeftEdge, includeLogicalRightEdge) : innerBorder;
   1826     IntPoint innerBorderAdjustment(innerBorder.rect().x() - unadjustedInnerBorder.rect().x(), innerBorder.rect().y() - unadjustedInnerBorder.rect().y());
   1827     if (haveAlphaColor)
   1828         paintTranslucentBorderSides(graphicsContext, style, outerBorder, unadjustedInnerBorder, innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias);
   1829     else
   1830         paintBorderSides(graphicsContext, style, outerBorder, unadjustedInnerBorder, innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias);
   1831 }
   1832 
   1833 void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext* graphicsContext, const LayoutRect& borderRect, const Path& borderPath, const BorderEdge edges[],
   1834     float thickness, float drawThickness, BoxSide side, const RenderStyle* style, Color color, EBorderStyle borderStyle, BackgroundBleedAvoidance bleedAvoidance,
   1835     bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
   1836 {
   1837     if (thickness <= 0)
   1838         return;
   1839 
   1840     if (borderStyle == DOUBLE && thickness < 3)
   1841         borderStyle = SOLID;
   1842 
   1843     switch (borderStyle) {
   1844     case BNONE:
   1845     case BHIDDEN:
   1846         return;
   1847     case DOTTED:
   1848     case DASHED: {
   1849         graphicsContext->setStrokeColor(color);
   1850 
   1851         // The stroke is doubled here because the provided path is the
   1852         // outside edge of the border so half the stroke is clipped off.
   1853         // The extra multiplier is so that the clipping mask can antialias
   1854         // the edges to prevent jaggies.
   1855         graphicsContext->setStrokeThickness(drawThickness * 2 * 1.1f);
   1856         graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke : DottedStroke);
   1857 
   1858         // If the number of dashes that fit in the path is odd and non-integral then we
   1859         // will have an awkwardly-sized dash at the end of the path. To try to avoid that
   1860         // here, we simply make the whitespace dashes ever so slightly bigger.
   1861         // FIXME: This could be even better if we tried to manipulate the dash offset
   1862         // and possibly the gapLength to get the corners dash-symmetrical.
   1863         float dashLength = thickness * ((borderStyle == DASHED) ? 3.0f : 1.0f);
   1864         float gapLength = dashLength;
   1865         float numberOfDashes = borderPath.length() / dashLength;
   1866         // Don't try to show dashes if we have less than 2 dashes + 2 gaps.
   1867         // FIXME: should do this test per side.
   1868         if (numberOfDashes >= 4) {
   1869             bool evenNumberOfFullDashes = !((int)numberOfDashes % 2);
   1870             bool integralNumberOfDashes = !(numberOfDashes - (int)numberOfDashes);
   1871             if (!evenNumberOfFullDashes && !integralNumberOfDashes) {
   1872                 float numberOfGaps = numberOfDashes / 2;
   1873                 gapLength += (dashLength  / numberOfGaps);
   1874             }
   1875 
   1876             DashArray lineDash;
   1877             lineDash.append(dashLength);
   1878             lineDash.append(gapLength);
   1879             graphicsContext->setLineDash(lineDash, dashLength);
   1880         }
   1881 
   1882         // FIXME: stroking the border path causes issues with tight corners:
   1883         // https://bugs.webkit.org/show_bug.cgi?id=58711
   1884         // Also, to get the best appearance we should stroke a path between the two borders.
   1885         graphicsContext->strokePath(borderPath);
   1886         return;
   1887     }
   1888     case DOUBLE: {
   1889         // Get the inner border rects for both the outer border line and the inner border line
   1890         int outerBorderTopWidth;
   1891         int innerBorderTopWidth;
   1892         edges[BSTop].getDoubleBorderStripeWidths(outerBorderTopWidth, innerBorderTopWidth);
   1893 
   1894         int outerBorderRightWidth;
   1895         int innerBorderRightWidth;
   1896         edges[BSRight].getDoubleBorderStripeWidths(outerBorderRightWidth, innerBorderRightWidth);
   1897 
   1898         int outerBorderBottomWidth;
   1899         int innerBorderBottomWidth;
   1900         edges[BSBottom].getDoubleBorderStripeWidths(outerBorderBottomWidth, innerBorderBottomWidth);
   1901 
   1902         int outerBorderLeftWidth;
   1903         int innerBorderLeftWidth;
   1904         edges[BSLeft].getDoubleBorderStripeWidths(outerBorderLeftWidth, innerBorderLeftWidth);
   1905 
   1906         // Draw inner border line
   1907         {
   1908             GraphicsContextStateSaver stateSaver(*graphicsContext);
   1909             RoundedRect innerClip = style->getRoundedInnerBorderFor(borderRect,
   1910                 innerBorderTopWidth, innerBorderBottomWidth, innerBorderLeftWidth, innerBorderRightWidth,
   1911                 includeLogicalLeftEdge, includeLogicalRightEdge);
   1912 
   1913             graphicsContext->clipRoundedRect(innerClip);
   1914             drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
   1915         }
   1916 
   1917         // Draw outer border line
   1918         {
   1919             GraphicsContextStateSaver stateSaver(*graphicsContext);
   1920             LayoutRect outerRect = borderRect;
   1921             if (bleedAvoidance == BackgroundBleedUseTransparencyLayer) {
   1922                 outerRect.inflate(1);
   1923                 ++outerBorderTopWidth;
   1924                 ++outerBorderBottomWidth;
   1925                 ++outerBorderLeftWidth;
   1926                 ++outerBorderRightWidth;
   1927             }
   1928 
   1929             RoundedRect outerClip = style->getRoundedInnerBorderFor(outerRect,
   1930                 outerBorderTopWidth, outerBorderBottomWidth, outerBorderLeftWidth, outerBorderRightWidth,
   1931                 includeLogicalLeftEdge, includeLogicalRightEdge);
   1932             graphicsContext->clipOutRoundedRect(outerClip);
   1933             drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
   1934         }
   1935         return;
   1936     }
   1937     case RIDGE:
   1938     case GROOVE:
   1939     {
   1940         EBorderStyle s1;
   1941         EBorderStyle s2;
   1942         if (borderStyle == GROOVE) {
   1943             s1 = INSET;
   1944             s2 = OUTSET;
   1945         } else {
   1946             s1 = OUTSET;
   1947             s2 = INSET;
   1948         }
   1949 
   1950         // Paint full border
   1951         drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s1, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
   1952 
   1953         // Paint inner only
   1954         GraphicsContextStateSaver stateSaver(*graphicsContext);
   1955         LayoutUnit topWidth = edges[BSTop].usedWidth() / 2;
   1956         LayoutUnit bottomWidth = edges[BSBottom].usedWidth() / 2;
   1957         LayoutUnit leftWidth = edges[BSLeft].usedWidth() / 2;
   1958         LayoutUnit rightWidth = edges[BSRight].usedWidth() / 2;
   1959 
   1960         RoundedRect clipRect = style->getRoundedInnerBorderFor(borderRect,
   1961             topWidth, bottomWidth, leftWidth, rightWidth,
   1962             includeLogicalLeftEdge, includeLogicalRightEdge);
   1963 
   1964         graphicsContext->clipRoundedRect(clipRect);
   1965         drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s2, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
   1966         return;
   1967     }
   1968     case INSET:
   1969         if (side == BSTop || side == BSLeft)
   1970             color = color.dark();
   1971         break;
   1972     case OUTSET:
   1973         if (side == BSBottom || side == BSRight)
   1974             color = color.dark();
   1975         break;
   1976     default:
   1977         break;
   1978     }
   1979 
   1980     graphicsContext->setStrokeStyle(NoStroke);
   1981     graphicsContext->setFillColor(color);
   1982     graphicsContext->drawRect(pixelSnappedIntRect(borderRect));
   1983 }
   1984 
   1985 void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext* graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
   1986                                                  BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches)
   1987 {
   1988     FloatPoint quad[4];
   1989 
   1990     const LayoutRect& outerRect = outerBorder.rect();
   1991     const LayoutRect& innerRect = innerBorder.rect();
   1992 
   1993     FloatPoint centerPoint(innerRect.location().x() + static_cast<float>(innerRect.width()) / 2, innerRect.location().y() + static_cast<float>(innerRect.height()) / 2);
   1994 
   1995     // For each side, create a quad that encompasses all parts of that side that may draw,
   1996     // including areas inside the innerBorder.
   1997     //
   1998     //         0----------------3
   1999     //       0  \              /  0
   2000     //       |\  1----------- 2  /|
   2001     //       | 1                1 |
   2002     //       | |                | |
   2003     //       | |                | |
   2004     //       | 2                2 |
   2005     //       |/  1------------2  \|
   2006     //       3  /              \  3
   2007     //         0----------------3
   2008     //
   2009     switch (side) {
   2010     case BSTop:
   2011         quad[0] = outerRect.minXMinYCorner();
   2012         quad[1] = innerRect.minXMinYCorner();
   2013         quad[2] = innerRect.maxXMinYCorner();
   2014         quad[3] = outerRect.maxXMinYCorner();
   2015 
   2016         if (!innerBorder.radii().topLeft().isZero()) {
   2017             findIntersection(quad[0], quad[1],
   2018                 FloatPoint(
   2019                     quad[1].x() + innerBorder.radii().topLeft().width(),
   2020                     quad[1].y()),
   2021                 FloatPoint(
   2022                     quad[1].x(),
   2023                     quad[1].y() + innerBorder.radii().topLeft().height()),
   2024                 quad[1]);
   2025         }
   2026 
   2027         if (!innerBorder.radii().topRight().isZero()) {
   2028             findIntersection(quad[3], quad[2],
   2029                 FloatPoint(
   2030                     quad[2].x() - innerBorder.radii().topRight().width(),
   2031                     quad[2].y()),
   2032                 FloatPoint(
   2033                     quad[2].x(),
   2034                     quad[2].y() + innerBorder.radii().topRight().height()),
   2035                 quad[2]);
   2036         }
   2037         break;
   2038 
   2039     case BSLeft:
   2040         quad[0] = outerRect.minXMinYCorner();
   2041         quad[1] = innerRect.minXMinYCorner();
   2042         quad[2] = innerRect.minXMaxYCorner();
   2043         quad[3] = outerRect.minXMaxYCorner();
   2044 
   2045         if (!innerBorder.radii().topLeft().isZero()) {
   2046             findIntersection(quad[0], quad[1],
   2047                 FloatPoint(
   2048                     quad[1].x() + innerBorder.radii().topLeft().width(),
   2049                     quad[1].y()),
   2050                 FloatPoint(
   2051                     quad[1].x(),
   2052                     quad[1].y() + innerBorder.radii().topLeft().height()),
   2053                 quad[1]);
   2054         }
   2055 
   2056         if (!innerBorder.radii().bottomLeft().isZero()) {
   2057             findIntersection(quad[3], quad[2],
   2058                 FloatPoint(
   2059                     quad[2].x() + innerBorder.radii().bottomLeft().width(),
   2060                     quad[2].y()),
   2061                 FloatPoint(
   2062                     quad[2].x(),
   2063                     quad[2].y() - innerBorder.radii().bottomLeft().height()),
   2064                 quad[2]);
   2065         }
   2066         break;
   2067 
   2068     case BSBottom:
   2069         quad[0] = outerRect.minXMaxYCorner();
   2070         quad[1] = innerRect.minXMaxYCorner();
   2071         quad[2] = innerRect.maxXMaxYCorner();
   2072         quad[3] = outerRect.maxXMaxYCorner();
   2073 
   2074         if (!innerBorder.radii().bottomLeft().isZero()) {
   2075             findIntersection(quad[0], quad[1],
   2076                 FloatPoint(
   2077                     quad[1].x() + innerBorder.radii().bottomLeft().width(),
   2078                     quad[1].y()),
   2079                 FloatPoint(
   2080                     quad[1].x(),
   2081                     quad[1].y() - innerBorder.radii().bottomLeft().height()),
   2082                 quad[1]);
   2083         }
   2084 
   2085         if (!innerBorder.radii().bottomRight().isZero()) {
   2086             findIntersection(quad[3], quad[2],
   2087                 FloatPoint(
   2088                     quad[2].x() - innerBorder.radii().bottomRight().width(),
   2089                     quad[2].y()),
   2090                 FloatPoint(
   2091                     quad[2].x(),
   2092                     quad[2].y() - innerBorder.radii().bottomRight().height()),
   2093                 quad[2]);
   2094         }
   2095         break;
   2096 
   2097     case BSRight:
   2098         quad[0] = outerRect.maxXMinYCorner();
   2099         quad[1] = innerRect.maxXMinYCorner();
   2100         quad[2] = innerRect.maxXMaxYCorner();
   2101         quad[3] = outerRect.maxXMaxYCorner();
   2102 
   2103         if (!innerBorder.radii().topRight().isZero()) {
   2104             findIntersection(quad[0], quad[1],
   2105                 FloatPoint(
   2106                     quad[1].x() - innerBorder.radii().topRight().width(),
   2107                     quad[1].y()),
   2108                 FloatPoint(
   2109                     quad[1].x(),
   2110                     quad[1].y() + innerBorder.radii().topRight().height()),
   2111                 quad[1]);
   2112         }
   2113 
   2114         if (!innerBorder.radii().bottomRight().isZero()) {
   2115             findIntersection(quad[3], quad[2],
   2116                 FloatPoint(
   2117                     quad[2].x() - innerBorder.radii().bottomRight().width(),
   2118                     quad[2].y()),
   2119                 FloatPoint(
   2120                     quad[2].x(),
   2121                     quad[2].y() - innerBorder.radii().bottomRight().height()),
   2122                 quad[2]);
   2123         }
   2124         break;
   2125     }
   2126 
   2127     // If the border matches both of its adjacent sides, don't anti-alias the clip, and
   2128     // if neither side matches, anti-alias the clip.
   2129     if (firstEdgeMatches == secondEdgeMatches) {
   2130         graphicsContext->clipConvexPolygon(4, quad, !firstEdgeMatches);
   2131         return;
   2132     }
   2133 
   2134     // If antialiasing settings for the first edge and second edge is different,
   2135     // they have to be addressed separately. We do this by breaking the quad into
   2136     // two parallelograms, made by moving quad[1] and quad[2].
   2137     float ax = quad[1].x() - quad[0].x();
   2138     float ay = quad[1].y() - quad[0].y();
   2139     float bx = quad[2].x() - quad[1].x();
   2140     float by = quad[2].y() - quad[1].y();
   2141     float cx = quad[3].x() - quad[2].x();
   2142     float cy = quad[3].y() - quad[2].y();
   2143 
   2144     const static float kEpsilon = 1e-2f;
   2145     float r1, r2;
   2146     if (fabsf(bx) < kEpsilon && fabsf(by) < kEpsilon) {
   2147         // The quad was actually a triangle.
   2148         r1 = r2 = 1.0f;
   2149     } else {
   2150         // Extend parallelogram a bit to hide calculation error
   2151         const static float kExtendFill = 1e-2f;
   2152 
   2153         r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill;
   2154         r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill;
   2155     }
   2156 
   2157     FloatPoint firstQuad[4];
   2158     firstQuad[0] = quad[0];
   2159     firstQuad[1] = quad[1];
   2160     firstQuad[2] = FloatPoint(quad[3].x() + r2 * ax, quad[3].y() + r2 * ay);
   2161     firstQuad[3] = quad[3];
   2162     graphicsContext->clipConvexPolygon(4, firstQuad, !firstEdgeMatches);
   2163 
   2164     FloatPoint secondQuad[4];
   2165     secondQuad[0] = quad[0];
   2166     secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy);
   2167     secondQuad[2] = quad[2];
   2168     secondQuad[3] = quad[3];
   2169     graphicsContext->clipConvexPolygon(4, secondQuad, !secondEdgeMatches);
   2170 }
   2171 
   2172 static IntRect calculateSideRectIncludingInner(const RoundedRect& outerBorder, const BorderEdge edges[], BoxSide side)
   2173 {
   2174     IntRect sideRect = outerBorder.rect();
   2175     int width;
   2176 
   2177     switch (side) {
   2178     case BSTop:
   2179         width = sideRect.height() - edges[BSBottom].width;
   2180         sideRect.setHeight(width);
   2181         break;
   2182     case BSBottom:
   2183         width = sideRect.height() - edges[BSTop].width;
   2184         sideRect.shiftYEdgeTo(sideRect.maxY() - width);
   2185         break;
   2186     case BSLeft:
   2187         width = sideRect.width() - edges[BSRight].width;
   2188         sideRect.setWidth(width);
   2189         break;
   2190     case BSRight:
   2191         width = sideRect.width() - edges[BSLeft].width;
   2192         sideRect.shiftXEdgeTo(sideRect.maxX() - width);
   2193         break;
   2194     }
   2195 
   2196     return sideRect;
   2197 }
   2198 
   2199 static RoundedRect calculateAdjustedInnerBorder(const RoundedRect&innerBorder, BoxSide side)
   2200 {
   2201     // Expand the inner border as necessary to make it a rounded rect (i.e. radii contained within each edge).
   2202     // This function relies on the fact we only get radii not contained within each edge if one of the radii
   2203     // for an edge is zero, so we can shift the arc towards the zero radius corner.
   2204     RoundedRect::Radii newRadii = innerBorder.radii();
   2205     IntRect newRect = innerBorder.rect();
   2206 
   2207     float overshoot;
   2208     float maxRadii;
   2209 
   2210     switch (side) {
   2211     case BSTop:
   2212         overshoot = newRadii.topLeft().width() + newRadii.topRight().width() - newRect.width();
   2213         if (overshoot > 0) {
   2214             ASSERT(!(newRadii.topLeft().width() && newRadii.topRight().width()));
   2215             newRect.setWidth(newRect.width() + overshoot);
   2216             if (!newRadii.topLeft().width())
   2217                 newRect.move(-overshoot, 0);
   2218         }
   2219         newRadii.setBottomLeft(IntSize(0, 0));
   2220         newRadii.setBottomRight(IntSize(0, 0));
   2221         maxRadii = max(newRadii.topLeft().height(), newRadii.topRight().height());
   2222         if (maxRadii > newRect.height())
   2223             newRect.setHeight(maxRadii);
   2224         break;
   2225 
   2226     case BSBottom:
   2227         overshoot = newRadii.bottomLeft().width() + newRadii.bottomRight().width() - newRect.width();
   2228         if (overshoot > 0) {
   2229             ASSERT(!(newRadii.bottomLeft().width() && newRadii.bottomRight().width()));
   2230             newRect.setWidth(newRect.width() + overshoot);
   2231             if (!newRadii.bottomLeft().width())
   2232                 newRect.move(-overshoot, 0);
   2233         }
   2234         newRadii.setTopLeft(IntSize(0, 0));
   2235         newRadii.setTopRight(IntSize(0, 0));
   2236         maxRadii = max(newRadii.bottomLeft().height(), newRadii.bottomRight().height());
   2237         if (maxRadii > newRect.height()) {
   2238             newRect.move(0, newRect.height() - maxRadii);
   2239             newRect.setHeight(maxRadii);
   2240         }
   2241         break;
   2242 
   2243     case BSLeft:
   2244         overshoot = newRadii.topLeft().height() + newRadii.bottomLeft().height() - newRect.height();
   2245         if (overshoot > 0) {
   2246             ASSERT(!(newRadii.topLeft().height() && newRadii.bottomLeft().height()));
   2247             newRect.setHeight(newRect.height() + overshoot);
   2248             if (!newRadii.topLeft().height())
   2249                 newRect.move(0, -overshoot);
   2250         }
   2251         newRadii.setTopRight(IntSize(0, 0));
   2252         newRadii.setBottomRight(IntSize(0, 0));
   2253         maxRadii = max(newRadii.topLeft().width(), newRadii.bottomLeft().width());
   2254         if (maxRadii > newRect.width())
   2255             newRect.setWidth(maxRadii);
   2256         break;
   2257 
   2258     case BSRight:
   2259         overshoot = newRadii.topRight().height() + newRadii.bottomRight().height() - newRect.height();
   2260         if (overshoot > 0) {
   2261             ASSERT(!(newRadii.topRight().height() && newRadii.bottomRight().height()));
   2262             newRect.setHeight(newRect.height() + overshoot);
   2263             if (!newRadii.topRight().height())
   2264                 newRect.move(0, -overshoot);
   2265         }
   2266         newRadii.setTopLeft(IntSize(0, 0));
   2267         newRadii.setBottomLeft(IntSize(0, 0));
   2268         maxRadii = max(newRadii.topRight().width(), newRadii.bottomRight().width());
   2269         if (maxRadii > newRect.width()) {
   2270             newRect.move(newRect.width() - maxRadii, 0);
   2271             newRect.setWidth(maxRadii);
   2272         }
   2273         break;
   2274     }
   2275 
   2276     return RoundedRect(newRect, newRadii);
   2277 }
   2278 
   2279 void RenderBoxModelObject::clipBorderSideForComplexInnerPath(GraphicsContext* graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
   2280     BoxSide side, const class BorderEdge edges[])
   2281 {
   2282     graphicsContext->clip(calculateSideRectIncludingInner(outerBorder, edges, side));
   2283     graphicsContext->clipOutRoundedRect(calculateAdjustedInnerBorder(innerBorder, side));
   2284 }
   2285 
   2286 void RenderBoxModelObject::getBorderEdgeInfo(BorderEdge edges[], const RenderStyle* style, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const
   2287 {
   2288     bool horizontal = style->isHorizontalWritingMode();
   2289 
   2290     edges[BSTop] = BorderEdge(style->borderTopWidth(),
   2291         resolveColor(style, CSSPropertyBorderTopColor),
   2292         style->borderTopStyle(),
   2293         style->borderTopIsTransparent(),
   2294         horizontal || includeLogicalLeftEdge);
   2295 
   2296     edges[BSRight] = BorderEdge(style->borderRightWidth(),
   2297         resolveColor(style, CSSPropertyBorderRightColor),
   2298         style->borderRightStyle(),
   2299         style->borderRightIsTransparent(),
   2300         !horizontal || includeLogicalRightEdge);
   2301 
   2302     edges[BSBottom] = BorderEdge(style->borderBottomWidth(),
   2303         resolveColor(style, CSSPropertyBorderBottomColor),
   2304         style->borderBottomStyle(),
   2305         style->borderBottomIsTransparent(),
   2306         horizontal || includeLogicalRightEdge);
   2307 
   2308     edges[BSLeft] = BorderEdge(style->borderLeftWidth(),
   2309         resolveColor(style, CSSPropertyBorderLeftColor),
   2310         style->borderLeftStyle(),
   2311         style->borderLeftIsTransparent(),
   2312         !horizontal || includeLogicalLeftEdge);
   2313 }
   2314 
   2315 bool RenderBoxModelObject::borderObscuresBackgroundEdge(const FloatSize& contextScale) const
   2316 {
   2317     BorderEdge edges[4];
   2318     getBorderEdgeInfo(edges, style());
   2319 
   2320     for (int i = BSTop; i <= BSLeft; ++i) {
   2321         const BorderEdge& currEdge = edges[i];
   2322         // FIXME: for vertical text
   2323         float axisScale = (i == BSTop || i == BSBottom) ? contextScale.height() : contextScale.width();
   2324         if (!currEdge.obscuresBackgroundEdge(axisScale))
   2325             return false;
   2326     }
   2327 
   2328     return true;
   2329 }
   2330 
   2331 bool RenderBoxModelObject::borderObscuresBackground() const
   2332 {
   2333     if (!style()->hasBorder())
   2334         return false;
   2335 
   2336     // Bail if we have any border-image for now. We could look at the image alpha to improve this.
   2337     if (style()->borderImage().image())
   2338         return false;
   2339 
   2340     BorderEdge edges[4];
   2341     getBorderEdgeInfo(edges, style());
   2342 
   2343     for (int i = BSTop; i <= BSLeft; ++i) {
   2344         const BorderEdge& currEdge = edges[i];
   2345         if (!currEdge.obscuresBackground())
   2346             return false;
   2347     }
   2348 
   2349     return true;
   2350 }
   2351 
   2352 bool RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* inlineFlowBox) const
   2353 {
   2354     if (bleedAvoidance != BackgroundBleedNone)
   2355         return false;
   2356 
   2357     if (style()->hasAppearance())
   2358         return false;
   2359 
   2360     bool hasOneNormalBoxShadow = false;
   2361     for (const ShadowData* currentShadow = style()->boxShadow(); currentShadow; currentShadow = currentShadow->next()) {
   2362         if (currentShadow->style() != Normal)
   2363             continue;
   2364 
   2365         if (hasOneNormalBoxShadow)
   2366             return false;
   2367         hasOneNormalBoxShadow = true;
   2368 
   2369         if (currentShadow->spread())
   2370             return false;
   2371     }
   2372 
   2373     if (!hasOneNormalBoxShadow)
   2374         return false;
   2375 
   2376     Color backgroundColor = resolveColor(CSSPropertyBackgroundColor);
   2377     if (backgroundColor.hasAlpha())
   2378         return false;
   2379 
   2380     const FillLayer* lastBackgroundLayer = style()->backgroundLayers();
   2381     for (const FillLayer* next = lastBackgroundLayer->next(); next; next = lastBackgroundLayer->next())
   2382         lastBackgroundLayer = next;
   2383 
   2384     if (lastBackgroundLayer->clip() != BorderFillBox)
   2385         return false;
   2386 
   2387     if (lastBackgroundLayer->image() && style()->hasBorderRadius())
   2388         return false;
   2389 
   2390     if (inlineFlowBox && !inlineFlowBox->boxShadowCanBeAppliedToBackground(*lastBackgroundLayer))
   2391         return false;
   2392 
   2393     if (hasOverflowClip() && lastBackgroundLayer->attachment() == LocalBackgroundAttachment)
   2394         return false;
   2395 
   2396     return true;
   2397 }
   2398 
   2399 static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowBlur, int shadowSpread, const IntSize& shadowOffset)
   2400 {
   2401     IntRect bounds(holeRect);
   2402 
   2403     bounds.inflate(shadowBlur);
   2404 
   2405     if (shadowSpread < 0)
   2406         bounds.inflate(-shadowSpread);
   2407 
   2408     IntRect offsetBounds = bounds;
   2409     offsetBounds.move(-shadowOffset);
   2410     return unionRect(bounds, offsetBounds);
   2411 }
   2412 
   2413 void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRect& paintRect, const RenderStyle* s, ShadowStyle shadowStyle, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
   2414 {
   2415     // FIXME: Deal with border-image.  Would be great to use border-image as a mask.
   2416     GraphicsContext* context = info.context;
   2417     if (context->paintingDisabled() || !s->boxShadow())
   2418         return;
   2419 
   2420     RoundedRect border = (shadowStyle == Inset) ? s->getRoundedInnerBorderFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge)
   2421                                                    : s->getRoundedBorderFor(paintRect, view(), includeLogicalLeftEdge, includeLogicalRightEdge);
   2422 
   2423     bool hasBorderRadius = s->hasBorderRadius();
   2424     bool isHorizontal = s->isHorizontalWritingMode();
   2425 
   2426     bool hasOpaqueBackground = s->visitedDependentColor(CSSPropertyBackgroundColor).isValid() && s->visitedDependentColor(CSSPropertyBackgroundColor).alpha() == 255;
   2427     for (const ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next()) {
   2428         if (shadow->style() != shadowStyle)
   2429             continue;
   2430 
   2431         IntSize shadowOffset(shadow->x(), shadow->y());
   2432         int shadowBlur = shadow->blur();
   2433         int shadowSpread = shadow->spread();
   2434 
   2435         if (shadowOffset.isZero() && !shadowBlur && !shadowSpread)
   2436             continue;
   2437 
   2438         const Color& shadowColor = resolveColor(shadow->color(), Color::stdShadowColor);
   2439 
   2440         if (shadow->style() == Normal) {
   2441             RoundedRect fillRect = border;
   2442             fillRect.inflate(shadowSpread);
   2443             if (fillRect.isEmpty())
   2444                 continue;
   2445 
   2446             IntRect shadowRect(border.rect());
   2447             shadowRect.inflate(shadowBlur + shadowSpread);
   2448             shadowRect.move(shadowOffset);
   2449 
   2450             // Draw only the shadow.
   2451             DrawLooper drawLooper;
   2452             drawLooper.addShadow(shadowOffset, shadowBlur, shadowColor,
   2453                 DrawLooper::ShadowRespectsTransforms, DrawLooper::ShadowIgnoresAlpha);
   2454             context->setDrawLooper(drawLooper);
   2455 
   2456             context->save();
   2457             if (hasBorderRadius) {
   2458                 RoundedRect rectToClipOut = border;
   2459 
   2460                 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
   2461                 // when painting the shadow. On the other hand, it introduces subpixel gaps along the
   2462                 // corners. Those are avoided by insetting the clipping path by one pixel.
   2463                 if (hasOpaqueBackground)
   2464                     rectToClipOut.inflateWithRadii(-1);
   2465 
   2466                 if (!rectToClipOut.isEmpty()) {
   2467                     context->clipOutRoundedRect(rectToClipOut);
   2468                 }
   2469 
   2470                 RoundedRect influenceRect(shadowRect, border.radii());
   2471                 influenceRect.expandRadii(2 * shadowBlur + shadowSpread);
   2472                 if (allCornersClippedOut(influenceRect, info.rect))
   2473                     context->fillRect(fillRect.rect(), Color::black);
   2474                 else {
   2475                     fillRect.expandRadii(shadowSpread);
   2476                     if (!fillRect.isRenderable())
   2477                         fillRect.adjustRadii();
   2478                     context->fillRoundedRect(fillRect, Color::black);
   2479                 }
   2480             } else {
   2481                 IntRect rectToClipOut = border.rect();
   2482 
   2483                 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
   2484                 // when painting the shadow. On the other hand, it introduces subpixel gaps along the
   2485                 // edges if they are not pixel-aligned. Those are avoided by insetting the clipping path
   2486                 // by one pixel.
   2487                 if (hasOpaqueBackground) {
   2488                     // FIXME: The function to decide on the policy based on the transform should be a named function.
   2489                     // FIXME: It's not clear if this check is right. What about integral scale factors?
   2490                     AffineTransform transform = context->getCTM();
   2491                     if (transform.a() != 1 || (transform.d() != 1 && transform.d() != -1) || transform.b() || transform.c())
   2492                         rectToClipOut.inflate(-1);
   2493                 }
   2494 
   2495                 if (!rectToClipOut.isEmpty()) {
   2496                     context->clipOut(rectToClipOut);
   2497                 }
   2498                 context->fillRect(fillRect.rect(), Color::black);
   2499             }
   2500             context->restore();
   2501             context->clearDrawLooper();
   2502         } else {
   2503             GraphicsContext::Edges clippedEdges = GraphicsContext::NoEdge;
   2504             if (!includeLogicalLeftEdge) {
   2505                 if (isHorizontal)
   2506                     clippedEdges |= GraphicsContext::LeftEdge;
   2507                 else
   2508                     clippedEdges |= GraphicsContext::TopEdge;
   2509             }
   2510             if (!includeLogicalRightEdge) {
   2511                 if (isHorizontal)
   2512                     clippedEdges |= GraphicsContext::RightEdge;
   2513                 else
   2514                     clippedEdges |= GraphicsContext::BottomEdge;
   2515             }
   2516             context->drawInnerShadow(border, shadowColor, shadowOffset, shadowBlur, shadowSpread, clippedEdges);
   2517         }
   2518     }
   2519 }
   2520 
   2521 LayoutUnit RenderBoxModelObject::containingBlockLogicalWidthForContent() const
   2522 {
   2523     return containingBlock()->availableLogicalWidth();
   2524 }
   2525 
   2526 RenderBoxModelObject* RenderBoxModelObject::continuation() const
   2527 {
   2528     if (!continuationMap)
   2529         return 0;
   2530     return continuationMap->get(this);
   2531 }
   2532 
   2533 void RenderBoxModelObject::setContinuation(RenderBoxModelObject* continuation)
   2534 {
   2535     if (continuation) {
   2536         if (!continuationMap)
   2537             continuationMap = new ContinuationMap;
   2538         continuationMap->set(this, continuation);
   2539     } else {
   2540         if (continuationMap)
   2541             continuationMap->remove(this);
   2542     }
   2543 }
   2544 
   2545 void RenderBoxModelObject::computeLayerHitTestRects(LayerHitTestRects& rects) const
   2546 {
   2547     RenderLayerModelObject::computeLayerHitTestRects(rects);
   2548 
   2549     // If there is a continuation then we need to consult it here, since this is
   2550     // the root of the tree walk and it wouldn't otherwise get picked up.
   2551     // Continuations should always be siblings in the tree, so any others should
   2552     // get picked up already by the tree walk.
   2553     if (continuation())
   2554         continuation()->computeLayerHitTestRects(rects);
   2555 }
   2556 
   2557 RenderObject* RenderBoxModelObject::firstLetterRemainingText() const
   2558 {
   2559     if (!firstLetterRemainingTextMap)
   2560         return 0;
   2561     return firstLetterRemainingTextMap->get(this);
   2562 }
   2563 
   2564 void RenderBoxModelObject::setFirstLetterRemainingText(RenderObject* remainingText)
   2565 {
   2566     if (remainingText) {
   2567         if (!firstLetterRemainingTextMap)
   2568             firstLetterRemainingTextMap = new FirstLetterRemainingTextMap;
   2569         firstLetterRemainingTextMap->set(this, remainingText);
   2570     } else if (firstLetterRemainingTextMap)
   2571         firstLetterRemainingTextMap->remove(this);
   2572 }
   2573 
   2574 LayoutRect RenderBoxModelObject::localCaretRectForEmptyElement(LayoutUnit width, LayoutUnit textIndentOffset)
   2575 {
   2576     ASSERT(!firstChild());
   2577 
   2578     // FIXME: This does not take into account either :first-line or :first-letter
   2579     // However, as soon as some content is entered, the line boxes will be
   2580     // constructed and this kludge is not called any more. So only the caret size
   2581     // of an empty :first-line'd block is wrong. I think we can live with that.
   2582     RenderStyle* currentStyle = firstLineStyle();
   2583     LayoutUnit height = lineHeight(true, currentStyle->isHorizontalWritingMode() ? HorizontalLine : VerticalLine);
   2584 
   2585     enum CaretAlignment { alignLeft, alignRight, alignCenter };
   2586 
   2587     CaretAlignment alignment = alignLeft;
   2588 
   2589     switch (currentStyle->textAlign()) {
   2590     case LEFT:
   2591     case WEBKIT_LEFT:
   2592         break;
   2593     case CENTER:
   2594     case WEBKIT_CENTER:
   2595         alignment = alignCenter;
   2596         break;
   2597     case RIGHT:
   2598     case WEBKIT_RIGHT:
   2599         alignment = alignRight;
   2600         break;
   2601     case JUSTIFY:
   2602     case TASTART:
   2603         if (!currentStyle->isLeftToRightDirection())
   2604             alignment = alignRight;
   2605         break;
   2606     case TAEND:
   2607         if (currentStyle->isLeftToRightDirection())
   2608             alignment = alignRight;
   2609         break;
   2610     }
   2611 
   2612     LayoutUnit x = borderLeft() + paddingLeft();
   2613     LayoutUnit maxX = width - borderRight() - paddingRight();
   2614 
   2615     switch (alignment) {
   2616     case alignLeft:
   2617         if (currentStyle->isLeftToRightDirection())
   2618             x += textIndentOffset;
   2619         break;
   2620     case alignCenter:
   2621         x = (x + maxX) / 2;
   2622         if (currentStyle->isLeftToRightDirection())
   2623             x += textIndentOffset / 2;
   2624         else
   2625             x -= textIndentOffset / 2;
   2626         break;
   2627     case alignRight:
   2628         x = maxX - caretWidth;
   2629         if (!currentStyle->isLeftToRightDirection())
   2630             x -= textIndentOffset;
   2631         break;
   2632     }
   2633     x = min(x, max<LayoutUnit>(maxX - caretWidth, 0));
   2634 
   2635     LayoutUnit y = paddingTop() + borderTop();
   2636 
   2637     return currentStyle->isHorizontalWritingMode() ? LayoutRect(x, y, caretWidth, height) : LayoutRect(y, x, height, caretWidth);
   2638 }
   2639 
   2640 bool RenderBoxModelObject::shouldAntialiasLines(GraphicsContext* context)
   2641 {
   2642     // FIXME: We may want to not antialias when scaled by an integral value,
   2643     // and we may want to antialias when translated by a non-integral value.
   2644     return !context->getCTM().isIdentityOrTranslationOrFlipped();
   2645 }
   2646 
   2647 void RenderBoxModelObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const
   2648 {
   2649     // We don't expect to be called during layout.
   2650     ASSERT(!view() || !view()->layoutStateEnabled());
   2651 
   2652     RenderObject* o = container();
   2653     if (!o)
   2654         return;
   2655 
   2656     // The point inside a box that's inside a region has its coordinates relative to the region,
   2657     // not the FlowThread that is its container in the RenderObject tree.
   2658     if (o->isRenderFlowThread() && isRenderBlock()) {
   2659         // FIXME: switch to Box instead of Block when we'll have range information for boxes as well, not just for blocks.
   2660         RenderRegion* startRegion;
   2661         RenderRegion* ignoredEndRegion;
   2662         toRenderFlowThread(o)->getRegionRangeForBox(toRenderBlock(this), startRegion, ignoredEndRegion);
   2663         // If there is no region to use the FlowThread, then there's no region range for the content in that FlowThread.
   2664         // An API like elementFromPoint might crash without this check.
   2665         if (startRegion)
   2666             o = startRegion;
   2667     }
   2668 
   2669     o->mapAbsoluteToLocalPoint(mode, transformState);
   2670 
   2671     LayoutSize containerOffset = offsetFromContainer(o, LayoutPoint());
   2672 
   2673     if (!style()->hasOutOfFlowPosition() && o->hasColumns()) {
   2674         RenderBlock* block = toRenderBlock(o);
   2675         LayoutPoint point(roundedLayoutPoint(transformState.mappedPoint()));
   2676         point -= containerOffset;
   2677         block->adjustForColumnRect(containerOffset, point);
   2678     }
   2679 
   2680     bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D());
   2681     if (mode & UseTransforms && shouldUseTransformFromContainer(o)) {
   2682         TransformationMatrix t;
   2683         getTransformFromContainer(o, containerOffset, t);
   2684         transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
   2685     } else
   2686         transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
   2687 }
   2688 
   2689 const RenderObject* RenderBoxModelObject::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const
   2690 {
   2691     ASSERT(ancestorToStopAt != this);
   2692 
   2693     bool ancestorSkipped;
   2694     RenderObject* container = this->container(ancestorToStopAt, &ancestorSkipped);
   2695     if (!container)
   2696         return 0;
   2697 
   2698     bool isInline = isRenderInline();
   2699     bool isFixedPos = !isInline && style()->position() == FixedPosition;
   2700     bool hasTransform = !isInline && hasLayer() && layer()->transform();
   2701 
   2702     LayoutSize adjustmentForSkippedAncestor;
   2703     if (ancestorSkipped) {
   2704         // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
   2705         // to just subtract the delta between the ancestor and o.
   2706         adjustmentForSkippedAncestor = -ancestorToStopAt->offsetFromAncestorContainer(container);
   2707     }
   2708 
   2709     bool offsetDependsOnPoint = false;
   2710     LayoutSize containerOffset = offsetFromContainer(container, LayoutPoint(), &offsetDependsOnPoint);
   2711 
   2712     bool preserve3D = container->style()->preserves3D() || style()->preserves3D();
   2713     if (shouldUseTransformFromContainer(container)) {
   2714         TransformationMatrix t;
   2715         getTransformFromContainer(container, containerOffset, t);
   2716         t.translateRight(adjustmentForSkippedAncestor.width(), adjustmentForSkippedAncestor.height());
   2717         geometryMap.push(this, t, preserve3D, offsetDependsOnPoint, isFixedPos, hasTransform);
   2718     } else {
   2719         containerOffset += adjustmentForSkippedAncestor;
   2720         geometryMap.push(this, containerOffset, preserve3D, offsetDependsOnPoint, isFixedPos, hasTransform);
   2721     }
   2722 
   2723     return ancestorSkipped ? ancestorToStopAt : container;
   2724 }
   2725 
   2726 void RenderBoxModelObject::moveChildTo(RenderBoxModelObject* toBoxModelObject, RenderObject* child, RenderObject* beforeChild, bool fullRemoveInsert)
   2727 {
   2728     // We assume that callers have cleared their positioned objects list for child moves (!fullRemoveInsert) so the
   2729     // positioned renderer maps don't become stale. It would be too slow to do the map lookup on each call.
   2730     ASSERT(!fullRemoveInsert || !isRenderBlock() || !toRenderBlock(this)->hasPositionedObjects());
   2731 
   2732     ASSERT(this == child->parent());
   2733     ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent());
   2734     if (fullRemoveInsert && (toBoxModelObject->isRenderBlock() || toBoxModelObject->isRenderInline())) {
   2735         // Takes care of adding the new child correctly if toBlock and fromBlock
   2736         // have different kind of children (block vs inline).
   2737         toBoxModelObject->addChild(virtualChildren()->removeChildNode(this, child), beforeChild);
   2738     } else
   2739         toBoxModelObject->virtualChildren()->insertChildNode(toBoxModelObject, virtualChildren()->removeChildNode(this, child, fullRemoveInsert), beforeChild, fullRemoveInsert);
   2740 }
   2741 
   2742 void RenderBoxModelObject::moveChildrenTo(RenderBoxModelObject* toBoxModelObject, RenderObject* startChild, RenderObject* endChild, RenderObject* beforeChild, bool fullRemoveInsert)
   2743 {
   2744     // This condition is rarely hit since this function is usually called on
   2745     // anonymous blocks which can no longer carry positioned objects (see r120761)
   2746     // or when fullRemoveInsert is false.
   2747     if (fullRemoveInsert && isRenderBlock()) {
   2748         RenderBlock* block = toRenderBlock(this);
   2749         block->removePositionedObjects(0);
   2750         block->removeFloatingObjects();
   2751     }
   2752 
   2753     ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent());
   2754     for (RenderObject* child = startChild; child && child != endChild; ) {
   2755         // Save our next sibling as moveChildTo will clear it.
   2756         RenderObject* nextSibling = child->nextSibling();
   2757         moveChildTo(toBoxModelObject, child, beforeChild, fullRemoveInsert);
   2758         child = nextSibling;
   2759     }
   2760 }
   2761 
   2762 } // namespace WebCore
   2763