WebCore/rendering/RenderBoxModelObject.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
       
     3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
       
     4  *           (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
       
     5  *           (C) 2005, 2006 Samuel Weinig (sam.weinig@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<RenderBoxModelObject*, IntSize> LastPaintSizeMap;
       
    54 
       
    55 class ImageQualityController : public Noncopyable {
       
    56 public:
       
    57     ImageQualityController();
       
    58     bool shouldPaintAtLowQuality(GraphicsContext*, RenderBoxModelObject*, Image*, const IntSize&);
       
    59     void objectDestroyed(RenderBoxModelObject*);
       
    60 
       
    61 private:
       
    62     void highQualityRepaintTimerFired(Timer<ImageQualityController>*);
       
    63     void restartTimer();
       
    64 
       
    65     LastPaintSizeMap m_lastPaintSizeMap;
       
    66     Timer<ImageQualityController> m_timer;
       
    67     bool m_animatedResizeIsActive;
       
    68 };
       
    69 
       
    70 ImageQualityController::ImageQualityController()
       
    71     : m_timer(this, &ImageQualityController::highQualityRepaintTimerFired)
       
    72     , m_animatedResizeIsActive(false)
       
    73 {
       
    74 }
       
    75 
       
    76 void ImageQualityController::objectDestroyed(RenderBoxModelObject* object)
       
    77 {
       
    78     m_lastPaintSizeMap.remove(object);
       
    79     if (m_lastPaintSizeMap.isEmpty()) {
       
    80         m_animatedResizeIsActive = false;
       
    81         m_timer.stop();
       
    82     }
       
    83 }
       
    84     
       
    85 void ImageQualityController::highQualityRepaintTimerFired(Timer<ImageQualityController>*)
       
    86 {
       
    87     if (m_animatedResizeIsActive) {
       
    88         m_animatedResizeIsActive = false;
       
    89         for (LastPaintSizeMap::iterator it = m_lastPaintSizeMap.begin(); it != m_lastPaintSizeMap.end(); ++it)
       
    90             it->first->repaint();
       
    91     }
       
    92 }
       
    93 
       
    94 void ImageQualityController::restartTimer()
       
    95 {
       
    96     m_timer.startOneShot(cLowQualityTimeThreshold);
       
    97 }
       
    98 
       
    99 bool ImageQualityController::shouldPaintAtLowQuality(GraphicsContext* context, RenderBoxModelObject* object, Image* image, const IntSize& size)
       
   100 {
       
   101     // If the image is not a bitmap image, then none of this is relevant and we just paint at high
       
   102     // quality.
       
   103     if (!image || !image->isBitmapImage() || context->paintingDisabled())
       
   104         return false;
       
   105 
       
   106     // Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image
       
   107     // is actually being scaled.
       
   108     IntSize imageSize(image->width(), image->height());
       
   109 
       
   110     // Look ourselves up in the hashtable.
       
   111     LastPaintSizeMap::iterator i = m_lastPaintSizeMap.find(object);
       
   112 
       
   113     const AffineTransform& currentTransform = context->getCTM();
       
   114     bool contextIsScaled = !currentTransform.isIdentityOrTranslationOrFlipped();
       
   115     if (!contextIsScaled && imageSize == size) {
       
   116         // There is no scale in effect. If we had a scale in effect before, we can just remove this object from the list.
       
   117         if (i != m_lastPaintSizeMap.end())
       
   118             m_lastPaintSizeMap.remove(object);
       
   119 
       
   120         return false;
       
   121     }
       
   122 
       
   123     // There is no need to hash scaled images that always use low quality mode when the page demands it. This is the iChat case.
       
   124     if (object->document()->page()->inLowQualityImageInterpolationMode()) {
       
   125         double totalPixels = static_cast<double>(image->width()) * static_cast<double>(image->height());
       
   126         if (totalPixels > cInterpolationCutoff)
       
   127             return true;
       
   128     }
       
   129     // If an animated resize is active, paint in low quality and kick the timer ahead.
       
   130     if (m_animatedResizeIsActive) {
       
   131         m_lastPaintSizeMap.set(object, size);
       
   132         restartTimer();
       
   133         return true;
       
   134     }
       
   135     // If this is the first time resizing this image, or its size is the
       
   136     // same as the last resize, draw at high res, but record the paint
       
   137     // size and set the timer.
       
   138     if (i == m_lastPaintSizeMap.end() || size == i->second) {
       
   139         restartTimer();
       
   140         m_lastPaintSizeMap.set(object, size);
       
   141         return false;
       
   142     }
       
   143     // If the timer is no longer active, draw at high quality and don't
       
   144     // set the timer.
       
   145     if (!m_timer.isActive()) {
       
   146         objectDestroyed(object);
       
   147         return false;
       
   148     }
       
   149     // This object has been resized to two different sizes while the timer
       
   150     // is active, so draw at low quality, set the flag for animated resizes and
       
   151     // the object to the list for high quality redraw.
       
   152     m_lastPaintSizeMap.set(object, size);
       
   153     m_animatedResizeIsActive = true;
       
   154     restartTimer();
       
   155     return true;
       
   156 }
       
   157 
       
   158 static ImageQualityController* imageQualityController()
       
   159 {
       
   160     static ImageQualityController* controller = new ImageQualityController;
       
   161     return controller;
       
   162 }
       
   163 
       
   164 void RenderBoxModelObject::setSelectionState(SelectionState s)
       
   165 {
       
   166     if (selectionState() == s)
       
   167         return;
       
   168     
       
   169     if (s == SelectionInside && selectionState() != SelectionNone)
       
   170         return;
       
   171 
       
   172     if ((s == SelectionStart && selectionState() == SelectionEnd)
       
   173         || (s == SelectionEnd && selectionState() == SelectionStart))
       
   174         RenderObject::setSelectionState(SelectionBoth);
       
   175     else
       
   176         RenderObject::setSelectionState(s);
       
   177     
       
   178     // FIXME:
       
   179     // We should consider whether it is OK propagating to ancestor RenderInlines.
       
   180     // This is a workaround for http://webkit.org/b/32123
       
   181     RenderBlock* cb = containingBlock();
       
   182     if (cb && !cb->isRenderView())
       
   183         cb->setSelectionState(s);
       
   184 }
       
   185 
       
   186 bool RenderBoxModelObject::shouldPaintAtLowQuality(GraphicsContext* context, Image* image, const IntSize& size)
       
   187 {
       
   188     return imageQualityController()->shouldPaintAtLowQuality(context, this, image, size);
       
   189 }
       
   190 
       
   191 RenderBoxModelObject::RenderBoxModelObject(Node* node)
       
   192     : RenderObject(node)
       
   193     , m_layer(0)
       
   194 {
       
   195 }
       
   196 
       
   197 RenderBoxModelObject::~RenderBoxModelObject()
       
   198 {
       
   199     // Our layer should have been destroyed and cleared by now
       
   200     ASSERT(!hasLayer());
       
   201     ASSERT(!m_layer);
       
   202     imageQualityController()->objectDestroyed(this);
       
   203 }
       
   204 
       
   205 void RenderBoxModelObject::destroyLayer()
       
   206 {
       
   207     ASSERT(!hasLayer()); // Callers should have already called setHasLayer(false)
       
   208     ASSERT(m_layer);
       
   209     m_layer->destroy(renderArena());
       
   210     m_layer = 0;
       
   211 }
       
   212 
       
   213 void RenderBoxModelObject::destroy()
       
   214 {
       
   215     // This must be done before we destroy the RenderObject.
       
   216     if (m_layer)
       
   217         m_layer->clearClipRects();
       
   218 
       
   219     // RenderObject::destroy calls back to destroyLayer() for layer destruction
       
   220     RenderObject::destroy();
       
   221 }
       
   222 
       
   223 bool RenderBoxModelObject::hasSelfPaintingLayer() const
       
   224 {
       
   225     return m_layer && m_layer->isSelfPaintingLayer();
       
   226 }
       
   227 
       
   228 void RenderBoxModelObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
       
   229 {
       
   230     s_wasFloating = isFloating();
       
   231     s_hadLayer = hasLayer();
       
   232     if (s_hadLayer)
       
   233         s_layerWasSelfPainting = layer()->isSelfPaintingLayer();
       
   234 
       
   235     // If our z-index changes value or our visibility changes,
       
   236     // we need to dirty our stacking context's z-order list.
       
   237     if (style() && newStyle) {
       
   238         if (parent()) {
       
   239             // Do a repaint with the old style first, e.g., for example if we go from
       
   240             // having an outline to not having an outline.
       
   241             if (diff == StyleDifferenceRepaintLayer) {
       
   242                 layer()->repaintIncludingDescendants();
       
   243                 if (!(style()->clip() == newStyle->clip()))
       
   244                     layer()->clearClipRectsIncludingDescendants();
       
   245             } else if (diff == StyleDifferenceRepaint || newStyle->outlineSize() < style()->outlineSize())
       
   246                 repaint();
       
   247         }
       
   248         
       
   249         if (diff == StyleDifferenceLayout) {
       
   250             // When a layout hint happens, we go ahead and do a repaint of the layer, since the layer could
       
   251             // end up being destroyed.
       
   252             if (hasLayer()) {
       
   253                 if (style()->position() != newStyle->position() ||
       
   254                     style()->zIndex() != newStyle->zIndex() ||
       
   255                     style()->hasAutoZIndex() != newStyle->hasAutoZIndex() ||
       
   256                     !(style()->clip() == newStyle->clip()) ||
       
   257                     style()->hasClip() != newStyle->hasClip() ||
       
   258                     style()->opacity() != newStyle->opacity() ||
       
   259                     style()->transform() != newStyle->transform())
       
   260                 layer()->repaintIncludingDescendants();
       
   261             } else if (newStyle->hasTransform() || newStyle->opacity() < 1) {
       
   262                 // If we don't have a layer yet, but we are going to get one because of transform or opacity,
       
   263                 //  then we need to repaint the old position of the object.
       
   264                 repaint();
       
   265             }
       
   266         }
       
   267 
       
   268         if (hasLayer() && (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() ||
       
   269                            style()->zIndex() != newStyle->zIndex() ||
       
   270                            style()->visibility() != newStyle->visibility())) {
       
   271             layer()->dirtyStackingContextZOrderLists();
       
   272             if (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() || style()->visibility() != newStyle->visibility())
       
   273                 layer()->dirtyZOrderLists();
       
   274         }
       
   275     }
       
   276 
       
   277     RenderObject::styleWillChange(diff, newStyle);
       
   278 }
       
   279 
       
   280 void RenderBoxModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
       
   281 {
       
   282     RenderObject::styleDidChange(diff, oldStyle);
       
   283     updateBoxModelInfoFromStyle();
       
   284     
       
   285     if (requiresLayer()) {
       
   286         if (!layer()) {
       
   287             if (s_wasFloating && isFloating())
       
   288                 setChildNeedsLayout(true);
       
   289             m_layer = new (renderArena()) RenderLayer(this);
       
   290             setHasLayer(true);
       
   291             m_layer->insertOnlyThisLayer();
       
   292             if (parent() && !needsLayout() && containingBlock())
       
   293                 m_layer->updateLayerPositions();
       
   294         }
       
   295     } else if (layer() && layer()->parent()) {
       
   296         setHasTransform(false); // Either a transform wasn't specified or the object doesn't support transforms, so just null out the bit.
       
   297         setHasReflection(false);
       
   298         m_layer->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer
       
   299         if (s_wasFloating && isFloating())
       
   300             setChildNeedsLayout(true);
       
   301     }
       
   302 
       
   303     if (layer()) {
       
   304         layer()->styleChanged(diff, oldStyle);
       
   305         if (s_hadLayer && layer()->isSelfPaintingLayer() != s_layerWasSelfPainting)
       
   306             setChildNeedsLayout(true);
       
   307     }
       
   308 }
       
   309 
       
   310 void RenderBoxModelObject::updateBoxModelInfoFromStyle()
       
   311 {
       
   312     // Set the appropriate bits for a box model object.  Since all bits are cleared in styleWillChange,
       
   313     // we only check for bits that could possibly be set to true.
       
   314     setHasBoxDecorations(hasBackground() || style()->hasBorder() || style()->hasAppearance() || style()->boxShadow());
       
   315     setInline(style()->isDisplayInlineType());
       
   316     setRelPositioned(style()->position() == RelativePosition);
       
   317 }
       
   318 
       
   319 int RenderBoxModelObject::relativePositionOffsetX() const
       
   320 {
       
   321     // Objects that shrink to avoid floats normally use available line width when computing containing block width.  However
       
   322     // in the case of relative positioning using percentages, we can't do this.  The offset should always be resolved using the
       
   323     // available width of the containing block.  Therefore we don't use containingBlockWidthForContent() here, but instead explicitly
       
   324     // call availableWidth on our containing block.
       
   325     if (!style()->left().isAuto()) {
       
   326         RenderBlock* cb = containingBlock();
       
   327         if (!style()->right().isAuto() && containingBlock()->style()->direction() == RTL)
       
   328             return -style()->right().calcValue(cb->availableWidth());
       
   329         return style()->left().calcValue(cb->availableWidth());
       
   330     }
       
   331     if (!style()->right().isAuto()) {
       
   332         RenderBlock* cb = containingBlock();
       
   333         return -style()->right().calcValue(cb->availableWidth());
       
   334     }
       
   335     return 0;
       
   336 }
       
   337 
       
   338 int RenderBoxModelObject::relativePositionOffsetY() const
       
   339 {
       
   340     if (!style()->top().isAuto())
       
   341         return style()->top().calcValue(containingBlock()->availableHeight());
       
   342     else if (!style()->bottom().isAuto())
       
   343         return -style()->bottom().calcValue(containingBlock()->availableHeight());
       
   344 
       
   345     return 0;
       
   346 }
       
   347 
       
   348 int RenderBoxModelObject::offsetLeft() const
       
   349 {
       
   350     // If the element is the HTML body element or does not have an associated box
       
   351     // return 0 and stop this algorithm.
       
   352     if (isBody())
       
   353         return 0;
       
   354     
       
   355     RenderBoxModelObject* offsetPar = offsetParent();
       
   356     int xPos = (isBox() ? toRenderBox(this)->x() : 0);
       
   357     
       
   358     // If the offsetParent of the element is null, or is the HTML body element,
       
   359     // return the distance between the canvas origin and the left border edge 
       
   360     // of the element and stop this algorithm.
       
   361     if (offsetPar) {
       
   362         if (offsetPar->isBox() && !offsetPar->isBody())
       
   363             xPos -= toRenderBox(offsetPar)->borderLeft();
       
   364         if (!isPositioned()) {
       
   365             if (isRelPositioned())
       
   366                 xPos += relativePositionOffsetX();
       
   367             RenderObject* curr = parent();
       
   368             while (curr && curr != offsetPar) {
       
   369                 // FIXME: What are we supposed to do inside SVG content?
       
   370                 if (curr->isBox() && !curr->isTableRow())
       
   371                     xPos += toRenderBox(curr)->x();
       
   372                 curr = curr->parent();
       
   373             }
       
   374             if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned())
       
   375                 xPos += toRenderBox(offsetPar)->x();
       
   376         }
       
   377     }
       
   378 
       
   379     return xPos;
       
   380 }
       
   381 
       
   382 int RenderBoxModelObject::offsetTop() const
       
   383 {
       
   384     // If the element is the HTML body element or does not have an associated box
       
   385     // return 0 and stop this algorithm.
       
   386     if (isBody())
       
   387         return 0;
       
   388     
       
   389     RenderBoxModelObject* offsetPar = offsetParent();
       
   390     int yPos = (isBox() ? toRenderBox(this)->y() : 0);
       
   391     
       
   392     // If the offsetParent of the element is null, or is the HTML body element,
       
   393     // return the distance between the canvas origin and the top border edge 
       
   394     // of the element and stop this algorithm.
       
   395     if (offsetPar) {
       
   396         if (offsetPar->isBox() && !offsetPar->isBody())
       
   397             yPos -= toRenderBox(offsetPar)->borderTop();
       
   398         if (!isPositioned()) {
       
   399             if (isRelPositioned())
       
   400                 yPos += relativePositionOffsetY();
       
   401             RenderObject* curr = parent();
       
   402             while (curr && curr != offsetPar) {
       
   403                 // FIXME: What are we supposed to do inside SVG content?
       
   404                 if (curr->isBox() && !curr->isTableRow())
       
   405                     yPos += toRenderBox(curr)->y();
       
   406                 curr = curr->parent();
       
   407             }
       
   408             if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned())
       
   409                 yPos += toRenderBox(offsetPar)->y();
       
   410         }
       
   411     }
       
   412     return yPos;
       
   413 }
       
   414 
       
   415 int RenderBoxModelObject::paddingTop(bool) const
       
   416 {
       
   417     int w = 0;
       
   418     Length padding = style()->paddingTop();
       
   419     if (padding.isPercent())
       
   420         w = containingBlock()->availableWidth();
       
   421     return padding.calcMinValue(w);
       
   422 }
       
   423 
       
   424 int RenderBoxModelObject::paddingBottom(bool) const
       
   425 {
       
   426     int w = 0;
       
   427     Length padding = style()->paddingBottom();
       
   428     if (padding.isPercent())
       
   429         w = containingBlock()->availableWidth();
       
   430     return padding.calcMinValue(w);
       
   431 }
       
   432 
       
   433 int RenderBoxModelObject::paddingLeft(bool) const
       
   434 {
       
   435     int w = 0;
       
   436     Length padding = style()->paddingLeft();
       
   437     if (padding.isPercent())
       
   438         w = containingBlock()->availableWidth();
       
   439     return padding.calcMinValue(w);
       
   440 }
       
   441 
       
   442 int RenderBoxModelObject::paddingRight(bool) const
       
   443 {
       
   444     int w = 0;
       
   445     Length padding = style()->paddingRight();
       
   446     if (padding.isPercent())
       
   447         w = containingBlock()->availableWidth();
       
   448     return padding.calcMinValue(w);
       
   449 }
       
   450 
       
   451 void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& c, const FillLayer* bgLayer, int tx, int ty, int w, int h, InlineFlowBox* box, CompositeOperator op, RenderObject* backgroundObject)
       
   452 {
       
   453     GraphicsContext* context = paintInfo.context;
       
   454     if (context->paintingDisabled())
       
   455         return;
       
   456 
       
   457     bool includeLeftEdge = box ? box->includeLeftEdge() : true;
       
   458     bool includeRightEdge = box ? box->includeRightEdge() : true;
       
   459     int bLeft = includeLeftEdge ? borderLeft() : 0;
       
   460     int bRight = includeRightEdge ? borderRight() : 0;
       
   461     int pLeft = includeLeftEdge ? paddingLeft() : 0;
       
   462     int pRight = includeRightEdge ? paddingRight() : 0;
       
   463 
       
   464     bool clippedToBorderRadius = false;
       
   465     if (style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge)) {
       
   466         IntRect borderRect(tx, ty, w, h);
       
   467 
       
   468         if (borderRect.isEmpty())
       
   469             return;
       
   470 
       
   471         context->save();
       
   472 
       
   473         IntSize topLeft, topRight, bottomLeft, bottomRight;
       
   474         style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight);
       
   475 
       
   476         context->addRoundedRectClip(borderRect, includeLeftEdge ? topLeft : IntSize(),
       
   477                                                 includeRightEdge ? topRight : IntSize(),
       
   478                                                 includeLeftEdge ? bottomLeft : IntSize(),
       
   479                                                 includeRightEdge ? bottomRight : IntSize());
       
   480         clippedToBorderRadius = true;
       
   481     }
       
   482 
       
   483     bool clippedWithLocalScrolling = hasOverflowClip() && bgLayer->attachment() == LocalBackgroundAttachment;
       
   484     if (clippedWithLocalScrolling) {
       
   485         // Clip to the overflow area.
       
   486         context->save();
       
   487         context->clip(toRenderBox(this)->overflowClipRect(tx, ty));
       
   488         
       
   489         // Now adjust our tx, ty, w, h to reflect a scrolled content box with borders at the ends.
       
   490         IntSize offset = layer()->scrolledContentOffset();
       
   491         tx -= offset.width();
       
   492         ty -= offset.height();
       
   493         w = bLeft + layer()->scrollWidth() + bRight;
       
   494         h = borderTop() + layer()->scrollHeight() + borderBottom();
       
   495     }
       
   496     
       
   497     if (bgLayer->clip() == PaddingFillBox || bgLayer->clip() == ContentFillBox) {
       
   498         // Clip to the padding or content boxes as necessary.
       
   499         bool includePadding = bgLayer->clip() == ContentFillBox;
       
   500         int x = tx + bLeft + (includePadding ? pLeft : 0);
       
   501         int y = ty + borderTop() + (includePadding ? paddingTop() : 0);
       
   502         int width = w - bLeft - bRight - (includePadding ? pLeft + pRight : 0);
       
   503         int height = h - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : 0);
       
   504         context->save();
       
   505         context->clip(IntRect(x, y, width, height));
       
   506     } else if (bgLayer->clip() == TextFillBox) {
       
   507         // We have to draw our text into a mask that can then be used to clip background drawing.
       
   508         // First figure out how big the mask has to be.  It should be no bigger than what we need
       
   509         // to actually render, so we should intersect the dirty rect with the border box of the background.
       
   510         IntRect maskRect(tx, ty, w, h);
       
   511         maskRect.intersect(paintInfo.rect);
       
   512         
       
   513         // Now create the mask.
       
   514         OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size());
       
   515         if (!maskImage)
       
   516             return;
       
   517         
       
   518         GraphicsContext* maskImageContext = maskImage->context();
       
   519         maskImageContext->translate(-maskRect.x(), -maskRect.y());
       
   520         
       
   521         // Now add the text to the clip.  We do this by painting using a special paint phase that signals to
       
   522         // InlineTextBoxes that they should just add their contents to the clip.
       
   523         PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, true, 0, 0);
       
   524         if (box)
       
   525             box->paint(info, tx - box->x(), ty - box->y());
       
   526         else {
       
   527             int x = isBox() ? toRenderBox(this)->x() : 0;
       
   528             int y = isBox() ? toRenderBox(this)->y() : 0;
       
   529             paint(info, tx - x, ty - y);
       
   530         }
       
   531         
       
   532         // The mask has been created.  Now we just need to clip to it.
       
   533         context->save();
       
   534         context->clipToImageBuffer(maskRect, maskImage.get());
       
   535     }
       
   536     
       
   537     StyleImage* bg = bgLayer->image();
       
   538     bool shouldPaintBackgroundImage = bg && bg->canRender(style()->effectiveZoom());
       
   539     Color bgColor = c;
       
   540 
       
   541     // When this style flag is set, change existing background colors and images to a solid white background.
       
   542     // If there's no bg color or image, leave it untouched to avoid affecting transparency.
       
   543     // We don't try to avoid loading the background images, because this style flag is only set
       
   544     // when printing, and at that point we've already loaded the background images anyway. (To avoid
       
   545     // loading the background images we'd have to do this check when applying styles rather than
       
   546     // while rendering.)
       
   547     if (style()->forceBackgroundsToWhite()) {
       
   548         // Note that we can't reuse this variable below because the bgColor might be changed
       
   549         bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && bgColor.alpha() > 0;
       
   550         if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) {
       
   551             bgColor = Color::white;
       
   552             shouldPaintBackgroundImage = false;
       
   553         }
       
   554     }
       
   555 
       
   556     bool isRoot = this->isRoot();
       
   557 
       
   558     // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with
       
   559     // no background in the child document should show the parent's background.
       
   560     bool isOpaqueRoot = false;
       
   561     if (isRoot) {
       
   562         isOpaqueRoot = true;
       
   563         if (!bgLayer->next() && !(bgColor.isValid() && bgColor.alpha() == 255) && view()->frameView()) {
       
   564             Element* ownerElement = document()->ownerElement();
       
   565             if (ownerElement) {
       
   566                 if (!ownerElement->hasTagName(frameTag)) {
       
   567                     // Locate the <body> element using the DOM.  This is easier than trying
       
   568                     // to crawl around a render tree with potential :before/:after content and
       
   569                     // anonymous blocks created by inline <body> tags etc.  We can locate the <body>
       
   570                     // render object very easily via the DOM.
       
   571                     HTMLElement* body = document()->body();
       
   572                     if (body) {
       
   573                         // Can't scroll a frameset document anyway.
       
   574                         isOpaqueRoot = body->hasLocalName(framesetTag);
       
   575                     }
       
   576                 }
       
   577             } else
       
   578                 isOpaqueRoot = !view()->frameView()->isTransparent();
       
   579         }
       
   580         view()->frameView()->setContentIsOpaque(isOpaqueRoot);
       
   581     }
       
   582 
       
   583     // Paint the color first underneath all images.
       
   584     if (!bgLayer->next()) {
       
   585         IntRect rect(tx, ty, w, h);
       
   586         rect.intersect(paintInfo.rect);
       
   587         // If we have an alpha and we are painting the root element, go ahead and blend with the base background color.
       
   588         if (isOpaqueRoot) {
       
   589             Color baseColor = view()->frameView()->baseBackgroundColor();
       
   590             if (baseColor.alpha() > 0) {
       
   591                 context->save();
       
   592                 context->setCompositeOperation(CompositeCopy);
       
   593                 context->fillRect(rect, baseColor, style()->colorSpace());
       
   594                 context->restore();
       
   595             } else
       
   596                 context->clearRect(rect);
       
   597         }
       
   598 
       
   599         if (bgColor.isValid() && bgColor.alpha() > 0)
       
   600             context->fillRect(rect, bgColor, style()->colorSpace());
       
   601     }
       
   602 
       
   603     // no progressive loading of the background image
       
   604     if (shouldPaintBackgroundImage) {
       
   605         IntRect destRect;
       
   606         IntPoint phase;
       
   607         IntSize tileSize;
       
   608 
       
   609         calculateBackgroundImageGeometry(bgLayer, tx, ty, w, h, destRect, phase, tileSize);
       
   610         IntPoint destOrigin = destRect.location();
       
   611         destRect.intersect(paintInfo.rect);
       
   612         if (!destRect.isEmpty()) {
       
   613             phase += destRect.location() - destOrigin;
       
   614             CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op;
       
   615             RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this;
       
   616             Image* image = bg->image(clientForBackgroundImage, tileSize);
       
   617             bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, tileSize);
       
   618             context->drawTiledImage(image, style()->colorSpace(), destRect, phase, tileSize, compositeOp, useLowQualityScaling);
       
   619         }
       
   620     }
       
   621 
       
   622     if (bgLayer->clip() != BorderFillBox)
       
   623         // Undo the background clip
       
   624         context->restore();
       
   625 
       
   626     if (clippedToBorderRadius)
       
   627         // Undo the border radius clip
       
   628         context->restore();
       
   629         
       
   630     if (clippedWithLocalScrolling) // Undo the clip for local background attachments.
       
   631         context->restore();
       
   632 }
       
   633 
       
   634 IntSize RenderBoxModelObject::calculateFillTileSize(const FillLayer* fillLayer, IntSize positioningAreaSize) const
       
   635 {
       
   636     StyleImage* image = fillLayer->image();
       
   637     image->setImageContainerSize(positioningAreaSize); // Use the box established by background-origin.
       
   638 
       
   639     EFillSizeType type = fillLayer->size().type;
       
   640 
       
   641     switch (type) {
       
   642         case SizeLength: {
       
   643             int w = positioningAreaSize.width();
       
   644             int h = positioningAreaSize.height();
       
   645 
       
   646             Length layerWidth = fillLayer->size().size.width();
       
   647             Length layerHeight = fillLayer->size().size.height();
       
   648 
       
   649             if (layerWidth.isFixed())
       
   650                 w = layerWidth.value();
       
   651             else if (layerWidth.isPercent())
       
   652                 w = layerWidth.calcValue(positioningAreaSize.width());
       
   653             
       
   654             if (layerHeight.isFixed())
       
   655                 h = layerHeight.value();
       
   656             else if (layerHeight.isPercent())
       
   657                 h = layerHeight.calcValue(positioningAreaSize.height());
       
   658             
       
   659             // If one of the values is auto we have to use the appropriate
       
   660             // scale to maintain our aspect ratio.
       
   661             if (layerWidth.isAuto() && !layerHeight.isAuto()) {
       
   662                 IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom());
       
   663                 if (imageIntrinsicSize.height())
       
   664                     w = imageIntrinsicSize.width() * h / imageIntrinsicSize.height();        
       
   665             } else if (!layerWidth.isAuto() && layerHeight.isAuto()) {
       
   666                 IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom());
       
   667                 if (imageIntrinsicSize.width())
       
   668                     h = imageIntrinsicSize.height() * w / imageIntrinsicSize.width();
       
   669             } else if (layerWidth.isAuto() && layerHeight.isAuto()) {
       
   670                 // If both width and height are auto, use the image's intrinsic size.
       
   671                 IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom());
       
   672                 w = imageIntrinsicSize.width();
       
   673                 h = imageIntrinsicSize.height();
       
   674             }
       
   675             
       
   676             return IntSize(max(1, w), max(1, h));
       
   677         }
       
   678         case Contain:
       
   679         case Cover: {
       
   680             IntSize imageIntrinsicSize = image->imageSize(this, 1);
       
   681             float horizontalScaleFactor = imageIntrinsicSize.width()
       
   682                 ? static_cast<float>(positioningAreaSize.width()) / imageIntrinsicSize.width() : 1;
       
   683             float verticalScaleFactor = imageIntrinsicSize.height()
       
   684                 ? static_cast<float>(positioningAreaSize.height()) / imageIntrinsicSize.height() : 1;
       
   685             float scaleFactor = type == Contain ? min(horizontalScaleFactor, verticalScaleFactor) : max(horizontalScaleFactor, verticalScaleFactor);
       
   686             return IntSize(max<int>(1, imageIntrinsicSize.width() * scaleFactor), max<int>(1, imageIntrinsicSize.height() * scaleFactor));
       
   687         }
       
   688         case SizeNone:
       
   689             break;
       
   690     }
       
   691 
       
   692     return image->imageSize(this, style()->effectiveZoom());
       
   693 }
       
   694 
       
   695 void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* fillLayer, int tx, int ty, int w, int h, 
       
   696                                                             IntRect& destRect, IntPoint& phase, IntSize& tileSize)
       
   697 {
       
   698     int left = 0;
       
   699     int top = 0;
       
   700     IntSize positioningAreaSize;
       
   701 
       
   702     // Determine the background positioning area and set destRect to the background painting area.
       
   703     // destRect will be adjusted later if the background is non-repeating.
       
   704     bool fixedAttachment = fillLayer->attachment() == FixedBackgroundAttachment;
       
   705 
       
   706 #if ENABLE(FAST_MOBILE_SCROLLING)
       
   707     if (view()->frameView() && view()->frameView()->canBlitOnScroll()) {
       
   708         // As a side effect of an optimization to blit on scroll, we do not honor the CSS
       
   709         // property "background-attachment: fixed" because it may result in rendering
       
   710         // artifacts. Note, these artifacts only appear if we are blitting on scroll of
       
   711         // a page that has fixed background images.
       
   712         fixedAttachment = false;
       
   713     }
       
   714 #endif
       
   715 
       
   716     if (!fixedAttachment) {
       
   717         destRect = IntRect(tx, ty, w, h);
       
   718 
       
   719         int right = 0;
       
   720         int bottom = 0;
       
   721         // Scroll and Local.
       
   722         if (fillLayer->origin() != BorderFillBox) {
       
   723             left = borderLeft();
       
   724             right = borderRight();
       
   725             top = borderTop();
       
   726             bottom = borderBottom();
       
   727             if (fillLayer->origin() == ContentFillBox) {
       
   728                 left += paddingLeft();
       
   729                 right += paddingRight();
       
   730                 top += paddingTop();
       
   731                 bottom += paddingBottom();
       
   732             }
       
   733         }
       
   734 
       
   735         // The background of the box generated by the root element covers the entire canvas including
       
   736         // its margins. Since those were added in already, we have to factor them out when computing
       
   737         // the background positioning area.
       
   738         if (isRoot()) {
       
   739             positioningAreaSize = IntSize(toRenderBox(this)->width() - left - right, toRenderBox(this)->height() - top - bottom);
       
   740             left += marginLeft();
       
   741             top += marginTop();
       
   742         } else
       
   743             positioningAreaSize = IntSize(w - left - right, h - top - bottom);
       
   744     } else {
       
   745         destRect = viewRect();
       
   746         positioningAreaSize = destRect.size();
       
   747     }
       
   748 
       
   749     tileSize = calculateFillTileSize(fillLayer, positioningAreaSize);
       
   750 
       
   751     EFillRepeat backgroundRepeatX = fillLayer->repeatX();
       
   752     EFillRepeat backgroundRepeatY = fillLayer->repeatY();
       
   753 
       
   754     int xPosition = fillLayer->xPosition().calcMinValue(positioningAreaSize.width() - tileSize.width(), true);
       
   755     if (backgroundRepeatX == RepeatFill)
       
   756         phase.setX(tileSize.width() ? tileSize.width() - (xPosition + left) % tileSize.width() : 0);
       
   757     else {
       
   758         destRect.move(max(xPosition + left, 0), 0);
       
   759         phase.setX(-min(xPosition + left, 0));
       
   760         destRect.setWidth(tileSize.width() + min(xPosition + left, 0));
       
   761     }
       
   762 
       
   763     int yPosition = fillLayer->yPosition().calcMinValue(positioningAreaSize.height() - tileSize.height(), true);
       
   764     if (backgroundRepeatY == RepeatFill)
       
   765         phase.setY(tileSize.height() ? tileSize.height() - (yPosition + top) % tileSize.height() : 0);
       
   766     else {
       
   767         destRect.move(0, max(yPosition + top, 0));
       
   768         phase.setY(-min(yPosition + top, 0));
       
   769         destRect.setHeight(tileSize.height() + min(yPosition + top, 0));
       
   770     }
       
   771 
       
   772     if (fixedAttachment)
       
   773         phase.move(max(tx - destRect.x(), 0), max(ty - destRect.y(), 0));
       
   774 
       
   775     destRect.intersect(IntRect(tx, ty, w, h));
       
   776 }
       
   777 
       
   778 int RenderBoxModelObject::verticalPosition(bool firstLine) const
       
   779 {
       
   780     // This method determines the vertical position for inline elements.
       
   781     ASSERT(isInline());
       
   782     if (!isInline())
       
   783         return 0;
       
   784 
       
   785     int vpos = 0;
       
   786     EVerticalAlign va = style()->verticalAlign();
       
   787     if (va == TOP)
       
   788         vpos = PositionTop;
       
   789     else if (va == BOTTOM)
       
   790         vpos = PositionBottom;
       
   791     else {
       
   792         bool checkParent = parent()->isRenderInline() && parent()->style()->verticalAlign() != TOP && parent()->style()->verticalAlign() != BOTTOM;
       
   793         vpos = checkParent ? toRenderInline(parent())->verticalPositionFromCache(firstLine) : 0;
       
   794         // don't allow elements nested inside text-top to have a different valignment.
       
   795         if (va == BASELINE)
       
   796             return vpos;
       
   797 
       
   798         const Font& f = parent()->style(firstLine)->font();
       
   799         int fontsize = f.pixelSize();
       
   800 
       
   801         if (va == SUB)
       
   802             vpos += fontsize / 5 + 1;
       
   803         else if (va == SUPER)
       
   804             vpos -= fontsize / 3 + 1;
       
   805         else if (va == TEXT_TOP)
       
   806             vpos += baselinePosition(firstLine) - f.ascent();
       
   807         else if (va == MIDDLE)
       
   808             vpos += -static_cast<int>(f.xHeight() / 2) - lineHeight(firstLine) / 2 + baselinePosition(firstLine);
       
   809         else if (va == TEXT_BOTTOM) {
       
   810             vpos += f.descent();
       
   811             // lineHeight - baselinePosition is always 0 for replaced elements (except inline blocks), so don't bother wasting time in that case.
       
   812             if (!isReplaced() || style()->display() == INLINE_BLOCK)
       
   813                 vpos -= (lineHeight(firstLine) - baselinePosition(firstLine));
       
   814         } else if (va == BASELINE_MIDDLE)
       
   815             vpos += -lineHeight(firstLine) / 2 + baselinePosition(firstLine);
       
   816         else if (va == LENGTH)
       
   817             vpos -= style()->verticalAlignLength().calcValue(lineHeight(firstLine));
       
   818     }
       
   819 
       
   820     return vpos;
       
   821 }
       
   822 
       
   823 bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style,
       
   824                                                const NinePieceImage& ninePieceImage, CompositeOperator op)
       
   825 {
       
   826     StyleImage* styleImage = ninePieceImage.image();
       
   827     if (!styleImage)
       
   828         return false;
       
   829 
       
   830     if (!styleImage->isLoaded())
       
   831         return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either.
       
   832 
       
   833     if (!styleImage->canRender(style->effectiveZoom()))
       
   834         return false;
       
   835 
       
   836     // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function
       
   837     // doesn't have any understanding of the zoom that is in effect on the tile.
       
   838     styleImage->setImageContainerSize(IntSize(w, h));
       
   839     IntSize imageSize = styleImage->imageSize(this, 1.0f);
       
   840     int imageWidth = imageSize.width();
       
   841     int imageHeight = imageSize.height();
       
   842 
       
   843     int topSlice = min(imageHeight, ninePieceImage.slices().top().calcValue(imageHeight));
       
   844     int bottomSlice = min(imageHeight, ninePieceImage.slices().bottom().calcValue(imageHeight));
       
   845     int leftSlice = min(imageWidth, ninePieceImage.slices().left().calcValue(imageWidth));
       
   846     int rightSlice = min(imageWidth, ninePieceImage.slices().right().calcValue(imageWidth));
       
   847 
       
   848     ENinePieceImageRule hRule = ninePieceImage.horizontalRule();
       
   849     ENinePieceImageRule vRule = ninePieceImage.verticalRule();
       
   850 
       
   851     bool fitToBorder = style->borderImage() == ninePieceImage;
       
   852     
       
   853     int leftWidth = fitToBorder ? style->borderLeftWidth() : leftSlice;
       
   854     int topWidth = fitToBorder ? style->borderTopWidth() : topSlice;
       
   855     int rightWidth = fitToBorder ? style->borderRightWidth() : rightSlice;
       
   856     int bottomWidth = fitToBorder ? style->borderBottomWidth() : bottomSlice;
       
   857 
       
   858     bool drawLeft = leftSlice > 0 && leftWidth > 0;
       
   859     bool drawTop = topSlice > 0 && topWidth > 0;
       
   860     bool drawRight = rightSlice > 0 && rightWidth > 0;
       
   861     bool drawBottom = bottomSlice > 0 && bottomWidth > 0;
       
   862     bool drawMiddle = (imageWidth - leftSlice - rightSlice) > 0 && (w - leftWidth - rightWidth) > 0 &&
       
   863                       (imageHeight - topSlice - bottomSlice) > 0 && (h - topWidth - bottomWidth) > 0;
       
   864 
       
   865     Image* image = styleImage->image(this, imageSize);
       
   866     ColorSpace colorSpace = style->colorSpace();
       
   867 
       
   868     if (drawLeft) {
       
   869         // Paint the top and bottom left corners.
       
   870 
       
   871         // The top left corner rect is (tx, ty, leftWidth, topWidth)
       
   872         // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice)
       
   873         if (drawTop)
       
   874             graphicsContext->drawImage(image, colorSpace, IntRect(tx, ty, leftWidth, topWidth),
       
   875                                        IntRect(0, 0, leftSlice, topSlice), op);
       
   876 
       
   877         // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth)
       
   878         // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice)
       
   879         if (drawBottom)
       
   880             graphicsContext->drawImage(image, colorSpace, IntRect(tx, ty + h - bottomWidth, leftWidth, bottomWidth),
       
   881                                        IntRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op);
       
   882 
       
   883         // Paint the left edge.
       
   884         // Have to scale and tile into the border rect.
       
   885         graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx, ty + topWidth, leftWidth,
       
   886                                         h - topWidth - bottomWidth),
       
   887                                         IntRect(0, topSlice, leftSlice, imageHeight - topSlice - bottomSlice),
       
   888                                         Image::StretchTile, (Image::TileRule)vRule, op);
       
   889     }
       
   890 
       
   891     if (drawRight) {
       
   892         // Paint the top and bottom right corners
       
   893         // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth)
       
   894         // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice)
       
   895         if (drawTop)
       
   896             graphicsContext->drawImage(image, colorSpace, IntRect(tx + w - rightWidth, ty, rightWidth, topWidth),
       
   897                                        IntRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op);
       
   898 
       
   899         // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth)
       
   900         // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice)
       
   901         if (drawBottom)
       
   902             graphicsContext->drawImage(image, colorSpace, IntRect(tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth),
       
   903                                        IntRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op);
       
   904 
       
   905         // Paint the right edge.
       
   906         graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + w - rightWidth, ty + topWidth, rightWidth,
       
   907                                         h - topWidth - bottomWidth),
       
   908                                         IntRect(imageWidth - rightSlice, topSlice, rightSlice, imageHeight - topSlice - bottomSlice),
       
   909                                         Image::StretchTile, (Image::TileRule)vRule, op);
       
   910     }
       
   911 
       
   912     // Paint the top edge.
       
   913     if (drawTop)
       
   914         graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + leftWidth, ty, w - leftWidth - rightWidth, topWidth),
       
   915                                         IntRect(leftSlice, 0, imageWidth - rightSlice - leftSlice, topSlice),
       
   916                                         (Image::TileRule)hRule, Image::StretchTile, op);
       
   917 
       
   918     // Paint the bottom edge.
       
   919     if (drawBottom)
       
   920         graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + leftWidth, ty + h - bottomWidth,
       
   921                                         w - leftWidth - rightWidth, bottomWidth),
       
   922                                         IntRect(leftSlice, imageHeight - bottomSlice, imageWidth - rightSlice - leftSlice, bottomSlice),
       
   923                                         (Image::TileRule)hRule, Image::StretchTile, op);
       
   924 
       
   925     // Paint the middle.
       
   926     if (drawMiddle)
       
   927         graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + leftWidth, ty + topWidth, w - leftWidth - rightWidth,
       
   928                                         h - topWidth - bottomWidth),
       
   929                                         IntRect(leftSlice, topSlice, imageWidth - rightSlice - leftSlice, imageHeight - topSlice - bottomSlice),
       
   930                                         (Image::TileRule)hRule, (Image::TileRule)vRule, op);
       
   931 
       
   932     return true;
       
   933 }
       
   934 
       
   935 #if HAVE(PATH_BASED_BORDER_RADIUS_DRAWING)
       
   936 static bool borderWillArcInnerEdge(const IntSize& firstRadius, const IntSize& secondRadius, int firstBorderWidth, int secondBorderWidth, int middleBorderWidth)
       
   937 {
       
   938     // FIXME: This test is insufficient. We need to take border style into account.
       
   939     return (!firstRadius.width() || firstRadius.width() >= firstBorderWidth)
       
   940             && (!firstRadius.height() || firstRadius.height() >= middleBorderWidth)
       
   941             && (!secondRadius.width() || secondRadius.width() >= secondBorderWidth)
       
   942             && (!secondRadius.height() || secondRadius.height() >= middleBorderWidth);
       
   943 }
       
   944 
       
   945 void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h,
       
   946                                        const RenderStyle* style, bool begin, bool end)
       
   947 {
       
   948     if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage()))
       
   949         return;
       
   950 
       
   951     if (graphicsContext->paintingDisabled())
       
   952         return;
       
   953 
       
   954     const Color& topColor = style->visitedDependentColor(CSSPropertyBorderTopColor);
       
   955     const Color& bottomColor = style->visitedDependentColor(CSSPropertyBorderBottomColor);
       
   956     const Color& leftColor = style->visitedDependentColor(CSSPropertyBorderLeftColor);
       
   957     const Color& rightColor = style->visitedDependentColor(CSSPropertyBorderRightColor);
       
   958 
       
   959     bool topTransparent = style->borderTopIsTransparent();
       
   960     bool bottomTransparent = style->borderBottomIsTransparent();
       
   961     bool rightTransparent = style->borderRightIsTransparent();
       
   962     bool leftTransparent = style->borderLeftIsTransparent();
       
   963 
       
   964     EBorderStyle topStyle = style->borderTopStyle();
       
   965     EBorderStyle bottomStyle = style->borderBottomStyle();
       
   966     EBorderStyle leftStyle = style->borderLeftStyle();
       
   967     EBorderStyle rightStyle = style->borderRightStyle();
       
   968 
       
   969     bool renderTop = topStyle > BHIDDEN && !topTransparent;
       
   970     bool renderLeft = leftStyle > BHIDDEN && begin && !leftTransparent;
       
   971     bool renderRight = rightStyle > BHIDDEN && end && !rightTransparent;
       
   972     bool renderBottom = bottomStyle > BHIDDEN && !bottomTransparent;
       
   973 
       
   974     bool renderRadii = false;
       
   975     Path roundedPath;
       
   976     IntSize topLeft, topRight, bottomLeft, bottomRight;
       
   977     IntRect borderRect(tx, ty, w, h);
       
   978 
       
   979     if (style->hasBorderRadius()) {
       
   980         IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius;
       
   981         style->getBorderRadiiForRect(borderRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
       
   982 
       
   983         IntRect innerBorderRect = borderInnerRect(borderRect, style->borderTopWidth(), style->borderBottomWidth(), 
       
   984             style->borderLeftWidth(), style->borderRightWidth());
       
   985 
       
   986         IntSize innerTopLeftRadius, innerTopRightRadius, innerBottomLeftRadius, innerBottomRightRadius;
       
   987         style->getInnerBorderRadiiForRectWithBorderWidths(innerBorderRect, style->borderTopWidth(), style->borderBottomWidth(), 
       
   988             style->borderLeftWidth(), style->borderRightWidth(), innerTopLeftRadius, innerTopRightRadius, 
       
   989             innerBottomLeftRadius, innerBottomRightRadius);
       
   990 
       
   991         if (begin) {
       
   992             topLeft = topLeftRadius;
       
   993             bottomLeft = bottomLeftRadius;
       
   994         }
       
   995         if (end) {
       
   996             topRight = topRightRadius;
       
   997             bottomRight = bottomRightRadius;
       
   998         }
       
   999 
       
  1000         renderRadii = true;
       
  1001 
       
  1002         // Clip to the inner and outer radii rects.
       
  1003         graphicsContext->save();
       
  1004         graphicsContext->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight);
       
  1005         graphicsContext->clipOutRoundedRect(innerBorderRect, innerTopLeftRadius, innerTopRightRadius, innerBottomLeftRadius, innerBottomRightRadius);
       
  1006 
       
  1007         roundedPath = Path::createRoundedRectangle(borderRect, topLeft, topRight, bottomLeft, bottomRight);
       
  1008         graphicsContext->addPath(roundedPath);
       
  1009     }
       
  1010 
       
  1011     bool upperLeftBorderStylesMatch = renderLeft && (topStyle == leftStyle) && (topColor == leftColor);
       
  1012     bool upperRightBorderStylesMatch = renderRight && (topStyle == rightStyle) && (topColor == rightColor) && (topStyle != OUTSET) && (topStyle != RIDGE) && (topStyle != INSET) && (topStyle != GROOVE);
       
  1013     bool lowerLeftBorderStylesMatch = renderLeft && (bottomStyle == leftStyle) && (bottomColor == leftColor) && (bottomStyle != OUTSET) && (bottomStyle != RIDGE) && (bottomStyle != INSET) && (bottomStyle != GROOVE);
       
  1014     bool lowerRightBorderStylesMatch = renderRight && (bottomStyle == rightStyle) && (bottomColor == rightColor);
       
  1015 
       
  1016     if (renderTop) {
       
  1017         int x = tx;
       
  1018         int x2 = tx + w;
       
  1019 
       
  1020         if (renderRadii && borderWillArcInnerEdge(topLeft, topRight, style->borderLeftWidth(), style->borderRightWidth(), style->borderTopWidth())) {
       
  1021             graphicsContext->save();
       
  1022             clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSTop, upperLeftBorderStylesMatch, upperRightBorderStylesMatch, style);
       
  1023             float thickness = max(max(style->borderTopWidth(), style->borderLeftWidth()), style->borderRightWidth());
       
  1024             drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderTopWidth(), thickness, BSTop, style, topColor, topStyle);
       
  1025             graphicsContext->restore();
       
  1026         } else {
       
  1027             bool ignoreLeft = (topColor == leftColor && topTransparent == leftTransparent && topStyle >= OUTSET
       
  1028                 && (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET));
       
  1029             bool ignoreRight = (topColor == rightColor && topTransparent == rightTransparent && topStyle >= OUTSET
       
  1030                 && (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET));
       
  1031 
       
  1032             drawLineForBoxSide(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, topColor, topStyle,
       
  1033                     ignoreLeft ? 0 : style->borderLeftWidth(), ignoreRight ? 0 : style->borderRightWidth());               
       
  1034         }
       
  1035     }
       
  1036 
       
  1037     if (renderBottom) {
       
  1038         int x = tx;
       
  1039         int x2 = tx + w;
       
  1040 
       
  1041         if (renderRadii && borderWillArcInnerEdge(bottomLeft, bottomRight, style->borderLeftWidth(), style->borderRightWidth(), style->borderBottomWidth())) {
       
  1042             graphicsContext->save();
       
  1043             clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSBottom, lowerLeftBorderStylesMatch, lowerRightBorderStylesMatch, style);
       
  1044             float thickness = max(max(style->borderBottomWidth(), style->borderLeftWidth()), style->borderRightWidth());
       
  1045             drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderBottomWidth(), thickness, BSBottom, style, bottomColor, bottomStyle);
       
  1046             graphicsContext->restore();
       
  1047         } else {
       
  1048             bool ignoreLeft = (bottomColor == leftColor && bottomTransparent == leftTransparent && bottomStyle >= OUTSET
       
  1049                 && (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET));
       
  1050 
       
  1051             bool ignoreRight = (bottomColor == rightColor && bottomTransparent == rightTransparent && bottomStyle >= OUTSET
       
  1052                 && (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET));
       
  1053 
       
  1054             drawLineForBoxSide(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bottomColor, 
       
  1055                         bottomStyle, ignoreLeft ? 0 : style->borderLeftWidth(), 
       
  1056                         ignoreRight ? 0 : style->borderRightWidth());
       
  1057         }
       
  1058     }
       
  1059 
       
  1060     if (renderLeft) {
       
  1061         int y = ty;
       
  1062         int y2 = ty + h;
       
  1063 
       
  1064         if (renderRadii && borderWillArcInnerEdge(bottomLeft, topLeft, style->borderBottomWidth(), style->borderTopWidth(), style->borderLeftWidth())) {
       
  1065             graphicsContext->save();
       
  1066             clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSLeft, upperLeftBorderStylesMatch, lowerLeftBorderStylesMatch, style);
       
  1067             float thickness = max(max(style->borderLeftWidth(), style->borderTopWidth()), style->borderBottomWidth());
       
  1068             drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderLeftWidth(), thickness, BSLeft, style, leftColor, leftStyle);
       
  1069             graphicsContext->restore();
       
  1070         } else {
       
  1071             bool ignoreTop = (topColor == leftColor && topTransparent == leftTransparent && leftStyle >= OUTSET
       
  1072                 && (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET));
       
  1073 
       
  1074             bool ignoreBottom = (bottomColor == leftColor && bottomTransparent == leftTransparent && leftStyle >= OUTSET
       
  1075                 && (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET));
       
  1076 
       
  1077             drawLineForBoxSide(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, leftColor,
       
  1078                         leftStyle, ignoreTop ? 0 : style->borderTopWidth(), ignoreBottom ? 0 : style->borderBottomWidth());
       
  1079         }
       
  1080     }
       
  1081 
       
  1082     if (renderRight) {
       
  1083         if (renderRadii && borderWillArcInnerEdge(bottomRight, topRight, style->borderBottomWidth(), style->borderTopWidth(), style->borderRightWidth())) {
       
  1084             graphicsContext->save();
       
  1085             clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSRight, upperRightBorderStylesMatch, lowerRightBorderStylesMatch, style);
       
  1086             float thickness = max(max(style->borderRightWidth(), style->borderTopWidth()), style->borderBottomWidth());
       
  1087             drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderRightWidth(), thickness, BSRight, style, rightColor, rightStyle);
       
  1088             graphicsContext->restore();
       
  1089         } else {
       
  1090             bool ignoreTop = ((topColor == rightColor) && (topTransparent == rightTransparent)
       
  1091                 && (rightStyle >= DOTTED || rightStyle == INSET)
       
  1092                 && (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET));
       
  1093 
       
  1094             bool ignoreBottom = ((bottomColor == rightColor) && (bottomTransparent == rightTransparent)
       
  1095                 && (rightStyle >= DOTTED || rightStyle == INSET)
       
  1096                 && (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET));
       
  1097 
       
  1098             int y = ty;
       
  1099             int y2 = ty + h;
       
  1100 
       
  1101             drawLineForBoxSide(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rightColor, 
       
  1102                 rightStyle, ignoreTop ? 0 : style->borderTopWidth(), 
       
  1103                 ignoreBottom ? 0 : style->borderBottomWidth());
       
  1104         }
       
  1105     }
       
  1106 
       
  1107     if (renderRadii)
       
  1108         graphicsContext->restore();
       
  1109 }
       
  1110 #else
       
  1111 void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h,
       
  1112                                        const RenderStyle* style, bool begin, bool end)
       
  1113 {
       
  1114     // FIXME: This old version of paintBorder should be removed when all ports implement 
       
  1115     // GraphicsContext::clipConvexPolygon()!! This should happen soon.
       
  1116     if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage()))
       
  1117         return;
       
  1118 
       
  1119     const Color& topColor = style->visitedDependentColor(CSSPropertyBorderTopColor);
       
  1120     const Color& bottomColor = style->visitedDependentColor(CSSPropertyBorderBottomColor);
       
  1121     const Color& leftColor = style->visitedDependentColor(CSSPropertyBorderLeftColor);
       
  1122     const Color& rightColor = style->visitedDependentColor(CSSPropertyBorderRightColor);
       
  1123 
       
  1124     bool topTransparent = style->borderTopIsTransparent();
       
  1125     bool bottomTransparent = style->borderBottomIsTransparent();
       
  1126     bool rightTransparent = style->borderRightIsTransparent();
       
  1127     bool leftTransparent = style->borderLeftIsTransparent();
       
  1128 
       
  1129     EBorderStyle topStyle = style->borderTopStyle();
       
  1130     EBorderStyle bottomStyle = style->borderBottomStyle();
       
  1131     EBorderStyle leftStyle = style->borderLeftStyle();
       
  1132     EBorderStyle rightStyle = style->borderRightStyle();
       
  1133 
       
  1134     bool renderTop = topStyle > BHIDDEN && !topTransparent;
       
  1135     bool renderLeft = leftStyle > BHIDDEN && begin && !leftTransparent;
       
  1136     bool renderRight = rightStyle > BHIDDEN && end && !rightTransparent;
       
  1137     bool renderBottom = bottomStyle > BHIDDEN && !bottomTransparent;
       
  1138 
       
  1139     bool renderRadii = false;
       
  1140     IntSize topLeft, topRight, bottomLeft, bottomRight;
       
  1141 
       
  1142     if (style->hasBorderRadius()) {
       
  1143         IntRect borderRect = IntRect(tx, ty, w, h);
       
  1144 
       
  1145         IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius;
       
  1146         style->getBorderRadiiForRect(borderRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
       
  1147 
       
  1148         if (begin) {
       
  1149             topLeft = topLeftRadius;
       
  1150             bottomLeft = bottomLeftRadius;
       
  1151         }
       
  1152         if (end) {
       
  1153             topRight = topRightRadius;
       
  1154             bottomRight = bottomRightRadius;
       
  1155         }
       
  1156 
       
  1157         renderRadii = true;
       
  1158 
       
  1159         // Clip to the rounded rectangle.
       
  1160         graphicsContext->save();
       
  1161         graphicsContext->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight);
       
  1162     }
       
  1163 
       
  1164     int firstAngleStart, secondAngleStart, firstAngleSpan, secondAngleSpan;
       
  1165     float thickness;
       
  1166     bool upperLeftBorderStylesMatch = renderLeft && (topStyle == leftStyle) && (topColor == leftColor);
       
  1167     bool upperRightBorderStylesMatch = renderRight && (topStyle == rightStyle) && (topColor == rightColor) && (topStyle != OUTSET) && (topStyle != RIDGE) && (topStyle != INSET) && (topStyle != GROOVE);
       
  1168     bool lowerLeftBorderStylesMatch = renderLeft && (bottomStyle == leftStyle) && (bottomColor == leftColor) && (bottomStyle != OUTSET) && (bottomStyle != RIDGE) && (bottomStyle != INSET) && (bottomStyle != GROOVE);
       
  1169     bool lowerRightBorderStylesMatch = renderRight && (bottomStyle == rightStyle) && (bottomColor == rightColor);
       
  1170 
       
  1171     if (renderTop) {
       
  1172         bool ignore_left = (renderRadii && topLeft.width() > 0) ||
       
  1173             (topColor == leftColor && topTransparent == leftTransparent && topStyle >= OUTSET &&
       
  1174              (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET));
       
  1175 
       
  1176         bool ignore_right = (renderRadii && topRight.width() > 0) ||
       
  1177             (topColor == rightColor && topTransparent == rightTransparent && topStyle >= OUTSET &&
       
  1178              (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET));
       
  1179 
       
  1180         int x = tx;
       
  1181         int x2 = tx + w;
       
  1182         if (renderRadii) {
       
  1183             x += topLeft.width();
       
  1184             x2 -= topRight.width();
       
  1185         }
       
  1186 
       
  1187         drawLineForBoxSide(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, topColor, topStyle,
       
  1188                    ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth());
       
  1189 
       
  1190         if (renderRadii) {
       
  1191             int leftY = ty;
       
  1192 
       
  1193             // We make the arc double thick and let the clip rect take care of clipping the extra off.
       
  1194             // We're doing this because it doesn't seem possible to match the curve of the clip exactly
       
  1195             // with the arc-drawing function.
       
  1196             thickness = style->borderTopWidth() * 2;
       
  1197 
       
  1198             if (topLeft.width()) {
       
  1199                 int leftX = tx;
       
  1200                 // The inner clip clips inside the arc. This is especially important for 1px borders.
       
  1201                 bool applyLeftInnerClip = (style->borderLeftWidth() < topLeft.width())
       
  1202                     && (style->borderTopWidth() < topLeft.height())
       
  1203                     && (topStyle != DOUBLE || style->borderTopWidth() > 6);
       
  1204                 if (applyLeftInnerClip) {
       
  1205                     graphicsContext->save();
       
  1206                     graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, topLeft.width() * 2, topLeft.height() * 2),
       
  1207                                                              style->borderTopWidth());
       
  1208                 }
       
  1209 
       
  1210                 firstAngleStart = 90;
       
  1211                 firstAngleSpan = upperLeftBorderStylesMatch ? 90 : 45;
       
  1212 
       
  1213                 // Draw upper left arc
       
  1214                 drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, topLeft, firstAngleStart, firstAngleSpan,
       
  1215                               BSTop, topColor, topStyle, true);
       
  1216                 if (applyLeftInnerClip)
       
  1217                     graphicsContext->restore();
       
  1218             }
       
  1219 
       
  1220             if (topRight.width()) {
       
  1221                 int rightX = tx + w - topRight.width() * 2;
       
  1222                 bool applyRightInnerClip = (style->borderRightWidth() < topRight.width())
       
  1223                     && (style->borderTopWidth() < topRight.height())
       
  1224                     && (topStyle != DOUBLE || style->borderTopWidth() > 6);
       
  1225                 if (applyRightInnerClip) {
       
  1226                     graphicsContext->save();
       
  1227                     graphicsContext->addInnerRoundedRectClip(IntRect(rightX, leftY, topRight.width() * 2, topRight.height() * 2),
       
  1228                                                              style->borderTopWidth());
       
  1229                 }
       
  1230 
       
  1231                 if (upperRightBorderStylesMatch) {
       
  1232                     secondAngleStart = 0;
       
  1233                     secondAngleSpan = 90;
       
  1234                 } else {
       
  1235                     secondAngleStart = 45;
       
  1236                     secondAngleSpan = 45;
       
  1237                 }
       
  1238 
       
  1239                 // Draw upper right arc
       
  1240                 drawArcForBoxSide(graphicsContext, rightX, leftY, thickness, topRight, secondAngleStart, secondAngleSpan,
       
  1241                               BSTop, topColor, topStyle, false);
       
  1242                 if (applyRightInnerClip)
       
  1243                     graphicsContext->restore();
       
  1244             }
       
  1245         }
       
  1246     }
       
  1247 
       
  1248     if (renderBottom) {
       
  1249         bool ignore_left = (renderRadii && bottomLeft.width() > 0) ||
       
  1250             (bottomColor == leftColor && bottomTransparent == leftTransparent && bottomStyle >= OUTSET &&
       
  1251              (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET));
       
  1252 
       
  1253         bool ignore_right = (renderRadii && bottomRight.width() > 0) ||
       
  1254             (bottomColor == rightColor && bottomTransparent == rightTransparent && bottomStyle >= OUTSET &&
       
  1255              (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET));
       
  1256 
       
  1257         int x = tx;
       
  1258         int x2 = tx + w;
       
  1259         if (renderRadii) {
       
  1260             x += bottomLeft.width();
       
  1261             x2 -= bottomRight.width();
       
  1262         }
       
  1263 
       
  1264         drawLineForBoxSide(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bottomColor, bottomStyle,
       
  1265                    ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth());
       
  1266 
       
  1267         if (renderRadii) {
       
  1268             thickness = style->borderBottomWidth() * 2;
       
  1269 
       
  1270             if (bottomLeft.width()) {
       
  1271                 int leftX = tx;
       
  1272                 int leftY = ty + h - bottomLeft.height() * 2;
       
  1273                 bool applyLeftInnerClip = (style->borderLeftWidth() < bottomLeft.width())
       
  1274                     && (style->borderBottomWidth() < bottomLeft.height())
       
  1275                     && (bottomStyle != DOUBLE || style->borderBottomWidth() > 6);
       
  1276                 if (applyLeftInnerClip) {
       
  1277                     graphicsContext->save();
       
  1278                     graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, bottomLeft.width() * 2, bottomLeft.height() * 2),
       
  1279                                                              style->borderBottomWidth());
       
  1280                 }
       
  1281 
       
  1282                 if (lowerLeftBorderStylesMatch) {
       
  1283                     firstAngleStart = 180;
       
  1284                     firstAngleSpan = 90;
       
  1285                 } else {
       
  1286                     firstAngleStart = 225;
       
  1287                     firstAngleSpan = 45;
       
  1288                 }
       
  1289 
       
  1290                 // Draw lower left arc
       
  1291                 drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, bottomLeft, firstAngleStart, firstAngleSpan,
       
  1292                               BSBottom, bottomColor, bottomStyle, true);
       
  1293                 if (applyLeftInnerClip)
       
  1294                     graphicsContext->restore();
       
  1295             }
       
  1296 
       
  1297             if (bottomRight.width()) {
       
  1298                 int rightY = ty + h - bottomRight.height() * 2;
       
  1299                 int rightX = tx + w - bottomRight.width() * 2;
       
  1300                 bool applyRightInnerClip = (style->borderRightWidth() < bottomRight.width())
       
  1301                     && (style->borderBottomWidth() < bottomRight.height())
       
  1302                     && (bottomStyle != DOUBLE || style->borderBottomWidth() > 6);
       
  1303                 if (applyRightInnerClip) {
       
  1304                     graphicsContext->save();
       
  1305                     graphicsContext->addInnerRoundedRectClip(IntRect(rightX, rightY, bottomRight.width() * 2, bottomRight.height() * 2),
       
  1306                                                              style->borderBottomWidth());
       
  1307                 }
       
  1308 
       
  1309                 secondAngleStart = 270;
       
  1310                 secondAngleSpan = lowerRightBorderStylesMatch ? 90 : 45;
       
  1311 
       
  1312                 // Draw lower right arc
       
  1313                 drawArcForBoxSide(graphicsContext, rightX, rightY, thickness, bottomRight, secondAngleStart, secondAngleSpan,
       
  1314                               BSBottom, bottomColor, bottomStyle, false);
       
  1315                 if (applyRightInnerClip)
       
  1316                     graphicsContext->restore();
       
  1317             }
       
  1318         }
       
  1319     }
       
  1320 
       
  1321     if (renderLeft) {
       
  1322         bool ignore_top = (renderRadii && topLeft.height() > 0) ||
       
  1323             (topColor == leftColor && topTransparent == leftTransparent && leftStyle >= OUTSET &&
       
  1324              (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET));
       
  1325 
       
  1326         bool ignore_bottom = (renderRadii && bottomLeft.height() > 0) ||
       
  1327             (bottomColor == leftColor && bottomTransparent == leftTransparent && leftStyle >= OUTSET &&
       
  1328              (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET));
       
  1329 
       
  1330         int y = ty;
       
  1331         int y2 = ty + h;
       
  1332         if (renderRadii) {
       
  1333             y += topLeft.height();
       
  1334             y2 -= bottomLeft.height();
       
  1335         }
       
  1336 
       
  1337         drawLineForBoxSide(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, leftColor, leftStyle,
       
  1338                    ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth());
       
  1339 
       
  1340         if (renderRadii && (!upperLeftBorderStylesMatch || !lowerLeftBorderStylesMatch)) {
       
  1341             int topX = tx;
       
  1342             thickness = style->borderLeftWidth() * 2;
       
  1343 
       
  1344             if (!upperLeftBorderStylesMatch && topLeft.width()) {
       
  1345                 int topY = ty;
       
  1346                 bool applyTopInnerClip = (style->borderLeftWidth() < topLeft.width())
       
  1347                     && (style->borderTopWidth() < topLeft.height())
       
  1348                     && (leftStyle != DOUBLE || style->borderLeftWidth() > 6);
       
  1349                 if (applyTopInnerClip) {
       
  1350                     graphicsContext->save();
       
  1351                     graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topLeft.width() * 2, topLeft.height() * 2),
       
  1352                                                              style->borderLeftWidth());
       
  1353                 }
       
  1354 
       
  1355                 firstAngleStart = 135;
       
  1356                 firstAngleSpan = 45;
       
  1357 
       
  1358                 // Draw top left arc
       
  1359                 drawArcForBoxSide(graphicsContext, topX, topY, thickness, topLeft, firstAngleStart, firstAngleSpan,
       
  1360                               BSLeft, leftColor, leftStyle, true);
       
  1361                 if (applyTopInnerClip)
       
  1362                     graphicsContext->restore();
       
  1363             }
       
  1364 
       
  1365             if (!lowerLeftBorderStylesMatch && bottomLeft.width()) {
       
  1366                 int bottomY = ty + h - bottomLeft.height() * 2;
       
  1367                 bool applyBottomInnerClip = (style->borderLeftWidth() < bottomLeft.width())
       
  1368                     && (style->borderBottomWidth() < bottomLeft.height())
       
  1369                     && (leftStyle != DOUBLE || style->borderLeftWidth() > 6);
       
  1370                 if (applyBottomInnerClip) {
       
  1371                     graphicsContext->save();
       
  1372                     graphicsContext->addInnerRoundedRectClip(IntRect(topX, bottomY, bottomLeft.width() * 2, bottomLeft.height() * 2),
       
  1373                                                              style->borderLeftWidth());
       
  1374                 }
       
  1375 
       
  1376                 secondAngleStart = 180;
       
  1377                 secondAngleSpan = 45;
       
  1378 
       
  1379                 // Draw bottom left arc
       
  1380                 drawArcForBoxSide(graphicsContext, topX, bottomY, thickness, bottomLeft, secondAngleStart, secondAngleSpan,
       
  1381                               BSLeft, leftColor, leftStyle, false);
       
  1382                 if (applyBottomInnerClip)
       
  1383                     graphicsContext->restore();
       
  1384             }
       
  1385         }
       
  1386     }
       
  1387 
       
  1388     if (renderRight) {
       
  1389         bool ignore_top = (renderRadii && topRight.height() > 0) ||
       
  1390             ((topColor == rightColor) && (topTransparent == rightTransparent) &&
       
  1391             (rightStyle >= DOTTED || rightStyle == INSET) &&
       
  1392             (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET));
       
  1393 
       
  1394         bool ignore_bottom = (renderRadii && bottomRight.height() > 0) ||
       
  1395             ((bottomColor == rightColor) && (bottomTransparent == rightTransparent) &&
       
  1396             (rightStyle >= DOTTED || rightStyle == INSET) &&
       
  1397             (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET));
       
  1398 
       
  1399         int y = ty;
       
  1400         int y2 = ty + h;
       
  1401         if (renderRadii) {
       
  1402             y += topRight.height();
       
  1403             y2 -= bottomRight.height();
       
  1404         }
       
  1405 
       
  1406         drawLineForBoxSide(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rightColor, rightStyle,
       
  1407                    ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth());
       
  1408 
       
  1409         if (renderRadii && (!upperRightBorderStylesMatch || !lowerRightBorderStylesMatch)) {
       
  1410             thickness = style->borderRightWidth() * 2;
       
  1411 
       
  1412             if (!upperRightBorderStylesMatch && topRight.width()) {
       
  1413                 int topX = tx + w - topRight.width() * 2;
       
  1414                 int topY = ty;
       
  1415                 bool applyTopInnerClip = (style->borderRightWidth() < topRight.width())
       
  1416                     && (style->borderTopWidth() < topRight.height())
       
  1417                     && (rightStyle != DOUBLE || style->borderRightWidth() > 6);
       
  1418                 if (applyTopInnerClip) {
       
  1419                     graphicsContext->save();
       
  1420                     graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topRight.width() * 2, topRight.height() * 2),
       
  1421                                                              style->borderRightWidth());
       
  1422                 }
       
  1423 
       
  1424                 firstAngleStart = 0;
       
  1425                 firstAngleSpan = 45;
       
  1426 
       
  1427                 // Draw top right arc
       
  1428                 drawArcForBoxSide(graphicsContext, topX, topY, thickness, topRight, firstAngleStart, firstAngleSpan,
       
  1429                               BSRight, rightColor, rightStyle, true);
       
  1430                 if (applyTopInnerClip)
       
  1431                     graphicsContext->restore();
       
  1432             }
       
  1433 
       
  1434             if (!lowerRightBorderStylesMatch && bottomRight.width()) {
       
  1435                 int bottomX = tx + w - bottomRight.width() * 2;
       
  1436                 int bottomY = ty + h - bottomRight.height() * 2;
       
  1437                 bool applyBottomInnerClip = (style->borderRightWidth() < bottomRight.width())
       
  1438                     && (style->borderBottomWidth() < bottomRight.height())
       
  1439                     && (rightStyle != DOUBLE || style->borderRightWidth() > 6);
       
  1440                 if (applyBottomInnerClip) {
       
  1441                     graphicsContext->save();
       
  1442                     graphicsContext->addInnerRoundedRectClip(IntRect(bottomX, bottomY, bottomRight.width() * 2, bottomRight.height() * 2),
       
  1443                                                              style->borderRightWidth());
       
  1444                 }
       
  1445 
       
  1446                 secondAngleStart = 315;
       
  1447                 secondAngleSpan = 45;
       
  1448 
       
  1449                 // Draw bottom right arc
       
  1450                 drawArcForBoxSide(graphicsContext, bottomX, bottomY, thickness, bottomRight, secondAngleStart, secondAngleSpan,
       
  1451                               BSRight, rightColor, rightStyle, false);
       
  1452                 if (applyBottomInnerClip)
       
  1453                     graphicsContext->restore();
       
  1454             }
       
  1455         }
       
  1456     }
       
  1457 
       
  1458     if (renderRadii)
       
  1459         graphicsContext->restore();
       
  1460 }
       
  1461 #endif
       
  1462 
       
  1463 void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext* graphicsContext, const IntRect& box, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches, const RenderStyle* style)
       
  1464 {
       
  1465     FloatPoint quad[4];
       
  1466     int tx = box.x();
       
  1467     int ty = box.y();
       
  1468     int w = box.width();
       
  1469     int h = box.height();
       
  1470 
       
  1471     // For each side, create an array of FloatPoints where each point is based on whichever value in each corner
       
  1472     // is larger -- the radius width/height or the border width/height -- as appropriate.
       
  1473     switch (side) {
       
  1474     case BSTop:
       
  1475         quad[0] = FloatPoint(tx, ty);
       
  1476         quad[1] = FloatPoint(
       
  1477             tx + max(topLeft.width(), (int) style->borderLeftWidth()), 
       
  1478             ty + max(topLeft.height(), (int) style->borderTopWidth()));
       
  1479         quad[2] = FloatPoint(
       
  1480             tx + w - max(topRight.width(), (int) style->borderRightWidth()), 
       
  1481             ty + max(topRight.height(), (int)style->borderTopWidth()));
       
  1482         quad[3] = FloatPoint(tx + w, ty);
       
  1483         break;
       
  1484     case BSLeft:
       
  1485         quad[0] = FloatPoint(tx, ty);
       
  1486         quad[1] = FloatPoint(
       
  1487             tx + max(topLeft.width(), (int) style->borderLeftWidth()), 
       
  1488             ty + max(topLeft.height(), (int) style->borderTopWidth()));
       
  1489         quad[2] = FloatPoint(
       
  1490             tx + max(bottomLeft.width(), (int) style->borderLeftWidth()), 
       
  1491             ty + h - max(bottomLeft.height(), (int)style->borderBottomWidth()));
       
  1492         quad[3] = FloatPoint(tx, ty + h);
       
  1493         break;
       
  1494     case BSBottom:
       
  1495         quad[0] = FloatPoint(tx, ty + h);
       
  1496         quad[1] = FloatPoint(
       
  1497             tx + max(bottomLeft.width(), (int) style->borderLeftWidth()), 
       
  1498             ty + h - max(bottomLeft.height(), (int)style->borderBottomWidth()));
       
  1499         quad[2] = FloatPoint(
       
  1500             tx + w - max(bottomRight.width(), (int) style->borderRightWidth()), 
       
  1501             ty + h - max(bottomRight.height(), (int)style->borderBottomWidth()));
       
  1502         quad[3] = FloatPoint(tx + w, ty + h);
       
  1503         break;
       
  1504     case BSRight:
       
  1505         quad[0] = FloatPoint(tx + w, ty);
       
  1506         quad[1] = FloatPoint(
       
  1507             tx + w - max(topRight.width(), (int) style->borderRightWidth()), 
       
  1508             ty + max(topRight.height(), (int) style->borderTopWidth()));
       
  1509         quad[2] = FloatPoint(
       
  1510             tx + w - max(bottomRight.width(), (int) style->borderRightWidth()), 
       
  1511             ty + h - max(bottomRight.height(), (int)style->borderBottomWidth()));
       
  1512         quad[3] = FloatPoint(tx + w, ty + h);
       
  1513         break;
       
  1514     default:
       
  1515         break;
       
  1516     }
       
  1517 
       
  1518     // If the border matches both of its adjacent sides, don't anti-alias the clip, and
       
  1519     // if neither side matches, anti-alias the clip.
       
  1520     if (firstEdgeMatches == secondEdgeMatches) {
       
  1521         graphicsContext->clipConvexPolygon(4, quad, !firstEdgeMatches);
       
  1522         return;
       
  1523     }
       
  1524 
       
  1525     FloatPoint firstQuad[4];
       
  1526     firstQuad[0] = quad[0];
       
  1527     firstQuad[1] = quad[1];
       
  1528     firstQuad[2] = side == BSTop || side == BSBottom ? FloatPoint(quad[3].x(), quad[2].y())
       
  1529         : firstQuad[2] = FloatPoint(quad[2].x(), quad[3].y());
       
  1530     firstQuad[3] = quad[3];
       
  1531     graphicsContext->clipConvexPolygon(4, firstQuad, !firstEdgeMatches);
       
  1532 
       
  1533     FloatPoint secondQuad[4];
       
  1534     secondQuad[0] = quad[0];
       
  1535     secondQuad[1] = side == BSTop || side == BSBottom ? FloatPoint(quad[0].x(), quad[1].y())
       
  1536         : FloatPoint(quad[1].x(), quad[0].y());
       
  1537     secondQuad[2] = quad[2];
       
  1538     secondQuad[3] = quad[3];
       
  1539     graphicsContext->clipConvexPolygon(4, secondQuad, !secondEdgeMatches);
       
  1540 }
       
  1541 
       
  1542 void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int ty, int w, int h, const RenderStyle* s, ShadowStyle shadowStyle, bool begin, bool end)
       
  1543 {
       
  1544     // FIXME: Deal with border-image.  Would be great to use border-image as a mask.
       
  1545 
       
  1546     if (context->paintingDisabled())
       
  1547         return;
       
  1548 
       
  1549     IntRect rect(tx, ty, w, h);
       
  1550     IntSize topLeft;
       
  1551     IntSize topRight;
       
  1552     IntSize bottomLeft;
       
  1553     IntSize bottomRight;
       
  1554 
       
  1555     bool hasBorderRadius = s->hasBorderRadius();
       
  1556     if (hasBorderRadius && (begin || end)) {
       
  1557         IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius;
       
  1558         s->getBorderRadiiForRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
       
  1559 
       
  1560         if (begin) {
       
  1561             if (shadowStyle == Inset) {
       
  1562                 topLeftRadius.expand(-borderLeft(), -borderTop());
       
  1563                 topLeftRadius.clampNegativeToZero();
       
  1564                 bottomLeftRadius.expand(-borderLeft(), -borderBottom());
       
  1565                 bottomLeftRadius.clampNegativeToZero();
       
  1566             }
       
  1567             topLeft = topLeftRadius;
       
  1568             bottomLeft = bottomLeftRadius;
       
  1569         }
       
  1570         if (end) {
       
  1571             if (shadowStyle == Inset) {
       
  1572                 topRightRadius.expand(-borderRight(), -borderTop());
       
  1573                 topRightRadius.clampNegativeToZero();
       
  1574                 bottomRightRadius.expand(-borderRight(), -borderBottom());
       
  1575                 bottomRightRadius.clampNegativeToZero();
       
  1576             }
       
  1577             topRight = topRightRadius;
       
  1578             bottomRight = bottomRightRadius;
       
  1579         }
       
  1580     }
       
  1581 
       
  1582     if (shadowStyle == Inset) {
       
  1583         rect.move(begin ? borderLeft() : 0, borderTop());
       
  1584         rect.setWidth(rect.width() - (begin ? borderLeft() : 0) - (end ? borderRight() : 0));
       
  1585         rect.setHeight(rect.height() - borderTop() - borderBottom());
       
  1586     }
       
  1587 
       
  1588     bool hasOpaqueBackground = s->visitedDependentColor(CSSPropertyBackgroundColor).isValid() && s->visitedDependentColor(CSSPropertyBackgroundColor).alpha() == 255;
       
  1589     for (const ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next()) {
       
  1590         if (shadow->style() != shadowStyle)
       
  1591             continue;
       
  1592 
       
  1593         IntSize shadowOffset(shadow->x(), shadow->y());
       
  1594         int shadowBlur = shadow->blur();
       
  1595         int shadowSpread = shadow->spread();
       
  1596         const Color& shadowColor = shadow->color();
       
  1597 
       
  1598         if (shadow->style() == Normal) {
       
  1599             IntRect fillRect(rect);
       
  1600             fillRect.inflate(shadowSpread);
       
  1601             if (fillRect.isEmpty())
       
  1602                 continue;
       
  1603 
       
  1604             IntRect shadowRect(rect);
       
  1605             shadowRect.inflate(shadowBlur + shadowSpread);
       
  1606             shadowRect.move(shadowOffset);
       
  1607 
       
  1608             context->save();
       
  1609             context->clip(shadowRect);
       
  1610 
       
  1611             // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not
       
  1612             // bleed in (due to antialiasing) if the context is transformed.
       
  1613             IntSize extraOffset(w + max(0, shadowOffset.width()) + shadowBlur + 2 * shadowSpread + 1, 0);
       
  1614             shadowOffset -= extraOffset;
       
  1615             fillRect.move(extraOffset);
       
  1616 
       
  1617             context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace());
       
  1618             if (hasBorderRadius) {
       
  1619                 IntRect rectToClipOut = rect;
       
  1620                 IntSize topLeftToClipOut = topLeft;
       
  1621                 IntSize topRightToClipOut = topRight;
       
  1622                 IntSize bottomLeftToClipOut = bottomLeft;
       
  1623                 IntSize bottomRightToClipOut = bottomRight;
       
  1624 
       
  1625                 if (shadowSpread < 0) {
       
  1626                     topLeft.expand(shadowSpread, shadowSpread);
       
  1627                     topLeft.clampNegativeToZero();
       
  1628 
       
  1629                     topRight.expand(shadowSpread, shadowSpread);
       
  1630                     topRight.clampNegativeToZero();
       
  1631 
       
  1632                     bottomLeft.expand(shadowSpread, shadowSpread);
       
  1633                     bottomLeft.clampNegativeToZero();
       
  1634 
       
  1635                     bottomRight.expand(shadowSpread, shadowSpread);
       
  1636                     bottomRight.clampNegativeToZero();
       
  1637                 }
       
  1638 
       
  1639                 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
       
  1640                 // when painting the shadow. On the other hand, it introduces subpixel gaps along the
       
  1641                 // corners. Those are avoided by insetting the clipping path by one pixel.
       
  1642                 if (hasOpaqueBackground) {
       
  1643                     rectToClipOut.inflate(-1);
       
  1644 
       
  1645                     topLeftToClipOut.expand(-1, -1);
       
  1646                     topLeftToClipOut.clampNegativeToZero();
       
  1647 
       
  1648                     topRightToClipOut.expand(-1, -1);
       
  1649                     topRightToClipOut.clampNegativeToZero();
       
  1650 
       
  1651                     bottomLeftToClipOut.expand(-1, -1);
       
  1652                     bottomLeftToClipOut.clampNegativeToZero();
       
  1653 
       
  1654                     bottomRightToClipOut.expand(-1, -1);
       
  1655                     bottomRightToClipOut.clampNegativeToZero();
       
  1656                 }
       
  1657 
       
  1658                 if (!rectToClipOut.isEmpty())
       
  1659                     context->clipOutRoundedRect(rectToClipOut, topLeftToClipOut, topRightToClipOut, bottomLeftToClipOut, bottomRightToClipOut);
       
  1660                 context->fillRoundedRect(fillRect, topLeft, topRight, bottomLeft, bottomRight, Color::black, s->colorSpace());
       
  1661             } else {
       
  1662                 IntRect rectToClipOut = rect;
       
  1663 
       
  1664                 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
       
  1665                 // when painting the shadow. On the other hand, it introduces subpixel gaps along the
       
  1666                 // edges if they are not pixel-aligned. Those are avoided by insetting the clipping path
       
  1667                 // by one pixel.
       
  1668                 if (hasOpaqueBackground) {
       
  1669                     AffineTransform currentTransformation = context->getCTM();
       
  1670                     if (currentTransformation.a() != 1 || (currentTransformation.d() != 1 && currentTransformation.d() != -1)
       
  1671                             || currentTransformation.b() || currentTransformation.c())
       
  1672                         rectToClipOut.inflate(-1);
       
  1673                 }
       
  1674 
       
  1675                 if (!rectToClipOut.isEmpty())
       
  1676                     context->clipOut(rectToClipOut);
       
  1677                 context->fillRect(fillRect, Color::black, s->colorSpace());
       
  1678             }
       
  1679 
       
  1680             context->restore();
       
  1681         } else {
       
  1682             // Inset shadow.
       
  1683             IntRect holeRect(rect);
       
  1684             holeRect.inflate(-shadowSpread);
       
  1685 
       
  1686             if (holeRect.isEmpty()) {
       
  1687                 if (hasBorderRadius)
       
  1688                     context->fillRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight, shadowColor, s->colorSpace());
       
  1689                 else
       
  1690                     context->fillRect(rect, shadowColor, s->colorSpace());
       
  1691                 continue;
       
  1692             }
       
  1693             if (!begin) {
       
  1694                 holeRect.move(-max(shadowOffset.width(), 0) - shadowBlur, 0);
       
  1695                 holeRect.setWidth(holeRect.width() + max(shadowOffset.width(), 0) + shadowBlur);
       
  1696             }
       
  1697             if (!end)
       
  1698                 holeRect.setWidth(holeRect.width() - min(shadowOffset.width(), 0) + shadowBlur);
       
  1699 
       
  1700             Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255);
       
  1701 
       
  1702             IntRect outerRect(rect);
       
  1703             outerRect.inflateX(w - 2 * shadowSpread);
       
  1704             outerRect.inflateY(h - 2 * shadowSpread);
       
  1705 
       
  1706             context->save();
       
  1707 
       
  1708             if (hasBorderRadius)
       
  1709                 context->clip(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight));
       
  1710             else
       
  1711                 context->clip(rect);
       
  1712 
       
  1713             IntSize extraOffset(2 * w + max(0, shadowOffset.width()) + shadowBlur - 2 * shadowSpread + 1, 0);
       
  1714             context->translate(extraOffset.width(), extraOffset.height());
       
  1715             shadowOffset -= extraOffset;
       
  1716 
       
  1717             context->beginPath();
       
  1718             context->addPath(Path::createRectangle(outerRect));
       
  1719 
       
  1720             if (hasBorderRadius) {
       
  1721                 if (shadowSpread > 0) {
       
  1722                     topLeft.expand(-shadowSpread, -shadowSpread);
       
  1723                     topLeft.clampNegativeToZero();
       
  1724 
       
  1725                     topRight.expand(-shadowSpread, -shadowSpread);
       
  1726                     topRight.clampNegativeToZero();
       
  1727 
       
  1728                     bottomLeft.expand(-shadowSpread, -shadowSpread);
       
  1729                     bottomLeft.clampNegativeToZero();
       
  1730 
       
  1731                     bottomRight.expand(-shadowSpread, -shadowSpread);
       
  1732                     bottomRight.clampNegativeToZero();
       
  1733                 }
       
  1734                 context->addPath(Path::createRoundedRectangle(holeRect, topLeft, topRight, bottomLeft, bottomRight));
       
  1735             } else
       
  1736                 context->addPath(Path::createRectangle(holeRect));
       
  1737 
       
  1738             context->setFillRule(RULE_EVENODD);
       
  1739             context->setFillColor(fillColor, s->colorSpace());
       
  1740             context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace());
       
  1741             context->fillPath();
       
  1742 
       
  1743             context->restore();
       
  1744         }
       
  1745     }
       
  1746 }
       
  1747 
       
  1748 int RenderBoxModelObject::containingBlockWidthForContent() const
       
  1749 {
       
  1750     return containingBlock()->availableWidth();
       
  1751 }
       
  1752 
       
  1753 } // namespace WebCore