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