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