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 "RenderBoxModelObject.h"
     28 
     29 #include "GraphicsContext.h"
     30 #include "HTMLFrameOwnerElement.h"
     31 #include "HTMLNames.h"
     32 #include "ImageBuffer.h"
     33 #include "Path.h"
     34 #include "RenderBlock.h"
     35 #include "RenderInline.h"
     36 #include "RenderLayer.h"
     37 #include "RenderView.h"
     38 #include <wtf/CurrentTime.h>
     39 
     40 using namespace std;
     41 
     42 namespace WebCore {
     43 
     44 using namespace HTMLNames;
     45 
     46 bool RenderBoxModelObject::s_wasFloating = false;
     47 bool RenderBoxModelObject::s_hadLayer = false;
     48 bool RenderBoxModelObject::s_layerWasSelfPainting = false;
     49 
     50 static const double cInterpolationCutoff = 800. * 800.;
     51 static const double cLowQualityTimeThreshold = 0.500; // 500 ms
     52 
     53 typedef HashMap<const void*, IntSize> LayerSizeMap;
     54 typedef HashMap<RenderBoxModelObject*, LayerSizeMap> ObjectLayerSizeMap;
     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 class ImageQualityController {
     67     WTF_MAKE_NONCOPYABLE(ImageQualityController); WTF_MAKE_FAST_ALLOCATED;
     68 public:
     69     ImageQualityController();
     70     bool shouldPaintAtLowQuality(GraphicsContext*, RenderBoxModelObject*, Image*, const void* layer, const IntSize&);
     71     void removeLayer(RenderBoxModelObject*, LayerSizeMap* innerMap, const void* layer);
     72     void set(RenderBoxModelObject*, LayerSizeMap* innerMap, const void* layer, const IntSize&);
     73     void objectDestroyed(RenderBoxModelObject*);
     74     bool isEmpty() { return m_objectLayerSizeMap.isEmpty(); }
     75 
     76 private:
     77     void highQualityRepaintTimerFired(Timer<ImageQualityController>*);
     78     void restartTimer();
     79 
     80     ObjectLayerSizeMap m_objectLayerSizeMap;
     81     Timer<ImageQualityController> m_timer;
     82     bool m_animatedResizeIsActive;
     83 };
     84 
     85 ImageQualityController::ImageQualityController()
     86     : m_timer(this, &ImageQualityController::highQualityRepaintTimerFired)
     87     , m_animatedResizeIsActive(false)
     88 {
     89 }
     90 
     91 void ImageQualityController::removeLayer(RenderBoxModelObject* object, LayerSizeMap* innerMap, const void* layer)
     92 {
     93     if (innerMap) {
     94         innerMap->remove(layer);
     95         if (innerMap->isEmpty())
     96             objectDestroyed(object);
     97     }
     98 }
     99 
    100 void ImageQualityController::set(RenderBoxModelObject* object, LayerSizeMap* innerMap, const void* layer, const IntSize& size)
    101 {
    102     if (innerMap)
    103         innerMap->set(layer, size);
    104     else {
    105         LayerSizeMap newInnerMap;
    106         newInnerMap.set(layer, size);
    107         m_objectLayerSizeMap.set(object, newInnerMap);
    108     }
    109 }
    110 
    111 void ImageQualityController::objectDestroyed(RenderBoxModelObject* object)
    112 {
    113     m_objectLayerSizeMap.remove(object);
    114     if (m_objectLayerSizeMap.isEmpty()) {
    115         m_animatedResizeIsActive = false;
    116         m_timer.stop();
    117     }
    118 }
    119 
    120 void ImageQualityController::highQualityRepaintTimerFired(Timer<ImageQualityController>*)
    121 {
    122     if (m_animatedResizeIsActive) {
    123         m_animatedResizeIsActive = false;
    124         for (ObjectLayerSizeMap::iterator it = m_objectLayerSizeMap.begin(); it != m_objectLayerSizeMap.end(); ++it)
    125             it->first->repaint();
    126     }
    127 }
    128 
    129 void ImageQualityController::restartTimer()
    130 {
    131     m_timer.startOneShot(cLowQualityTimeThreshold);
    132 }
    133 
    134 bool ImageQualityController::shouldPaintAtLowQuality(GraphicsContext* context, RenderBoxModelObject* object, Image* image, const void *layer, const IntSize& size)
    135 {
    136     // If the image is not a bitmap image, then none of this is relevant and we just paint at high
    137     // quality.
    138     if (!image || !image->isBitmapImage() || context->paintingDisabled())
    139         return false;
    140 
    141     // Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image
    142     // is actually being scaled.
    143     IntSize imageSize(image->width(), image->height());
    144 
    145     // Look ourselves up in the hashtables.
    146     ObjectLayerSizeMap::iterator i = m_objectLayerSizeMap.find(object);
    147     LayerSizeMap* innerMap = i != m_objectLayerSizeMap.end() ? &i->second : 0;
    148     IntSize oldSize;
    149     bool isFirstResize = true;
    150     if (innerMap) {
    151         LayerSizeMap::iterator j = innerMap->find(layer);
    152         if (j != innerMap->end()) {
    153             isFirstResize = false;
    154             oldSize = j->second;
    155         }
    156     }
    157 
    158     const AffineTransform& currentTransform = context->getCTM();
    159     bool contextIsScaled = !currentTransform.isIdentityOrTranslationOrFlipped();
    160     if (!contextIsScaled && imageSize == size) {
    161         // There is no scale in effect. If we had a scale in effect before, we can just remove this object from the list.
    162         removeLayer(object, innerMap, layer);
    163         return false;
    164     }
    165 
    166     // There is no need to hash scaled images that always use low quality mode when the page demands it. This is the iChat case.
    167     if (object->document()->page()->inLowQualityImageInterpolationMode()) {
    168         double totalPixels = static_cast<double>(image->width()) * static_cast<double>(image->height());
    169         if (totalPixels > cInterpolationCutoff)
    170             return true;
    171     }
    172 
    173     // If an animated resize is active, paint in low quality and kick the timer ahead.
    174     if (m_animatedResizeIsActive) {
    175         set(object, innerMap, layer, size);
    176         restartTimer();
    177         return true;
    178     }
    179     // If this is the first time resizing this image, or its size is the
    180     // same as the last resize, draw at high res, but record the paint
    181     // size and set the timer.
    182     if (isFirstResize || oldSize == size) {
    183         restartTimer();
    184         set(object, innerMap, layer, size);
    185         return false;
    186     }
    187     // If the timer is no longer active, draw at high quality and don't
    188     // set the timer.
    189     if (!m_timer.isActive()) {
    190         removeLayer(object, innerMap, layer);
    191         return false;
    192     }
    193     // This object has been resized to two different sizes while the timer
    194     // is active, so draw at low quality, set the flag for animated resizes and
    195     // the object to the list for high quality redraw.
    196     set(object, innerMap, layer, size);
    197     m_animatedResizeIsActive = true;
    198     restartTimer();
    199     return true;
    200 }
    201 
    202 static ImageQualityController* gImageQualityController = 0;
    203 
    204 static ImageQualityController* imageQualityController()
    205 {
    206     if (!gImageQualityController)
    207         gImageQualityController = new ImageQualityController;
    208 
    209     return gImageQualityController;
    210 }
    211 
    212 void RenderBoxModelObject::setSelectionState(SelectionState s)
    213 {
    214     if (selectionState() == s)
    215         return;
    216 
    217     if (s == SelectionInside && selectionState() != SelectionNone)
    218         return;
    219 
    220     if ((s == SelectionStart && selectionState() == SelectionEnd)
    221         || (s == SelectionEnd && selectionState() == SelectionStart))
    222         RenderObject::setSelectionState(SelectionBoth);
    223     else
    224         RenderObject::setSelectionState(s);
    225 
    226     // FIXME:
    227     // We should consider whether it is OK propagating to ancestor RenderInlines.
    228     // This is a workaround for http://webkit.org/b/32123
    229     RenderBlock* cb = containingBlock();
    230     if (cb && !cb->isRenderView())
    231         cb->setSelectionState(s);
    232 }
    233 
    234 bool RenderBoxModelObject::shouldPaintAtLowQuality(GraphicsContext* context, Image* image, const void* layer, const IntSize& size)
    235 {
    236     return imageQualityController()->shouldPaintAtLowQuality(context, this, image, layer, size);
    237 }
    238 
    239 RenderBoxModelObject::RenderBoxModelObject(Node* node)
    240     : RenderObject(node)
    241     , m_layer(0)
    242 {
    243 }
    244 
    245 RenderBoxModelObject::~RenderBoxModelObject()
    246 {
    247     // Our layer should have been destroyed and cleared by now
    248     ASSERT(!hasLayer());
    249     ASSERT(!m_layer);
    250     if (gImageQualityController) {
    251         gImageQualityController->objectDestroyed(this);
    252         if (gImageQualityController->isEmpty()) {
    253             delete gImageQualityController;
    254             gImageQualityController = 0;
    255         }
    256     }
    257 }
    258 
    259 void RenderBoxModelObject::destroyLayer()
    260 {
    261     ASSERT(!hasLayer()); // Callers should have already called setHasLayer(false)
    262     ASSERT(m_layer);
    263     m_layer->destroy(renderArena());
    264     m_layer = 0;
    265 }
    266 
    267 void RenderBoxModelObject::destroy()
    268 {
    269     // This must be done before we destroy the RenderObject.
    270     if (m_layer)
    271         m_layer->clearClipRects();
    272 
    273     // A continuation of this RenderObject should be destroyed at subclasses.
    274     ASSERT(!continuation());
    275 
    276     // RenderObject::destroy calls back to destroyLayer() for layer destruction
    277     RenderObject::destroy();
    278 }
    279 
    280 bool RenderBoxModelObject::hasSelfPaintingLayer() const
    281 {
    282     return m_layer && m_layer->isSelfPaintingLayer();
    283 }
    284 
    285 void RenderBoxModelObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
    286 {
    287     s_wasFloating = isFloating();
    288     s_hadLayer = hasLayer();
    289     if (s_hadLayer)
    290         s_layerWasSelfPainting = layer()->isSelfPaintingLayer();
    291 
    292     // If our z-index changes value or our visibility changes,
    293     // we need to dirty our stacking context's z-order list.
    294     if (style() && newStyle) {
    295         if (parent()) {
    296             // Do a repaint with the old style first, e.g., for example if we go from
    297             // having an outline to not having an outline.
    298             if (diff == StyleDifferenceRepaintLayer) {
    299                 layer()->repaintIncludingDescendants();
    300                 if (!(style()->clip() == newStyle->clip()))
    301                     layer()->clearClipRectsIncludingDescendants();
    302             } else if (diff == StyleDifferenceRepaint || newStyle->outlineSize() < style()->outlineSize())
    303                 repaint();
    304         }
    305 
    306         if (diff == StyleDifferenceLayout || diff == StyleDifferenceSimplifiedLayout) {
    307             // When a layout hint happens, we go ahead and do a repaint of the layer, since the layer could
    308             // end up being destroyed.
    309             if (hasLayer()) {
    310                 if (style()->position() != newStyle->position() ||
    311                     style()->zIndex() != newStyle->zIndex() ||
    312                     style()->hasAutoZIndex() != newStyle->hasAutoZIndex() ||
    313                     !(style()->clip() == newStyle->clip()) ||
    314                     style()->hasClip() != newStyle->hasClip() ||
    315                     style()->opacity() != newStyle->opacity() ||
    316                     style()->transform() != newStyle->transform())
    317                 layer()->repaintIncludingDescendants();
    318             } else if (newStyle->hasTransform() || newStyle->opacity() < 1) {
    319                 // If we don't have a layer yet, but we are going to get one because of transform or opacity,
    320                 //  then we need to repaint the old position of the object.
    321                 repaint();
    322             }
    323         }
    324 
    325         if (hasLayer() && (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() ||
    326                            style()->zIndex() != newStyle->zIndex() ||
    327                            style()->visibility() != newStyle->visibility())) {
    328             layer()->dirtyStackingContextZOrderLists();
    329             if (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() || style()->visibility() != newStyle->visibility())
    330                 layer()->dirtyZOrderLists();
    331         }
    332     }
    333 
    334     RenderObject::styleWillChange(diff, newStyle);
    335 }
    336 
    337 void RenderBoxModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
    338 {
    339     RenderObject::styleDidChange(diff, oldStyle);
    340     updateBoxModelInfoFromStyle();
    341 
    342     if (requiresLayer()) {
    343         if (!layer()) {
    344             if (s_wasFloating && isFloating())
    345                 setChildNeedsLayout(true);
    346             m_layer = new (renderArena()) RenderLayer(this);
    347             setHasLayer(true);
    348             m_layer->insertOnlyThisLayer();
    349             if (parent() && !needsLayout() && containingBlock()) {
    350                 m_layer->setNeedsFullRepaint();
    351                 m_layer->updateLayerPositions();
    352             }
    353         }
    354     } else if (layer() && layer()->parent()) {
    355         setHasTransform(false); // Either a transform wasn't specified or the object doesn't support transforms, so just null out the bit.
    356         setHasReflection(false);
    357         m_layer->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer
    358         if (s_wasFloating && isFloating())
    359             setChildNeedsLayout(true);
    360     }
    361 
    362     if (layer()) {
    363         layer()->styleChanged(diff, oldStyle);
    364         if (s_hadLayer && layer()->isSelfPaintingLayer() != s_layerWasSelfPainting)
    365             setChildNeedsLayout(true);
    366     }
    367 }
    368 
    369 void RenderBoxModelObject::updateBoxModelInfoFromStyle()
    370 {
    371     // Set the appropriate bits for a box model object.  Since all bits are cleared in styleWillChange,
    372     // we only check for bits that could possibly be set to true.
    373     setHasBoxDecorations(hasBackground() || style()->hasBorder() || style()->hasAppearance() || style()->boxShadow());
    374     setInline(style()->isDisplayInlineType());
    375     setRelPositioned(style()->position() == RelativePosition);
    376     setHorizontalWritingMode(style()->isHorizontalWritingMode());
    377 }
    378 
    379 int RenderBoxModelObject::relativePositionOffsetX() const
    380 {
    381     // Objects that shrink to avoid floats normally use available line width when computing containing block width.  However
    382     // in the case of relative positioning using percentages, we can't do this.  The offset should always be resolved using the
    383     // available width of the containing block.  Therefore we don't use containingBlockLogicalWidthForContent() here, but instead explicitly
    384     // call availableWidth on our containing block.
    385     if (!style()->left().isAuto()) {
    386         RenderBlock* cb = containingBlock();
    387         if (!style()->right().isAuto() && !cb->style()->isLeftToRightDirection())
    388             return -style()->right().calcValue(cb->availableWidth());
    389         return style()->left().calcValue(cb->availableWidth());
    390     }
    391     if (!style()->right().isAuto()) {
    392         RenderBlock* cb = containingBlock();
    393         return -style()->right().calcValue(cb->availableWidth());
    394     }
    395     return 0;
    396 }
    397 
    398 int RenderBoxModelObject::relativePositionOffsetY() const
    399 {
    400     RenderBlock* containingBlock = this->containingBlock();
    401 
    402     // If the containing block of a relatively positioned element does not
    403     // specify a height, a percentage top or bottom offset should be resolved as
    404     // auto. An exception to this is if the containing block has the WinIE quirk
    405     // where <html> and <body> assume the size of the viewport. In this case,
    406     // calculate the percent offset based on this height.
    407     // See <https://bugs.webkit.org/show_bug.cgi?id=26396>.
    408     if (!style()->top().isAuto()
    409         && (!containingBlock->style()->height().isAuto()
    410             || !style()->top().isPercent()
    411             || containingBlock->stretchesToViewport()))
    412         return style()->top().calcValue(containingBlock->availableHeight());
    413 
    414     if (!style()->bottom().isAuto()
    415         && (!containingBlock->style()->height().isAuto()
    416             || !style()->bottom().isPercent()
    417             || containingBlock->stretchesToViewport()))
    418         return -style()->bottom().calcValue(containingBlock->availableHeight());
    419 
    420     return 0;
    421 }
    422 
    423 int RenderBoxModelObject::offsetLeft() const
    424 {
    425     // If the element is the HTML body element or does not have an associated box
    426     // return 0 and stop this algorithm.
    427     if (isBody())
    428         return 0;
    429 
    430     RenderBoxModelObject* offsetPar = offsetParent();
    431     int xPos = (isBox() ? toRenderBox(this)->x() : 0);
    432 
    433     // If the offsetParent of the element is null, or is the HTML body element,
    434     // return the distance between the canvas origin and the left border edge
    435     // of the element and stop this algorithm.
    436     if (offsetPar) {
    437         if (offsetPar->isBox() && !offsetPar->isBody())
    438             xPos -= toRenderBox(offsetPar)->borderLeft();
    439         if (!isPositioned()) {
    440             if (isRelPositioned())
    441                 xPos += relativePositionOffsetX();
    442             RenderObject* curr = parent();
    443             while (curr && curr != offsetPar) {
    444                 // FIXME: What are we supposed to do inside SVG content?
    445                 if (curr->isBox() && !curr->isTableRow())
    446                     xPos += toRenderBox(curr)->x();
    447                 curr = curr->parent();
    448             }
    449             if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned())
    450                 xPos += toRenderBox(offsetPar)->x();
    451         }
    452     }
    453 
    454     return xPos;
    455 }
    456 
    457 int RenderBoxModelObject::offsetTop() const
    458 {
    459     // If the element is the HTML body element or does not have an associated box
    460     // return 0 and stop this algorithm.
    461     if (isBody())
    462         return 0;
    463 
    464     RenderBoxModelObject* offsetPar = offsetParent();
    465     int yPos = (isBox() ? toRenderBox(this)->y() : 0);
    466 
    467     // If the offsetParent of the element is null, or is the HTML body element,
    468     // return the distance between the canvas origin and the top border edge
    469     // of the element and stop this algorithm.
    470     if (offsetPar) {
    471         if (offsetPar->isBox() && !offsetPar->isBody())
    472             yPos -= toRenderBox(offsetPar)->borderTop();
    473         if (!isPositioned()) {
    474             if (isRelPositioned())
    475                 yPos += relativePositionOffsetY();
    476             RenderObject* curr = parent();
    477             while (curr && curr != offsetPar) {
    478                 // FIXME: What are we supposed to do inside SVG content?
    479                 if (curr->isBox() && !curr->isTableRow())
    480                     yPos += toRenderBox(curr)->y();
    481                 curr = curr->parent();
    482             }
    483             if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned())
    484                 yPos += toRenderBox(offsetPar)->y();
    485         }
    486     }
    487     return yPos;
    488 }
    489 
    490 int RenderBoxModelObject::paddingTop(bool) const
    491 {
    492     int w = 0;
    493     Length padding = style()->paddingTop();
    494     if (padding.isPercent())
    495         w = containingBlock()->availableLogicalWidth();
    496     return padding.calcMinValue(w);
    497 }
    498 
    499 int RenderBoxModelObject::paddingBottom(bool) const
    500 {
    501     int w = 0;
    502     Length padding = style()->paddingBottom();
    503     if (padding.isPercent())
    504         w = containingBlock()->availableLogicalWidth();
    505     return padding.calcMinValue(w);
    506 }
    507 
    508 int RenderBoxModelObject::paddingLeft(bool) const
    509 {
    510     int w = 0;
    511     Length padding = style()->paddingLeft();
    512     if (padding.isPercent())
    513         w = containingBlock()->availableLogicalWidth();
    514     return padding.calcMinValue(w);
    515 }
    516 
    517 int RenderBoxModelObject::paddingRight(bool) const
    518 {
    519     int w = 0;
    520     Length padding = style()->paddingRight();
    521     if (padding.isPercent())
    522         w = containingBlock()->availableLogicalWidth();
    523     return padding.calcMinValue(w);
    524 }
    525 
    526 int RenderBoxModelObject::paddingBefore(bool) const
    527 {
    528     int w = 0;
    529     Length padding = style()->paddingBefore();
    530     if (padding.isPercent())
    531         w = containingBlock()->availableLogicalWidth();
    532     return padding.calcMinValue(w);
    533 }
    534 
    535 int RenderBoxModelObject::paddingAfter(bool) const
    536 {
    537     int w = 0;
    538     Length padding = style()->paddingAfter();
    539     if (padding.isPercent())
    540         w = containingBlock()->availableLogicalWidth();
    541     return padding.calcMinValue(w);
    542 }
    543 
    544 int RenderBoxModelObject::paddingStart(bool) const
    545 {
    546     int w = 0;
    547     Length padding = style()->paddingStart();
    548     if (padding.isPercent())
    549         w = containingBlock()->availableLogicalWidth();
    550     return padding.calcMinValue(w);
    551 }
    552 
    553 int RenderBoxModelObject::paddingEnd(bool) const
    554 {
    555     int w = 0;
    556     Length padding = style()->paddingEnd();
    557     if (padding.isPercent())
    558         w = containingBlock()->availableLogicalWidth();
    559     return padding.calcMinValue(w);
    560 }
    561 
    562 RoundedIntRect RenderBoxModelObject::getBackgroundRoundedRect(const IntRect& borderRect, InlineFlowBox* box, int inlineBoxWidth, int inlineBoxHeight,
    563     bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
    564 {
    565     RoundedIntRect border = style()->getRoundedBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge);
    566     if (box && (box->nextLineBox() || box->prevLineBox())) {
    567         RoundedIntRect segmentBorder = style()->getRoundedBorderFor(IntRect(0, 0, inlineBoxWidth, inlineBoxHeight), includeLogicalLeftEdge, includeLogicalRightEdge);
    568         border.setRadii(segmentBorder.radii());
    569     }
    570 
    571     return border;
    572 }
    573 
    574 void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& color, const FillLayer* bgLayer, int tx, int ty, int w, int h,
    575     InlineFlowBox* box, int inlineBoxWidth, int inlineBoxHeight, CompositeOperator op, RenderObject* backgroundObject)
    576 {
    577     GraphicsContext* context = paintInfo.context;
    578     if (context->paintingDisabled())
    579         return;
    580 
    581     bool includeLeftEdge = box ? box->includeLogicalLeftEdge() : true;
    582     bool includeRightEdge = box ? box->includeLogicalRightEdge() : true;
    583 
    584     bool hasRoundedBorder = style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge);
    585     bool clippedWithLocalScrolling = hasOverflowClip() && bgLayer->attachment() == LocalBackgroundAttachment;
    586     bool isBorderFill = bgLayer->clip() == BorderFillBox;
    587     bool isRoot = this->isRoot();
    588 
    589     Color bgColor = color;
    590     StyleImage* bgImage = bgLayer->image();
    591     bool shouldPaintBackgroundImage = bgImage && bgImage->canRender(style()->effectiveZoom());
    592 
    593     // When this style flag is set, change existing background colors and images to a solid white background.
    594     // If there's no bg color or image, leave it untouched to avoid affecting transparency.
    595     // We don't try to avoid loading the background images, because this style flag is only set
    596     // when printing, and at that point we've already loaded the background images anyway. (To avoid
    597     // loading the background images we'd have to do this check when applying styles rather than
    598     // while rendering.)
    599     if (style()->forceBackgroundsToWhite()) {
    600         // Note that we can't reuse this variable below because the bgColor might be changed
    601         bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && bgColor.alpha() > 0;
    602         if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) {
    603             bgColor = Color::white;
    604             shouldPaintBackgroundImage = false;
    605         }
    606     }
    607 
    608     bool colorVisible = bgColor.isValid() && bgColor.alpha() > 0;
    609 
    610     // Fast path for drawing simple color backgrounds.
    611     if (!isRoot && !clippedWithLocalScrolling && !shouldPaintBackgroundImage && isBorderFill) {
    612         if (!colorVisible)
    613             return;
    614 
    615         IntRect borderRect(tx, ty, w, h);
    616         if (borderRect.isEmpty())
    617             return;
    618 
    619         if (hasRoundedBorder) {
    620             RoundedIntRect border = getBackgroundRoundedRect(borderRect, box, inlineBoxWidth, inlineBoxHeight, includeLeftEdge, includeRightEdge);
    621             context->fillRoundedRect(border, bgColor, style()->colorSpace());
    622         } else
    623             context->fillRect(borderRect, bgColor, style()->colorSpace());
    624 
    625         return;
    626     }
    627 
    628     bool clippedToBorderRadius = false;
    629     if (hasRoundedBorder) {
    630         IntRect borderRect(tx, ty, w, h);
    631 
    632         if (borderRect.isEmpty())
    633             return;
    634 
    635         context->save();
    636 
    637         RoundedIntRect border = getBackgroundRoundedRect(borderRect, box, inlineBoxWidth, inlineBoxHeight, includeLeftEdge, includeRightEdge);
    638         context->addRoundedRectClip(border);
    639         clippedToBorderRadius = true;
    640     }
    641 
    642     int bLeft = includeLeftEdge ? borderLeft() : 0;
    643     int bRight = includeRightEdge ? borderRight() : 0;
    644     int pLeft = includeLeftEdge ? paddingLeft() : 0;
    645     int pRight = includeRightEdge ? paddingRight() : 0;
    646 
    647     if (clippedWithLocalScrolling) {
    648         // Clip to the overflow area.
    649         context->save();
    650         context->clip(toRenderBox(this)->overflowClipRect(tx, ty));
    651 
    652         // Now adjust our tx, ty, w, h to reflect a scrolled content box with borders at the ends.
    653         IntSize offset = layer()->scrolledContentOffset();
    654         tx -= offset.width();
    655         ty -= offset.height();
    656         w = bLeft + layer()->scrollWidth() + bRight;
    657         h = borderTop() + layer()->scrollHeight() + borderBottom();
    658     }
    659 
    660     if (bgLayer->clip() == PaddingFillBox || bgLayer->clip() == ContentFillBox) {
    661         // Clip to the padding or content boxes as necessary.
    662         bool includePadding = bgLayer->clip() == ContentFillBox;
    663         int x = tx + bLeft + (includePadding ? pLeft : 0);
    664         int y = ty + borderTop() + (includePadding ? paddingTop() : 0);
    665         int width = w - bLeft - bRight - (includePadding ? pLeft + pRight : 0);
    666         int height = h - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : 0);
    667         context->save();
    668         context->clip(IntRect(x, y, width, height));
    669     } else if (bgLayer->clip() == TextFillBox) {
    670         // We have to draw our text into a mask that can then be used to clip background drawing.
    671         // First figure out how big the mask has to be.  It should be no bigger than what we need
    672         // to actually render, so we should intersect the dirty rect with the border box of the background.
    673         IntRect maskRect(tx, ty, w, h);
    674         maskRect.intersect(paintInfo.rect);
    675 
    676         // Now create the mask.
    677         OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size());
    678         if (!maskImage)
    679             return;
    680 
    681         GraphicsContext* maskImageContext = maskImage->context();
    682         maskImageContext->translate(-maskRect.x(), -maskRect.y());
    683 
    684         // Now add the text to the clip.  We do this by painting using a special paint phase that signals to
    685         // InlineTextBoxes that they should just add their contents to the clip.
    686         PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, true, 0, 0);
    687         if (box) {
    688             RootInlineBox* root = box->root();
    689             box->paint(info, tx - box->x(), ty - box->y(), root->lineTop(), root->lineBottom());
    690         } else {
    691             int x = isBox() ? toRenderBox(this)->x() : 0;
    692             int y = isBox() ? toRenderBox(this)->y() : 0;
    693             paint(info, tx - x, ty - y);
    694         }
    695 
    696         // The mask has been created.  Now we just need to clip to it.
    697         context->save();
    698         context->clipToImageBuffer(maskImage.get(), maskRect);
    699     }
    700 
    701     // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with
    702     // no background in the child document should show the parent's background.
    703     bool isOpaqueRoot = false;
    704     if (isRoot) {
    705         isOpaqueRoot = true;
    706         if (!bgLayer->next() && !(bgColor.isValid() && bgColor.alpha() == 255) && view()->frameView()) {
    707             Element* ownerElement = document()->ownerElement();
    708             if (ownerElement) {
    709                 if (!ownerElement->hasTagName(frameTag)) {
    710                     // Locate the <body> element using the DOM.  This is easier than trying
    711                     // to crawl around a render tree with potential :before/:after content and
    712                     // anonymous blocks created by inline <body> tags etc.  We can locate the <body>
    713                     // render object very easily via the DOM.
    714                     HTMLElement* body = document()->body();
    715                     if (body) {
    716                         // Can't scroll a frameset document anyway.
    717                         isOpaqueRoot = body->hasLocalName(framesetTag);
    718                     }
    719 #if ENABLE(SVG)
    720                     else {
    721                         // SVG documents and XML documents with SVG root nodes are transparent.
    722                         isOpaqueRoot = !document()->hasSVGRootNode();
    723                     }
    724 #endif
    725                 }
    726             } else
    727                 isOpaqueRoot = !view()->frameView()->isTransparent();
    728         }
    729         view()->frameView()->setContentIsOpaque(isOpaqueRoot);
    730     }
    731 
    732     // Paint the color first underneath all images.
    733     if (!bgLayer->next()) {
    734         IntRect rect(tx, ty, w, h);
    735         rect.intersect(paintInfo.rect);
    736         // If we have an alpha and we are painting the root element, go ahead and blend with the base background color.
    737         if (isOpaqueRoot) {
    738             Color baseColor = view()->frameView()->baseBackgroundColor();
    739             if (baseColor.alpha() > 0) {
    740                 CompositeOperator previousOperator = context->compositeOperation();
    741                 context->setCompositeOperation(CompositeCopy);
    742                 context->fillRect(rect, baseColor, style()->colorSpace());
    743                 context->setCompositeOperation(previousOperator);
    744             } else
    745                 context->clearRect(rect);
    746         }
    747 
    748         if (bgColor.isValid() && bgColor.alpha() > 0)
    749             context->fillRect(rect, bgColor, style()->colorSpace());
    750     }
    751 
    752     // no progressive loading of the background image
    753     if (shouldPaintBackgroundImage) {
    754         IntRect destRect;
    755         IntPoint phase;
    756         IntSize tileSize;
    757 
    758         calculateBackgroundImageGeometry(bgLayer, tx, ty, w, h, destRect, phase, tileSize);
    759         IntPoint destOrigin = destRect.location();
    760         destRect.intersect(paintInfo.rect);
    761         if (!destRect.isEmpty()) {
    762             phase += destRect.location() - destOrigin;
    763             CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op;
    764             RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this;
    765             RefPtr<Image> image = bgImage->image(clientForBackgroundImage, tileSize);
    766             bool useLowQualityScaling = shouldPaintAtLowQuality(context, image.get(), bgLayer, tileSize);
    767             context->drawTiledImage(image.get(), style()->colorSpace(), destRect, phase, tileSize, compositeOp, useLowQualityScaling);
    768         }
    769     }
    770 
    771     if (!isBorderFill) // Undo the background clip
    772         context->restore();
    773 
    774     if (clippedToBorderRadius) // Undo the border radius clip
    775         context->restore();
    776 
    777     if (clippedWithLocalScrolling) // Undo the clip for local background attachments.
    778         context->restore();
    779 }
    780 
    781 IntSize RenderBoxModelObject::calculateFillTileSize(const FillLayer* fillLayer, IntSize positioningAreaSize) const
    782 {
    783     StyleImage* image = fillLayer->image();
    784     image->setImageContainerSize(positioningAreaSize); // Use the box established by background-origin.
    785 
    786     EFillSizeType type = fillLayer->size().type;
    787 
    788     switch (type) {
    789         case SizeLength: {
    790             int w = positioningAreaSize.width();
    791             int h = positioningAreaSize.height();
    792 
    793             Length layerWidth = fillLayer->size().size.width();
    794             Length layerHeight = fillLayer->size().size.height();
    795 
    796             if (layerWidth.isFixed())
    797                 w = layerWidth.value();
    798             else if (layerWidth.isPercent())
    799                 w = layerWidth.calcValue(positioningAreaSize.width());
    800 
    801             if (layerHeight.isFixed())
    802                 h = layerHeight.value();
    803             else if (layerHeight.isPercent())
    804                 h = layerHeight.calcValue(positioningAreaSize.height());
    805 
    806             // If one of the values is auto we have to use the appropriate
    807             // scale to maintain our aspect ratio.
    808             if (layerWidth.isAuto() && !layerHeight.isAuto()) {
    809                 IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom());
    810                 if (imageIntrinsicSize.height())
    811                     w = imageIntrinsicSize.width() * h / imageIntrinsicSize.height();
    812             } else if (!layerWidth.isAuto() && layerHeight.isAuto()) {
    813                 IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom());
    814                 if (imageIntrinsicSize.width())
    815                     h = imageIntrinsicSize.height() * w / imageIntrinsicSize.width();
    816             } else if (layerWidth.isAuto() && layerHeight.isAuto()) {
    817                 // If both width and height are auto, use the image's intrinsic size.
    818                 IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom());
    819                 w = imageIntrinsicSize.width();
    820                 h = imageIntrinsicSize.height();
    821             }
    822 
    823             return IntSize(max(1, w), max(1, h));
    824         }
    825         case Contain:
    826         case Cover: {
    827             IntSize imageIntrinsicSize = image->imageSize(this, 1);
    828             float horizontalScaleFactor = imageIntrinsicSize.width()
    829                 ? static_cast<float>(positioningAreaSize.width()) / imageIntrinsicSize.width() : 1;
    830             float verticalScaleFactor = imageIntrinsicSize.height()
    831                 ? static_cast<float>(positioningAreaSize.height()) / imageIntrinsicSize.height() : 1;
    832             float scaleFactor = type == Contain ? min(horizontalScaleFactor, verticalScaleFactor) : max(horizontalScaleFactor, verticalScaleFactor);
    833             return IntSize(max<int>(1, imageIntrinsicSize.width() * scaleFactor), max<int>(1, imageIntrinsicSize.height() * scaleFactor));
    834         }
    835         case SizeNone:
    836             break;
    837     }
    838 
    839     return image->imageSize(this, style()->effectiveZoom());
    840 }
    841 
    842 void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* fillLayer, int tx, int ty, int w, int h,
    843                                                             IntRect& destRect, IntPoint& phase, IntSize& tileSize)
    844 {
    845     int left = 0;
    846     int top = 0;
    847     IntSize positioningAreaSize;
    848 
    849     // Determine the background positioning area and set destRect to the background painting area.
    850     // destRect will be adjusted later if the background is non-repeating.
    851     bool fixedAttachment = fillLayer->attachment() == FixedBackgroundAttachment;
    852 
    853 #if ENABLE(FAST_MOBILE_SCROLLING)
    854     if (view()->frameView() && view()->frameView()->canBlitOnScroll()) {
    855         // As a side effect of an optimization to blit on scroll, we do not honor the CSS
    856         // property "background-attachment: fixed" because it may result in rendering
    857         // artifacts. Note, these artifacts only appear if we are blitting on scroll of
    858         // a page that has fixed background images.
    859         fixedAttachment = false;
    860     }
    861 #endif
    862 
    863     if (!fixedAttachment) {
    864         destRect = IntRect(tx, ty, w, h);
    865 
    866         int right = 0;
    867         int bottom = 0;
    868         // Scroll and Local.
    869         if (fillLayer->origin() != BorderFillBox) {
    870             left = borderLeft();
    871             right = borderRight();
    872             top = borderTop();
    873             bottom = borderBottom();
    874             if (fillLayer->origin() == ContentFillBox) {
    875                 left += paddingLeft();
    876                 right += paddingRight();
    877                 top += paddingTop();
    878                 bottom += paddingBottom();
    879             }
    880         }
    881 
    882         // The background of the box generated by the root element covers the entire canvas including
    883         // its margins. Since those were added in already, we have to factor them out when computing
    884         // the background positioning area.
    885         if (isRoot()) {
    886             positioningAreaSize = IntSize(toRenderBox(this)->width() - left - right, toRenderBox(this)->height() - top - bottom);
    887             left += marginLeft();
    888             top += marginTop();
    889         } else
    890             positioningAreaSize = IntSize(w - left - right, h - top - bottom);
    891     } else {
    892         destRect = viewRect();
    893         positioningAreaSize = destRect.size();
    894     }
    895 
    896     tileSize = calculateFillTileSize(fillLayer, positioningAreaSize);
    897 
    898     EFillRepeat backgroundRepeatX = fillLayer->repeatX();
    899     EFillRepeat backgroundRepeatY = fillLayer->repeatY();
    900 
    901     int xPosition = fillLayer->xPosition().calcMinValue(positioningAreaSize.width() - tileSize.width(), true);
    902     if (backgroundRepeatX == RepeatFill)
    903         phase.setX(tileSize.width() ? tileSize.width() - (xPosition + left) % tileSize.width() : 0);
    904     else {
    905         destRect.move(max(xPosition + left, 0), 0);
    906         phase.setX(-min(xPosition + left, 0));
    907         destRect.setWidth(tileSize.width() + min(xPosition + left, 0));
    908     }
    909 
    910     int yPosition = fillLayer->yPosition().calcMinValue(positioningAreaSize.height() - tileSize.height(), true);
    911     if (backgroundRepeatY == RepeatFill)
    912         phase.setY(tileSize.height() ? tileSize.height() - (yPosition + top) % tileSize.height() : 0);
    913     else {
    914         destRect.move(0, max(yPosition + top, 0));
    915         phase.setY(-min(yPosition + top, 0));
    916         destRect.setHeight(tileSize.height() + min(yPosition + top, 0));
    917     }
    918 
    919     if (fixedAttachment)
    920         phase.move(max(tx - destRect.x(), 0), max(ty - destRect.y(), 0));
    921 
    922     destRect.intersect(IntRect(tx, ty, w, h));
    923 }
    924 
    925 bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style,
    926                                                const NinePieceImage& ninePieceImage, CompositeOperator op)
    927 {
    928     StyleImage* styleImage = ninePieceImage.image();
    929     if (!styleImage)
    930         return false;
    931 
    932     if (!styleImage->isLoaded())
    933         return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either.
    934 
    935     if (!styleImage->canRender(style->effectiveZoom()))
    936         return false;
    937 
    938     // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function
    939     // doesn't have any understanding of the zoom that is in effect on the tile.
    940     styleImage->setImageContainerSize(IntSize(w, h));
    941     IntSize imageSize = styleImage->imageSize(this, 1.0f);
    942     int imageWidth = imageSize.width();
    943     int imageHeight = imageSize.height();
    944 
    945     int topSlice = min(imageHeight, ninePieceImage.slices().top().calcValue(imageHeight));
    946     int bottomSlice = min(imageHeight, ninePieceImage.slices().bottom().calcValue(imageHeight));
    947     int leftSlice = min(imageWidth, ninePieceImage.slices().left().calcValue(imageWidth));
    948     int rightSlice = min(imageWidth, ninePieceImage.slices().right().calcValue(imageWidth));
    949 
    950     ENinePieceImageRule hRule = ninePieceImage.horizontalRule();
    951     ENinePieceImageRule vRule = ninePieceImage.verticalRule();
    952 
    953     bool fitToBorder = style->borderImage() == ninePieceImage;
    954 
    955     int leftWidth = fitToBorder ? style->borderLeftWidth() : leftSlice;
    956     int topWidth = fitToBorder ? style->borderTopWidth() : topSlice;
    957     int rightWidth = fitToBorder ? style->borderRightWidth() : rightSlice;
    958     int bottomWidth = fitToBorder ? style->borderBottomWidth() : bottomSlice;
    959 
    960     bool drawLeft = leftSlice > 0 && leftWidth > 0;
    961     bool drawTop = topSlice > 0 && topWidth > 0;
    962     bool drawRight = rightSlice > 0 && rightWidth > 0;
    963     bool drawBottom = bottomSlice > 0 && bottomWidth > 0;
    964     bool drawMiddle = (imageWidth - leftSlice - rightSlice) > 0 && (w - leftWidth - rightWidth) > 0 &&
    965                       (imageHeight - topSlice - bottomSlice) > 0 && (h - topWidth - bottomWidth) > 0;
    966 
    967     RefPtr<Image> image = styleImage->image(this, imageSize);
    968     ColorSpace colorSpace = style->colorSpace();
    969 
    970     if (drawLeft) {
    971         // Paint the top and bottom left corners.
    972 
    973         // The top left corner rect is (tx, ty, leftWidth, topWidth)
    974         // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice)
    975         if (drawTop)
    976             graphicsContext->drawImage(image.get(), colorSpace, IntRect(tx, ty, leftWidth, topWidth),
    977                                        IntRect(0, 0, leftSlice, topSlice), op);
    978 
    979         // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth)
    980         // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice)
    981         if (drawBottom)
    982             graphicsContext->drawImage(image.get(), colorSpace, IntRect(tx, ty + h - bottomWidth, leftWidth, bottomWidth),
    983                                        IntRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op);
    984 
    985         // Paint the left edge.
    986         // Have to scale and tile into the border rect.
    987         graphicsContext->drawTiledImage(image.get(), colorSpace, IntRect(tx, ty + topWidth, leftWidth,
    988                                         h - topWidth - bottomWidth),
    989                                         IntRect(0, topSlice, leftSlice, imageHeight - topSlice - bottomSlice),
    990                                         Image::StretchTile, (Image::TileRule)vRule, op);
    991     }
    992 
    993     if (drawRight) {
    994         // Paint the top and bottom right corners
    995         // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth)
    996         // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice)
    997         if (drawTop)
    998             graphicsContext->drawImage(image.get(), colorSpace, IntRect(tx + w - rightWidth, ty, rightWidth, topWidth),
    999                                        IntRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op);
   1000 
   1001         // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth)
   1002         // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice)
   1003         if (drawBottom)
   1004             graphicsContext->drawImage(image.get(), colorSpace, IntRect(tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth),
   1005                                        IntRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op);
   1006 
   1007         // Paint the right edge.
   1008         graphicsContext->drawTiledImage(image.get(), colorSpace, IntRect(tx + w - rightWidth, ty + topWidth, rightWidth,
   1009                                         h - topWidth - bottomWidth),
   1010                                         IntRect(imageWidth - rightSlice, topSlice, rightSlice, imageHeight - topSlice - bottomSlice),
   1011                                         Image::StretchTile, (Image::TileRule)vRule, op);
   1012     }
   1013 
   1014     // Paint the top edge.
   1015     if (drawTop)
   1016         graphicsContext->drawTiledImage(image.get(), colorSpace, IntRect(tx + leftWidth, ty, w - leftWidth - rightWidth, topWidth),
   1017                                         IntRect(leftSlice, 0, imageWidth - rightSlice - leftSlice, topSlice),
   1018                                         (Image::TileRule)hRule, Image::StretchTile, op);
   1019 
   1020     // Paint the bottom edge.
   1021     if (drawBottom)
   1022         graphicsContext->drawTiledImage(image.get(), colorSpace, IntRect(tx + leftWidth, ty + h - bottomWidth,
   1023                                         w - leftWidth - rightWidth, bottomWidth),
   1024                                         IntRect(leftSlice, imageHeight - bottomSlice, imageWidth - rightSlice - leftSlice, bottomSlice),
   1025                                         (Image::TileRule)hRule, Image::StretchTile, op);
   1026 
   1027     // Paint the middle.
   1028     if (drawMiddle)
   1029         graphicsContext->drawTiledImage(image.get(), colorSpace, IntRect(tx + leftWidth, ty + topWidth, w - leftWidth - rightWidth,
   1030                                         h - topWidth - bottomWidth),
   1031                                         IntRect(leftSlice, topSlice, imageWidth - rightSlice - leftSlice, imageHeight - topSlice - bottomSlice),
   1032                                         (Image::TileRule)hRule, (Image::TileRule)vRule, op);
   1033 
   1034     return true;
   1035 }
   1036 
   1037 #if HAVE(PATH_BASED_BORDER_RADIUS_DRAWING)
   1038 static bool borderWillArcInnerEdge(const IntSize& firstRadius, const IntSize& secondRadius)
   1039 {
   1040     return !firstRadius.isZero() || !secondRadius.isZero();
   1041 }
   1042 
   1043 enum BorderEdgeFlag {
   1044     TopBorderEdge = 1 << BSTop,
   1045     RightBorderEdge = 1 << BSRight,
   1046     BottomBorderEdge = 1 << BSBottom,
   1047     LeftBorderEdge = 1 << BSLeft,
   1048     AllBorderEdges = TopBorderEdge | BottomBorderEdge | LeftBorderEdge | RightBorderEdge
   1049 };
   1050 
   1051 static inline BorderEdgeFlag edgeFlagForSide(BoxSide side)
   1052 {
   1053     return static_cast<BorderEdgeFlag>(1 << side);
   1054 }
   1055 
   1056 static inline bool includesEdge(BorderEdgeFlags flags, BoxSide side)
   1057 {
   1058     return flags & edgeFlagForSide(side);
   1059 }
   1060 
   1061 class BorderEdge {
   1062 public:
   1063     BorderEdge(int edgeWidth, const Color& edgeColor, EBorderStyle edgeStyle, bool edgeIsTransparent, bool edgeIsPresent)
   1064         : width(edgeWidth)
   1065         , color(edgeColor)
   1066         , style(edgeStyle)
   1067         , isTransparent(edgeIsTransparent)
   1068         , isPresent(edgeIsPresent)
   1069     {
   1070         if (style == DOUBLE && edgeWidth < 3)
   1071             style = SOLID;
   1072     }
   1073 
   1074     bool hasVisibleColorAndStyle() const { return style > BHIDDEN && !isTransparent; }
   1075     bool shouldRender() const { return isPresent && hasVisibleColorAndStyle(); }
   1076     bool presentButInvisible() const { return usedWidth() && !hasVisibleColorAndStyle(); }
   1077 
   1078     int usedWidth() const { return isPresent ? width : 0; }
   1079 
   1080     void getDoubleBorderStripeWidths(int& outerWidth, int& innerWidth) const
   1081     {
   1082         int fullWidth = usedWidth();
   1083         outerWidth = fullWidth / 3;
   1084         innerWidth = fullWidth * 2 / 3;
   1085 
   1086         // We need certain integer rounding results
   1087         if (fullWidth % 3 == 2)
   1088             outerWidth += 1;
   1089 
   1090         if (fullWidth % 3 == 1)
   1091             innerWidth += 1;
   1092     }
   1093 
   1094     int width;
   1095     Color color;
   1096     EBorderStyle style;
   1097     bool isTransparent;
   1098     bool isPresent;
   1099 };
   1100 
   1101 inline bool edgesShareColor(const BorderEdge& firstEdge, const BorderEdge& secondEdge)
   1102 {
   1103     return firstEdge.color == secondEdge.color;
   1104 }
   1105 
   1106 inline bool styleRequiresClipPolygon(EBorderStyle style)
   1107 {
   1108     return style == DOTTED || style == DASHED; // These are drawn with a stroke, so we have to clip to get corner miters.
   1109 }
   1110 
   1111 static bool borderStyleFillsBorderArea(EBorderStyle style)
   1112 {
   1113     return !(style == DOTTED || style == DASHED || style == DOUBLE);
   1114 }
   1115 
   1116 static bool borderStyleHasInnerDetail(EBorderStyle style)
   1117 {
   1118     return style == GROOVE || style == RIDGE || style == DOUBLE;
   1119 }
   1120 
   1121 static bool borderStyleIsDottedOrDashed(EBorderStyle style)
   1122 {
   1123     return style == DOTTED || style == DASHED;
   1124 }
   1125 
   1126 // OUTSET darkens the bottom and right (and maybe lightens the top and left)
   1127 // INSET darkens the top and left (and maybe lightens the bottom and right)
   1128 static inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, BoxSide side, BoxSide adjacentSide)
   1129 {
   1130     // These styles match at the top/left and bottom/right.
   1131     if (style == INSET || style == GROOVE || style == RIDGE || style == OUTSET) {
   1132         const BorderEdgeFlags topRightFlags = edgeFlagForSide(BSTop) | edgeFlagForSide(BSRight);
   1133         const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edgeFlagForSide(BSLeft);
   1134 
   1135         BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacentSide);
   1136         return flags == topRightFlags || flags == bottomLeftFlags;
   1137     }
   1138     return false;
   1139 }
   1140 
   1141 static inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[])
   1142 {
   1143     if (edges[side].shouldRender() != edges[adjacentSide].shouldRender())
   1144         return false;
   1145 
   1146     if (!edgesShareColor(edges[side], edges[adjacentSide]))
   1147         return false;
   1148 
   1149     return !borderStyleHasUnmatchedColorsAtCorner(edges[side].style, side, adjacentSide);
   1150 }
   1151 
   1152 // This assumes that we draw in order: top, bottom, left, right.
   1153 static inline bool willBeOverdrawn(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[])
   1154 {
   1155     switch (side) {
   1156     case BSTop:
   1157     case BSBottom:
   1158         if (edges[adjacentSide].presentButInvisible())
   1159             return false;
   1160 
   1161         if (!edgesShareColor(edges[side], edges[adjacentSide]) && edges[adjacentSide].color.hasAlpha())
   1162             return false;
   1163 
   1164         if (!borderStyleFillsBorderArea(edges[adjacentSide].style))
   1165             return false;
   1166 
   1167         return true;
   1168 
   1169     case BSLeft:
   1170     case BSRight:
   1171         // These draw last, so are never overdrawn.
   1172         return false;
   1173     }
   1174     return false;
   1175 }
   1176 
   1177 static inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorderStyle style, EBorderStyle adjacentStyle)
   1178 {
   1179     if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE || adjacentStyle == RIDGE)
   1180         return true;
   1181 
   1182     if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjacentStyle))
   1183         return true;
   1184 
   1185     if (style != adjacentStyle)
   1186         return true;
   1187 
   1188     return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide);
   1189 }
   1190 
   1191 static bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[], bool allowOverdraw)
   1192 {
   1193     if ((edges[side].isTransparent && edges[adjacentSide].isTransparent) || !edges[adjacentSide].isPresent)
   1194         return false;
   1195 
   1196     if (allowOverdraw && willBeOverdrawn(side, adjacentSide, edges))
   1197         return false;
   1198 
   1199     if (!edgesShareColor(edges[side], edges[adjacentSide]))
   1200         return true;
   1201 
   1202     if (borderStylesRequireMitre(side, adjacentSide, edges[side].style, edges[adjacentSide].style))
   1203         return true;
   1204 
   1205     return false;
   1206 }
   1207 
   1208 void RenderBoxModelObject::paintOneBorderSide(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedIntRect& outerBorder, const RoundedIntRect& innerBorder,
   1209     const IntRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adjacentSide2, const BorderEdge edges[], const Path* path,
   1210     bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor)
   1211 {
   1212     const BorderEdge& edgeToRender = edges[side];
   1213     const BorderEdge& adjacentEdge1 = edges[adjacentSide1];
   1214     const BorderEdge& adjacentEdge2 = edges[adjacentSide2];
   1215 
   1216     bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, edges, !antialias);
   1217     bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, edges, !antialias);
   1218 
   1219     bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1, edges);
   1220     bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2, edges);
   1221 
   1222     const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.color;
   1223 
   1224     if (path) {
   1225         graphicsContext->save();
   1226         clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, adjacentSide1StylesMatch, adjacentSide2StylesMatch);
   1227         float thickness = max(max(edgeToRender.width, adjacentEdge1.width), adjacentEdge2.width);
   1228         drawBoxSideFromPath(graphicsContext, outerBorder.rect(), *path, edges, edgeToRender.width, thickness, side, style, colorToPaint, edgeToRender.style, includeLogicalLeftEdge, includeLogicalRightEdge);
   1229         graphicsContext->restore();
   1230     } else {
   1231         bool didClip = false;
   1232 
   1233         if (styleRequiresClipPolygon(edgeToRender.style) && (mitreAdjacentSide1 || mitreAdjacentSide2)) {
   1234             graphicsContext->save();
   1235             clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, !mitreAdjacentSide1, !mitreAdjacentSide2);
   1236             didClip = true;
   1237             // Since we clipped, no need to draw with a mitre.
   1238             mitreAdjacentSide1 = false;
   1239             mitreAdjacentSide2 = false;
   1240         }
   1241 
   1242         drawLineForBoxSide(graphicsContext, sideRect.x(), sideRect.y(), sideRect.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.style,
   1243                 mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? adjacentEdge2.width : 0, antialias);
   1244 
   1245         if (didClip)
   1246             graphicsContext->restore();
   1247     }
   1248 }
   1249 
   1250 void RenderBoxModelObject::paintBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedIntRect& outerBorder, const RoundedIntRect& innerBorder,
   1251                                             const BorderEdge edges[], BorderEdgeFlags edgeSet, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor)
   1252 {
   1253     bool renderRadii = outerBorder.isRounded();
   1254 
   1255     Path roundedPath;
   1256     if (renderRadii)
   1257         roundedPath.addRoundedRect(outerBorder);
   1258 
   1259     if (edges[BSTop].shouldRender() && includesEdge(edgeSet, BSTop)) {
   1260         IntRect sideRect = outerBorder.rect();
   1261         sideRect.setHeight(edges[BSTop].width);
   1262 
   1263         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSTop].style) || borderWillArcInnerEdge(innerBorder.radii().topLeft(), innerBorder.radii().topRight()));
   1264         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSTop, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
   1265     }
   1266 
   1267     if (edges[BSBottom].shouldRender() && includesEdge(edgeSet, BSBottom)) {
   1268         IntRect sideRect = outerBorder.rect();
   1269         sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].width);
   1270 
   1271         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSBottom].style) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().bottomRight()));
   1272         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSBottom, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
   1273     }
   1274 
   1275     if (edges[BSLeft].shouldRender() && includesEdge(edgeSet, BSLeft)) {
   1276         IntRect sideRect = outerBorder.rect();
   1277         sideRect.setWidth(edges[BSLeft].width);
   1278 
   1279         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSLeft].style) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().topLeft()));
   1280         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSLeft, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
   1281     }
   1282 
   1283     if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) {
   1284         IntRect sideRect = outerBorder.rect();
   1285         sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width);
   1286 
   1287         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSRight].style) || borderWillArcInnerEdge(innerBorder.radii().bottomRight(), innerBorder.radii().topRight()));
   1288         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSRight, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
   1289     }
   1290 }
   1291 
   1292 void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedIntRect& outerBorder, const RoundedIntRect& innerBorder,
   1293                                                        const BorderEdge edges[], bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias)
   1294 {
   1295     BorderEdgeFlags edgesToDraw = AllBorderEdges;
   1296     while (edgesToDraw) {
   1297         // Find undrawn edges sharing a color.
   1298         Color commonColor;
   1299 
   1300         BorderEdgeFlags commonColorEdgeSet = 0;
   1301         for (int i = BSTop; i <= BSLeft; ++i) {
   1302             BoxSide currSide = static_cast<BoxSide>(i);
   1303             if (!includesEdge(edgesToDraw, currSide))
   1304                 continue;
   1305 
   1306             bool includeEdge;
   1307             if (!commonColorEdgeSet) {
   1308                 commonColor = edges[currSide].color;
   1309                 includeEdge = true;
   1310             } else
   1311                 includeEdge = edges[currSide].color == commonColor;
   1312 
   1313             if (includeEdge)
   1314                 commonColorEdgeSet |= edgeFlagForSide(currSide);
   1315         }
   1316 
   1317         bool useTransparencyLayer = commonColor.hasAlpha();
   1318         if (useTransparencyLayer) {
   1319             graphicsContext->beginTransparencyLayer(static_cast<float>(commonColor.alpha()) / 255);
   1320             commonColor = Color(commonColor.red(), commonColor.green(), commonColor.blue());
   1321         }
   1322 
   1323         paintBorderSides(graphicsContext, style, outerBorder, innerBorder, edges, commonColorEdgeSet, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, &commonColor);
   1324 
   1325         if (useTransparencyLayer)
   1326             graphicsContext->endTransparencyLayer();
   1327 
   1328         edgesToDraw &= ~commonColorEdgeSet;
   1329     }
   1330 }
   1331 
   1332 void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h,
   1333                                        const RenderStyle* style, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
   1334 {
   1335     if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage()))
   1336         return;
   1337 
   1338     if (graphicsContext->paintingDisabled())
   1339         return;
   1340 
   1341     bool horizontal = style->isHorizontalWritingMode();
   1342 
   1343     BorderEdge edges[4] = {
   1344         // BSTop
   1345         BorderEdge(style->borderTopWidth(),
   1346                     style->visitedDependentColor(CSSPropertyBorderTopColor),
   1347                     style->borderTopStyle(),
   1348                     style->borderTopIsTransparent(),
   1349                     horizontal || includeLogicalLeftEdge),
   1350         // BSRight
   1351         BorderEdge(style->borderRightWidth(),
   1352                     style->visitedDependentColor(CSSPropertyBorderRightColor),
   1353                     style->borderRightStyle(),
   1354                     style->borderRightIsTransparent(),
   1355                     !horizontal || includeLogicalRightEdge),
   1356         // BSBottom
   1357         BorderEdge(style->borderBottomWidth(),
   1358                     style->visitedDependentColor(CSSPropertyBorderBottomColor),
   1359                     style->borderBottomStyle(),
   1360                     style->borderBottomIsTransparent(),
   1361                     horizontal || includeLogicalRightEdge),
   1362         // BSLeft
   1363         BorderEdge(style->borderLeftWidth(),
   1364                     style->visitedDependentColor(CSSPropertyBorderLeftColor),
   1365                     style->borderLeftStyle(),
   1366                     style->borderLeftIsTransparent(),
   1367                     !horizontal || includeLogicalLeftEdge)
   1368     };
   1369 
   1370     IntRect borderRect(tx, ty, w, h);
   1371     RoundedIntRect outerBorder = style->getRoundedBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge);
   1372     RoundedIntRect innerBorder = style->getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge);
   1373 
   1374     const AffineTransform& currentCTM = graphicsContext->getCTM();
   1375     // FIXME: this isn't quite correct. We may want to antialias when scaled by a non-integral value, or when the translation is non-integral.
   1376     bool antialias = !currentCTM.isIdentityOrTranslationOrFlipped();
   1377 
   1378     bool haveAlphaColor = false;
   1379     bool haveAllSolidEdges = true;
   1380     bool allEdgesVisible = true;
   1381     bool allEdgesShareColor = true;
   1382     int firstVisibleEdge = -1;
   1383 
   1384     for (int i = BSTop; i <= BSLeft; ++i) {
   1385         const BorderEdge& currEdge = edges[i];
   1386         if (currEdge.presentButInvisible()) {
   1387             allEdgesVisible = false;
   1388             continue;
   1389         }
   1390 
   1391         if (!currEdge.width)
   1392             continue;
   1393 
   1394         if (firstVisibleEdge == -1)
   1395             firstVisibleEdge = i;
   1396         else if (currEdge.color != edges[firstVisibleEdge].color)
   1397             allEdgesShareColor = false;
   1398 
   1399         if (currEdge.color.hasAlpha())
   1400             haveAlphaColor = true;
   1401 
   1402         if (currEdge.style != SOLID)
   1403             haveAllSolidEdges = false;
   1404     }
   1405 
   1406     // isRenderable() check avoids issue described in https://bugs.webkit.org/show_bug.cgi?id=38787
   1407     if (haveAllSolidEdges && allEdgesVisible && allEdgesShareColor && innerBorder.isRenderable()) {
   1408         // Fast path for drawing all solid edges.
   1409         if (outerBorder.isRounded() || haveAlphaColor) {
   1410             Path path;
   1411 
   1412             // FIXME: Path should take a RoundedIntRect directly.
   1413             if (outerBorder.isRounded())
   1414                 path.addRoundedRect(outerBorder);
   1415             else
   1416                 path.addRect(outerBorder.rect());
   1417 
   1418             if (innerBorder.isRounded())
   1419                 path.addRoundedRect(innerBorder);
   1420             else
   1421                 path.addRect(innerBorder.rect());
   1422 
   1423             graphicsContext->setFillRule(RULE_EVENODD);
   1424             graphicsContext->setFillColor(edges[firstVisibleEdge].color, style->colorSpace());
   1425             graphicsContext->fillPath(path);
   1426         } else
   1427             paintBorderSides(graphicsContext, style, outerBorder, innerBorder, edges, AllBorderEdges, includeLogicalLeftEdge, includeLogicalRightEdge, antialias);
   1428 
   1429         return;
   1430     }
   1431 
   1432     if (outerBorder.isRounded()) {
   1433         // Clip to the inner and outer radii rects.
   1434         graphicsContext->save();
   1435         graphicsContext->addRoundedRectClip(outerBorder);
   1436         graphicsContext->clipOutRoundedRect(innerBorder);
   1437     }
   1438 
   1439     if (haveAlphaColor)
   1440         paintTranslucentBorderSides(graphicsContext, style, outerBorder, innerBorder, edges, includeLogicalLeftEdge, includeLogicalRightEdge, antialias);
   1441     else
   1442         paintBorderSides(graphicsContext, style, outerBorder, innerBorder, edges, AllBorderEdges, includeLogicalLeftEdge, includeLogicalRightEdge, antialias);
   1443 
   1444     if (outerBorder.isRounded())
   1445         graphicsContext->restore();
   1446 }
   1447 
   1448 void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext* graphicsContext, const IntRect& borderRect, const Path& borderPath, const BorderEdge edges[],
   1449                                     float thickness, float drawThickness, BoxSide side, const RenderStyle* style,
   1450                                     Color color, EBorderStyle borderStyle, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
   1451 {
   1452     if (thickness <= 0)
   1453         return;
   1454 
   1455     if (borderStyle == DOUBLE && thickness < 3)
   1456         borderStyle = SOLID;
   1457 
   1458     switch (borderStyle) {
   1459     case BNONE:
   1460     case BHIDDEN:
   1461         return;
   1462     case DOTTED:
   1463     case DASHED: {
   1464         graphicsContext->setStrokeColor(color, style->colorSpace());
   1465 
   1466         // The stroke is doubled here because the provided path is the
   1467         // outside edge of the border so half the stroke is clipped off.
   1468         // The extra multiplier is so that the clipping mask can antialias
   1469         // the edges to prevent jaggies.
   1470         graphicsContext->setStrokeThickness(drawThickness * 2 * 1.1f);
   1471         graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke : DottedStroke);
   1472 
   1473         // If the number of dashes that fit in the path is odd and non-integral then we
   1474         // will have an awkwardly-sized dash at the end of the path. To try to avoid that
   1475         // here, we simply make the whitespace dashes ever so slightly bigger.
   1476         // FIXME: This could be even better if we tried to manipulate the dash offset
   1477         // and possibly the gapLength to get the corners dash-symmetrical.
   1478         float dashLength = thickness * ((borderStyle == DASHED) ? 3.0f : 1.0f);
   1479         float gapLength = dashLength;
   1480         float numberOfDashes = borderPath.length() / dashLength;
   1481         // Don't try to show dashes if we have less than 2 dashes + 2 gaps.
   1482         // FIXME: should do this test per side.
   1483         if (numberOfDashes >= 4) {
   1484             bool evenNumberOfFullDashes = !((int)numberOfDashes % 2);
   1485             bool integralNumberOfDashes = !(numberOfDashes - (int)numberOfDashes);
   1486             if (!evenNumberOfFullDashes && !integralNumberOfDashes) {
   1487                 float numberOfGaps = numberOfDashes / 2;
   1488                 gapLength += (dashLength  / numberOfGaps);
   1489             }
   1490 
   1491             DashArray lineDash;
   1492             lineDash.append(dashLength);
   1493             lineDash.append(gapLength);
   1494             graphicsContext->setLineDash(lineDash, dashLength);
   1495         }
   1496 
   1497         // FIXME: stroking the border path causes issues with tight corners:
   1498         // https://bugs.webkit.org/show_bug.cgi?id=58711
   1499         // Also, to get the best appearance we should stroke a path between the two borders.
   1500         graphicsContext->strokePath(borderPath);
   1501         return;
   1502     }
   1503     case DOUBLE: {
   1504         // Get the inner border rects for both the outer border line and the inner border line
   1505         int outerBorderTopWidth;
   1506         int innerBorderTopWidth;
   1507         edges[BSTop].getDoubleBorderStripeWidths(outerBorderTopWidth, innerBorderTopWidth);
   1508 
   1509         int outerBorderRightWidth;
   1510         int innerBorderRightWidth;
   1511         edges[BSRight].getDoubleBorderStripeWidths(outerBorderRightWidth, innerBorderRightWidth);
   1512 
   1513         int outerBorderBottomWidth;
   1514         int innerBorderBottomWidth;
   1515         edges[BSBottom].getDoubleBorderStripeWidths(outerBorderBottomWidth, innerBorderBottomWidth);
   1516 
   1517         int outerBorderLeftWidth;
   1518         int innerBorderLeftWidth;
   1519         edges[BSLeft].getDoubleBorderStripeWidths(outerBorderLeftWidth, innerBorderLeftWidth);
   1520 
   1521         // Draw inner border line
   1522         graphicsContext->save();
   1523 
   1524         RoundedIntRect innerClip = style->getRoundedInnerBorderFor(borderRect,
   1525             innerBorderTopWidth, innerBorderBottomWidth, innerBorderLeftWidth, innerBorderRightWidth,
   1526             includeLogicalLeftEdge, includeLogicalRightEdge);
   1527 
   1528         graphicsContext->addRoundedRectClip(innerClip);
   1529         drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, includeLogicalLeftEdge, includeLogicalRightEdge);
   1530         graphicsContext->restore();
   1531 
   1532         // Draw outer border line
   1533         graphicsContext->save();
   1534 
   1535         RoundedIntRect outerClip = style->getRoundedInnerBorderFor(borderRect,
   1536             outerBorderTopWidth, outerBorderBottomWidth, outerBorderLeftWidth, outerBorderRightWidth,
   1537             includeLogicalLeftEdge, includeLogicalRightEdge);
   1538 
   1539         graphicsContext->clipOutRoundedRect(outerClip);
   1540         drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, includeLogicalLeftEdge, includeLogicalRightEdge);
   1541         graphicsContext->restore();
   1542         return;
   1543     }
   1544     case RIDGE:
   1545     case GROOVE:
   1546     {
   1547         EBorderStyle s1;
   1548         EBorderStyle s2;
   1549         if (borderStyle == GROOVE) {
   1550             s1 = INSET;
   1551             s2 = OUTSET;
   1552         } else {
   1553             s1 = OUTSET;
   1554             s2 = INSET;
   1555         }
   1556 
   1557         // Paint full border
   1558         drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s1, includeLogicalLeftEdge, includeLogicalRightEdge);
   1559 
   1560         // Paint inner only
   1561         graphicsContext->save();
   1562 
   1563         int topWidth = edges[BSTop].usedWidth() / 2;
   1564         int bottomWidth = edges[BSBottom].usedWidth() / 2;
   1565         int leftWidth = edges[BSLeft].usedWidth() / 2;
   1566         int rightWidth = edges[BSRight].usedWidth() / 2;
   1567 
   1568         RoundedIntRect clipRect = style->getRoundedInnerBorderFor(borderRect,
   1569             topWidth, bottomWidth, leftWidth, rightWidth,
   1570             includeLogicalLeftEdge, includeLogicalRightEdge);
   1571 
   1572         graphicsContext->addRoundedRectClip(clipRect);
   1573         drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s2, includeLogicalLeftEdge, includeLogicalRightEdge);
   1574         graphicsContext->restore();
   1575         return;
   1576     }
   1577     case INSET:
   1578         if (side == BSTop || side == BSLeft)
   1579             color = color.dark();
   1580         break;
   1581     case OUTSET:
   1582         if (side == BSBottom || side == BSRight)
   1583             color = color.dark();
   1584         break;
   1585     default:
   1586         break;
   1587     }
   1588 
   1589     graphicsContext->setStrokeStyle(NoStroke);
   1590     graphicsContext->setFillColor(color, style->colorSpace());
   1591     graphicsContext->drawRect(borderRect);
   1592 }
   1593 #else
   1594 void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h,
   1595                                        const RenderStyle* style, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
   1596 {
   1597     // FIXME: This old version of paintBorder should be removed when all ports implement
   1598     // GraphicsContext::clipConvexPolygon()!! This should happen soon.
   1599     if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage()))
   1600         return;
   1601 
   1602     const Color& topColor = style->visitedDependentColor(CSSPropertyBorderTopColor);
   1603     const Color& bottomColor = style->visitedDependentColor(CSSPropertyBorderBottomColor);
   1604     const Color& leftColor = style->visitedDependentColor(CSSPropertyBorderLeftColor);
   1605     const Color& rightColor = style->visitedDependentColor(CSSPropertyBorderRightColor);
   1606 
   1607     bool topTransparent = style->borderTopIsTransparent();
   1608     bool bottomTransparent = style->borderBottomIsTransparent();
   1609     bool rightTransparent = style->borderRightIsTransparent();
   1610     bool leftTransparent = style->borderLeftIsTransparent();
   1611 
   1612     EBorderStyle topStyle = style->borderTopStyle();
   1613     EBorderStyle bottomStyle = style->borderBottomStyle();
   1614     EBorderStyle leftStyle = style->borderLeftStyle();
   1615     EBorderStyle rightStyle = style->borderRightStyle();
   1616 
   1617     bool horizontal = style->isHorizontalWritingMode();
   1618     bool renderTop = topStyle > BHIDDEN && !topTransparent && (horizontal || includeLogicalLeftEdge);
   1619     bool renderLeft = leftStyle > BHIDDEN && !leftTransparent && (!horizontal || includeLogicalLeftEdge);
   1620     bool renderRight = rightStyle > BHIDDEN && !rightTransparent && (!horizontal || includeLogicalRightEdge);
   1621     bool renderBottom = bottomStyle > BHIDDEN && !bottomTransparent && (horizontal || includeLogicalRightEdge);
   1622 
   1623 
   1624     RoundedIntRect border(tx, ty, w, h);
   1625     if (style->hasBorderRadius()) {
   1626         border.includeLogicalEdges(style->getRoundedBorderFor(border.rect()).radii(),
   1627                                    horizontal, includeLogicalLeftEdge, includeLogicalRightEdge);
   1628         if (border.isRounded()) {
   1629             graphicsContext->save();
   1630             graphicsContext->addRoundedRectClip(border);
   1631         }
   1632     }
   1633 
   1634     int firstAngleStart, secondAngleStart, firstAngleSpan, secondAngleSpan;
   1635     float thickness;
   1636     bool renderRadii = border.isRounded();
   1637     bool upperLeftBorderStylesMatch = renderLeft && (topStyle == leftStyle) && (topColor == leftColor);
   1638     bool upperRightBorderStylesMatch = renderRight && (topStyle == rightStyle) && (topColor == rightColor) && (topStyle != OUTSET) && (topStyle != RIDGE) && (topStyle != INSET) && (topStyle != GROOVE);
   1639     bool lowerLeftBorderStylesMatch = renderLeft && (bottomStyle == leftStyle) && (bottomColor == leftColor) && (bottomStyle != OUTSET) && (bottomStyle != RIDGE) && (bottomStyle != INSET) && (bottomStyle != GROOVE);
   1640     bool lowerRightBorderStylesMatch = renderRight && (bottomStyle == rightStyle) && (bottomColor == rightColor);
   1641 
   1642     if (renderTop) {
   1643         bool ignoreLeft = (renderRadii && border.radii().topLeft().width() > 0)
   1644             || (topColor == leftColor && topTransparent == leftTransparent && topStyle >= OUTSET
   1645                 && (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET));
   1646 
   1647         bool ignoreRight = (renderRadii && border.radii().topRight().width() > 0)
   1648             || (topColor == rightColor && topTransparent == rightTransparent && topStyle >= OUTSET
   1649                 && (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET));
   1650 
   1651         int x = tx;
   1652         int x2 = tx + w;
   1653         if (renderRadii) {
   1654             x += border.radii().topLeft().width();
   1655             x2 -= border.radii().topRight().width();
   1656         }
   1657 
   1658         drawLineForBoxSide(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, topColor, topStyle,
   1659                    ignoreLeft ? 0 : style->borderLeftWidth(), ignoreRight ? 0 : style->borderRightWidth());
   1660 
   1661         if (renderRadii) {
   1662             int leftY = ty;
   1663 
   1664             // We make the arc double thick and let the clip rect take care of clipping the extra off.
   1665             // We're doing this because it doesn't seem possible to match the curve of the clip exactly
   1666             // with the arc-drawing function.
   1667             thickness = style->borderTopWidth() * 2;
   1668 
   1669             if (border.radii().topLeft().width()) {
   1670                 int leftX = tx;
   1671                 // The inner clip clips inside the arc. This is especially important for 1px borders.
   1672                 bool applyLeftInnerClip = (style->borderLeftWidth() < border.radii().topLeft().width())
   1673                     && (style->borderTopWidth() < border.radii().topLeft().height())
   1674                     && (topStyle != DOUBLE || style->borderTopWidth() > 6);
   1675                 if (applyLeftInnerClip) {
   1676                     graphicsContext->save();
   1677                     graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, border.radii().topLeft().width() * 2, border.radii().topLeft().height() * 2),
   1678                                                              style->borderTopWidth());
   1679                 }
   1680 
   1681                 firstAngleStart = 90;
   1682                 firstAngleSpan = upperLeftBorderStylesMatch ? 90 : 45;
   1683 
   1684                 // Draw upper left arc
   1685                 drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, border.radii().topLeft(), firstAngleStart, firstAngleSpan,
   1686                               BSTop, topColor, topStyle, true);
   1687                 if (applyLeftInnerClip)
   1688                     graphicsContext->restore();
   1689             }
   1690 
   1691             if (border.radii().topRight().width()) {
   1692                 int rightX = tx + w - border.radii().topRight().width() * 2;
   1693                 bool applyRightInnerClip = (style->borderRightWidth() < border.radii().topRight().width())
   1694                     && (style->borderTopWidth() < border.radii().topRight().height())
   1695                     && (topStyle != DOUBLE || style->borderTopWidth() > 6);
   1696                 if (applyRightInnerClip) {
   1697                     graphicsContext->save();
   1698                     graphicsContext->addInnerRoundedRectClip(IntRect(rightX, leftY, border.radii().topRight().width() * 2, border.radii().topRight().height() * 2),
   1699                                                              style->borderTopWidth());
   1700                 }
   1701 
   1702                 if (upperRightBorderStylesMatch) {
   1703                     secondAngleStart = 0;
   1704                     secondAngleSpan = 90;
   1705                 } else {
   1706                     secondAngleStart = 45;
   1707                     secondAngleSpan = 45;
   1708                 }
   1709 
   1710                 // Draw upper right arc
   1711                 drawArcForBoxSide(graphicsContext, rightX, leftY, thickness, border.radii().topRight(), secondAngleStart, secondAngleSpan,
   1712                               BSTop, topColor, topStyle, false);
   1713                 if (applyRightInnerClip)
   1714                     graphicsContext->restore();
   1715             }
   1716         }
   1717     }
   1718 
   1719     if (renderBottom) {
   1720         bool ignoreLeft = (renderRadii && border.radii().bottomLeft().width() > 0)
   1721             || (bottomColor == leftColor && bottomTransparent == leftTransparent && bottomStyle >= OUTSET
   1722                 && (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET));
   1723 
   1724         bool ignoreRight = (renderRadii && border.radii().bottomRight().width() > 0)
   1725             || (bottomColor == rightColor && bottomTransparent == rightTransparent && bottomStyle >= OUTSET
   1726                 && (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET));
   1727 
   1728         int x = tx;
   1729         int x2 = tx + w;
   1730         if (renderRadii) {
   1731             x += border.radii().bottomLeft().width();
   1732             x2 -= border.radii().bottomRight().width();
   1733         }
   1734 
   1735         drawLineForBoxSide(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bottomColor, bottomStyle,
   1736                    ignoreLeft ? 0 : style->borderLeftWidth(), ignoreRight ? 0 : style->borderRightWidth());
   1737 
   1738         if (renderRadii) {
   1739             thickness = style->borderBottomWidth() * 2;
   1740 
   1741             if (border.radii().bottomLeft().width()) {
   1742                 int leftX = tx;
   1743                 int leftY = ty + h - border.radii().bottomLeft().height() * 2;
   1744                 bool applyLeftInnerClip = (style->borderLeftWidth() < border.radii().bottomLeft().width())
   1745                     && (style->borderBottomWidth() < border.radii().bottomLeft().height())
   1746                     && (bottomStyle != DOUBLE || style->borderBottomWidth() > 6);
   1747                 if (applyLeftInnerClip) {
   1748                     graphicsContext->save();
   1749                     graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, border.radii().bottomLeft().width() * 2, border.radii().bottomLeft().height() * 2),
   1750                                                              style->borderBottomWidth());
   1751                 }
   1752 
   1753                 if (lowerLeftBorderStylesMatch) {
   1754                     firstAngleStart = 180;
   1755                     firstAngleSpan = 90;
   1756                 } else {
   1757                     firstAngleStart = 225;
   1758                     firstAngleSpan = 45;
   1759                 }
   1760 
   1761                 // Draw lower left arc
   1762                 drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, border.radii().bottomLeft(), firstAngleStart, firstAngleSpan,
   1763                               BSBottom, bottomColor, bottomStyle, true);
   1764                 if (applyLeftInnerClip)
   1765                     graphicsContext->restore();
   1766             }
   1767 
   1768             if (border.radii().bottomRight().width()) {
   1769                 int rightY = ty + h - border.radii().bottomRight().height() * 2;
   1770                 int rightX = tx + w - border.radii().bottomRight().width() * 2;
   1771                 bool applyRightInnerClip = (style->borderRightWidth() < border.radii().bottomRight().width())
   1772                     && (style->borderBottomWidth() < border.radii().bottomRight().height())
   1773                     && (bottomStyle != DOUBLE || style->borderBottomWidth() > 6);
   1774                 if (applyRightInnerClip) {
   1775                     graphicsContext->save();
   1776                     graphicsContext->addInnerRoundedRectClip(IntRect(rightX, rightY, border.radii().bottomRight().width() * 2, border.radii().bottomRight().height() * 2),
   1777                                                              style->borderBottomWidth());
   1778                 }
   1779 
   1780                 secondAngleStart = 270;
   1781                 secondAngleSpan = lowerRightBorderStylesMatch ? 90 : 45;
   1782 
   1783                 // Draw lower right arc
   1784                 drawArcForBoxSide(graphicsContext, rightX, rightY, thickness, border.radii().bottomRight(), secondAngleStart, secondAngleSpan,
   1785                               BSBottom, bottomColor, bottomStyle, false);
   1786                 if (applyRightInnerClip)
   1787                     graphicsContext->restore();
   1788             }
   1789         }
   1790     }
   1791 
   1792     if (renderLeft) {
   1793         bool ignoreTop = (renderRadii && border.radii().topLeft().height() > 0)
   1794             || (topColor == leftColor && topTransparent == leftTransparent && leftStyle >= OUTSET
   1795                 && (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET));
   1796 
   1797         bool ignoreBottom = (renderRadii && border.radii().bottomLeft().height() > 0)
   1798             || (bottomColor == leftColor && bottomTransparent == leftTransparent && leftStyle >= OUTSET
   1799                 && (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET));
   1800 
   1801         int y = ty;
   1802         int y2 = ty + h;
   1803         if (renderRadii) {
   1804             y += border.radii().topLeft().height();
   1805             y2 -= border.radii().bottomLeft().height();
   1806         }
   1807 
   1808         drawLineForBoxSide(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, leftColor, leftStyle,
   1809                    ignoreTop ? 0 : style->borderTopWidth(), ignoreBottom ? 0 : style->borderBottomWidth());
   1810 
   1811         if (renderRadii && (!upperLeftBorderStylesMatch || !lowerLeftBorderStylesMatch)) {
   1812             int topX = tx;
   1813             thickness = style->borderLeftWidth() * 2;
   1814 
   1815             if (!upperLeftBorderStylesMatch && border.radii().topLeft().width()) {
   1816                 int topY = ty;
   1817                 bool applyTopInnerClip = (style->borderLeftWidth() < border.radii().topLeft().width())
   1818                     && (style->borderTopWidth() < border.radii().topLeft().height())
   1819                     && (leftStyle != DOUBLE || style->borderLeftWidth() > 6);
   1820                 if (applyTopInnerClip) {
   1821                     graphicsContext->save();
   1822                     graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, border.radii().topLeft().width() * 2, border.radii().topLeft().height() * 2),
   1823                                                              style->borderLeftWidth());
   1824                 }
   1825 
   1826                 firstAngleStart = 135;
   1827                 firstAngleSpan = 45;
   1828 
   1829                 // Draw top left arc
   1830                 drawArcForBoxSide(graphicsContext, topX, topY, thickness, border.radii().topLeft(), firstAngleStart, firstAngleSpan,
   1831                               BSLeft, leftColor, leftStyle, true);
   1832                 if (applyTopInnerClip)
   1833                     graphicsContext->restore();
   1834             }
   1835 
   1836             if (!lowerLeftBorderStylesMatch && border.radii().bottomLeft().width()) {
   1837                 int bottomY = ty + h - border.radii().bottomLeft().height() * 2;
   1838                 bool applyBottomInnerClip = (style->borderLeftWidth() < border.radii().bottomLeft().width())
   1839                     && (style->borderBottomWidth() < border.radii().bottomLeft().height())
   1840                     && (leftStyle != DOUBLE || style->borderLeftWidth() > 6);
   1841                 if (applyBottomInnerClip) {
   1842                     graphicsContext->save();
   1843                     graphicsContext->addInnerRoundedRectClip(IntRect(topX, bottomY, border.radii().bottomLeft().width() * 2, border.radii().bottomLeft().height() * 2),
   1844                                                              style->borderLeftWidth());
   1845                 }
   1846 
   1847                 secondAngleStart = 180;
   1848                 secondAngleSpan = 45;
   1849 
   1850                 // Draw bottom left arc
   1851                 drawArcForBoxSide(graphicsContext, topX, bottomY, thickness, border.radii().bottomLeft(), secondAngleStart, secondAngleSpan,
   1852                               BSLeft, leftColor, leftStyle, false);
   1853                 if (applyBottomInnerClip)
   1854                     graphicsContext->restore();
   1855             }
   1856         }
   1857     }
   1858 
   1859     if (renderRight) {
   1860         bool ignoreTop = (renderRadii && border.radii().topRight().height() > 0)
   1861             || ((topColor == rightColor) && (topTransparent == rightTransparent)
   1862                 && (rightStyle >= DOTTED || rightStyle == INSET)
   1863                 && (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET));
   1864 
   1865         bool ignoreBottom = (renderRadii && border.radii().bottomRight().height() > 0)
   1866             || ((bottomColor == rightColor) && (bottomTransparent == rightTransparent)
   1867                 && (rightStyle >= DOTTED || rightStyle == INSET)
   1868                 && (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET));
   1869 
   1870         int y = ty;
   1871         int y2 = ty + h;
   1872         if (renderRadii) {
   1873             y += border.radii().topRight().height();
   1874             y2 -= border.radii().bottomRight().height();
   1875         }
   1876 
   1877         drawLineForBoxSide(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rightColor, rightStyle,
   1878                    ignoreTop ? 0 : style->borderTopWidth(), ignoreBottom ? 0 : style->borderBottomWidth());
   1879 
   1880         if (renderRadii && (!upperRightBorderStylesMatch || !lowerRightBorderStylesMatch)) {
   1881             thickness = style->borderRightWidth() * 2;
   1882 
   1883             if (!upperRightBorderStylesMatch && border.radii().topRight().width()) {
   1884                 int topX = tx + w - border.radii().topRight().width() * 2;
   1885                 int topY = ty;
   1886                 bool applyTopInnerClip = (style->borderRightWidth() < border.radii().topRight().width())
   1887                     && (style->borderTopWidth() < border.radii().topRight().height())
   1888                     && (rightStyle != DOUBLE || style->borderRightWidth() > 6);
   1889                 if (applyTopInnerClip) {
   1890                     graphicsContext->save();
   1891                     graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, border.radii().topRight().width() * 2, border.radii().topRight().height() * 2),
   1892                                                              style->borderRightWidth());
   1893                 }
   1894 
   1895                 firstAngleStart = 0;
   1896                 firstAngleSpan = 45;
   1897 
   1898                 // Draw top right arc
   1899                 drawArcForBoxSide(graphicsContext, topX, topY, thickness, border.radii().topRight(), firstAngleStart, firstAngleSpan,
   1900                               BSRight, rightColor, rightStyle, true);
   1901                 if (applyTopInnerClip)
   1902                     graphicsContext->restore();
   1903             }
   1904 
   1905             if (!lowerRightBorderStylesMatch && border.radii().bottomRight().width()) {
   1906                 int bottomX = tx + w - border.radii().bottomRight().width() * 2;
   1907                 int bottomY = ty + h - border.radii().bottomRight().height() * 2;
   1908                 bool applyBottomInnerClip = (style->borderRightWidth() < border.radii().bottomRight().width())
   1909                     && (style->borderBottomWidth() < border.radii().bottomRight().height())
   1910                     && (rightStyle != DOUBLE || style->borderRightWidth() > 6);
   1911                 if (applyBottomInnerClip) {
   1912                     graphicsContext->save();
   1913                     graphicsContext->addInnerRoundedRectClip(IntRect(bottomX, bottomY, border.radii().bottomRight().width() * 2, border.radii().bottomRight().height() * 2),
   1914                                                              style->borderRightWidth());
   1915                 }
   1916 
   1917                 secondAngleStart = 315;
   1918                 secondAngleSpan = 45;
   1919 
   1920                 // Draw bottom right arc
   1921                 drawArcForBoxSide(graphicsContext, bottomX, bottomY, thickness, border.radii().bottomRight(), secondAngleStart, secondAngleSpan,
   1922                               BSRight, rightColor, rightStyle, false);
   1923                 if (applyBottomInnerClip)
   1924                     graphicsContext->restore();
   1925             }
   1926         }
   1927     }
   1928 
   1929     if (renderRadii)
   1930         graphicsContext->restore();
   1931 }
   1932 #endif
   1933 
   1934 static void findInnerVertex(const FloatPoint& outerCorner, const FloatPoint& innerCorner, const FloatPoint& centerPoint, FloatPoint& result)
   1935 {
   1936     // If the line between outer and inner corner is towards the horizontal, intersect with a vertical line through the center,
   1937     // otherwise with a horizontal line through the center. The points that form this line are arbitrary (we use 0, 100).
   1938     // Note that if findIntersection fails, it will leave result untouched.
   1939     if (fabs(outerCorner.x() - innerCorner.x()) > fabs(outerCorner.y() - innerCorner.y()))
   1940         findIntersection(outerCorner, innerCorner, FloatPoint(centerPoint.x(), 0), FloatPoint(centerPoint.x(), 100), result);
   1941     else
   1942         findIntersection(outerCorner, innerCorner, FloatPoint(0, centerPoint.y()), FloatPoint(100, centerPoint.y()), result);
   1943 }
   1944 
   1945 void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext* graphicsContext, const RoundedIntRect& outerBorder, const RoundedIntRect& innerBorder,
   1946                                                  BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches)
   1947 {
   1948     FloatPoint quad[4];
   1949 
   1950     const IntRect& outerRect = outerBorder.rect();
   1951     const IntRect& innerRect = innerBorder.rect();
   1952 
   1953     FloatPoint centerPoint(innerRect.location().x() + static_cast<float>(innerRect.width()) / 2, innerRect.location().y() + static_cast<float>(innerRect.height()) / 2);
   1954 
   1955     // For each side, create a quad that encompasses all parts of that side that may draw,
   1956     // including areas inside the innerBorder.
   1957     //
   1958     //         0----------------3
   1959     //       0  \              /  0
   1960     //       |\  1----------- 2  /|
   1961     //       | 1                1 |
   1962     //       | |                | |
   1963     //       | |                | |
   1964     //       | 2                2 |
   1965     //       |/  1------------2  \|
   1966     //       3  /              \  3
   1967     //         0----------------3
   1968     //
   1969     switch (side) {
   1970     case BSTop:
   1971         quad[0] = outerRect.minXMinYCorner();
   1972         quad[1] = innerRect.minXMinYCorner();
   1973         quad[2] = innerRect.maxXMinYCorner();
   1974         quad[3] = outerRect.maxXMinYCorner();
   1975 
   1976         if (!innerBorder.radii().topLeft().isZero())
   1977             findInnerVertex(outerRect.minXMinYCorner(), innerRect.minXMinYCorner(), centerPoint, quad[1]);
   1978 
   1979         if (!innerBorder.radii().topRight().isZero())
   1980             findInnerVertex(outerRect.maxXMinYCorner(), innerRect.maxXMinYCorner(), centerPoint, quad[2]);
   1981         break;
   1982 
   1983     case BSLeft:
   1984         quad[0] = outerRect.minXMinYCorner();
   1985         quad[1] = innerRect.minXMinYCorner();
   1986         quad[2] = innerRect.minXMaxYCorner();
   1987         quad[3] = outerRect.minXMaxYCorner();
   1988 
   1989         if (!innerBorder.radii().topLeft().isZero())
   1990             findInnerVertex(outerRect.minXMinYCorner(), innerRect.minXMinYCorner(), centerPoint, quad[1]);
   1991 
   1992         if (!innerBorder.radii().bottomLeft().isZero())
   1993             findInnerVertex(outerRect.minXMaxYCorner(), innerRect.minXMaxYCorner(), centerPoint, quad[2]);
   1994         break;
   1995 
   1996     case BSBottom:
   1997         quad[0] = outerRect.minXMaxYCorner();
   1998         quad[1] = innerRect.minXMaxYCorner();
   1999         quad[2] = innerRect.maxXMaxYCorner();
   2000         quad[3] = outerRect.maxXMaxYCorner();
   2001 
   2002         if (!innerBorder.radii().bottomLeft().isZero())
   2003             findInnerVertex(outerRect.minXMaxYCorner(), innerRect.minXMaxYCorner(), centerPoint, quad[1]);
   2004 
   2005         if (!innerBorder.radii().bottomRight().isZero())
   2006             findInnerVertex(outerRect.maxXMaxYCorner(), innerRect.maxXMaxYCorner(), centerPoint, quad[2]);
   2007         break;
   2008 
   2009     case BSRight:
   2010         quad[0] = outerRect.maxXMinYCorner();
   2011         quad[1] = innerRect.maxXMinYCorner();
   2012         quad[2] = innerRect.maxXMaxYCorner();
   2013         quad[3] = outerRect.maxXMaxYCorner();
   2014 
   2015         if (!innerBorder.radii().topRight().isZero())
   2016             findInnerVertex(outerRect.maxXMinYCorner(), innerRect.maxXMinYCorner(), centerPoint, quad[1]);
   2017 
   2018         if (!innerBorder.radii().bottomRight().isZero())
   2019             findInnerVertex(outerRect.maxXMaxYCorner(), innerRect.maxXMaxYCorner(), centerPoint, quad[2]);
   2020         break;
   2021     }
   2022 
   2023     // If the border matches both of its adjacent sides, don't anti-alias the clip, and
   2024     // if neither side matches, anti-alias the clip.
   2025     if (firstEdgeMatches == secondEdgeMatches) {
   2026         graphicsContext->clipConvexPolygon(4, quad, !firstEdgeMatches);
   2027         return;
   2028     }
   2029 
   2030     // Square off the end which shouldn't be affected by antialiasing, and clip.
   2031     FloatPoint firstQuad[4];
   2032     firstQuad[0] = quad[0];
   2033     firstQuad[1] = quad[1];
   2034     firstQuad[2] = side == BSTop || side == BSBottom ? FloatPoint(quad[3].x(), quad[2].y())
   2035         : FloatPoint(quad[2].x(), quad[3].y());
   2036     firstQuad[3] = quad[3];
   2037     graphicsContext->clipConvexPolygon(4, firstQuad, !firstEdgeMatches);
   2038 
   2039     FloatPoint secondQuad[4];
   2040     secondQuad[0] = quad[0];
   2041     secondQuad[1] = side == BSTop || side == BSBottom ? FloatPoint(quad[0].x(), quad[1].y())
   2042         : FloatPoint(quad[1].x(), quad[0].y());
   2043     secondQuad[2] = quad[2];
   2044     secondQuad[3] = quad[3];
   2045     // Antialiasing affects the second side.
   2046     graphicsContext->clipConvexPolygon(4, secondQuad, !secondEdgeMatches);
   2047 }
   2048 
   2049 static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowBlur, int shadowSpread, const IntSize& shadowOffset)
   2050 {
   2051     IntRect bounds(holeRect);
   2052 
   2053     bounds.inflate(shadowBlur);
   2054 
   2055     if (shadowSpread < 0)
   2056         bounds.inflate(-shadowSpread);
   2057 
   2058     IntRect offsetBounds = bounds;
   2059     offsetBounds.move(-shadowOffset);
   2060     return unionRect(bounds, offsetBounds);
   2061 }
   2062 
   2063 void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int ty, int w, int h, const RenderStyle* s, ShadowStyle shadowStyle, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
   2064 {
   2065     // FIXME: Deal with border-image.  Would be great to use border-image as a mask.
   2066 
   2067     if (context->paintingDisabled() || !s->boxShadow())
   2068         return;
   2069 
   2070     IntRect borderRect(tx, ty, w, h);
   2071     RoundedIntRect border = (shadowStyle == Inset) ? s->getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge)
   2072                                                    : s->getRoundedBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge);
   2073 
   2074     bool hasBorderRadius = s->hasBorderRadius();
   2075     bool isHorizontal = s->isHorizontalWritingMode();
   2076 
   2077     bool hasOpaqueBackground = s->visitedDependentColor(CSSPropertyBackgroundColor).isValid() && s->visitedDependentColor(CSSPropertyBackgroundColor).alpha() == 255;
   2078     for (const ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next()) {
   2079         if (shadow->style() != shadowStyle)
   2080             continue;
   2081 
   2082         IntSize shadowOffset(shadow->x(), shadow->y());
   2083         int shadowBlur = shadow->blur();
   2084         int shadowSpread = shadow->spread();
   2085         const Color& shadowColor = shadow->color();
   2086 
   2087         if (shadow->style() == Normal) {
   2088             RoundedIntRect fillRect = border;
   2089             fillRect.inflate(shadowSpread);
   2090             if (fillRect.isEmpty())
   2091                 continue;
   2092 
   2093             IntRect shadowRect(border.rect());
   2094             shadowRect.inflate(shadowBlur + shadowSpread);
   2095             shadowRect.move(shadowOffset);
   2096 
   2097             context->save();
   2098             context->clip(shadowRect);
   2099 
   2100             // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not
   2101             // bleed in (due to antialiasing) if the context is transformed.
   2102             IntSize extraOffset(w + max(0, shadowOffset.width()) + shadowBlur + 2 * shadowSpread + 1, 0);
   2103             shadowOffset -= extraOffset;
   2104             fillRect.move(extraOffset);
   2105 
   2106             if (shadow->isWebkitBoxShadow())
   2107                 context->setLegacyShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace());
   2108             else
   2109                 context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace());
   2110 
   2111             if (hasBorderRadius) {
   2112                 RoundedIntRect rectToClipOut = border;
   2113 
   2114                 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
   2115                 // when painting the shadow. On the other hand, it introduces subpixel gaps along the
   2116                 // corners. Those are avoided by insetting the clipping path by one pixel.
   2117                 if (hasOpaqueBackground) {
   2118                     rectToClipOut.inflateWithRadii(-1);
   2119                 }
   2120 
   2121                 if (!rectToClipOut.isEmpty())
   2122                     context->clipOutRoundedRect(rectToClipOut);
   2123 
   2124                 fillRect.expandRadii(shadowSpread);
   2125                 context->fillRoundedRect(fillRect, Color::black, s->colorSpace());
   2126             } else {
   2127                 IntRect rectToClipOut = border.rect();
   2128 
   2129                 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
   2130                 // when painting the shadow. On the other hand, it introduces subpixel gaps along the
   2131                 // edges if they are not pixel-aligned. Those are avoided by insetting the clipping path
   2132                 // by one pixel.
   2133                 if (hasOpaqueBackground) {
   2134                     AffineTransform currentTransformation = context->getCTM();
   2135                     if (currentTransformation.a() != 1 || (currentTransformation.d() != 1 && currentTransformation.d() != -1)
   2136                             || currentTransformation.b() || currentTransformation.c())
   2137                         rectToClipOut.inflate(-1);
   2138                 }
   2139 
   2140                 if (!rectToClipOut.isEmpty())
   2141                     context->clipOut(rectToClipOut);
   2142                 context->fillRect(fillRect.rect(), Color::black, s->colorSpace());
   2143             }
   2144 
   2145             context->restore();
   2146         } else {
   2147             // Inset shadow.
   2148             IntRect holeRect(border.rect());
   2149             holeRect.inflate(-shadowSpread);
   2150 
   2151             if (holeRect.isEmpty()) {
   2152                 if (hasBorderRadius)
   2153                     context->fillRoundedRect(border, shadowColor, s->colorSpace());
   2154                 else
   2155                     context->fillRect(border.rect(), shadowColor, s->colorSpace());
   2156                 continue;
   2157             }
   2158 
   2159             if (!includeLogicalLeftEdge) {
   2160                 if (isHorizontal) {
   2161                     holeRect.move(-max(shadowOffset.width(), 0) - shadowBlur, 0);
   2162                     holeRect.setWidth(holeRect.width() + max(shadowOffset.width(), 0) + shadowBlur);
   2163                 } else {
   2164                     holeRect.move(0, -max(shadowOffset.height(), 0) - shadowBlur);
   2165                     holeRect.setHeight(holeRect.height() + max(shadowOffset.height(), 0) + shadowBlur);
   2166                 }
   2167             }
   2168             if (!includeLogicalRightEdge) {
   2169                 if (isHorizontal)
   2170                     holeRect.setWidth(holeRect.width() - min(shadowOffset.width(), 0) + shadowBlur);
   2171                 else
   2172                     holeRect.setHeight(holeRect.height() - min(shadowOffset.height(), 0) + shadowBlur);
   2173             }
   2174 
   2175             Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255);
   2176 
   2177             IntRect outerRect = areaCastingShadowInHole(border.rect(), shadowBlur, shadowSpread, shadowOffset);
   2178             RoundedIntRect roundedHole(holeRect, border.radii());
   2179 
   2180             context->save();
   2181 
   2182             if (hasBorderRadius) {
   2183                 Path path;
   2184                 path.addRoundedRect(border);
   2185                 context->clip(path);
   2186                 roundedHole.shrinkRadii(shadowSpread);
   2187             } else
   2188                 context->clip(border.rect());
   2189 
   2190             IntSize extraOffset(2 * w + max(0, shadowOffset.width()) + shadowBlur - 2 * shadowSpread + 1, 0);
   2191             context->translate(extraOffset.width(), extraOffset.height());
   2192             shadowOffset -= extraOffset;
   2193 
   2194             if (shadow->isWebkitBoxShadow())
   2195                 context->setLegacyShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace());
   2196             else
   2197                 context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace());
   2198 
   2199             context->fillRectWithRoundedHole(outerRect, roundedHole, fillColor, s->colorSpace());
   2200 
   2201             context->restore();
   2202         }
   2203     }
   2204 }
   2205 
   2206 int RenderBoxModelObject::containingBlockLogicalWidthForContent() const
   2207 {
   2208     return containingBlock()->availableLogicalWidth();
   2209 }
   2210 
   2211 RenderBoxModelObject* RenderBoxModelObject::continuation() const
   2212 {
   2213     if (!continuationMap)
   2214         return 0;
   2215     return continuationMap->get(this);
   2216 }
   2217 
   2218 void RenderBoxModelObject::setContinuation(RenderBoxModelObject* continuation)
   2219 {
   2220     if (continuation) {
   2221         if (!continuationMap)
   2222             continuationMap = new ContinuationMap;
   2223         continuationMap->set(this, continuation);
   2224     } else {
   2225         if (continuationMap)
   2226             continuationMap->remove(this);
   2227     }
   2228 }
   2229 
   2230 } // namespace WebCore
   2231