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