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