2010-09-20 Daniel Bates <dbates@rim.com>
[WebKit-https.git] / WebCore / rendering / RenderBoxModelObject.cpp
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     RenderBlock* containingBlock = this->containingBlock();
341
342     // If the containing block of a relatively positioned element does not
343     // specify a height, a percentage top or bottom offset should be resolved as
344     // auto. An exception to this is if the containing block has the WinIE quirk
345     // where <html> and <body> assume the size of the viewport. In this case,
346     // calculate the percent offset based on this height.
347     // See <https://bugs.webkit.org/show_bug.cgi?id=26396>.
348     if (!style()->top().isAuto()
349         && (!containingBlock->style()->height().isAuto()
350             || !style()->top().isPercent()
351             || containingBlock->stretchesToViewHeight()))
352         return style()->top().calcValue(containingBlock->availableHeight());
353
354     if (!style()->bottom().isAuto()
355         && (!containingBlock->style()->height().isAuto()
356             || !style()->bottom().isPercent()
357             || containingBlock->stretchesToViewHeight()))
358         return -style()->bottom().calcValue(containingBlock->availableHeight());
359
360     return 0;
361 }
362
363 int RenderBoxModelObject::offsetLeft() const
364 {
365     // If the element is the HTML body element or does not have an associated box
366     // return 0 and stop this algorithm.
367     if (isBody())
368         return 0;
369     
370     RenderBoxModelObject* offsetPar = offsetParent();
371     int xPos = (isBox() ? toRenderBox(this)->x() : 0);
372     
373     // If the offsetParent of the element is null, or is the HTML body element,
374     // return the distance between the canvas origin and the left border edge 
375     // of the element and stop this algorithm.
376     if (offsetPar) {
377         if (offsetPar->isBox() && !offsetPar->isBody())
378             xPos -= toRenderBox(offsetPar)->borderLeft();
379         if (!isPositioned()) {
380             if (isRelPositioned())
381                 xPos += relativePositionOffsetX();
382             RenderObject* curr = parent();
383             while (curr && curr != offsetPar) {
384                 // FIXME: What are we supposed to do inside SVG content?
385                 if (curr->isBox() && !curr->isTableRow())
386                     xPos += toRenderBox(curr)->x();
387                 curr = curr->parent();
388             }
389             if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned())
390                 xPos += toRenderBox(offsetPar)->x();
391         }
392     }
393
394     return xPos;
395 }
396
397 int RenderBoxModelObject::offsetTop() const
398 {
399     // If the element is the HTML body element or does not have an associated box
400     // return 0 and stop this algorithm.
401     if (isBody())
402         return 0;
403     
404     RenderBoxModelObject* offsetPar = offsetParent();
405     int yPos = (isBox() ? toRenderBox(this)->y() : 0);
406     
407     // If the offsetParent of the element is null, or is the HTML body element,
408     // return the distance between the canvas origin and the top border edge 
409     // of the element and stop this algorithm.
410     if (offsetPar) {
411         if (offsetPar->isBox() && !offsetPar->isBody())
412             yPos -= toRenderBox(offsetPar)->borderTop();
413         if (!isPositioned()) {
414             if (isRelPositioned())
415                 yPos += relativePositionOffsetY();
416             RenderObject* curr = parent();
417             while (curr && curr != offsetPar) {
418                 // FIXME: What are we supposed to do inside SVG content?
419                 if (curr->isBox() && !curr->isTableRow())
420                     yPos += toRenderBox(curr)->y();
421                 curr = curr->parent();
422             }
423             if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned())
424                 yPos += toRenderBox(offsetPar)->y();
425         }
426     }
427     return yPos;
428 }
429
430 int RenderBoxModelObject::paddingTop(bool) const
431 {
432     int w = 0;
433     Length padding = style()->paddingTop();
434     if (padding.isPercent())
435         w = containingBlock()->availableLogicalWidth();
436     return padding.calcMinValue(w);
437 }
438
439 int RenderBoxModelObject::paddingBottom(bool) const
440 {
441     int w = 0;
442     Length padding = style()->paddingBottom();
443     if (padding.isPercent())
444         w = containingBlock()->availableLogicalWidth();
445     return padding.calcMinValue(w);
446 }
447
448 int RenderBoxModelObject::paddingLeft(bool) const
449 {
450     int w = 0;
451     Length padding = style()->paddingLeft();
452     if (padding.isPercent())
453         w = containingBlock()->availableLogicalWidth();
454     return padding.calcMinValue(w);
455 }
456
457 int RenderBoxModelObject::paddingRight(bool) const
458 {
459     int w = 0;
460     Length padding = style()->paddingRight();
461     if (padding.isPercent())
462         w = containingBlock()->availableLogicalWidth();
463     return padding.calcMinValue(w);
464 }
465
466 int RenderBoxModelObject::paddingBefore(bool) const
467 {
468     int w = 0;
469     Length padding = style()->paddingBefore();
470     if (padding.isPercent())
471         w = containingBlock()->availableLogicalWidth();
472     return padding.calcMinValue(w);
473 }
474
475 int RenderBoxModelObject::paddingAfter(bool) const
476 {
477     int w = 0;
478     Length padding = style()->paddingAfter();
479     if (padding.isPercent())
480         w = containingBlock()->availableLogicalWidth();
481     return padding.calcMinValue(w);
482 }
483
484 int RenderBoxModelObject::paddingStart(bool) const
485 {
486     int w = 0;
487     Length padding = style()->paddingStart();
488     if (padding.isPercent())
489         w = containingBlock()->availableLogicalWidth();
490     return padding.calcMinValue(w);
491 }
492
493 int RenderBoxModelObject::paddingEnd(bool) const
494 {
495     int w = 0;
496     Length padding = style()->paddingEnd();
497     if (padding.isPercent())
498         w = containingBlock()->availableLogicalWidth();
499     return padding.calcMinValue(w);
500 }
501
502 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)
503 {
504     GraphicsContext* context = paintInfo.context;
505     if (context->paintingDisabled())
506         return;
507
508     bool includeLeftEdge = box ? box->includeLeftEdge() : true;
509     bool includeRightEdge = box ? box->includeRightEdge() : true;
510     int bLeft = includeLeftEdge ? borderLeft() : 0;
511     int bRight = includeRightEdge ? borderRight() : 0;
512     int pLeft = includeLeftEdge ? paddingLeft() : 0;
513     int pRight = includeRightEdge ? paddingRight() : 0;
514
515     bool clippedToBorderRadius = false;
516     if (style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge)) {
517         IntRect borderRect(tx, ty, w, h);
518
519         if (borderRect.isEmpty())
520             return;
521
522         context->save();
523
524         IntSize topLeft, topRight, bottomLeft, bottomRight;
525         style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight);
526
527         context->addRoundedRectClip(borderRect, includeLeftEdge ? topLeft : IntSize(),
528                                                 includeRightEdge ? topRight : IntSize(),
529                                                 includeLeftEdge ? bottomLeft : IntSize(),
530                                                 includeRightEdge ? bottomRight : IntSize());
531         clippedToBorderRadius = true;
532     }
533
534     bool clippedWithLocalScrolling = hasOverflowClip() && bgLayer->attachment() == LocalBackgroundAttachment;
535     if (clippedWithLocalScrolling) {
536         // Clip to the overflow area.
537         context->save();
538         context->clip(toRenderBox(this)->overflowClipRect(tx, ty));
539         
540         // Now adjust our tx, ty, w, h to reflect a scrolled content box with borders at the ends.
541         IntSize offset = layer()->scrolledContentOffset();
542         tx -= offset.width();
543         ty -= offset.height();
544         w = bLeft + layer()->scrollWidth() + bRight;
545         h = borderTop() + layer()->scrollHeight() + borderBottom();
546     }
547     
548     if (bgLayer->clip() == PaddingFillBox || bgLayer->clip() == ContentFillBox) {
549         // Clip to the padding or content boxes as necessary.
550         bool includePadding = bgLayer->clip() == ContentFillBox;
551         int x = tx + bLeft + (includePadding ? pLeft : 0);
552         int y = ty + borderTop() + (includePadding ? paddingTop() : 0);
553         int width = w - bLeft - bRight - (includePadding ? pLeft + pRight : 0);
554         int height = h - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : 0);
555         context->save();
556         context->clip(IntRect(x, y, width, height));
557     } else if (bgLayer->clip() == TextFillBox) {
558         // We have to draw our text into a mask that can then be used to clip background drawing.
559         // First figure out how big the mask has to be.  It should be no bigger than what we need
560         // to actually render, so we should intersect the dirty rect with the border box of the background.
561         IntRect maskRect(tx, ty, w, h);
562         maskRect.intersect(paintInfo.rect);
563         
564         // Now create the mask.
565         OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size());
566         if (!maskImage)
567             return;
568         
569         GraphicsContext* maskImageContext = maskImage->context();
570         maskImageContext->translate(-maskRect.x(), -maskRect.y());
571         
572         // Now add the text to the clip.  We do this by painting using a special paint phase that signals to
573         // InlineTextBoxes that they should just add their contents to the clip.
574         PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, true, 0, 0);
575         if (box)
576             box->paint(info, tx - box->x(), ty - box->y());
577         else {
578             int x = isBox() ? toRenderBox(this)->x() : 0;
579             int y = isBox() ? toRenderBox(this)->y() : 0;
580             paint(info, tx - x, ty - y);
581         }
582         
583         // The mask has been created.  Now we just need to clip to it.
584         context->save();
585         context->clipToImageBuffer(maskImage.get(), maskRect);
586     }
587     
588     StyleImage* bg = bgLayer->image();
589     bool shouldPaintBackgroundImage = bg && bg->canRender(style()->effectiveZoom());
590     Color bgColor = c;
591
592     // When this style flag is set, change existing background colors and images to a solid white background.
593     // If there's no bg color or image, leave it untouched to avoid affecting transparency.
594     // We don't try to avoid loading the background images, because this style flag is only set
595     // when printing, and at that point we've already loaded the background images anyway. (To avoid
596     // loading the background images we'd have to do this check when applying styles rather than
597     // while rendering.)
598     if (style()->forceBackgroundsToWhite()) {
599         // Note that we can't reuse this variable below because the bgColor might be changed
600         bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && bgColor.alpha() > 0;
601         if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) {
602             bgColor = Color::white;
603             shouldPaintBackgroundImage = false;
604         }
605     }
606
607     bool isRoot = this->isRoot();
608
609     // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with
610     // no background in the child document should show the parent's background.
611     bool isOpaqueRoot = false;
612     if (isRoot) {
613         isOpaqueRoot = true;
614         if (!bgLayer->next() && !(bgColor.isValid() && bgColor.alpha() == 255) && view()->frameView()) {
615             Element* ownerElement = document()->ownerElement();
616             if (ownerElement) {
617                 if (!ownerElement->hasTagName(frameTag)) {
618                     // Locate the <body> element using the DOM.  This is easier than trying
619                     // to crawl around a render tree with potential :before/:after content and
620                     // anonymous blocks created by inline <body> tags etc.  We can locate the <body>
621                     // render object very easily via the DOM.
622                     HTMLElement* body = document()->body();
623                     if (body) {
624                         // Can't scroll a frameset document anyway.
625                         isOpaqueRoot = body->hasLocalName(framesetTag);
626                     }
627                 }
628             } else
629                 isOpaqueRoot = !view()->frameView()->isTransparent();
630         }
631         view()->frameView()->setContentIsOpaque(isOpaqueRoot);
632     }
633
634     // Paint the color first underneath all images.
635     if (!bgLayer->next()) {
636         IntRect rect(tx, ty, w, h);
637         rect.intersect(paintInfo.rect);
638         // If we have an alpha and we are painting the root element, go ahead and blend with the base background color.
639         if (isOpaqueRoot) {
640             Color baseColor = view()->frameView()->baseBackgroundColor();
641             if (baseColor.alpha() > 0) {
642                 context->save();
643                 context->setCompositeOperation(CompositeCopy);
644                 context->fillRect(rect, baseColor, style()->colorSpace());
645                 context->restore();
646             } else
647                 context->clearRect(rect);
648         }
649
650         if (bgColor.isValid() && bgColor.alpha() > 0)
651             context->fillRect(rect, bgColor, style()->colorSpace());
652     }
653
654     // no progressive loading of the background image
655     if (shouldPaintBackgroundImage) {
656         IntRect destRect;
657         IntPoint phase;
658         IntSize tileSize;
659
660         calculateBackgroundImageGeometry(bgLayer, tx, ty, w, h, destRect, phase, tileSize);
661         IntPoint destOrigin = destRect.location();
662         destRect.intersect(paintInfo.rect);
663         if (!destRect.isEmpty()) {
664             phase += destRect.location() - destOrigin;
665             CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op;
666             RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this;
667             Image* image = bg->image(clientForBackgroundImage, tileSize);
668             bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, tileSize);
669             context->drawTiledImage(image, style()->colorSpace(), destRect, phase, tileSize, compositeOp, useLowQualityScaling);
670         }
671     }
672
673     if (bgLayer->clip() != BorderFillBox)
674         // Undo the background clip
675         context->restore();
676
677     if (clippedToBorderRadius)
678         // Undo the border radius clip
679         context->restore();
680         
681     if (clippedWithLocalScrolling) // Undo the clip for local background attachments.
682         context->restore();
683 }
684
685 IntSize RenderBoxModelObject::calculateFillTileSize(const FillLayer* fillLayer, IntSize positioningAreaSize) const
686 {
687     StyleImage* image = fillLayer->image();
688     image->setImageContainerSize(positioningAreaSize); // Use the box established by background-origin.
689
690     EFillSizeType type = fillLayer->size().type;
691
692     switch (type) {
693         case SizeLength: {
694             int w = positioningAreaSize.width();
695             int h = positioningAreaSize.height();
696
697             Length layerWidth = fillLayer->size().size.width();
698             Length layerHeight = fillLayer->size().size.height();
699
700             if (layerWidth.isFixed())
701                 w = layerWidth.value();
702             else if (layerWidth.isPercent())
703                 w = layerWidth.calcValue(positioningAreaSize.width());
704             
705             if (layerHeight.isFixed())
706                 h = layerHeight.value();
707             else if (layerHeight.isPercent())
708                 h = layerHeight.calcValue(positioningAreaSize.height());
709             
710             // If one of the values is auto we have to use the appropriate
711             // scale to maintain our aspect ratio.
712             if (layerWidth.isAuto() && !layerHeight.isAuto()) {
713                 IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom());
714                 if (imageIntrinsicSize.height())
715                     w = imageIntrinsicSize.width() * h / imageIntrinsicSize.height();        
716             } else if (!layerWidth.isAuto() && layerHeight.isAuto()) {
717                 IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom());
718                 if (imageIntrinsicSize.width())
719                     h = imageIntrinsicSize.height() * w / imageIntrinsicSize.width();
720             } else if (layerWidth.isAuto() && layerHeight.isAuto()) {
721                 // If both width and height are auto, use the image's intrinsic size.
722                 IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom());
723                 w = imageIntrinsicSize.width();
724                 h = imageIntrinsicSize.height();
725             }
726             
727             return IntSize(max(1, w), max(1, h));
728         }
729         case Contain:
730         case Cover: {
731             IntSize imageIntrinsicSize = image->imageSize(this, 1);
732             float horizontalScaleFactor = imageIntrinsicSize.width()
733                 ? static_cast<float>(positioningAreaSize.width()) / imageIntrinsicSize.width() : 1;
734             float verticalScaleFactor = imageIntrinsicSize.height()
735                 ? static_cast<float>(positioningAreaSize.height()) / imageIntrinsicSize.height() : 1;
736             float scaleFactor = type == Contain ? min(horizontalScaleFactor, verticalScaleFactor) : max(horizontalScaleFactor, verticalScaleFactor);
737             return IntSize(max<int>(1, imageIntrinsicSize.width() * scaleFactor), max<int>(1, imageIntrinsicSize.height() * scaleFactor));
738         }
739         case SizeNone:
740             break;
741     }
742
743     return image->imageSize(this, style()->effectiveZoom());
744 }
745
746 void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* fillLayer, int tx, int ty, int w, int h, 
747                                                             IntRect& destRect, IntPoint& phase, IntSize& tileSize)
748 {
749     int left = 0;
750     int top = 0;
751     IntSize positioningAreaSize;
752
753     // Determine the background positioning area and set destRect to the background painting area.
754     // destRect will be adjusted later if the background is non-repeating.
755     bool fixedAttachment = fillLayer->attachment() == FixedBackgroundAttachment;
756
757 #if ENABLE(FAST_MOBILE_SCROLLING)
758     if (view()->frameView() && view()->frameView()->canBlitOnScroll()) {
759         // As a side effect of an optimization to blit on scroll, we do not honor the CSS
760         // property "background-attachment: fixed" because it may result in rendering
761         // artifacts. Note, these artifacts only appear if we are blitting on scroll of
762         // a page that has fixed background images.
763         fixedAttachment = false;
764     }
765 #endif
766
767     if (!fixedAttachment) {
768         destRect = IntRect(tx, ty, w, h);
769
770         int right = 0;
771         int bottom = 0;
772         // Scroll and Local.
773         if (fillLayer->origin() != BorderFillBox) {
774             left = borderLeft();
775             right = borderRight();
776             top = borderTop();
777             bottom = borderBottom();
778             if (fillLayer->origin() == ContentFillBox) {
779                 left += paddingLeft();
780                 right += paddingRight();
781                 top += paddingTop();
782                 bottom += paddingBottom();
783             }
784         }
785
786         // The background of the box generated by the root element covers the entire canvas including
787         // its margins. Since those were added in already, we have to factor them out when computing
788         // the background positioning area.
789         if (isRoot()) {
790             positioningAreaSize = IntSize(toRenderBox(this)->width() - left - right, toRenderBox(this)->height() - top - bottom);
791             left += marginLeft();
792             top += marginTop();
793         } else
794             positioningAreaSize = IntSize(w - left - right, h - top - bottom);
795     } else {
796         destRect = viewRect();
797         positioningAreaSize = destRect.size();
798     }
799
800     tileSize = calculateFillTileSize(fillLayer, positioningAreaSize);
801
802     EFillRepeat backgroundRepeatX = fillLayer->repeatX();
803     EFillRepeat backgroundRepeatY = fillLayer->repeatY();
804
805     int xPosition = fillLayer->xPosition().calcMinValue(positioningAreaSize.width() - tileSize.width(), true);
806     if (backgroundRepeatX == RepeatFill)
807         phase.setX(tileSize.width() ? tileSize.width() - (xPosition + left) % tileSize.width() : 0);
808     else {
809         destRect.move(max(xPosition + left, 0), 0);
810         phase.setX(-min(xPosition + left, 0));
811         destRect.setWidth(tileSize.width() + min(xPosition + left, 0));
812     }
813
814     int yPosition = fillLayer->yPosition().calcMinValue(positioningAreaSize.height() - tileSize.height(), true);
815     if (backgroundRepeatY == RepeatFill)
816         phase.setY(tileSize.height() ? tileSize.height() - (yPosition + top) % tileSize.height() : 0);
817     else {
818         destRect.move(0, max(yPosition + top, 0));
819         phase.setY(-min(yPosition + top, 0));
820         destRect.setHeight(tileSize.height() + min(yPosition + top, 0));
821     }
822
823     if (fixedAttachment)
824         phase.move(max(tx - destRect.x(), 0), max(ty - destRect.y(), 0));
825
826     destRect.intersect(IntRect(tx, ty, w, h));
827 }
828
829 int RenderBoxModelObject::verticalPosition(bool firstLine) const
830 {
831     // This method determines the vertical position for inline elements.
832     ASSERT(isInline());
833     if (!isInline())
834         return 0;
835
836     int vpos = 0;
837     EVerticalAlign va = style()->verticalAlign();
838     if (va == TOP)
839         vpos = PositionTop;
840     else if (va == BOTTOM)
841         vpos = PositionBottom;
842     else {
843         bool checkParent = parent()->isRenderInline() && parent()->style()->verticalAlign() != TOP && parent()->style()->verticalAlign() != BOTTOM;
844         vpos = checkParent ? toRenderInline(parent())->verticalPositionFromCache(firstLine) : 0;
845         // don't allow elements nested inside text-top to have a different valignment.
846         if (va == BASELINE)
847             return vpos;
848
849         const Font& f = parent()->style(firstLine)->font();
850         int fontsize = f.pixelSize();
851
852         if (va == SUB)
853             vpos += fontsize / 5 + 1;
854         else if (va == SUPER)
855             vpos -= fontsize / 3 + 1;
856         else if (va == TEXT_TOP)
857             vpos += baselinePosition(firstLine) - f.ascent();
858         else if (va == MIDDLE)
859             vpos += -static_cast<int>(f.xHeight() / 2) - lineHeight(firstLine) / 2 + baselinePosition(firstLine);
860         else if (va == TEXT_BOTTOM) {
861             vpos += f.descent();
862             // lineHeight - baselinePosition is always 0 for replaced elements (except inline blocks), so don't bother wasting time in that case.
863             if (!isReplaced() || style()->display() == INLINE_BLOCK)
864                 vpos -= (lineHeight(firstLine) - baselinePosition(firstLine));
865         } else if (va == BASELINE_MIDDLE)
866             vpos += -lineHeight(firstLine) / 2 + baselinePosition(firstLine);
867         else if (va == LENGTH)
868             vpos -= style()->verticalAlignLength().calcValue(lineHeight(firstLine));
869     }
870
871     return vpos;
872 }
873
874 bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style,
875                                                const NinePieceImage& ninePieceImage, CompositeOperator op)
876 {
877     StyleImage* styleImage = ninePieceImage.image();
878     if (!styleImage)
879         return false;
880
881     if (!styleImage->isLoaded())
882         return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either.
883
884     if (!styleImage->canRender(style->effectiveZoom()))
885         return false;
886
887     // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function
888     // doesn't have any understanding of the zoom that is in effect on the tile.
889     styleImage->setImageContainerSize(IntSize(w, h));
890     IntSize imageSize = styleImage->imageSize(this, 1.0f);
891     int imageWidth = imageSize.width();
892     int imageHeight = imageSize.height();
893
894     int topSlice = min(imageHeight, ninePieceImage.slices().top().calcValue(imageHeight));
895     int bottomSlice = min(imageHeight, ninePieceImage.slices().bottom().calcValue(imageHeight));
896     int leftSlice = min(imageWidth, ninePieceImage.slices().left().calcValue(imageWidth));
897     int rightSlice = min(imageWidth, ninePieceImage.slices().right().calcValue(imageWidth));
898
899     ENinePieceImageRule hRule = ninePieceImage.horizontalRule();
900     ENinePieceImageRule vRule = ninePieceImage.verticalRule();
901
902     bool fitToBorder = style->borderImage() == ninePieceImage;
903     
904     int leftWidth = fitToBorder ? style->borderLeftWidth() : leftSlice;
905     int topWidth = fitToBorder ? style->borderTopWidth() : topSlice;
906     int rightWidth = fitToBorder ? style->borderRightWidth() : rightSlice;
907     int bottomWidth = fitToBorder ? style->borderBottomWidth() : bottomSlice;
908
909     bool drawLeft = leftSlice > 0 && leftWidth > 0;
910     bool drawTop = topSlice > 0 && topWidth > 0;
911     bool drawRight = rightSlice > 0 && rightWidth > 0;
912     bool drawBottom = bottomSlice > 0 && bottomWidth > 0;
913     bool drawMiddle = (imageWidth - leftSlice - rightSlice) > 0 && (w - leftWidth - rightWidth) > 0 &&
914                       (imageHeight - topSlice - bottomSlice) > 0 && (h - topWidth - bottomWidth) > 0;
915
916     Image* image = styleImage->image(this, imageSize);
917     ColorSpace colorSpace = style->colorSpace();
918
919     if (drawLeft) {
920         // Paint the top and bottom left corners.
921
922         // The top left corner rect is (tx, ty, leftWidth, topWidth)
923         // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice)
924         if (drawTop)
925             graphicsContext->drawImage(image, colorSpace, IntRect(tx, ty, leftWidth, topWidth),
926                                        IntRect(0, 0, leftSlice, topSlice), op);
927
928         // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth)
929         // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice)
930         if (drawBottom)
931             graphicsContext->drawImage(image, colorSpace, IntRect(tx, ty + h - bottomWidth, leftWidth, bottomWidth),
932                                        IntRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op);
933
934         // Paint the left edge.
935         // Have to scale and tile into the border rect.
936         graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx, ty + topWidth, leftWidth,
937                                         h - topWidth - bottomWidth),
938                                         IntRect(0, topSlice, leftSlice, imageHeight - topSlice - bottomSlice),
939                                         Image::StretchTile, (Image::TileRule)vRule, op);
940     }
941
942     if (drawRight) {
943         // Paint the top and bottom right corners
944         // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth)
945         // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice)
946         if (drawTop)
947             graphicsContext->drawImage(image, colorSpace, IntRect(tx + w - rightWidth, ty, rightWidth, topWidth),
948                                        IntRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op);
949
950         // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth)
951         // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice)
952         if (drawBottom)
953             graphicsContext->drawImage(image, colorSpace, IntRect(tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth),
954                                        IntRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op);
955
956         // Paint the right edge.
957         graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + w - rightWidth, ty + topWidth, rightWidth,
958                                         h - topWidth - bottomWidth),
959                                         IntRect(imageWidth - rightSlice, topSlice, rightSlice, imageHeight - topSlice - bottomSlice),
960                                         Image::StretchTile, (Image::TileRule)vRule, op);
961     }
962
963     // Paint the top edge.
964     if (drawTop)
965         graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + leftWidth, ty, w - leftWidth - rightWidth, topWidth),
966                                         IntRect(leftSlice, 0, imageWidth - rightSlice - leftSlice, topSlice),
967                                         (Image::TileRule)hRule, Image::StretchTile, op);
968
969     // Paint the bottom edge.
970     if (drawBottom)
971         graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + leftWidth, ty + h - bottomWidth,
972                                         w - leftWidth - rightWidth, bottomWidth),
973                                         IntRect(leftSlice, imageHeight - bottomSlice, imageWidth - rightSlice - leftSlice, bottomSlice),
974                                         (Image::TileRule)hRule, Image::StretchTile, op);
975
976     // Paint the middle.
977     if (drawMiddle)
978         graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + leftWidth, ty + topWidth, w - leftWidth - rightWidth,
979                                         h - topWidth - bottomWidth),
980                                         IntRect(leftSlice, topSlice, imageWidth - rightSlice - leftSlice, imageHeight - topSlice - bottomSlice),
981                                         (Image::TileRule)hRule, (Image::TileRule)vRule, op);
982
983     return true;
984 }
985
986 #if HAVE(PATH_BASED_BORDER_RADIUS_DRAWING)
987 static bool borderWillArcInnerEdge(const IntSize& firstRadius, const IntSize& secondRadius, int firstBorderWidth, int secondBorderWidth, int middleBorderWidth)
988 {
989     // FIXME: This test is insufficient. We need to take border style into account.
990     return (!firstRadius.width() || firstRadius.width() >= firstBorderWidth)
991             && (!firstRadius.height() || firstRadius.height() >= middleBorderWidth)
992             && (!secondRadius.width() || secondRadius.width() >= secondBorderWidth)
993             && (!secondRadius.height() || secondRadius.height() >= middleBorderWidth);
994 }
995
996 void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h,
997                                        const RenderStyle* style, bool begin, bool end)
998 {
999     if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage()))
1000         return;
1001
1002     if (graphicsContext->paintingDisabled())
1003         return;
1004
1005     const Color& topColor = style->visitedDependentColor(CSSPropertyBorderTopColor);
1006     const Color& bottomColor = style->visitedDependentColor(CSSPropertyBorderBottomColor);
1007     const Color& leftColor = style->visitedDependentColor(CSSPropertyBorderLeftColor);
1008     const Color& rightColor = style->visitedDependentColor(CSSPropertyBorderRightColor);
1009
1010     bool topTransparent = style->borderTopIsTransparent();
1011     bool bottomTransparent = style->borderBottomIsTransparent();
1012     bool rightTransparent = style->borderRightIsTransparent();
1013     bool leftTransparent = style->borderLeftIsTransparent();
1014
1015     EBorderStyle topStyle = style->borderTopStyle();
1016     EBorderStyle bottomStyle = style->borderBottomStyle();
1017     EBorderStyle leftStyle = style->borderLeftStyle();
1018     EBorderStyle rightStyle = style->borderRightStyle();
1019
1020     bool renderTop = topStyle > BHIDDEN && !topTransparent;
1021     bool renderLeft = leftStyle > BHIDDEN && begin && !leftTransparent;
1022     bool renderRight = rightStyle > BHIDDEN && end && !rightTransparent;
1023     bool renderBottom = bottomStyle > BHIDDEN && !bottomTransparent;
1024
1025     bool renderRadii = false;
1026     Path roundedPath;
1027     IntSize topLeft, topRight, bottomLeft, bottomRight;
1028     IntRect borderRect(tx, ty, w, h);
1029
1030     if (style->hasBorderRadius()) {
1031         IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius;
1032         style->getBorderRadiiForRect(borderRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
1033
1034         IntRect innerBorderRect = borderInnerRect(borderRect, style->borderTopWidth(), style->borderBottomWidth(), 
1035             style->borderLeftWidth(), style->borderRightWidth());
1036
1037         IntSize innerTopLeftRadius, innerTopRightRadius, innerBottomLeftRadius, innerBottomRightRadius;
1038         style->getInnerBorderRadiiForRectWithBorderWidths(innerBorderRect, style->borderTopWidth(), style->borderBottomWidth(), 
1039             style->borderLeftWidth(), style->borderRightWidth(), innerTopLeftRadius, innerTopRightRadius, 
1040             innerBottomLeftRadius, innerBottomRightRadius);
1041
1042         if (begin) {
1043             topLeft = topLeftRadius;
1044             bottomLeft = bottomLeftRadius;
1045         }
1046         if (end) {
1047             topRight = topRightRadius;
1048             bottomRight = bottomRightRadius;
1049         }
1050
1051         renderRadii = true;
1052
1053         // Clip to the inner and outer radii rects.
1054         graphicsContext->save();
1055         graphicsContext->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight);
1056         graphicsContext->clipOutRoundedRect(innerBorderRect, innerTopLeftRadius, innerTopRightRadius, innerBottomLeftRadius, innerBottomRightRadius);
1057
1058         roundedPath = Path::createRoundedRectangle(borderRect, topLeft, topRight, bottomLeft, bottomRight);
1059         graphicsContext->addPath(roundedPath);
1060     }
1061
1062     bool upperLeftBorderStylesMatch = renderLeft && (topStyle == leftStyle) && (topColor == leftColor);
1063     bool upperRightBorderStylesMatch = renderRight && (topStyle == rightStyle) && (topColor == rightColor) && (topStyle != OUTSET) && (topStyle != RIDGE) && (topStyle != INSET) && (topStyle != GROOVE);
1064     bool lowerLeftBorderStylesMatch = renderLeft && (bottomStyle == leftStyle) && (bottomColor == leftColor) && (bottomStyle != OUTSET) && (bottomStyle != RIDGE) && (bottomStyle != INSET) && (bottomStyle != GROOVE);
1065     bool lowerRightBorderStylesMatch = renderRight && (bottomStyle == rightStyle) && (bottomColor == rightColor);
1066
1067     if (renderTop) {
1068         int x = tx;
1069         int x2 = tx + w;
1070
1071         if (renderRadii && borderWillArcInnerEdge(topLeft, topRight, style->borderLeftWidth(), style->borderRightWidth(), style->borderTopWidth())) {
1072             graphicsContext->save();
1073             clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSTop, upperLeftBorderStylesMatch, upperRightBorderStylesMatch, style);
1074             float thickness = max(max(style->borderTopWidth(), style->borderLeftWidth()), style->borderRightWidth());
1075             drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderTopWidth(), thickness, BSTop, style, topColor, topStyle);
1076             graphicsContext->restore();
1077         } else {
1078             bool ignoreLeft = (topColor == leftColor && topTransparent == leftTransparent && topStyle >= OUTSET
1079                 && (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET));
1080             bool ignoreRight = (topColor == rightColor && topTransparent == rightTransparent && topStyle >= OUTSET
1081                 && (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET));
1082
1083             drawLineForBoxSide(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, topColor, topStyle,
1084                     ignoreLeft ? 0 : style->borderLeftWidth(), ignoreRight ? 0 : style->borderRightWidth());               
1085         }
1086     }
1087
1088     if (renderBottom) {
1089         int x = tx;
1090         int x2 = tx + w;
1091
1092         if (renderRadii && borderWillArcInnerEdge(bottomLeft, bottomRight, style->borderLeftWidth(), style->borderRightWidth(), style->borderBottomWidth())) {
1093             graphicsContext->save();
1094             clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSBottom, lowerLeftBorderStylesMatch, lowerRightBorderStylesMatch, style);
1095             float thickness = max(max(style->borderBottomWidth(), style->borderLeftWidth()), style->borderRightWidth());
1096             drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderBottomWidth(), thickness, BSBottom, style, bottomColor, bottomStyle);
1097             graphicsContext->restore();
1098         } else {
1099             bool ignoreLeft = (bottomColor == leftColor && bottomTransparent == leftTransparent && bottomStyle >= OUTSET
1100                 && (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET));
1101
1102             bool ignoreRight = (bottomColor == rightColor && bottomTransparent == rightTransparent && bottomStyle >= OUTSET
1103                 && (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET));
1104
1105             drawLineForBoxSide(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bottomColor, 
1106                         bottomStyle, ignoreLeft ? 0 : style->borderLeftWidth(), 
1107                         ignoreRight ? 0 : style->borderRightWidth());
1108         }
1109     }
1110
1111     if (renderLeft) {
1112         int y = ty;
1113         int y2 = ty + h;
1114
1115         if (renderRadii && borderWillArcInnerEdge(bottomLeft, topLeft, style->borderBottomWidth(), style->borderTopWidth(), style->borderLeftWidth())) {
1116             graphicsContext->save();
1117             clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSLeft, upperLeftBorderStylesMatch, lowerLeftBorderStylesMatch, style);
1118             float thickness = max(max(style->borderLeftWidth(), style->borderTopWidth()), style->borderBottomWidth());
1119             drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderLeftWidth(), thickness, BSLeft, style, leftColor, leftStyle);
1120             graphicsContext->restore();
1121         } else {
1122             bool ignoreTop = (topColor == leftColor && topTransparent == leftTransparent && leftStyle >= OUTSET
1123                 && (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET));
1124
1125             bool ignoreBottom = (bottomColor == leftColor && bottomTransparent == leftTransparent && leftStyle >= OUTSET
1126                 && (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET));
1127
1128             drawLineForBoxSide(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, leftColor,
1129                         leftStyle, ignoreTop ? 0 : style->borderTopWidth(), ignoreBottom ? 0 : style->borderBottomWidth());
1130         }
1131     }
1132
1133     if (renderRight) {
1134         if (renderRadii && borderWillArcInnerEdge(bottomRight, topRight, style->borderBottomWidth(), style->borderTopWidth(), style->borderRightWidth())) {
1135             graphicsContext->save();
1136             clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSRight, upperRightBorderStylesMatch, lowerRightBorderStylesMatch, style);
1137             float thickness = max(max(style->borderRightWidth(), style->borderTopWidth()), style->borderBottomWidth());
1138             drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderRightWidth(), thickness, BSRight, style, rightColor, rightStyle);
1139             graphicsContext->restore();
1140         } else {
1141             bool ignoreTop = ((topColor == rightColor) && (topTransparent == rightTransparent)
1142                 && (rightStyle >= DOTTED || rightStyle == INSET)
1143                 && (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET));
1144
1145             bool ignoreBottom = ((bottomColor == rightColor) && (bottomTransparent == rightTransparent)
1146                 && (rightStyle >= DOTTED || rightStyle == INSET)
1147                 && (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET));
1148
1149             int y = ty;
1150             int y2 = ty + h;
1151
1152             drawLineForBoxSide(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rightColor, 
1153                 rightStyle, ignoreTop ? 0 : style->borderTopWidth(), 
1154                 ignoreBottom ? 0 : style->borderBottomWidth());
1155         }
1156     }
1157
1158     if (renderRadii)
1159         graphicsContext->restore();
1160 }
1161 #else
1162 void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h,
1163                                        const RenderStyle* style, bool begin, bool end)
1164 {
1165     // FIXME: This old version of paintBorder should be removed when all ports implement 
1166     // GraphicsContext::clipConvexPolygon()!! This should happen soon.
1167     if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage()))
1168         return;
1169
1170     const Color& topColor = style->visitedDependentColor(CSSPropertyBorderTopColor);
1171     const Color& bottomColor = style->visitedDependentColor(CSSPropertyBorderBottomColor);
1172     const Color& leftColor = style->visitedDependentColor(CSSPropertyBorderLeftColor);
1173     const Color& rightColor = style->visitedDependentColor(CSSPropertyBorderRightColor);
1174
1175     bool topTransparent = style->borderTopIsTransparent();
1176     bool bottomTransparent = style->borderBottomIsTransparent();
1177     bool rightTransparent = style->borderRightIsTransparent();
1178     bool leftTransparent = style->borderLeftIsTransparent();
1179
1180     EBorderStyle topStyle = style->borderTopStyle();
1181     EBorderStyle bottomStyle = style->borderBottomStyle();
1182     EBorderStyle leftStyle = style->borderLeftStyle();
1183     EBorderStyle rightStyle = style->borderRightStyle();
1184
1185     bool renderTop = topStyle > BHIDDEN && !topTransparent;
1186     bool renderLeft = leftStyle > BHIDDEN && begin && !leftTransparent;
1187     bool renderRight = rightStyle > BHIDDEN && end && !rightTransparent;
1188     bool renderBottom = bottomStyle > BHIDDEN && !bottomTransparent;
1189
1190     bool renderRadii = false;
1191     IntSize topLeft, topRight, bottomLeft, bottomRight;
1192
1193     if (style->hasBorderRadius()) {
1194         IntRect borderRect = IntRect(tx, ty, w, h);
1195
1196         IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius;
1197         style->getBorderRadiiForRect(borderRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
1198
1199         if (begin) {
1200             topLeft = topLeftRadius;
1201             bottomLeft = bottomLeftRadius;
1202         }
1203         if (end) {
1204             topRight = topRightRadius;
1205             bottomRight = bottomRightRadius;
1206         }
1207
1208         renderRadii = true;
1209
1210         // Clip to the rounded rectangle.
1211         graphicsContext->save();
1212         graphicsContext->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight);
1213     }
1214
1215     int firstAngleStart, secondAngleStart, firstAngleSpan, secondAngleSpan;
1216     float thickness;
1217     bool upperLeftBorderStylesMatch = renderLeft && (topStyle == leftStyle) && (topColor == leftColor);
1218     bool upperRightBorderStylesMatch = renderRight && (topStyle == rightStyle) && (topColor == rightColor) && (topStyle != OUTSET) && (topStyle != RIDGE) && (topStyle != INSET) && (topStyle != GROOVE);
1219     bool lowerLeftBorderStylesMatch = renderLeft && (bottomStyle == leftStyle) && (bottomColor == leftColor) && (bottomStyle != OUTSET) && (bottomStyle != RIDGE) && (bottomStyle != INSET) && (bottomStyle != GROOVE);
1220     bool lowerRightBorderStylesMatch = renderRight && (bottomStyle == rightStyle) && (bottomColor == rightColor);
1221
1222     if (renderTop) {
1223         bool ignore_left = (renderRadii && topLeft.width() > 0) ||
1224             (topColor == leftColor && topTransparent == leftTransparent && topStyle >= OUTSET &&
1225              (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET));
1226
1227         bool ignore_right = (renderRadii && topRight.width() > 0) ||
1228             (topColor == rightColor && topTransparent == rightTransparent && topStyle >= OUTSET &&
1229              (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET));
1230
1231         int x = tx;
1232         int x2 = tx + w;
1233         if (renderRadii) {
1234             x += topLeft.width();
1235             x2 -= topRight.width();
1236         }
1237
1238         drawLineForBoxSide(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, topColor, topStyle,
1239                    ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth());
1240
1241         if (renderRadii) {
1242             int leftY = ty;
1243
1244             // We make the arc double thick and let the clip rect take care of clipping the extra off.
1245             // We're doing this because it doesn't seem possible to match the curve of the clip exactly
1246             // with the arc-drawing function.
1247             thickness = style->borderTopWidth() * 2;
1248
1249             if (topLeft.width()) {
1250                 int leftX = tx;
1251                 // The inner clip clips inside the arc. This is especially important for 1px borders.
1252                 bool applyLeftInnerClip = (style->borderLeftWidth() < topLeft.width())
1253                     && (style->borderTopWidth() < topLeft.height())
1254                     && (topStyle != DOUBLE || style->borderTopWidth() > 6);
1255                 if (applyLeftInnerClip) {
1256                     graphicsContext->save();
1257                     graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, topLeft.width() * 2, topLeft.height() * 2),
1258                                                              style->borderTopWidth());
1259                 }
1260
1261                 firstAngleStart = 90;
1262                 firstAngleSpan = upperLeftBorderStylesMatch ? 90 : 45;
1263
1264                 // Draw upper left arc
1265                 drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, topLeft, firstAngleStart, firstAngleSpan,
1266                               BSTop, topColor, topStyle, true);
1267                 if (applyLeftInnerClip)
1268                     graphicsContext->restore();
1269             }
1270
1271             if (topRight.width()) {
1272                 int rightX = tx + w - topRight.width() * 2;
1273                 bool applyRightInnerClip = (style->borderRightWidth() < topRight.width())
1274                     && (style->borderTopWidth() < topRight.height())
1275                     && (topStyle != DOUBLE || style->borderTopWidth() > 6);
1276                 if (applyRightInnerClip) {
1277                     graphicsContext->save();
1278                     graphicsContext->addInnerRoundedRectClip(IntRect(rightX, leftY, topRight.width() * 2, topRight.height() * 2),
1279                                                              style->borderTopWidth());
1280                 }
1281
1282                 if (upperRightBorderStylesMatch) {
1283                     secondAngleStart = 0;
1284                     secondAngleSpan = 90;
1285                 } else {
1286                     secondAngleStart = 45;
1287                     secondAngleSpan = 45;
1288                 }
1289
1290                 // Draw upper right arc
1291                 drawArcForBoxSide(graphicsContext, rightX, leftY, thickness, topRight, secondAngleStart, secondAngleSpan,
1292                               BSTop, topColor, topStyle, false);
1293                 if (applyRightInnerClip)
1294                     graphicsContext->restore();
1295             }
1296         }
1297     }
1298
1299     if (renderBottom) {
1300         bool ignore_left = (renderRadii && bottomLeft.width() > 0) ||
1301             (bottomColor == leftColor && bottomTransparent == leftTransparent && bottomStyle >= OUTSET &&
1302              (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET));
1303
1304         bool ignore_right = (renderRadii && bottomRight.width() > 0) ||
1305             (bottomColor == rightColor && bottomTransparent == rightTransparent && bottomStyle >= OUTSET &&
1306              (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET));
1307
1308         int x = tx;
1309         int x2 = tx + w;
1310         if (renderRadii) {
1311             x += bottomLeft.width();
1312             x2 -= bottomRight.width();
1313         }
1314
1315         drawLineForBoxSide(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bottomColor, bottomStyle,
1316                    ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth());
1317
1318         if (renderRadii) {
1319             thickness = style->borderBottomWidth() * 2;
1320
1321             if (bottomLeft.width()) {
1322                 int leftX = tx;
1323                 int leftY = ty + h - bottomLeft.height() * 2;
1324                 bool applyLeftInnerClip = (style->borderLeftWidth() < bottomLeft.width())
1325                     && (style->borderBottomWidth() < bottomLeft.height())
1326                     && (bottomStyle != DOUBLE || style->borderBottomWidth() > 6);
1327                 if (applyLeftInnerClip) {
1328                     graphicsContext->save();
1329                     graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, bottomLeft.width() * 2, bottomLeft.height() * 2),
1330                                                              style->borderBottomWidth());
1331                 }
1332
1333                 if (lowerLeftBorderStylesMatch) {
1334                     firstAngleStart = 180;
1335                     firstAngleSpan = 90;
1336                 } else {
1337                     firstAngleStart = 225;
1338                     firstAngleSpan = 45;
1339                 }
1340
1341                 // Draw lower left arc
1342                 drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, bottomLeft, firstAngleStart, firstAngleSpan,
1343                               BSBottom, bottomColor, bottomStyle, true);
1344                 if (applyLeftInnerClip)
1345                     graphicsContext->restore();
1346             }
1347
1348             if (bottomRight.width()) {
1349                 int rightY = ty + h - bottomRight.height() * 2;
1350                 int rightX = tx + w - bottomRight.width() * 2;
1351                 bool applyRightInnerClip = (style->borderRightWidth() < bottomRight.width())
1352                     && (style->borderBottomWidth() < bottomRight.height())
1353                     && (bottomStyle != DOUBLE || style->borderBottomWidth() > 6);
1354                 if (applyRightInnerClip) {
1355                     graphicsContext->save();
1356                     graphicsContext->addInnerRoundedRectClip(IntRect(rightX, rightY, bottomRight.width() * 2, bottomRight.height() * 2),
1357                                                              style->borderBottomWidth());
1358                 }
1359
1360                 secondAngleStart = 270;
1361                 secondAngleSpan = lowerRightBorderStylesMatch ? 90 : 45;
1362
1363                 // Draw lower right arc
1364                 drawArcForBoxSide(graphicsContext, rightX, rightY, thickness, bottomRight, secondAngleStart, secondAngleSpan,
1365                               BSBottom, bottomColor, bottomStyle, false);
1366                 if (applyRightInnerClip)
1367                     graphicsContext->restore();
1368             }
1369         }
1370     }
1371
1372     if (renderLeft) {
1373         bool ignore_top = (renderRadii && topLeft.height() > 0) ||
1374             (topColor == leftColor && topTransparent == leftTransparent && leftStyle >= OUTSET &&
1375              (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET));
1376
1377         bool ignore_bottom = (renderRadii && bottomLeft.height() > 0) ||
1378             (bottomColor == leftColor && bottomTransparent == leftTransparent && leftStyle >= OUTSET &&
1379              (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET));
1380
1381         int y = ty;
1382         int y2 = ty + h;
1383         if (renderRadii) {
1384             y += topLeft.height();
1385             y2 -= bottomLeft.height();
1386         }
1387
1388         drawLineForBoxSide(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, leftColor, leftStyle,
1389                    ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth());
1390
1391         if (renderRadii && (!upperLeftBorderStylesMatch || !lowerLeftBorderStylesMatch)) {
1392             int topX = tx;
1393             thickness = style->borderLeftWidth() * 2;
1394
1395             if (!upperLeftBorderStylesMatch && topLeft.width()) {
1396                 int topY = ty;
1397                 bool applyTopInnerClip = (style->borderLeftWidth() < topLeft.width())
1398                     && (style->borderTopWidth() < topLeft.height())
1399                     && (leftStyle != DOUBLE || style->borderLeftWidth() > 6);
1400                 if (applyTopInnerClip) {
1401                     graphicsContext->save();
1402                     graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topLeft.width() * 2, topLeft.height() * 2),
1403                                                              style->borderLeftWidth());
1404                 }
1405
1406                 firstAngleStart = 135;
1407                 firstAngleSpan = 45;
1408
1409                 // Draw top left arc
1410                 drawArcForBoxSide(graphicsContext, topX, topY, thickness, topLeft, firstAngleStart, firstAngleSpan,
1411                               BSLeft, leftColor, leftStyle, true);
1412                 if (applyTopInnerClip)
1413                     graphicsContext->restore();
1414             }
1415
1416             if (!lowerLeftBorderStylesMatch && bottomLeft.width()) {
1417                 int bottomY = ty + h - bottomLeft.height() * 2;
1418                 bool applyBottomInnerClip = (style->borderLeftWidth() < bottomLeft.width())
1419                     && (style->borderBottomWidth() < bottomLeft.height())
1420                     && (leftStyle != DOUBLE || style->borderLeftWidth() > 6);
1421                 if (applyBottomInnerClip) {
1422                     graphicsContext->save();
1423                     graphicsContext->addInnerRoundedRectClip(IntRect(topX, bottomY, bottomLeft.width() * 2, bottomLeft.height() * 2),
1424                                                              style->borderLeftWidth());
1425                 }
1426
1427                 secondAngleStart = 180;
1428                 secondAngleSpan = 45;
1429
1430                 // Draw bottom left arc
1431                 drawArcForBoxSide(graphicsContext, topX, bottomY, thickness, bottomLeft, secondAngleStart, secondAngleSpan,
1432                               BSLeft, leftColor, leftStyle, false);
1433                 if (applyBottomInnerClip)
1434                     graphicsContext->restore();
1435             }
1436         }
1437     }
1438
1439     if (renderRight) {
1440         bool ignore_top = (renderRadii && topRight.height() > 0) ||
1441             ((topColor == rightColor) && (topTransparent == rightTransparent) &&
1442             (rightStyle >= DOTTED || rightStyle == INSET) &&
1443             (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET));
1444
1445         bool ignore_bottom = (renderRadii && bottomRight.height() > 0) ||
1446             ((bottomColor == rightColor) && (bottomTransparent == rightTransparent) &&
1447             (rightStyle >= DOTTED || rightStyle == INSET) &&
1448             (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET));
1449
1450         int y = ty;
1451         int y2 = ty + h;
1452         if (renderRadii) {
1453             y += topRight.height();
1454             y2 -= bottomRight.height();
1455         }
1456
1457         drawLineForBoxSide(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rightColor, rightStyle,
1458                    ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth());
1459
1460         if (renderRadii && (!upperRightBorderStylesMatch || !lowerRightBorderStylesMatch)) {
1461             thickness = style->borderRightWidth() * 2;
1462
1463             if (!upperRightBorderStylesMatch && topRight.width()) {
1464                 int topX = tx + w - topRight.width() * 2;
1465                 int topY = ty;
1466                 bool applyTopInnerClip = (style->borderRightWidth() < topRight.width())
1467                     && (style->borderTopWidth() < topRight.height())
1468                     && (rightStyle != DOUBLE || style->borderRightWidth() > 6);
1469                 if (applyTopInnerClip) {
1470                     graphicsContext->save();
1471                     graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topRight.width() * 2, topRight.height() * 2),
1472                                                              style->borderRightWidth());
1473                 }
1474
1475                 firstAngleStart = 0;
1476                 firstAngleSpan = 45;
1477
1478                 // Draw top right arc
1479                 drawArcForBoxSide(graphicsContext, topX, topY, thickness, topRight, firstAngleStart, firstAngleSpan,
1480                               BSRight, rightColor, rightStyle, true);
1481                 if (applyTopInnerClip)
1482                     graphicsContext->restore();
1483             }
1484
1485             if (!lowerRightBorderStylesMatch && bottomRight.width()) {
1486                 int bottomX = tx + w - bottomRight.width() * 2;
1487                 int bottomY = ty + h - bottomRight.height() * 2;
1488                 bool applyBottomInnerClip = (style->borderRightWidth() < bottomRight.width())
1489                     && (style->borderBottomWidth() < bottomRight.height())
1490                     && (rightStyle != DOUBLE || style->borderRightWidth() > 6);
1491                 if (applyBottomInnerClip) {
1492                     graphicsContext->save();
1493                     graphicsContext->addInnerRoundedRectClip(IntRect(bottomX, bottomY, bottomRight.width() * 2, bottomRight.height() * 2),
1494                                                              style->borderRightWidth());
1495                 }
1496
1497                 secondAngleStart = 315;
1498                 secondAngleSpan = 45;
1499
1500                 // Draw bottom right arc
1501                 drawArcForBoxSide(graphicsContext, bottomX, bottomY, thickness, bottomRight, secondAngleStart, secondAngleSpan,
1502                               BSRight, rightColor, rightStyle, false);
1503                 if (applyBottomInnerClip)
1504                     graphicsContext->restore();
1505             }
1506         }
1507     }
1508
1509     if (renderRadii)
1510         graphicsContext->restore();
1511 }
1512 #endif
1513
1514 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)
1515 {
1516     FloatPoint quad[4];
1517     int tx = box.x();
1518     int ty = box.y();
1519     int w = box.width();
1520     int h = box.height();
1521
1522     // For each side, create an array of FloatPoints where each point is based on whichever value in each corner
1523     // is larger -- the radius width/height or the border width/height -- as appropriate.
1524     switch (side) {
1525     case BSTop:
1526         quad[0] = FloatPoint(tx, ty);
1527         quad[1] = FloatPoint(
1528             tx + max(topLeft.width(), (int) style->borderLeftWidth()), 
1529             ty + max(topLeft.height(), (int) style->borderTopWidth()));
1530         quad[2] = FloatPoint(
1531             tx + w - max(topRight.width(), (int) style->borderRightWidth()), 
1532             ty + max(topRight.height(), (int)style->borderTopWidth()));
1533         quad[3] = FloatPoint(tx + w, ty);
1534         break;
1535     case BSLeft:
1536         quad[0] = FloatPoint(tx, ty);
1537         quad[1] = FloatPoint(
1538             tx + max(topLeft.width(), (int) style->borderLeftWidth()), 
1539             ty + max(topLeft.height(), (int) style->borderTopWidth()));
1540         quad[2] = FloatPoint(
1541             tx + max(bottomLeft.width(), (int) style->borderLeftWidth()), 
1542             ty + h - max(bottomLeft.height(), (int)style->borderBottomWidth()));
1543         quad[3] = FloatPoint(tx, ty + h);
1544         break;
1545     case BSBottom:
1546         quad[0] = FloatPoint(tx, ty + h);
1547         quad[1] = FloatPoint(
1548             tx + max(bottomLeft.width(), (int) style->borderLeftWidth()), 
1549             ty + h - max(bottomLeft.height(), (int)style->borderBottomWidth()));
1550         quad[2] = FloatPoint(
1551             tx + w - max(bottomRight.width(), (int) style->borderRightWidth()), 
1552             ty + h - max(bottomRight.height(), (int)style->borderBottomWidth()));
1553         quad[3] = FloatPoint(tx + w, ty + h);
1554         break;
1555     case BSRight:
1556         quad[0] = FloatPoint(tx + w, ty);
1557         quad[1] = FloatPoint(
1558             tx + w - max(topRight.width(), (int) style->borderRightWidth()), 
1559             ty + max(topRight.height(), (int) style->borderTopWidth()));
1560         quad[2] = FloatPoint(
1561             tx + w - max(bottomRight.width(), (int) style->borderRightWidth()), 
1562             ty + h - max(bottomRight.height(), (int)style->borderBottomWidth()));
1563         quad[3] = FloatPoint(tx + w, ty + h);
1564         break;
1565     default:
1566         break;
1567     }
1568
1569     // If the border matches both of its adjacent sides, don't anti-alias the clip, and
1570     // if neither side matches, anti-alias the clip.
1571     if (firstEdgeMatches == secondEdgeMatches) {
1572         graphicsContext->clipConvexPolygon(4, quad, !firstEdgeMatches);
1573         return;
1574     }
1575
1576     FloatPoint firstQuad[4];
1577     firstQuad[0] = quad[0];
1578     firstQuad[1] = quad[1];
1579     firstQuad[2] = side == BSTop || side == BSBottom ? FloatPoint(quad[3].x(), quad[2].y())
1580         : FloatPoint(quad[2].x(), quad[3].y());
1581     firstQuad[3] = quad[3];
1582     graphicsContext->clipConvexPolygon(4, firstQuad, !firstEdgeMatches);
1583
1584     FloatPoint secondQuad[4];
1585     secondQuad[0] = quad[0];
1586     secondQuad[1] = side == BSTop || side == BSBottom ? FloatPoint(quad[0].x(), quad[1].y())
1587         : FloatPoint(quad[1].x(), quad[0].y());
1588     secondQuad[2] = quad[2];
1589     secondQuad[3] = quad[3];
1590     graphicsContext->clipConvexPolygon(4, secondQuad, !secondEdgeMatches);
1591 }
1592
1593 static inline void uniformlyExpandBorderRadii(int delta, IntSize& topLeft, IntSize& topRight, IntSize& bottomLeft, IntSize& bottomRight)
1594 {
1595     topLeft.expand(delta, delta);
1596     topLeft.clampNegativeToZero();
1597     topRight.expand(delta, delta);
1598     topRight.clampNegativeToZero();
1599     bottomLeft.expand(delta, delta);
1600     bottomLeft.clampNegativeToZero();
1601     bottomRight.expand(delta, delta);
1602     bottomRight.clampNegativeToZero();
1603 }
1604
1605 void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int ty, int w, int h, const RenderStyle* s, ShadowStyle shadowStyle, bool begin, bool end)
1606 {
1607     // FIXME: Deal with border-image.  Would be great to use border-image as a mask.
1608
1609     if (context->paintingDisabled())
1610         return;
1611
1612     IntRect rect(tx, ty, w, h);
1613     IntSize topLeft;
1614     IntSize topRight;
1615     IntSize bottomLeft;
1616     IntSize bottomRight;
1617
1618     bool hasBorderRadius = s->hasBorderRadius();
1619     if (hasBorderRadius && (begin || end)) {
1620         IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius;
1621         s->getBorderRadiiForRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
1622
1623         if (begin) {
1624             if (shadowStyle == Inset) {
1625                 topLeftRadius.expand(-borderLeft(), -borderTop());
1626                 topLeftRadius.clampNegativeToZero();
1627                 bottomLeftRadius.expand(-borderLeft(), -borderBottom());
1628                 bottomLeftRadius.clampNegativeToZero();
1629             }
1630             topLeft = topLeftRadius;
1631             bottomLeft = bottomLeftRadius;
1632         }
1633         if (end) {
1634             if (shadowStyle == Inset) {
1635                 topRightRadius.expand(-borderRight(), -borderTop());
1636                 topRightRadius.clampNegativeToZero();
1637                 bottomRightRadius.expand(-borderRight(), -borderBottom());
1638                 bottomRightRadius.clampNegativeToZero();
1639             }
1640             topRight = topRightRadius;
1641             bottomRight = bottomRightRadius;
1642         }
1643     }
1644
1645     if (shadowStyle == Inset) {
1646         rect.move(begin ? borderLeft() : 0, borderTop());
1647         rect.setWidth(rect.width() - (begin ? borderLeft() : 0) - (end ? borderRight() : 0));
1648         rect.setHeight(rect.height() - borderTop() - borderBottom());
1649     }
1650
1651     bool hasOpaqueBackground = s->visitedDependentColor(CSSPropertyBackgroundColor).isValid() && s->visitedDependentColor(CSSPropertyBackgroundColor).alpha() == 255;
1652     for (const ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next()) {
1653         if (shadow->style() != shadowStyle)
1654             continue;
1655
1656         IntSize shadowOffset(shadow->x(), shadow->y());
1657         int shadowBlur = shadow->blur();
1658         int shadowSpread = shadow->spread();
1659         const Color& shadowColor = shadow->color();
1660
1661         if (shadow->style() == Normal) {
1662             IntRect fillRect(rect);
1663             fillRect.inflate(shadowSpread);
1664             if (fillRect.isEmpty())
1665                 continue;
1666
1667             IntRect shadowRect(rect);
1668             shadowRect.inflate(shadowBlur + shadowSpread);
1669             shadowRect.move(shadowOffset);
1670
1671             context->save();
1672             context->clip(shadowRect);
1673
1674             // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not
1675             // bleed in (due to antialiasing) if the context is transformed.
1676             IntSize extraOffset(w + max(0, shadowOffset.width()) + shadowBlur + 2 * shadowSpread + 1, 0);
1677             shadowOffset -= extraOffset;
1678             fillRect.move(extraOffset);
1679
1680             context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace());
1681             if (hasBorderRadius) {
1682                 IntRect rectToClipOut = rect;
1683                 IntSize topLeftToClipOut = topLeft;
1684                 IntSize topRightToClipOut = topRight;
1685                 IntSize bottomLeftToClipOut = bottomLeft;
1686                 IntSize bottomRightToClipOut = bottomRight;
1687
1688                 if (shadowSpread < 0)
1689                     uniformlyExpandBorderRadii(shadowSpread, topLeft, topRight, bottomLeft, bottomRight);
1690
1691                 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
1692                 // when painting the shadow. On the other hand, it introduces subpixel gaps along the
1693                 // corners. Those are avoided by insetting the clipping path by one pixel.
1694                 if (hasOpaqueBackground) {
1695                     rectToClipOut.inflate(-1);
1696                     uniformlyExpandBorderRadii(-1, topLeftToClipOut, topRightToClipOut, bottomLeftToClipOut, bottomRightToClipOut);
1697                 }
1698
1699                 if (!rectToClipOut.isEmpty())
1700                     context->clipOutRoundedRect(rectToClipOut, topLeftToClipOut, topRightToClipOut, bottomLeftToClipOut, bottomRightToClipOut);
1701                 context->fillRoundedRect(fillRect, topLeft, topRight, bottomLeft, bottomRight, Color::black, s->colorSpace());
1702             } else {
1703                 IntRect rectToClipOut = rect;
1704
1705                 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
1706                 // when painting the shadow. On the other hand, it introduces subpixel gaps along the
1707                 // edges if they are not pixel-aligned. Those are avoided by insetting the clipping path
1708                 // by one pixel.
1709                 if (hasOpaqueBackground) {
1710                     AffineTransform currentTransformation = context->getCTM();
1711                     if (currentTransformation.a() != 1 || (currentTransformation.d() != 1 && currentTransformation.d() != -1)
1712                             || currentTransformation.b() || currentTransformation.c())
1713                         rectToClipOut.inflate(-1);
1714                 }
1715
1716                 if (!rectToClipOut.isEmpty())
1717                     context->clipOut(rectToClipOut);
1718                 context->fillRect(fillRect, Color::black, s->colorSpace());
1719             }
1720
1721             context->restore();
1722         } else {
1723             // Inset shadow.
1724             IntRect holeRect(rect);
1725             holeRect.inflate(-shadowSpread);
1726
1727             if (holeRect.isEmpty()) {
1728                 if (hasBorderRadius)
1729                     context->fillRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight, shadowColor, s->colorSpace());
1730                 else
1731                     context->fillRect(rect, shadowColor, s->colorSpace());
1732                 continue;
1733             }
1734             if (!begin) {
1735                 holeRect.move(-max(shadowOffset.width(), 0) - shadowBlur, 0);
1736                 holeRect.setWidth(holeRect.width() + max(shadowOffset.width(), 0) + shadowBlur);
1737             }
1738             if (!end)
1739                 holeRect.setWidth(holeRect.width() - min(shadowOffset.width(), 0) + shadowBlur);
1740
1741             Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255);
1742
1743             IntRect outerRect(rect);
1744             outerRect.inflateX(w - 2 * shadowSpread);
1745             outerRect.inflateY(h - 2 * shadowSpread);
1746
1747             context->save();
1748
1749             if (hasBorderRadius)
1750                 context->clip(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight));
1751             else
1752                 context->clip(rect);
1753
1754             IntSize extraOffset(2 * w + max(0, shadowOffset.width()) + shadowBlur - 2 * shadowSpread + 1, 0);
1755             context->translate(extraOffset.width(), extraOffset.height());
1756             shadowOffset -= extraOffset;
1757
1758             context->beginPath();
1759             context->addPath(Path::createRectangle(outerRect));
1760
1761             if (hasBorderRadius) {
1762                 if (shadowSpread > 0)
1763                     uniformlyExpandBorderRadii(-shadowSpread, topLeft, topRight, bottomLeft, bottomRight);
1764                 context->addPath(Path::createRoundedRectangle(holeRect, topLeft, topRight, bottomLeft, bottomRight));
1765             } else
1766                 context->addPath(Path::createRectangle(holeRect));
1767
1768             context->setFillRule(RULE_EVENODD);
1769             context->setFillColor(fillColor, s->colorSpace());
1770             context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace());
1771             context->fillPath();
1772
1773             context->restore();
1774         }
1775     }
1776 }
1777
1778 int RenderBoxModelObject::containingBlockWidthForContent() const
1779 {
1780     return containingBlock()->availableWidth();
1781 }
1782
1783 } // namespace WebCore