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