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