Micro-optimize RenderBoxModelObject::computedCSSPadding().
[WebKit-https.git] / Source / WebCore / rendering / RenderBoxModelObject.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
5  *           (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
6  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved.
7  * Copyright (C) 2010 Google Inc. All rights reserved.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  *
24  */
25
26 #include "config.h"
27 #include "RenderBoxModelObject.h"
28
29 #include "Frame.h"
30 #include "GraphicsContext.h"
31 #include "HTMLFrameOwnerElement.h"
32 #include "HTMLNames.h"
33 #include "ImageBuffer.h"
34 #include "ImageQualityController.h"
35 #include "Page.h"
36 #include "Path.h"
37 #include "RenderBlock.h"
38 #include "RenderInline.h"
39 #include "RenderLayer.h"
40 #include "RenderNamedFlowFragment.h"
41 #include "RenderNamedFlowThread.h"
42 #include "RenderRegion.h"
43 #include "RenderTable.h"
44 #include "RenderView.h"
45 #include "ScrollingConstraints.h"
46 #include "Settings.h"
47 #include "TransformState.h"
48
49 #if USE(ACCELERATED_COMPOSITING)
50 #include "RenderLayerBacking.h"
51 #include "RenderLayerCompositor.h"
52 #endif
53
54 namespace WebCore {
55
56 using namespace HTMLNames;
57
58 // The HashMap for storing continuation pointers.
59 // An inline can be split with blocks occuring in between the inline content.
60 // When this occurs we need a pointer to the next object. We can basically be
61 // split into a sequence of inlines and blocks. The continuation will either be
62 // an anonymous block (that houses other blocks) or it will be an inline flow.
63 // <b><i><p>Hello</p></i></b>. In this example the <i> will have a block as
64 // its continuation but the <b> will just have an inline as its continuation.
65 typedef HashMap<const RenderBoxModelObject*, RenderBoxModelObject*> ContinuationMap;
66 static ContinuationMap* continuationMap = 0;
67
68 // This HashMap is similar to the continuation map, but connects first-letter
69 // renderers to their remaining text fragments.
70 typedef HashMap<const RenderBoxModelObject*, RenderTextFragment*> FirstLetterRemainingTextMap;
71 static FirstLetterRemainingTextMap* firstLetterRemainingTextMap = 0;
72
73 void RenderBoxModelObject::setSelectionState(SelectionState state)
74 {
75     if (state == SelectionInside && selectionState() != SelectionNone)
76         return;
77
78     if ((state == SelectionStart && selectionState() == SelectionEnd)
79         || (state == SelectionEnd && selectionState() == SelectionStart))
80         RenderLayerModelObject::setSelectionState(SelectionBoth);
81     else
82         RenderLayerModelObject::setSelectionState(state);
83
84     // FIXME: We should consider whether it is OK propagating to ancestor RenderInlines.
85     // This is a workaround for http://webkit.org/b/32123
86     // The containing block can be null in case of an orphaned tree.
87     RenderBlock* containingBlock = this->containingBlock();
88     if (containingBlock && !containingBlock->isRenderView())
89         containingBlock->setSelectionState(state);
90 }
91
92 #if USE(ACCELERATED_COMPOSITING)
93 void RenderBoxModelObject::contentChanged(ContentChangeType changeType)
94 {
95     if (!hasLayer())
96         return;
97
98     layer()->contentChanged(changeType);
99 }
100
101 bool RenderBoxModelObject::hasAcceleratedCompositing() const
102 {
103     return view().compositor().hasAcceleratedCompositing();
104 }
105
106 bool RenderBoxModelObject::startTransition(double timeOffset, CSSPropertyID propertyId, const RenderStyle* fromStyle, const RenderStyle* toStyle)
107 {
108     ASSERT(hasLayer());
109     ASSERT(isComposited());
110     return layer()->backing()->startTransition(timeOffset, propertyId, fromStyle, toStyle);
111 }
112
113 void RenderBoxModelObject::transitionPaused(double timeOffset, CSSPropertyID propertyId)
114 {
115     ASSERT(hasLayer());
116     ASSERT(isComposited());
117     layer()->backing()->transitionPaused(timeOffset, propertyId);
118 }
119
120 void RenderBoxModelObject::transitionFinished(CSSPropertyID propertyId)
121 {
122     ASSERT(hasLayer());
123     ASSERT(isComposited());
124     layer()->backing()->transitionFinished(propertyId);
125 }
126
127 bool RenderBoxModelObject::startAnimation(double timeOffset, const Animation* animation, const KeyframeList& keyframes)
128 {
129     ASSERT(hasLayer());
130     ASSERT(isComposited());
131     return layer()->backing()->startAnimation(timeOffset, animation, keyframes);
132 }
133
134 void RenderBoxModelObject::animationPaused(double timeOffset, const String& name)
135 {
136     ASSERT(hasLayer());
137     ASSERT(isComposited());
138     layer()->backing()->animationPaused(timeOffset, name);
139 }
140
141 void RenderBoxModelObject::animationFinished(const String& name)
142 {
143     ASSERT(hasLayer());
144     ASSERT(isComposited());
145     layer()->backing()->animationFinished(name);
146 }
147
148 void RenderBoxModelObject::suspendAnimations(double time)
149 {
150     ASSERT(hasLayer());
151     ASSERT(isComposited());
152     layer()->backing()->suspendAnimations(time);
153 }
154 #endif
155
156 bool RenderBoxModelObject::shouldPaintAtLowQuality(GraphicsContext* context, Image* image, const void* layer, const LayoutSize& size)
157 {
158     return view().imageQualityController().shouldPaintAtLowQuality(context, this, image, layer, size);
159 }
160
161 RenderBoxModelObject::RenderBoxModelObject(Element& element, PassRef<RenderStyle> style, unsigned baseTypeFlags)
162     : RenderLayerModelObject(element, std::move(style), baseTypeFlags | RenderBoxModelObjectFlag)
163 {
164 }
165
166 RenderBoxModelObject::RenderBoxModelObject(Document& document, PassRef<RenderStyle> style, unsigned baseTypeFlags)
167     : RenderLayerModelObject(document, std::move(style), baseTypeFlags | RenderBoxModelObjectFlag)
168 {
169 }
170
171 RenderBoxModelObject::~RenderBoxModelObject()
172 {
173 }
174
175 void RenderBoxModelObject::willBeDestroyed()
176 {
177     // A continuation of this RenderObject should be destroyed at subclasses.
178     ASSERT(!continuation());
179
180     // If this is a first-letter object with a remaining text fragment then the
181     // entry needs to be cleared from the map.
182     if (firstLetterRemainingText())
183         setFirstLetterRemainingText(0);
184
185     if (!documentBeingDestroyed())
186         view().imageQualityController().rendererWillBeDestroyed(*this);
187
188     RenderLayerModelObject::willBeDestroyed();
189 }
190
191 void RenderBoxModelObject::updateFromStyle()
192 {
193     RenderLayerModelObject::updateFromStyle();
194
195     // Set the appropriate bits for a box model object.  Since all bits are cleared in styleWillChange,
196     // we only check for bits that could possibly be set to true.
197     const RenderStyle& styleToUse = style();
198     setHasBoxDecorations(hasBackground() || styleToUse.hasBorder() || styleToUse.hasAppearance() || styleToUse.boxShadow());
199     setInline(styleToUse.isDisplayInlineType());
200     setPositionState(styleToUse.position());
201     setHorizontalWritingMode(styleToUse.isHorizontalWritingMode());
202 }
203
204 static LayoutSize accumulateInFlowPositionOffsets(const RenderObject* child)
205 {
206     if (!child->isAnonymousBlock() || !child->isInFlowPositioned())
207         return LayoutSize();
208     LayoutSize offset;
209     RenderElement* p = toRenderBlock(child)->inlineElementContinuation();
210     while (p && p->isRenderInline()) {
211         if (p->isInFlowPositioned()) {
212             RenderInline* renderInline = toRenderInline(p);
213             offset += renderInline->offsetForInFlowPosition();
214         }
215         p = p->parent();
216     }
217     return offset;
218 }
219
220 bool RenderBoxModelObject::hasAutoHeightOrContainingBlockWithAutoHeight() const
221 {
222     Length logicalHeightLength = style().logicalHeight();
223     if (logicalHeightLength.isAuto())
224         return true;
225
226     // For percentage heights: The percentage is calculated with respect to the height of the generated box's
227     // containing block. If the height of the containing block is not specified explicitly (i.e., it depends
228     // on content height), and this element is not absolutely positioned, the value computes to 'auto'.
229     if (!logicalHeightLength.isPercent() || isOutOfFlowPositioned() || document().inQuirksMode())
230         return false;
231
232     // Anonymous block boxes are ignored when resolving percentage values that would refer to it:
233     // the closest non-anonymous ancestor box is used instead.
234     RenderBlock* cb = containingBlock(); 
235     while (cb->isAnonymous() && !cb->isRenderView())
236         cb = cb->containingBlock();
237
238     // Matching RenderBox::percentageLogicalHeightIsResolvableFromBlock() by
239     // ignoring table cell's attribute value, where it says that table cells violate
240     // what the CSS spec says to do with heights. Basically we
241     // don't care if the cell specified a height or not.
242     if (cb->isTableCell())
243         return false;
244     
245     if (!cb->style().logicalHeight().isAuto() || (!cb->style().logicalTop().isAuto() && !cb->style().logicalBottom().isAuto()))
246         return false;
247
248     return true;
249 }
250
251 LayoutSize RenderBoxModelObject::relativePositionOffset() const
252 {
253     LayoutSize offset = accumulateInFlowPositionOffsets(this);
254
255     RenderBlock* containingBlock = this->containingBlock();
256
257     // Objects that shrink to avoid floats normally use available line width when computing containing block width.  However
258     // in the case of relative positioning using percentages, we can't do this.  The offset should always be resolved using the
259     // available width of the containing block.  Therefore we don't use containingBlockLogicalWidthForContent() here, but instead explicitly
260     // call availableWidth on our containing block.
261     if (!style().left().isAuto()) {
262         if (!style().right().isAuto() && !containingBlock->style().isLeftToRightDirection())
263             offset.setWidth(-valueForLength(style().right(), containingBlock->availableWidth()));
264         else
265             offset.expand(valueForLength(style().left(), containingBlock->availableWidth()), 0);
266     } else if (!style().right().isAuto()) {
267         offset.expand(-valueForLength(style().right(), containingBlock->availableWidth()), 0);
268     }
269
270     // If the containing block of a relatively positioned element does not
271     // specify a height, a percentage top or bottom offset should be resolved as
272     // auto. An exception to this is if the containing block has the WinIE quirk
273     // where <html> and <body> assume the size of the viewport. In this case,
274     // calculate the percent offset based on this height.
275     // See <https://bugs.webkit.org/show_bug.cgi?id=26396>.
276     if (!style().top().isAuto()
277         && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight()
278             || !style().top().isPercent()
279             || containingBlock->stretchesToViewport()))
280         offset.expand(0, valueForLength(style().top(), containingBlock->availableHeight()));
281
282     else if (!style().bottom().isAuto()
283         && (!containingBlock->hasAutoHeightOrContainingBlockWithAutoHeight()
284             || !style().bottom().isPercent()
285             || containingBlock->stretchesToViewport()))
286         offset.expand(0, -valueForLength(style().bottom(), containingBlock->availableHeight()));
287
288     return offset;
289 }
290
291 LayoutPoint RenderBoxModelObject::adjustedPositionRelativeToOffsetParent(const LayoutPoint& startPoint) const
292 {
293     // If the element is the HTML body element or doesn't have a parent
294     // return 0 and stop this algorithm.
295     if (isBody() || !parent())
296         return LayoutPoint();
297
298     LayoutPoint referencePoint = startPoint;
299     referencePoint.move(parent()->offsetForColumns(referencePoint));
300     
301     // If the offsetParent of the element is null, or is the HTML body element,
302     // return the distance between the canvas origin and the left border edge 
303     // of the element and stop this algorithm.
304     if (const RenderBoxModelObject* offsetParent = this->offsetParent()) {
305         if (offsetParent->isBox() && !offsetParent->isBody() && !offsetParent->isTable())
306             referencePoint.move(-toRenderBox(offsetParent)->borderLeft(), -toRenderBox(offsetParent)->borderTop());
307         if (!isOutOfFlowPositioned() || flowThreadContainingBlock()) {
308             if (isRelPositioned())
309                 referencePoint.move(relativePositionOffset());
310             else if (isStickyPositioned())
311                 referencePoint.move(stickyPositionOffset());
312             
313             // CSS regions specification says that region flows should return the body element as their offsetParent.
314             // Since we will bypass the body’s renderer anyway, just end the loop if we encounter a region flow (named flow thread).
315             // See http://dev.w3.org/csswg/css-regions/#cssomview-offset-attributes
316             auto curr = parent();
317             while (curr != offsetParent && !curr->isRenderNamedFlowThread()) {
318                 // FIXME: What are we supposed to do inside SVG content?
319                 if (!isOutOfFlowPositioned()) {
320                     if (curr->isBox() && !curr->isTableRow())
321                         referencePoint.moveBy(toRenderBox(curr)->topLeftLocation());
322                     referencePoint.move(curr->parent()->offsetForColumns(referencePoint));
323                 }
324                 curr = curr->parent();
325             }
326             
327             // Compute the offset position for elements inside named flow threads for which the offsetParent was the body.
328             // See https://bugs.webkit.org/show_bug.cgi?id=115899
329             if (curr->isRenderNamedFlowThread())
330                 referencePoint = toRenderNamedFlowThread(curr)->adjustedPositionRelativeToOffsetParent(*this, referencePoint);
331             else if (offsetParent->isBox() && offsetParent->isBody() && !offsetParent->isPositioned())
332                 referencePoint.moveBy(toRenderBox(offsetParent)->topLeftLocation());
333         }
334     }
335
336     return referencePoint;
337 }
338
339 void RenderBoxModelObject::computeStickyPositionConstraints(StickyPositionViewportConstraints& constraints, const FloatRect& constrainingRect) const
340 {
341     constraints.setConstrainingRectAtLastLayout(constrainingRect);
342
343     RenderBlock* containingBlock = this->containingBlock();
344     RenderLayer* enclosingClippingLayer = layer()->enclosingOverflowClipLayer(ExcludeSelf);
345     RenderBox& enclosingClippingBox = enclosingClippingLayer ? toRenderBox(enclosingClippingLayer->renderer()) : view();
346
347     LayoutRect containerContentRect;
348     if (!enclosingClippingLayer || (containingBlock != &enclosingClippingBox))
349         containerContentRect = containingBlock->contentBoxRect();
350     else {
351         containerContentRect = containingBlock->layoutOverflowRect();
352         LayoutPoint containerLocation = containerContentRect.location() + LayoutPoint(containingBlock->borderLeft() + containingBlock->paddingLeft(),
353             containingBlock->borderTop() + containingBlock->paddingTop());
354         containerContentRect.setLocation(containerLocation);
355     }
356
357     LayoutUnit maxWidth = containingBlock->availableLogicalWidth();
358
359     // Sticky positioned element ignore any override logical width on the containing block (as they don't call
360     // containingBlockLogicalWidthForContent). It's unclear whether this is totally fine.
361     LayoutBoxExtent minMargin(minimumValueForLength(style().marginTop(), maxWidth),
362         minimumValueForLength(style().marginRight(), maxWidth),
363         minimumValueForLength(style().marginBottom(), maxWidth),
364         minimumValueForLength(style().marginLeft(), maxWidth));
365
366     // Compute the container-relative area within which the sticky element is allowed to move.
367     containerContentRect.contract(minMargin);
368
369     // Finally compute container rect relative to the scrolling ancestor.
370     FloatRect containerRectRelativeToScrollingAncestor = containingBlock->localToContainerQuad(FloatRect(containerContentRect), &enclosingClippingBox).boundingBox();
371     if (enclosingClippingLayer) {
372         FloatPoint containerLocationRelativeToScrollingAncestor = containerRectRelativeToScrollingAncestor.location() -
373             FloatSize(enclosingClippingBox.borderLeft() + enclosingClippingBox.paddingLeft(),
374             enclosingClippingBox.borderTop() + enclosingClippingBox.paddingTop());
375         if (&enclosingClippingBox != containingBlock)
376             containerLocationRelativeToScrollingAncestor += enclosingClippingLayer->scrollOffset();
377         containerRectRelativeToScrollingAncestor.setLocation(containerLocationRelativeToScrollingAncestor);
378     }
379     constraints.setContainingBlockRect(containerRectRelativeToScrollingAncestor);
380
381     // Now compute the sticky box rect, also relative to the scrolling ancestor.
382     LayoutRect stickyBoxRect = frameRectForStickyPositioning();
383     LayoutRect flippedStickyBoxRect = stickyBoxRect;
384     containingBlock->flipForWritingMode(flippedStickyBoxRect);
385     FloatRect stickyBoxRelativeToScrollingAnecstor = flippedStickyBoxRect;
386
387     // FIXME: sucks to call localToContainerQuad again, but we can't just offset from the previously computed rect if there are transforms.
388     // Map to the view to avoid including page scale factor.
389     FloatPoint stickyLocationRelativeToScrollingAncestor = flippedStickyBoxRect.location() + containingBlock->localToContainerQuad(FloatRect(FloatPoint(), containingBlock->size()), &enclosingClippingBox).boundingBox().location();
390     if (enclosingClippingLayer) {
391         stickyLocationRelativeToScrollingAncestor -= FloatSize(enclosingClippingBox.borderLeft() + enclosingClippingBox.paddingLeft(),
392             enclosingClippingBox.borderTop() + enclosingClippingBox.paddingTop());
393         if (&enclosingClippingBox != containingBlock)
394             stickyLocationRelativeToScrollingAncestor += enclosingClippingLayer->scrollOffset();
395     }
396     // FIXME: For now, assume that |this| is not transformed.
397     stickyBoxRelativeToScrollingAnecstor.setLocation(stickyLocationRelativeToScrollingAncestor);
398     constraints.setStickyBoxRect(stickyBoxRelativeToScrollingAnecstor);
399
400     if (!style().left().isAuto()) {
401         constraints.setLeftOffset(valueForLength(style().left(), constrainingRect.width()));
402         constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeLeft);
403     }
404
405     if (!style().right().isAuto()) {
406         constraints.setRightOffset(valueForLength(style().right(), constrainingRect.width()));
407         constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeRight);
408     }
409
410     if (!style().top().isAuto()) {
411         constraints.setTopOffset(valueForLength(style().top(), constrainingRect.height()));
412         constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeTop);
413     }
414
415     if (!style().bottom().isAuto()) {
416         constraints.setBottomOffset(valueForLength(style().bottom(), constrainingRect.height()));
417         constraints.addAnchorEdge(ViewportConstraints::AnchorEdgeBottom);
418     }
419 }
420
421 LayoutSize RenderBoxModelObject::stickyPositionOffset() const
422 {
423     FloatRect constrainingRect;
424
425     ASSERT(hasLayer());
426     RenderLayer* enclosingClippingLayer = layer()->enclosingOverflowClipLayer(ExcludeSelf);
427     if (enclosingClippingLayer) {
428         RenderBox& enclosingClippingBox = toRenderBox(enclosingClippingLayer->renderer());
429         LayoutRect clipRect = enclosingClippingBox.overflowClipRect(LayoutPoint(), 0); // FIXME: make this work in regions.
430         constrainingRect = enclosingClippingBox.localToContainerQuad(FloatRect(clipRect), &view()).boundingBox();
431
432         FloatPoint scrollOffset = FloatPoint() + enclosingClippingLayer->scrollOffset();
433         constrainingRect.setLocation(scrollOffset);
434     } else {
435 #if PLATFORM(IOS)
436         LayoutRect viewportRect = view().frameView().customFixedPositionLayoutRect();
437 #else
438         LayoutRect viewportRect = view().frameView().viewportConstrainedVisibleContentRect();
439 #endif
440         float scale = view().frameView().frame().frameScaleFactor();
441         viewportRect.scale(1 / scale);
442         constrainingRect = viewportRect;
443     }
444     
445     StickyPositionViewportConstraints constraints;
446     computeStickyPositionConstraints(constraints, constrainingRect);
447     
448     // The sticky offset is physical, so we can just return the delta computed in absolute coords (though it may be wrong with transforms).
449     return LayoutSize(constraints.computeStickyOffset(constrainingRect));
450 }
451
452 LayoutSize RenderBoxModelObject::offsetForInFlowPosition() const
453 {
454     if (isRelPositioned())
455         return relativePositionOffset();
456
457     if (isStickyPositioned())
458         return stickyPositionOffset();
459
460     return LayoutSize();
461 }
462
463 LayoutUnit RenderBoxModelObject::offsetLeft() const
464 {
465     // Note that RenderInline and RenderBox override this to pass a different
466     // startPoint to adjustedPositionRelativeToOffsetParent.
467     return adjustedPositionRelativeToOffsetParent(LayoutPoint()).x();
468 }
469
470 LayoutUnit RenderBoxModelObject::offsetTop() const
471 {
472     // Note that RenderInline and RenderBox override this to pass a different
473     // startPoint to adjustedPositionRelativeToOffsetParent.
474     return adjustedPositionRelativeToOffsetParent(LayoutPoint()).y();
475 }
476
477 int RenderBoxModelObject::pixelSnappedOffsetWidth() const
478 {
479     return snapSizeToPixel(offsetWidth(), offsetLeft());
480 }
481
482 int RenderBoxModelObject::pixelSnappedOffsetHeight() const
483 {
484     return snapSizeToPixel(offsetHeight(), offsetTop());
485 }
486
487 LayoutUnit RenderBoxModelObject::computedCSSPadding(const Length& padding) const
488 {
489     LayoutUnit w = 0;
490     if (padding.isPercent())
491         w = containingBlockLogicalWidthForContent();
492     return minimumValueForLength(padding, w);
493 }
494
495 RoundedRect RenderBoxModelObject::getBackgroundRoundedRect(const LayoutRect& borderRect, InlineFlowBox* box, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHeight,
496     bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const
497 {
498     RoundedRect border = style().getRoundedBorderFor(borderRect, &view(), includeLogicalLeftEdge, includeLogicalRightEdge);
499     if (box && (box->nextLineBox() || box->prevLineBox())) {
500         RoundedRect segmentBorder = style().getRoundedBorderFor(LayoutRect(0, 0, inlineBoxWidth, inlineBoxHeight), &view(), includeLogicalLeftEdge, includeLogicalRightEdge);
501         border.setRadii(segmentBorder.radii());
502     }
503
504     return border;
505 }
506
507 void RenderBoxModelObject::clipRoundedInnerRect(GraphicsContext * context, const LayoutRect& rect, const RoundedRect& clipRect)
508 {
509     if (clipRect.isRenderable())
510         context->clipRoundedRect(clipRect);
511     else {
512         // We create a rounded rect for each of the corners and clip it, while making sure we clip opposing corners together.
513         if (!clipRect.radii().topLeft().isEmpty() || !clipRect.radii().bottomRight().isEmpty()) {
514             IntRect topCorner(clipRect.rect().x(), clipRect.rect().y(), rect.maxX() - clipRect.rect().x(), rect.maxY() - clipRect.rect().y());
515             RoundedRect::Radii topCornerRadii;
516             topCornerRadii.setTopLeft(clipRect.radii().topLeft());
517             context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii));
518
519             IntRect bottomCorner(rect.x(), rect.y(), clipRect.rect().maxX() - rect.x(), clipRect.rect().maxY() - rect.y());
520             RoundedRect::Radii bottomCornerRadii;
521             bottomCornerRadii.setBottomRight(clipRect.radii().bottomRight());
522             context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii));
523         } 
524
525         if (!clipRect.radii().topRight().isEmpty() || !clipRect.radii().bottomLeft().isEmpty()) {
526             IntRect topCorner(rect.x(), clipRect.rect().y(), clipRect.rect().maxX() - rect.x(), rect.maxY() - clipRect.rect().y());
527             RoundedRect::Radii topCornerRadii;
528             topCornerRadii.setTopRight(clipRect.radii().topRight());
529             context->clipRoundedRect(RoundedRect(topCorner, topCornerRadii));
530
531             IntRect bottomCorner(clipRect.rect().x(), rect.y(), rect.maxX() - clipRect.rect().x(), clipRect.rect().maxY() - rect.y());
532             RoundedRect::Radii bottomCornerRadii;
533             bottomCornerRadii.setBottomLeft(clipRect.radii().bottomLeft());
534             context->clipRoundedRect(RoundedRect(bottomCorner, bottomCornerRadii));
535         }
536     }
537 }
538
539 static LayoutRect shrinkRectByOnePixel(GraphicsContext* context, const LayoutRect& rect)
540 {
541     LayoutRect shrunkRect = rect;
542     AffineTransform transform = context->getCTM();
543     shrunkRect.inflateX(-static_cast<LayoutUnit>(ceil(1 / transform.xScale())));
544     shrunkRect.inflateY(-static_cast<LayoutUnit>(ceil(1 / transform.yScale())));
545     return shrunkRect;
546 }
547
548 LayoutRect RenderBoxModelObject::borderInnerRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& rect, BackgroundBleedAvoidance bleedAvoidance) const
549 {
550     // We shrink the rectangle by one pixel on each side to make it fully overlap the anti-aliased background border
551     return (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? shrinkRectByOnePixel(context, rect) : rect;
552 }
553
554 RoundedRect RenderBoxModelObject::backgroundRoundedRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& borderRect, BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const
555 {
556     if (bleedAvoidance == BackgroundBleedShrinkBackground) {
557         // We shrink the rectangle by one pixel on each side because the bleed is one pixel maximum.
558         return getBackgroundRoundedRect(shrinkRectByOnePixel(context, borderRect), box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogicalRightEdge);
559     }
560     if (bleedAvoidance == BackgroundBleedBackgroundOverBorder)
561         return style().getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge);
562
563     return getBackgroundRoundedRect(borderRect, box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogicalRightEdge);
564 }
565
566 static void applyBoxShadowForBackground(GraphicsContext* context, RenderStyle* style)
567 {
568     const ShadowData* boxShadow = style->boxShadow();
569     while (boxShadow->style() != Normal)
570         boxShadow = boxShadow->next();
571
572     FloatSize shadowOffset(boxShadow->x(), boxShadow->y());
573     if (!boxShadow->isWebkitBoxShadow())
574         context->setShadow(shadowOffset, boxShadow->radius(), boxShadow->color(), style->colorSpace());
575     else
576         context->setLegacyShadow(shadowOffset, boxShadow->radius(), boxShadow->color(), style->colorSpace());
577 }
578
579 void RenderBoxModelObject::paintMaskForTextFillBox(ImageBuffer* maskImage, const IntRect& maskRect, InlineFlowBox* box, const LayoutRect& scrolledPaintRect, RenderRegion* region)
580 {
581     GraphicsContext* maskImageContext = maskImage->context();
582     maskImageContext->translate(-maskRect.x(), -maskRect.y());
583
584     // Now add the text to the clip. We do this by painting using a special paint phase that signals to
585     // InlineTextBoxes that they should just add their contents to the clip.
586     PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, PaintBehaviorForceBlackText, 0, region);
587     if (box) {
588         const RootInlineBox& rootBox = box->root();
589         box->paint(info, LayoutPoint(scrolledPaintRect.x() - box->x(), scrolledPaintRect.y() - box->y()), rootBox.lineTop(), rootBox.lineBottom());
590     } else if (isRenderNamedFlowFragmentContainer()) {
591         RenderNamedFlowFragment* region = toRenderBlockFlow(this)->renderNamedFlowFragment();
592         if (!region->flowThread())
593             return;
594         region->flowThread()->layer()->paintNamedFlowThreadInsideRegion(maskImageContext, region, maskRect, maskRect.location(), PaintBehaviorForceBlackText, RenderLayer::PaintLayerTemporaryClipRects);
595     } else {
596         LayoutSize localOffset = isBox() ? toRenderBox(this)->locationOffset() : LayoutSize();
597         paint(info, scrolledPaintRect.location() - localOffset);
598     }
599 }
600
601 void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& color, const FillLayer* bgLayer, const LayoutRect& rect,
602     BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, CompositeOperator op, RenderElement* backgroundObject)
603 {
604     GraphicsContext* context = paintInfo.context;
605     if (context->paintingDisabled() || rect.isEmpty())
606         return;
607
608     bool includeLeftEdge = box ? box->includeLogicalLeftEdge() : true;
609     bool includeRightEdge = box ? box->includeLogicalRightEdge() : true;
610
611     bool hasRoundedBorder = style().hasBorderRadius() && (includeLeftEdge || includeRightEdge);
612     bool clippedWithLocalScrolling = hasOverflowClip() && bgLayer->attachment() == LocalBackgroundAttachment;
613     bool isBorderFill = bgLayer->clip() == BorderFillBox;
614     bool isRoot = this->isRoot();
615
616     Color bgColor = color;
617     StyleImage* bgImage = bgLayer->image();
618     bool shouldPaintBackgroundImage = bgImage && bgImage->canRender(this, style().effectiveZoom());
619     
620     bool forceBackgroundToWhite = false;
621     if (document().printing()) {
622         if (style().printColorAdjust() == PrintColorAdjustEconomy)
623             forceBackgroundToWhite = true;
624         if (frame().settings().shouldPrintBackgrounds())
625             forceBackgroundToWhite = false;
626     }
627
628     // When printing backgrounds is disabled or using economy mode,
629     // change existing background colors and images to a solid white background.
630     // If there's no bg color or image, leave it untouched to avoid affecting transparency.
631     // We don't try to avoid loading the background images, because this style flag is only set
632     // when printing, and at that point we've already loaded the background images anyway. (To avoid
633     // loading the background images we'd have to do this check when applying styles rather than
634     // while rendering.)
635     if (forceBackgroundToWhite) {
636         // Note that we can't reuse this variable below because the bgColor might be changed
637         bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && bgColor.alpha();
638         if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) {
639             bgColor = Color::white;
640             shouldPaintBackgroundImage = false;
641         }
642     }
643
644     bool colorVisible = bgColor.isValid() && bgColor.alpha();
645     
646     // Fast path for drawing simple color backgrounds.
647     if (!isRoot && !clippedWithLocalScrolling && !shouldPaintBackgroundImage && isBorderFill && !bgLayer->next()) {
648         if (!colorVisible)
649             return;
650
651         bool boxShadowShouldBeAppliedToBackground = this->boxShadowShouldBeAppliedToBackground(bleedAvoidance, box);
652         GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAppliedToBackground);
653         if (boxShadowShouldBeAppliedToBackground)
654             applyBoxShadowForBackground(context, &style());
655
656         if (hasRoundedBorder && bleedAvoidance != BackgroundBleedUseTransparencyLayer) {
657             RoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge);
658             if (border.isRenderable())
659                 context->fillRoundedRect(border, bgColor, style().colorSpace());
660             else {
661                 context->save();
662                 clipRoundedInnerRect(context, rect, border);
663                 context->fillRect(border.rect(), bgColor, style().colorSpace());
664                 context->restore();
665             }
666         } else
667             context->fillRect(pixelSnappedIntRect(rect), bgColor, style().colorSpace());
668
669         return;
670     }
671
672     // BorderFillBox radius clipping is taken care of by BackgroundBleedUseTransparencyLayer
673     bool clipToBorderRadius = hasRoundedBorder && !(isBorderFill && bleedAvoidance == BackgroundBleedUseTransparencyLayer);
674     GraphicsContextStateSaver clipToBorderStateSaver(*context, clipToBorderRadius);
675     if (clipToBorderRadius) {
676         RoundedRect border = isBorderFill ? backgroundRoundedRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge) : getBackgroundRoundedRect(rect, box, boxSize.width(), boxSize.height(), includeLeftEdge, includeRightEdge);
677
678         // Clip to the padding or content boxes as necessary.
679         if (bgLayer->clip() == ContentFillBox) {
680             border = style().getRoundedInnerBorderFor(border.rect(),
681                 paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), includeLeftEdge, includeRightEdge);
682         } else if (bgLayer->clip() == PaddingFillBox)
683             border = style().getRoundedInnerBorderFor(border.rect(), includeLeftEdge, includeRightEdge);
684
685         clipRoundedInnerRect(context, rect, border);
686     }
687     
688     int bLeft = includeLeftEdge ? borderLeft() : 0;
689     int bRight = includeRightEdge ? borderRight() : 0;
690     LayoutUnit pLeft = includeLeftEdge ? paddingLeft() : LayoutUnit();
691     LayoutUnit pRight = includeRightEdge ? paddingRight() : LayoutUnit();
692
693     GraphicsContextStateSaver clipWithScrollingStateSaver(*context, clippedWithLocalScrolling);
694     LayoutRect scrolledPaintRect = rect;
695     if (clippedWithLocalScrolling) {
696         // Clip to the overflow area.
697         RenderBox* thisBox = toRenderBox(this);
698         context->clip(thisBox->overflowClipRect(rect.location(), paintInfo.renderRegion));
699         
700         // Adjust the paint rect to reflect a scrolled content box with borders at the ends.
701         IntSize offset = thisBox->scrolledContentOffset();
702         scrolledPaintRect.move(-offset);
703         scrolledPaintRect.setWidth(bLeft + layer()->scrollWidth() + bRight);
704         scrolledPaintRect.setHeight(borderTop() + layer()->scrollHeight() + borderBottom());
705     }
706     
707     GraphicsContextStateSaver backgroundClipStateSaver(*context, false);
708     std::unique_ptr<ImageBuffer> maskImage;
709     IntRect maskRect;
710
711     if (bgLayer->clip() == PaddingFillBox || bgLayer->clip() == ContentFillBox) {
712         // Clip to the padding or content boxes as necessary.
713         if (!clipToBorderRadius) {
714             bool includePadding = bgLayer->clip() == ContentFillBox;
715             LayoutRect clipRect = LayoutRect(scrolledPaintRect.x() + bLeft + (includePadding ? pLeft : LayoutUnit()),
716                 scrolledPaintRect.y() + borderTop() + (includePadding ? paddingTop() : LayoutUnit()),
717                 scrolledPaintRect.width() - bLeft - bRight - (includePadding ? pLeft + pRight : LayoutUnit()),
718                 scrolledPaintRect.height() - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : LayoutUnit()));
719             backgroundClipStateSaver.save();
720             context->clip(clipRect);
721         }
722     } else if (bgLayer->clip() == TextFillBox) {
723         // We have to draw our text into a mask that can then be used to clip background drawing.
724         // First figure out how big the mask has to be.  It should be no bigger than what we need
725         // to actually render, so we should intersect the dirty rect with the border box of the background.
726         maskRect = pixelSnappedIntRect(rect);
727         maskRect.intersect(paintInfo.rect);
728
729         // Now create the mask.
730         maskImage = context->createCompatibleBuffer(maskRect.size());
731         if (!maskImage)
732             return;
733         paintMaskForTextFillBox(maskImage.get(), maskRect, box, scrolledPaintRect, paintInfo.renderRegion);
734
735         // The mask has been created.  Now we just need to clip to it.
736         backgroundClipStateSaver.save();
737         context->clip(maskRect);
738         context->beginTransparencyLayer(1);
739     }
740
741     // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with
742     // no background in the child document should show the parent's background.
743     bool isOpaqueRoot = false;
744     if (isRoot) {
745         isOpaqueRoot = true;
746         if (!bgLayer->next() && !(bgColor.isValid() && bgColor.alpha() == 255)) {
747             Element* ownerElement = document().ownerElement();
748             if (ownerElement) {
749                 if (!ownerElement->hasTagName(frameTag)) {
750                     // Locate the <body> element using the DOM.  This is easier than trying
751                     // to crawl around a render tree with potential :before/:after content and
752                     // anonymous blocks created by inline <body> tags etc.  We can locate the <body>
753                     // render object very easily via the DOM.
754                     HTMLElement* body = document().body();
755                     if (body) {
756                         // Can't scroll a frameset document anyway.
757                         isOpaqueRoot = body->hasLocalName(framesetTag);
758                     }
759 #if ENABLE(SVG)
760                     else {
761                         // SVG documents and XML documents with SVG root nodes are transparent.
762                         isOpaqueRoot = !document().hasSVGRootNode();
763                     }
764 #endif
765                 }
766             } else
767                 isOpaqueRoot = !view().frameView().isTransparent();
768         }
769         view().frameView().setContentIsOpaque(isOpaqueRoot);
770     }
771
772     // Paint the color first underneath all images, culled if background image occludes it.
773     // FIXME: In the bgLayer->hasFiniteBounds() case, we could improve the culling test
774     // by verifying whether the background image covers the entire layout rect.
775     if (!bgLayer->next()) {
776         IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect));
777         bool boxShadowShouldBeAppliedToBackground = this->boxShadowShouldBeAppliedToBackground(bleedAvoidance, box);
778         if (boxShadowShouldBeAppliedToBackground || !shouldPaintBackgroundImage || !bgLayer->hasOpaqueImage(this) || !bgLayer->hasRepeatXY()) {
779             if (!boxShadowShouldBeAppliedToBackground)
780                 backgroundRect.intersect(paintInfo.rect);
781
782             // If we have an alpha and we are painting the root element, go ahead and blend with the base background color.
783             Color baseColor;
784             bool shouldClearBackground = false;
785             if (isOpaqueRoot) {
786                 baseColor = view().frameView().baseBackgroundColor();
787                 if (!baseColor.alpha())
788                     shouldClearBackground = true;
789             }
790
791             GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAppliedToBackground);
792             if (boxShadowShouldBeAppliedToBackground)
793                 applyBoxShadowForBackground(context, &style());
794
795             if (baseColor.alpha()) {
796                 if (bgColor.alpha())
797                     baseColor = baseColor.blend(bgColor);
798
799                 context->fillRect(backgroundRect, baseColor, style().colorSpace(), CompositeCopy);
800             } else if (bgColor.alpha()) {
801                 CompositeOperator operation = shouldClearBackground ? CompositeCopy : context->compositeOperation();
802                 context->fillRect(backgroundRect, bgColor, style().colorSpace(), operation);
803             } else if (shouldClearBackground)
804                 context->clearRect(backgroundRect);
805         }
806     }
807
808     // no progressive loading of the background image
809     if (shouldPaintBackgroundImage) {
810         BackgroundImageGeometry geometry;
811         calculateBackgroundImageGeometry(paintInfo.paintContainer, bgLayer, scrolledPaintRect, geometry, backgroundObject);
812         geometry.clip(paintInfo.rect);
813         if (!geometry.destRect().isEmpty()) {
814             CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op;
815             auto clientForBackgroundImage = backgroundObject ? backgroundObject : this;
816             RefPtr<Image> image = bgImage->image(clientForBackgroundImage, geometry.tileSize());
817             context->setDrawLuminanceMask(bgLayer->maskSourceType() == MaskLuminance);
818             bool useLowQualityScaling = shouldPaintAtLowQuality(context, image.get(), bgLayer, geometry.tileSize());
819             if (image.get())
820                 image->setSpaceSize(geometry.spaceSize());
821             context->drawTiledImage(image.get(), style().colorSpace(), geometry.destRect(), geometry.relativePhase(), geometry.tileSize(), 
822                 compositeOp, useLowQualityScaling, bgLayer->blendMode());
823         }
824     }
825
826     if (bgLayer->clip() == TextFillBox) {
827         context->drawImageBuffer(maskImage.get(), ColorSpaceDeviceRGB, maskRect, CompositeDestinationIn);
828         context->endTransparencyLayer();
829     }
830 }
831
832 static inline int resolveWidthForRatio(int height, const FloatSize& intrinsicRatio)
833 {
834     return ceilf(height * intrinsicRatio.width() / intrinsicRatio.height());
835 }
836
837 static inline int resolveHeightForRatio(int width, const FloatSize& intrinsicRatio)
838 {
839     return ceilf(width * intrinsicRatio.height() / intrinsicRatio.width());
840 }
841
842 static inline IntSize resolveAgainstIntrinsicWidthOrHeightAndRatio(const IntSize& size, const FloatSize& intrinsicRatio, int useWidth, int useHeight)
843 {
844     if (intrinsicRatio.isEmpty()) {
845         if (useWidth)
846             return IntSize(useWidth, size.height());
847         return IntSize(size.width(), useHeight);
848     }
849
850     if (useWidth)
851         return IntSize(useWidth, resolveHeightForRatio(useWidth, intrinsicRatio));
852     return IntSize(resolveWidthForRatio(useHeight, intrinsicRatio), useHeight);
853 }
854
855 static inline IntSize resolveAgainstIntrinsicRatio(const IntSize& size, const FloatSize& intrinsicRatio)
856 {
857     // Two possible solutions: (size.width(), solutionHeight) or (solutionWidth, size.height())
858     // "... must be assumed to be the largest dimensions..." = easiest answer: the rect with the largest surface area.
859
860     int solutionWidth = resolveWidthForRatio(size.height(), intrinsicRatio);
861     int solutionHeight = resolveHeightForRatio(size.width(), intrinsicRatio);
862     if (solutionWidth <= size.width()) {
863         if (solutionHeight <= size.height()) {
864             // If both solutions fit, choose the one covering the larger area.
865             int areaOne = solutionWidth * size.height();
866             int areaTwo = size.width() * solutionHeight;
867             if (areaOne < areaTwo)
868                 return IntSize(size.width(), solutionHeight);
869             return IntSize(solutionWidth, size.height());
870         }
871
872         // Only the first solution fits.
873         return IntSize(solutionWidth, size.height());
874     }
875
876     // Only the second solution fits, assert that.
877     ASSERT(solutionHeight <= size.height());
878     return IntSize(size.width(), solutionHeight);
879 }
880
881 IntSize RenderBoxModelObject::calculateImageIntrinsicDimensions(StyleImage* image, const IntSize& positioningAreaSize, ScaleByEffectiveZoomOrNot shouldScaleOrNot) const
882 {
883     // A generated image without a fixed size, will always return the container size as intrinsic size.
884     if (image->isGeneratedImage() && image->usesImageContainerSize())
885         return IntSize(positioningAreaSize.width(), positioningAreaSize.height());
886
887     Length intrinsicWidth;
888     Length intrinsicHeight;
889     FloatSize intrinsicRatio;
890     image->computeIntrinsicDimensions(this, intrinsicWidth, intrinsicHeight, intrinsicRatio);
891
892     // Intrinsic dimensions expressed as percentages must be resolved relative to the dimensions of the rectangle
893     // that establishes the coordinate system for the 'background-position' property. 
894     
895     // FIXME: Remove unnecessary rounding when layout is off ints: webkit.org/b/63656
896     if (intrinsicWidth.isPercent() && intrinsicHeight.isPercent() && intrinsicRatio.isEmpty()) {
897         // Resolve width/height percentages against positioningAreaSize, only if no intrinsic ratio is provided.
898         int resolvedWidth = static_cast<int>(round(positioningAreaSize.width() * intrinsicWidth.percent() / 100));
899         int resolvedHeight = static_cast<int>(round(positioningAreaSize.height() * intrinsicHeight.percent() / 100));
900         return IntSize(resolvedWidth, resolvedHeight);
901     }
902
903     IntSize resolvedSize(intrinsicWidth.isFixed() ? intrinsicWidth.value() : 0, intrinsicHeight.isFixed() ? intrinsicHeight.value() : 0);
904     IntSize minimumSize(resolvedSize.width() > 0 ? 1 : 0, resolvedSize.height() > 0 ? 1 : 0);
905     if (shouldScaleOrNot == ScaleByEffectiveZoom)
906         resolvedSize.scale(style().effectiveZoom());
907     resolvedSize.clampToMinimumSize(minimumSize);
908
909     if (!resolvedSize.isEmpty())
910         return resolvedSize;
911
912     // If the image has one of either an intrinsic width or an intrinsic height:
913     // * and an intrinsic aspect ratio, then the missing dimension is calculated from the given dimension and the ratio.
914     // * and no intrinsic aspect ratio, then the missing dimension is assumed to be the size of the rectangle that
915     //   establishes the coordinate system for the 'background-position' property.
916     if (resolvedSize.width() > 0 || resolvedSize.height() > 0)
917         return resolveAgainstIntrinsicWidthOrHeightAndRatio(positioningAreaSize, intrinsicRatio, resolvedSize.width(), resolvedSize.height());
918
919     // If the image has no intrinsic dimensions and has an intrinsic ratio the dimensions must be assumed to be the
920     // largest dimensions at that ratio such that neither dimension exceeds the dimensions of the rectangle that
921     // establishes the coordinate system for the 'background-position' property.
922     if (!intrinsicRatio.isEmpty())
923         return resolveAgainstIntrinsicRatio(positioningAreaSize, intrinsicRatio);
924
925     // If the image has no intrinsic ratio either, then the dimensions must be assumed to be the rectangle that
926     // establishes the coordinate system for the 'background-position' property.
927     return positioningAreaSize;
928 }
929
930 static inline void applySubPixelHeuristicForTileSize(LayoutSize& tileSize, const IntSize& positioningAreaSize)
931 {
932     tileSize.setWidth(positioningAreaSize.width() - tileSize.width() <= 1 ? tileSize.width().ceil() : tileSize.width().floor());
933     tileSize.setHeight(positioningAreaSize.height() - tileSize.height() <= 1 ? tileSize.height().ceil() : tileSize.height().floor());
934 }
935
936 IntSize RenderBoxModelObject::calculateFillTileSize(const FillLayer* fillLayer, const IntSize& positioningAreaSize) const
937 {
938     StyleImage* image = fillLayer->image();
939     EFillSizeType type = fillLayer->size().type;
940
941     IntSize imageIntrinsicSize = calculateImageIntrinsicDimensions(image, positioningAreaSize, ScaleByEffectiveZoom);
942     imageIntrinsicSize.scale(1 / image->imageScaleFactor(), 1 / image->imageScaleFactor());
943     switch (type) {
944         case SizeLength: {
945             LayoutSize tileSize = positioningAreaSize;
946
947             Length layerWidth = fillLayer->size().size.width();
948             Length layerHeight = fillLayer->size().size.height();
949
950             if (layerWidth.isFixed())
951                 tileSize.setWidth(layerWidth.value());
952             else if (layerWidth.isPercent() || layerWidth.isViewportPercentage())
953                 tileSize.setWidth(valueForLength(layerWidth, positioningAreaSize.width()));
954             
955             if (layerHeight.isFixed())
956                 tileSize.setHeight(layerHeight.value());
957             else if (layerHeight.isPercent() || layerHeight.isViewportPercentage())
958                 tileSize.setHeight(valueForLength(layerHeight, positioningAreaSize.height()));
959
960             applySubPixelHeuristicForTileSize(tileSize, positioningAreaSize);
961
962             // If one of the values is auto we have to use the appropriate
963             // scale to maintain our aspect ratio.
964             if (layerWidth.isAuto() && !layerHeight.isAuto()) {
965                 if (imageIntrinsicSize.height())
966                     tileSize.setWidth(imageIntrinsicSize.width() * tileSize.height() / imageIntrinsicSize.height());
967             } else if (!layerWidth.isAuto() && layerHeight.isAuto()) {
968                 if (imageIntrinsicSize.width())
969                     tileSize.setHeight(imageIntrinsicSize.height() * tileSize.width() / imageIntrinsicSize.width());
970             } else if (layerWidth.isAuto() && layerHeight.isAuto()) {
971                 // If both width and height are auto, use the image's intrinsic size.
972                 tileSize = imageIntrinsicSize;
973             }
974             
975             tileSize.clampNegativeToZero();
976             return flooredIntSize(tileSize);
977         }
978         case SizeNone: {
979             // If both values are ‘auto’ then the intrinsic width and/or height of the image should be used, if any.
980             if (!imageIntrinsicSize.isEmpty())
981                 return imageIntrinsicSize;
982
983             // If the image has neither an intrinsic width nor an intrinsic height, its size is determined as for ‘contain’.
984             type = Contain;
985         }
986         case Contain:
987         case Cover: {
988             float horizontalScaleFactor = imageIntrinsicSize.width()
989                 ? static_cast<float>(positioningAreaSize.width()) / imageIntrinsicSize.width() : 1;
990             float verticalScaleFactor = imageIntrinsicSize.height()
991                 ? static_cast<float>(positioningAreaSize.height()) / imageIntrinsicSize.height() : 1;
992             float scaleFactor = type == Contain ? std::min(horizontalScaleFactor, verticalScaleFactor) : std::max(horizontalScaleFactor, verticalScaleFactor);
993             return IntSize(std::max(1, static_cast<int>(imageIntrinsicSize.width() * scaleFactor)), std::max(1, static_cast<int>(imageIntrinsicSize.height() * scaleFactor)));
994        }
995     }
996
997     ASSERT_NOT_REACHED();
998     return IntSize();
999 }
1000
1001 void RenderBoxModelObject::BackgroundImageGeometry::setNoRepeatX(int xOffset)
1002 {
1003     m_destRect.move(std::max(xOffset, 0), 0);
1004     m_phase.setX(-std::min(xOffset, 0));
1005     m_destRect.setWidth(m_tileSize.width() + std::min(xOffset, 0));
1006 }
1007 void RenderBoxModelObject::BackgroundImageGeometry::setNoRepeatY(int yOffset)
1008 {
1009     m_destRect.move(0, std::max(yOffset, 0));
1010     m_phase.setY(-std::min(yOffset, 0));
1011     m_destRect.setHeight(m_tileSize.height() + std::min(yOffset, 0));
1012 }
1013
1014 void RenderBoxModelObject::BackgroundImageGeometry::useFixedAttachment(const IntPoint& attachmentPoint)
1015 {
1016     IntPoint alignedPoint = attachmentPoint;
1017     m_phase.move(std::max(alignedPoint.x() - m_destRect.x(), 0), std::max(alignedPoint.y() - m_destRect.y(), 0));
1018 }
1019
1020 void RenderBoxModelObject::BackgroundImageGeometry::clip(const IntRect& clipRect)
1021 {
1022     m_destRect.intersect(clipRect);
1023 }
1024
1025 IntPoint RenderBoxModelObject::BackgroundImageGeometry::relativePhase() const
1026 {
1027     IntPoint phase = m_phase;
1028     phase += m_destRect.location() - m_destOrigin;
1029     return phase;
1030 }
1031
1032 bool RenderBoxModelObject::fixedBackgroundPaintsInLocalCoordinates() const
1033 {
1034 #if USE(ACCELERATED_COMPOSITING)
1035     if (!isRoot())
1036         return false;
1037
1038     if (view().frameView().paintBehavior() & PaintBehaviorFlattenCompositingLayers)
1039         return false;
1040
1041     RenderLayer* rootLayer = view().layer();
1042     if (!rootLayer || !rootLayer->isComposited())
1043         return false;
1044
1045     return rootLayer->backing()->backgroundLayerPaintsFixedRootBackground();
1046 #else
1047     return false;
1048 #endif
1049 }
1050
1051 static inline int getSpace(int areaSize, int tileSize)
1052 {
1053     int numberOfTiles = areaSize / tileSize;
1054     int space = -1;
1055
1056     if (numberOfTiles > 1)
1057         space = roundedLayoutUnit((float)(areaSize - numberOfTiles * tileSize) / (numberOfTiles - 1));
1058
1059     return space;
1060 }
1061
1062 void RenderBoxModelObject::calculateBackgroundImageGeometry(const RenderLayerModelObject* paintContainer, const FillLayer* fillLayer, const LayoutRect& paintRect,
1063     BackgroundImageGeometry& geometry, RenderElement* backgroundObject) const
1064 {
1065     LayoutUnit left = 0;
1066     LayoutUnit top = 0;
1067     IntSize positioningAreaSize;
1068     IntRect snappedPaintRect = pixelSnappedIntRect(paintRect);
1069
1070     // Determine the background positioning area and set destRect to the background painting area.
1071     // destRect will be adjusted later if the background is non-repeating.
1072     // FIXME: transforms spec says that fixed backgrounds behave like scroll inside transforms. https://bugs.webkit.org/show_bug.cgi?id=15679
1073     bool fixedAttachment = fillLayer->attachment() == FixedBackgroundAttachment;
1074     
1075 #if ENABLE(FAST_MOBILE_SCROLLING)
1076     if (view().frameView().canBlitOnScroll()) {
1077         // As a side effect of an optimization to blit on scroll, we do not honor the CSS
1078         // property "background-attachment: fixed" because it may result in rendering
1079         // artifacts. Note, these artifacts only appear if we are blitting on scroll of
1080         // a page that has fixed background images.
1081         fixedAttachment = false;
1082     }
1083 #endif
1084
1085     if (!fixedAttachment) {
1086         geometry.setDestRect(snappedPaintRect);
1087
1088         LayoutUnit right = 0;
1089         LayoutUnit bottom = 0;
1090         // Scroll and Local.
1091         if (fillLayer->origin() != BorderFillBox) {
1092             left = borderLeft();
1093             right = borderRight();
1094             top = borderTop();
1095             bottom = borderBottom();
1096             if (fillLayer->origin() == ContentFillBox) {
1097                 left += paddingLeft();
1098                 right += paddingRight();
1099                 top += paddingTop();
1100                 bottom += paddingBottom();
1101             }
1102         }
1103
1104         // The background of the box generated by the root element covers the entire canvas including
1105         // its margins. Since those were added in already, we have to factor them out when computing
1106         // the background positioning area.
1107         if (isRoot()) {
1108             positioningAreaSize = pixelSnappedIntSize(toRenderBox(this)->size() - LayoutSize(left + right, top + bottom), toRenderBox(this)->location());
1109             if (view().frameView().hasExtendedBackground()) {
1110                 IntRect extendedBackgroundRect = view().frameView().extendedBackgroundRect();
1111                 left += (marginLeft() - extendedBackgroundRect.x());
1112                 top += (marginTop() - extendedBackgroundRect.y());
1113             }
1114         } else
1115             positioningAreaSize = pixelSnappedIntSize(paintRect.size() - LayoutSize(left + right, top + bottom), paintRect.location());
1116     } else {
1117         geometry.setHasNonLocalGeometry();
1118
1119         IntRect viewportRect = pixelSnappedIntRect(view().viewRect());
1120         if (fixedBackgroundPaintsInLocalCoordinates())
1121             viewportRect.setLocation(IntPoint());
1122         else
1123             viewportRect.setLocation(IntPoint(view().frameView().scrollOffsetForFixedPosition()));
1124
1125         if (paintContainer) {
1126             IntPoint absoluteContainerOffset = roundedIntPoint(paintContainer->localToAbsolute(FloatPoint()));
1127             viewportRect.moveBy(-absoluteContainerOffset);
1128         }
1129
1130         geometry.setDestRect(pixelSnappedIntRect(viewportRect));
1131         positioningAreaSize = geometry.destRect().size();
1132     }
1133
1134     auto clientForBackgroundImage = backgroundObject ? backgroundObject : this;
1135     IntSize fillTileSize = calculateFillTileSize(fillLayer, positioningAreaSize);
1136     fillLayer->image()->setContainerSizeForRenderer(clientForBackgroundImage, fillTileSize, style().effectiveZoom());
1137     geometry.setTileSize(fillTileSize);
1138
1139     EFillRepeat backgroundRepeatX = fillLayer->repeatX();
1140     EFillRepeat backgroundRepeatY = fillLayer->repeatY();
1141     int availableWidth = positioningAreaSize.width() - geometry.tileSize().width();
1142     int availableHeight = positioningAreaSize.height() - geometry.tileSize().height();
1143
1144     LayoutUnit computedXPosition = minimumValueForLength(fillLayer->xPosition(), availableWidth, true);
1145     if (backgroundRepeatX == RoundFill && positioningAreaSize.width() > 0 && fillTileSize.width() > 0) {
1146         long nrTiles = lroundf((float)positioningAreaSize.width() / fillTileSize.width());
1147         if (!nrTiles)
1148             nrTiles = 1;
1149
1150         if (fillLayer->size().size.height().isAuto() && backgroundRepeatY != RoundFill)
1151             fillTileSize.setHeight(fillTileSize.height() * positioningAreaSize.width() / (nrTiles * fillTileSize.width()));
1152
1153         fillTileSize.setWidth(positioningAreaSize.width() / nrTiles);
1154         geometry.setTileSize(fillTileSize);
1155         geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().width() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0);
1156         geometry.setSpaceSize(FloatSize());
1157     }
1158
1159     LayoutUnit computedYPosition = minimumValueForLength(fillLayer->yPosition(), availableHeight, true);
1160     if (backgroundRepeatY == RoundFill && positioningAreaSize.height() > 0 && fillTileSize.height() > 0) {
1161         long nrTiles = lroundf((float)positioningAreaSize.height() / fillTileSize.height());
1162         if (!nrTiles)
1163             nrTiles = 1;
1164
1165         if (fillLayer->size().size.width().isAuto() && backgroundRepeatX != RoundFill)
1166             fillTileSize.setWidth(fillTileSize.width() * positioningAreaSize.height() / (nrTiles * fillTileSize.height()));
1167
1168         fillTileSize.setHeight(positioningAreaSize.height() / nrTiles);
1169         geometry.setTileSize(fillTileSize);
1170         geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().height() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0);
1171         geometry.setSpaceSize(FloatSize());
1172     }
1173
1174     if (backgroundRepeatX == RepeatFill) {
1175         geometry.setPhaseX(geometry.tileSize().width() ? geometry.tileSize().width() - roundToInt(computedXPosition + left) % geometry.tileSize().width() : 0);
1176         geometry.setSpaceSize(FloatSize(0, geometry.spaceSize().height()));
1177     } else if (backgroundRepeatX == SpaceFill && fillTileSize.width() > 0) {
1178         int space = getSpace(positioningAreaSize.width(), geometry.tileSize().width());
1179         int actualWidth = geometry.tileSize().width() + space;
1180
1181         if (space >= 0) {
1182             computedXPosition = minimumValueForLength(Length(), availableWidth, true);
1183             geometry.setSpaceSize(FloatSize(space, 0));
1184             geometry.setPhaseX(actualWidth ? actualWidth - roundToInt(computedXPosition + left) % actualWidth : 0);
1185         } else
1186             backgroundRepeatX = NoRepeatFill;
1187     }
1188     if (backgroundRepeatX == NoRepeatFill) {
1189         int xOffset = fillLayer->backgroundXOrigin() == RightEdge ? availableWidth - computedXPosition : computedXPosition;
1190         geometry.setNoRepeatX(left + xOffset);
1191         geometry.setSpaceSize(FloatSize(0, geometry.spaceSize().height()));
1192     }
1193
1194     if (backgroundRepeatY == RepeatFill) {
1195         geometry.setPhaseY(geometry.tileSize().height() ? geometry.tileSize().height() - roundToInt(computedYPosition + top) % geometry.tileSize().height() : 0);
1196         geometry.setSpaceSize(FloatSize(geometry.spaceSize().width(), 0));
1197     } else if (backgroundRepeatY == SpaceFill && fillTileSize.height() > 0) {
1198         int space = getSpace(positioningAreaSize.height(), geometry.tileSize().height());
1199         int actualHeight = geometry.tileSize().height() + space;
1200
1201         if (space >= 0) {
1202             computedYPosition = minimumValueForLength(Length(), availableHeight, true);
1203             geometry.setSpaceSize(FloatSize(geometry.spaceSize().width(), space));
1204             geometry.setPhaseY(actualHeight ? actualHeight - roundToInt(computedYPosition + top) % actualHeight : 0);
1205         } else
1206             backgroundRepeatY = NoRepeatFill;
1207     }
1208     if (backgroundRepeatY == NoRepeatFill) {
1209         int yOffset = fillLayer->backgroundYOrigin() == BottomEdge ? availableHeight - computedYPosition : computedYPosition;
1210         geometry.setNoRepeatY(top + yOffset);
1211         geometry.setSpaceSize(FloatSize(geometry.spaceSize().width(), 0));
1212     }
1213
1214     if (fixedAttachment)
1215         geometry.useFixedAttachment(snappedPaintRect.location());
1216
1217     geometry.clip(snappedPaintRect);
1218     geometry.setDestOrigin(geometry.destRect().location());
1219 }
1220
1221 void RenderBoxModelObject::getGeometryForBackgroundImage(const RenderLayerModelObject* paintContainer, IntRect& destRect, IntPoint& phase, IntSize& tileSize) const
1222 {
1223     const FillLayer* backgroundLayer = style().backgroundLayers();
1224     BackgroundImageGeometry geometry;
1225     calculateBackgroundImageGeometry(paintContainer, backgroundLayer, destRect, geometry);
1226     phase = geometry.phase();
1227     tileSize = geometry.tileSize();
1228     destRect = geometry.destRect();
1229 }
1230
1231 static LayoutUnit computeBorderImageSide(Length borderSlice, LayoutUnit borderSide, LayoutUnit imageSide, LayoutUnit boxExtent, RenderView* renderView)
1232 {
1233     if (borderSlice.isRelative())
1234         return borderSlice.value() * borderSide;
1235     if (borderSlice.isAuto())
1236         return imageSide;
1237     return valueForLength(borderSlice, boxExtent, renderView);
1238 }
1239
1240 bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, const LayoutRect& rect, const RenderStyle* style,
1241                                                const NinePieceImage& ninePieceImage, CompositeOperator op)
1242 {
1243     StyleImage* styleImage = ninePieceImage.image();
1244     if (!styleImage)
1245         return false;
1246
1247     if (!styleImage->isLoaded())
1248         return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either.
1249
1250     if (!styleImage->canRender(this, style->effectiveZoom()))
1251         return false;
1252
1253     // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function
1254     // doesn't have any understanding of the zoom that is in effect on the tile.
1255     LayoutRect rectWithOutsets = rect;
1256     rectWithOutsets.expand(style->imageOutsets(ninePieceImage));
1257     IntRect borderImageRect = pixelSnappedIntRect(rectWithOutsets);
1258
1259     IntSize imageSize = calculateImageIntrinsicDimensions(styleImage, borderImageRect.size(), DoNotScaleByEffectiveZoom);
1260
1261     // If both values are ‘auto’ then the intrinsic width and/or height of the image should be used, if any.
1262     styleImage->setContainerSizeForRenderer(this, imageSize, style->effectiveZoom());
1263
1264     int imageWidth = imageSize.width();
1265     int imageHeight = imageSize.height();
1266     RenderView* renderView = &view();
1267
1268     float imageScaleFactor = styleImage->imageScaleFactor();
1269     int topSlice = std::min<int>(imageHeight, valueForLength(ninePieceImage.imageSlices().top(), imageHeight)) * imageScaleFactor;
1270     int rightSlice = std::min<int>(imageWidth, valueForLength(ninePieceImage.imageSlices().right(), imageWidth)) * imageScaleFactor;
1271     int bottomSlice = std::min<int>(imageHeight, valueForLength(ninePieceImage.imageSlices().bottom(), imageHeight)) * imageScaleFactor;
1272     int leftSlice = std::min<int>(imageWidth, valueForLength(ninePieceImage.imageSlices().left(), imageWidth)) * imageScaleFactor;
1273
1274     ENinePieceImageRule hRule = ninePieceImage.horizontalRule();
1275     ENinePieceImageRule vRule = ninePieceImage.verticalRule();
1276
1277     int topWidth = computeBorderImageSide(ninePieceImage.borderSlices().top(), style->borderTopWidth(), topSlice, borderImageRect.height(), renderView);
1278     int rightWidth = computeBorderImageSide(ninePieceImage.borderSlices().right(), style->borderRightWidth(), rightSlice, borderImageRect.width(), renderView);
1279     int bottomWidth = computeBorderImageSide(ninePieceImage.borderSlices().bottom(), style->borderBottomWidth(), bottomSlice, borderImageRect.height(), renderView);
1280     int leftWidth = computeBorderImageSide(ninePieceImage.borderSlices().left(), style->borderLeftWidth(), leftSlice, borderImageRect.width(), renderView);
1281     
1282     // Reduce the widths if they're too large.
1283     // The spec says: Given Lwidth as the width of the border image area, Lheight as its height, and Wside as the border image width
1284     // offset for the side, let f = min(Lwidth/(Wleft+Wright), Lheight/(Wtop+Wbottom)). If f < 1, then all W are reduced by
1285     // multiplying them by f.
1286     int borderSideWidth = std::max(1, leftWidth + rightWidth);
1287     int borderSideHeight = std::max(1, topWidth + bottomWidth);
1288     float borderSideScaleFactor = std::min((float)borderImageRect.width() / borderSideWidth, (float)borderImageRect.height() / borderSideHeight);
1289     if (borderSideScaleFactor < 1) {
1290         topWidth *= borderSideScaleFactor;
1291         rightWidth *= borderSideScaleFactor;
1292         bottomWidth *= borderSideScaleFactor;
1293         leftWidth *= borderSideScaleFactor;
1294     }
1295
1296     bool drawLeft = leftSlice > 0 && leftWidth > 0;
1297     bool drawTop = topSlice > 0 && topWidth > 0;
1298     bool drawRight = rightSlice > 0 && rightWidth > 0;
1299     bool drawBottom = bottomSlice > 0 && bottomWidth > 0;
1300     bool drawMiddle = ninePieceImage.fill() && (imageWidth - leftSlice - rightSlice) > 0 && (borderImageRect.width() - leftWidth - rightWidth) > 0
1301                       && (imageHeight - topSlice - bottomSlice) > 0 && (borderImageRect.height() - topWidth - bottomWidth) > 0;
1302
1303     RefPtr<Image> image = styleImage->image(this, imageSize);
1304     ColorSpace colorSpace = style->colorSpace();
1305     
1306     float destinationWidth = borderImageRect.width() - leftWidth - rightWidth;
1307     float destinationHeight = borderImageRect.height() - topWidth - bottomWidth;
1308     
1309     float sourceWidth = imageWidth - leftSlice - rightSlice;
1310     float sourceHeight = imageHeight - topSlice - bottomSlice;
1311     
1312     float leftSideScale = drawLeft ? (float)leftWidth / leftSlice : 1;
1313     float rightSideScale = drawRight ? (float)rightWidth / rightSlice : 1;
1314     float topSideScale = drawTop ? (float)topWidth / topSlice : 1;
1315     float bottomSideScale = drawBottom ? (float)bottomWidth / bottomSlice : 1;
1316     
1317     if (drawLeft) {
1318         // Paint the top and bottom left corners.
1319
1320         // The top left corner rect is (tx, ty, leftWidth, topWidth)
1321         // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice)
1322         if (drawTop)
1323             graphicsContext->drawImage(image.get(), colorSpace, IntRect(borderImageRect.location(), IntSize(leftWidth, topWidth)),
1324                 LayoutRect(0, 0, leftSlice, topSlice), op, ImageOrientationDescription());
1325
1326         // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth)
1327         // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice)
1328         if (drawBottom)
1329             graphicsContext->drawImage(image.get(), colorSpace, IntRect(borderImageRect.x(), borderImageRect.maxY() - bottomWidth, leftWidth, bottomWidth),
1330                 LayoutRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op, ImageOrientationDescription());
1331
1332         // Paint the left edge.
1333         // Have to scale and tile into the border rect.
1334         if (sourceHeight > 0)
1335             graphicsContext->drawTiledImage(image.get(), colorSpace, IntRect(borderImageRect.x(), borderImageRect.y() + topWidth, leftWidth,
1336                                             destinationHeight),
1337                                             IntRect(0, topSlice, leftSlice, sourceHeight),
1338                                             FloatSize(leftSideScale, leftSideScale), Image::StretchTile, (Image::TileRule)vRule, op);
1339     }
1340
1341     if (drawRight) {
1342         // Paint the top and bottom right corners
1343         // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth)
1344         // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice)
1345         if (drawTop)
1346             graphicsContext->drawImage(image.get(), colorSpace, IntRect(borderImageRect.maxX() - rightWidth, borderImageRect.y(), rightWidth, topWidth),
1347                 LayoutRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op, ImageOrientationDescription());
1348
1349         // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth)
1350         // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice)
1351         if (drawBottom)
1352             graphicsContext->drawImage(image.get(), colorSpace, IntRect(borderImageRect.maxX() - rightWidth, borderImageRect.maxY() - bottomWidth, rightWidth, bottomWidth),
1353                 LayoutRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op, ImageOrientationDescription());
1354
1355         // Paint the right edge.
1356         if (sourceHeight > 0)
1357             graphicsContext->drawTiledImage(image.get(), colorSpace, IntRect(borderImageRect.maxX() - rightWidth, borderImageRect.y() + topWidth, rightWidth,
1358                                             destinationHeight),
1359                                             IntRect(imageWidth - rightSlice, topSlice, rightSlice, sourceHeight),
1360                                             FloatSize(rightSideScale, rightSideScale),
1361                                             Image::StretchTile, (Image::TileRule)vRule, op);
1362     }
1363
1364     // Paint the top edge.
1365     if (drawTop && sourceWidth > 0)
1366         graphicsContext->drawTiledImage(image.get(), colorSpace, IntRect(borderImageRect.x() + leftWidth, borderImageRect.y(), destinationWidth, topWidth),
1367                                         IntRect(leftSlice, 0, sourceWidth, topSlice),
1368                                         FloatSize(topSideScale, topSideScale), (Image::TileRule)hRule, Image::StretchTile, op);
1369
1370     // Paint the bottom edge.
1371     if (drawBottom && sourceWidth > 0)
1372         graphicsContext->drawTiledImage(image.get(), colorSpace, IntRect(borderImageRect.x() + leftWidth, borderImageRect.maxY() - bottomWidth,
1373                                         destinationWidth, bottomWidth),
1374                                         IntRect(leftSlice, imageHeight - bottomSlice, sourceWidth, bottomSlice),
1375                                         FloatSize(bottomSideScale, bottomSideScale),
1376                                         (Image::TileRule)hRule, Image::StretchTile, op);
1377
1378     // Paint the middle.
1379     if (drawMiddle) {
1380         FloatSize middleScaleFactor(1, 1);
1381         if (drawTop)
1382             middleScaleFactor.setWidth(topSideScale);
1383         else if (drawBottom)
1384             middleScaleFactor.setWidth(bottomSideScale);
1385         if (drawLeft)
1386             middleScaleFactor.setHeight(leftSideScale);
1387         else if (drawRight)
1388             middleScaleFactor.setHeight(rightSideScale);
1389             
1390         // For "stretch" rules, just override the scale factor and replace. We only had to do this for the
1391         // center tile, since sides don't even use the scale factor unless they have a rule other than "stretch".
1392         // The middle however can have "stretch" specified in one axis but not the other, so we have to
1393         // correct the scale here.
1394         if (hRule == StretchImageRule)
1395             middleScaleFactor.setWidth(destinationWidth / sourceWidth);
1396             
1397         if (vRule == StretchImageRule)
1398             middleScaleFactor.setHeight(destinationHeight / sourceHeight);
1399         
1400         graphicsContext->drawTiledImage(image.get(), colorSpace,
1401             IntRect(borderImageRect.x() + leftWidth, borderImageRect.y() + topWidth, destinationWidth, destinationHeight),
1402             IntRect(leftSlice, topSlice, sourceWidth, sourceHeight),
1403             middleScaleFactor, (Image::TileRule)hRule, (Image::TileRule)vRule, op);
1404     }
1405
1406     return true;
1407 }
1408
1409 class BorderEdge {
1410 public:
1411     BorderEdge(int edgeWidth, const Color& edgeColor, EBorderStyle edgeStyle, bool edgeIsTransparent, bool edgeIsPresent = true)
1412         : width(edgeWidth)
1413         , color(edgeColor)
1414         , style(edgeStyle)
1415         , isTransparent(edgeIsTransparent)
1416         , isPresent(edgeIsPresent)
1417     {
1418         if (style == DOUBLE && edgeWidth < 3)
1419             style = SOLID;
1420     }
1421     
1422     BorderEdge()
1423         : width(0)
1424         , style(BHIDDEN)
1425         , isTransparent(false)
1426         , isPresent(false)
1427     {
1428     }
1429     
1430     bool hasVisibleColorAndStyle() const { return style > BHIDDEN && !isTransparent; }
1431     bool shouldRender() const { return isPresent && width && hasVisibleColorAndStyle(); }
1432     bool presentButInvisible() const { return usedWidth() && !hasVisibleColorAndStyle(); }
1433     bool obscuresBackgroundEdge(float scale) const
1434     {
1435         if (!isPresent || isTransparent || (width * scale) < 2 || color.hasAlpha() || style == BHIDDEN)
1436             return false;
1437
1438         if (style == DOTTED || style == DASHED)
1439             return false;
1440
1441         if (style == DOUBLE)
1442             return width >= 5 * scale; // The outer band needs to be >= 2px wide at unit scale.
1443
1444         return true;
1445     }
1446     bool obscuresBackground() const
1447     {
1448         if (!isPresent || isTransparent || color.hasAlpha() || style == BHIDDEN)
1449             return false;
1450
1451         if (style == DOTTED || style == DASHED || style == DOUBLE)
1452             return false;
1453
1454         return true;
1455     }
1456
1457     int usedWidth() const { return isPresent ? width : 0; }
1458     
1459     void getDoubleBorderStripeWidths(int& outerWidth, int& innerWidth) const
1460     {
1461         int fullWidth = usedWidth();
1462         outerWidth = fullWidth / 3;
1463         innerWidth = fullWidth * 2 / 3;
1464
1465         // We need certain integer rounding results
1466         if (fullWidth % 3 == 2)
1467             outerWidth += 1;
1468
1469         if (fullWidth % 3 == 1)
1470             innerWidth += 1;
1471     }
1472     
1473     int width;
1474     Color color;
1475     EBorderStyle style;
1476     bool isTransparent;
1477     bool isPresent;
1478 };
1479
1480 static bool allCornersClippedOut(const RoundedRect& border, const LayoutRect& clipRect)
1481 {
1482     LayoutRect boundingRect = border.rect();
1483     if (clipRect.contains(boundingRect))
1484         return false;
1485
1486     RoundedRect::Radii radii = border.radii();
1487
1488     LayoutRect topLeftRect(boundingRect.location(), radii.topLeft());
1489     if (clipRect.intersects(topLeftRect))
1490         return false;
1491
1492     LayoutRect topRightRect(boundingRect.location(), radii.topRight());
1493     topRightRect.setX(boundingRect.maxX() - topRightRect.width());
1494     if (clipRect.intersects(topRightRect))
1495         return false;
1496
1497     LayoutRect bottomLeftRect(boundingRect.location(), radii.bottomLeft());
1498     bottomLeftRect.setY(boundingRect.maxY() - bottomLeftRect.height());
1499     if (clipRect.intersects(bottomLeftRect))
1500         return false;
1501
1502     LayoutRect bottomRightRect(boundingRect.location(), radii.bottomRight());
1503     bottomRightRect.setX(boundingRect.maxX() - bottomRightRect.width());
1504     bottomRightRect.setY(boundingRect.maxY() - bottomRightRect.height());
1505     if (clipRect.intersects(bottomRightRect))
1506         return false;
1507
1508     return true;
1509 }
1510
1511 static bool borderWillArcInnerEdge(const LayoutSize& firstRadius, const FloatSize& secondRadius)
1512 {
1513     return !firstRadius.isZero() || !secondRadius.isZero();
1514 }
1515
1516 enum BorderEdgeFlag {
1517     TopBorderEdge = 1 << BSTop,
1518     RightBorderEdge = 1 << BSRight,
1519     BottomBorderEdge = 1 << BSBottom,
1520     LeftBorderEdge = 1 << BSLeft,
1521     AllBorderEdges = TopBorderEdge | BottomBorderEdge | LeftBorderEdge | RightBorderEdge
1522 };
1523
1524 static inline BorderEdgeFlag edgeFlagForSide(BoxSide side)
1525 {
1526     return static_cast<BorderEdgeFlag>(1 << side);
1527 }
1528
1529 static inline bool includesEdge(BorderEdgeFlags flags, BoxSide side)
1530 {
1531     return flags & edgeFlagForSide(side);
1532 }
1533
1534 static inline bool includesAdjacentEdges(BorderEdgeFlags flags)
1535 {
1536     return (flags & (TopBorderEdge | RightBorderEdge)) == (TopBorderEdge | RightBorderEdge)
1537         || (flags & (RightBorderEdge | BottomBorderEdge)) == (RightBorderEdge | BottomBorderEdge)
1538         || (flags & (BottomBorderEdge | LeftBorderEdge)) == (BottomBorderEdge | LeftBorderEdge)
1539         || (flags & (LeftBorderEdge | TopBorderEdge)) == (LeftBorderEdge | TopBorderEdge);
1540 }
1541
1542 inline bool edgesShareColor(const BorderEdge& firstEdge, const BorderEdge& secondEdge)
1543 {
1544     return firstEdge.color == secondEdge.color;
1545 }
1546
1547 inline bool styleRequiresClipPolygon(EBorderStyle style)
1548 {
1549     return style == DOTTED || style == DASHED; // These are drawn with a stroke, so we have to clip to get corner miters.
1550 }
1551
1552 static bool borderStyleFillsBorderArea(EBorderStyle style)
1553 {
1554     return !(style == DOTTED || style == DASHED || style == DOUBLE);
1555 }
1556
1557 static bool borderStyleHasInnerDetail(EBorderStyle style)
1558 {
1559     return style == GROOVE || style == RIDGE || style == DOUBLE;
1560 }
1561
1562 static bool borderStyleIsDottedOrDashed(EBorderStyle style)
1563 {
1564     return style == DOTTED || style == DASHED;
1565 }
1566
1567 // OUTSET darkens the bottom and right (and maybe lightens the top and left)
1568 // INSET darkens the top and left (and maybe lightens the bottom and right)
1569 static inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, BoxSide side, BoxSide adjacentSide)
1570 {
1571     // These styles match at the top/left and bottom/right.
1572     if (style == INSET || style == GROOVE || style == RIDGE || style == OUTSET) {
1573         const BorderEdgeFlags topRightFlags = edgeFlagForSide(BSTop) | edgeFlagForSide(BSRight);
1574         const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edgeFlagForSide(BSLeft);
1575
1576         BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacentSide);
1577         return flags == topRightFlags || flags == bottomLeftFlags;
1578     }
1579     return false;
1580 }
1581
1582 static inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[])
1583 {
1584     if (edges[side].shouldRender() != edges[adjacentSide].shouldRender())
1585         return false;
1586
1587     if (!edgesShareColor(edges[side], edges[adjacentSide]))
1588         return false;
1589
1590     return !borderStyleHasUnmatchedColorsAtCorner(edges[side].style, side, adjacentSide);
1591 }
1592
1593
1594 static inline bool colorNeedsAntiAliasAtCorner(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[])
1595 {
1596     if (!edges[side].color.hasAlpha())
1597         return false;
1598
1599     if (edges[side].shouldRender() != edges[adjacentSide].shouldRender())
1600         return false;
1601
1602     if (!edgesShareColor(edges[side], edges[adjacentSide]))
1603         return true;
1604
1605     return borderStyleHasUnmatchedColorsAtCorner(edges[side].style, side, adjacentSide);
1606 }
1607
1608 // This assumes that we draw in order: top, bottom, left, right.
1609 static inline bool willBeOverdrawn(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[])
1610 {
1611     switch (side) {
1612     case BSTop:
1613     case BSBottom:
1614         if (edges[adjacentSide].presentButInvisible())
1615             return false;
1616
1617         if (!edgesShareColor(edges[side], edges[adjacentSide]) && edges[adjacentSide].color.hasAlpha())
1618             return false;
1619         
1620         if (!borderStyleFillsBorderArea(edges[adjacentSide].style))
1621             return false;
1622
1623         return true;
1624
1625     case BSLeft:
1626     case BSRight:
1627         // These draw last, so are never overdrawn.
1628         return false;
1629     }
1630     return false;
1631 }
1632
1633 static inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorderStyle style, EBorderStyle adjacentStyle)
1634 {
1635     if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE || adjacentStyle == RIDGE)
1636         return true;
1637
1638     if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjacentStyle))
1639         return true;
1640
1641     if (style != adjacentStyle)
1642         return true;
1643
1644     return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide);
1645 }
1646
1647 static bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[], bool allowOverdraw)
1648 {
1649     if ((edges[side].isTransparent && edges[adjacentSide].isTransparent) || !edges[adjacentSide].isPresent)
1650         return false;
1651
1652     if (allowOverdraw && willBeOverdrawn(side, adjacentSide, edges))
1653         return false;
1654
1655     if (!edgesShareColor(edges[side], edges[adjacentSide]))
1656         return true;
1657
1658     if (borderStylesRequireMitre(side, adjacentSide, edges[side].style, edges[adjacentSide].style))
1659         return true;
1660     
1661     return false;
1662 }
1663
1664 void RenderBoxModelObject::paintOneBorderSide(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
1665     const IntRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adjacentSide2, const BorderEdge edges[], const Path* path,
1666     BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor)
1667 {
1668     const BorderEdge& edgeToRender = edges[side];
1669     ASSERT(edgeToRender.width);
1670     const BorderEdge& adjacentEdge1 = edges[adjacentSide1];
1671     const BorderEdge& adjacentEdge2 = edges[adjacentSide2];
1672
1673     bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, edges, !antialias);
1674     bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, edges, !antialias);
1675     
1676     bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1, edges);
1677     bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2, edges);
1678
1679     const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.color;
1680
1681     if (path) {
1682         GraphicsContextStateSaver stateSaver(*graphicsContext);
1683         if (innerBorder.isRenderable())
1684             clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, adjacentSide1StylesMatch, adjacentSide2StylesMatch);
1685         else
1686             clipBorderSideForComplexInnerPath(graphicsContext, outerBorder, innerBorder, side, edges);
1687         float thickness = std::max(std::max(edgeToRender.width, adjacentEdge1.width), adjacentEdge2.width);
1688         drawBoxSideFromPath(graphicsContext, outerBorder.rect(), *path, edges, edgeToRender.width, thickness, side, style,
1689             colorToPaint, edgeToRender.style, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
1690     } else {
1691         bool clipForStyle = styleRequiresClipPolygon(edgeToRender.style) && (mitreAdjacentSide1 || mitreAdjacentSide2);
1692         bool clipAdjacentSide1 = colorNeedsAntiAliasAtCorner(side, adjacentSide1, edges) && mitreAdjacentSide1;
1693         bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSide2, edges) && mitreAdjacentSide2;
1694         bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2;
1695         
1696         GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip);
1697         if (shouldClip) {
1698             bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitreAdjacentSide1);
1699             bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitreAdjacentSide2);
1700             clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, !aliasAdjacentSide1, !aliasAdjacentSide2);
1701             // Since we clipped, no need to draw with a mitre.
1702             mitreAdjacentSide1 = false;
1703             mitreAdjacentSide2 = false;
1704         }
1705         
1706         drawLineForBoxSide(graphicsContext, sideRect.x(), sideRect.y(), sideRect.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.style,
1707                 mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? adjacentEdge2.width : 0, antialias);
1708     }
1709 }
1710
1711 static IntRect calculateSideRect(const RoundedRect& outerBorder, const BorderEdge edges[], int side)
1712 {
1713     IntRect sideRect = outerBorder.rect();
1714     int width = edges[side].width;
1715
1716     if (side == BSTop)
1717         sideRect.setHeight(width);
1718     else if (side == BSBottom)
1719         sideRect.shiftYEdgeTo(sideRect.maxY() - width);
1720     else if (side == BSLeft)
1721         sideRect.setWidth(width);
1722     else
1723         sideRect.shiftXEdgeTo(sideRect.maxX() - width);
1724
1725     return sideRect;
1726 }
1727
1728 void RenderBoxModelObject::paintBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
1729     const IntPoint& innerBorderAdjustment, const BorderEdge edges[], BorderEdgeFlags edgeSet, BackgroundBleedAvoidance bleedAvoidance,
1730     bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor)
1731 {
1732     bool renderRadii = outerBorder.isRounded();
1733
1734     Path roundedPath;
1735     if (renderRadii)
1736         roundedPath.addRoundedRect(outerBorder);
1737     
1738     // The inner border adjustment for bleed avoidance mode BackgroundBleedBackgroundOverBorder
1739     // is only applied to sideRect, which is okay since BackgroundBleedBackgroundOverBorder
1740     // is only to be used for solid borders and the shape of the border painted by drawBoxSideFromPath
1741     // only depends on sideRect when painting solid borders.
1742
1743     if (edges[BSTop].shouldRender() && includesEdge(edgeSet, BSTop)) {
1744         IntRect sideRect = outerBorder.rect();
1745         sideRect.setHeight(edges[BSTop].width + innerBorderAdjustment.y());
1746
1747         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSTop].style) || borderWillArcInnerEdge(innerBorder.radii().topLeft(), innerBorder.radii().topRight()));
1748         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSTop, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
1749     }
1750
1751     if (edges[BSBottom].shouldRender() && includesEdge(edgeSet, BSBottom)) {
1752         IntRect sideRect = outerBorder.rect();
1753         sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].width - innerBorderAdjustment.y());
1754
1755         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSBottom].style) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().bottomRight()));
1756         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSBottom, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
1757     }
1758
1759     if (edges[BSLeft].shouldRender() && includesEdge(edgeSet, BSLeft)) {
1760         IntRect sideRect = outerBorder.rect();
1761         sideRect.setWidth(edges[BSLeft].width + innerBorderAdjustment.x());
1762
1763         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSLeft].style) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().topLeft()));
1764         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSLeft, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
1765     }
1766
1767     if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) {
1768         IntRect sideRect = outerBorder.rect();
1769         sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width - innerBorderAdjustment.x());
1770
1771         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSRight].style) || borderWillArcInnerEdge(innerBorder.radii().bottomRight(), innerBorder.radii().topRight()));
1772         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSRight, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
1773     }
1774 }
1775
1776 void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, const IntPoint& innerBorderAdjustment,
1777     const BorderEdge edges[], BorderEdgeFlags edgesToDraw, BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias)
1778 {
1779     // willBeOverdrawn assumes that we draw in order: top, bottom, left, right.
1780     // This is different from BoxSide enum order.
1781     static BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight };
1782
1783     while (edgesToDraw) {
1784         // Find undrawn edges sharing a color.
1785         Color commonColor;
1786         
1787         BorderEdgeFlags commonColorEdgeSet = 0;
1788         for (size_t i = 0; i < sizeof(paintOrder) / sizeof(paintOrder[0]); ++i) {
1789             BoxSide currSide = paintOrder[i];
1790             if (!includesEdge(edgesToDraw, currSide))
1791                 continue;
1792
1793             bool includeEdge;
1794             if (!commonColorEdgeSet) {
1795                 commonColor = edges[currSide].color;
1796                 includeEdge = true;
1797             } else
1798                 includeEdge = edges[currSide].color == commonColor;
1799
1800             if (includeEdge)
1801                 commonColorEdgeSet |= edgeFlagForSide(currSide);
1802         }
1803
1804         bool useTransparencyLayer = includesAdjacentEdges(commonColorEdgeSet) && commonColor.hasAlpha();
1805         if (useTransparencyLayer) {
1806             graphicsContext->beginTransparencyLayer(static_cast<float>(commonColor.alpha()) / 255);
1807             commonColor = Color(commonColor.red(), commonColor.green(), commonColor.blue());
1808         }
1809
1810         paintBorderSides(graphicsContext, style, outerBorder, innerBorder, innerBorderAdjustment, edges, commonColorEdgeSet, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, &commonColor);
1811             
1812         if (useTransparencyLayer)
1813             graphicsContext->endTransparencyLayer();
1814         
1815         edgesToDraw &= ~commonColorEdgeSet;
1816     }
1817 }
1818
1819 void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect& rect, const RenderStyle* style,
1820                                        BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
1821 {
1822     GraphicsContext* graphicsContext = info.context;
1823     // border-image is not affected by border-radius.
1824     if (paintNinePieceImage(graphicsContext, rect, style, style->borderImage()))
1825         return;
1826
1827     if (graphicsContext->paintingDisabled())
1828         return;
1829
1830     BorderEdge edges[4];
1831     getBorderEdgeInfo(edges, style, includeLogicalLeftEdge, includeLogicalRightEdge);
1832     RoundedRect outerBorder = style->getRoundedBorderFor(rect, &view(), includeLogicalLeftEdge, includeLogicalRightEdge);
1833     RoundedRect innerBorder = style->getRoundedInnerBorderFor(borderInnerRectAdjustedForBleedAvoidance(graphicsContext, rect, bleedAvoidance), includeLogicalLeftEdge, includeLogicalRightEdge);
1834
1835     bool haveAlphaColor = false;
1836     bool haveAllSolidEdges = true;
1837     bool haveAllDoubleEdges = true;
1838     int numEdgesVisible = 4;
1839     bool allEdgesShareColor = true;
1840     int firstVisibleEdge = -1;
1841     BorderEdgeFlags edgesToDraw = 0;
1842
1843     for (int i = BSTop; i <= BSLeft; ++i) {
1844         const BorderEdge& currEdge = edges[i];
1845
1846         if (edges[i].shouldRender())
1847             edgesToDraw |= edgeFlagForSide(static_cast<BoxSide>(i));
1848
1849         if (currEdge.presentButInvisible()) {
1850             --numEdgesVisible;
1851             allEdgesShareColor = false;
1852             continue;
1853         }
1854         
1855         if (!currEdge.width) {
1856             --numEdgesVisible;
1857             continue;
1858         }
1859
1860         if (firstVisibleEdge == -1)
1861             firstVisibleEdge = i;
1862         else if (currEdge.color != edges[firstVisibleEdge].color)
1863             allEdgesShareColor = false;
1864
1865         if (currEdge.color.hasAlpha())
1866             haveAlphaColor = true;
1867         
1868         if (currEdge.style != SOLID)
1869             haveAllSolidEdges = false;
1870
1871         if (currEdge.style != DOUBLE)
1872             haveAllDoubleEdges = false;
1873     }
1874
1875     // If no corner intersects the clip region, we can pretend outerBorder is
1876     // rectangular to improve performance.
1877     if (haveAllSolidEdges && outerBorder.isRounded() && allCornersClippedOut(outerBorder, info.rect))
1878         outerBorder.setRadii(RoundedRect::Radii());
1879
1880     // isRenderable() check avoids issue described in https://bugs.webkit.org/show_bug.cgi?id=38787
1881     if ((haveAllSolidEdges || haveAllDoubleEdges) && allEdgesShareColor && innerBorder.isRenderable()) {
1882         // Fast path for drawing all solid edges and all unrounded double edges
1883         if (numEdgesVisible == 4 && (outerBorder.isRounded() || haveAlphaColor)
1884             && (haveAllSolidEdges || (!outerBorder.isRounded() && !innerBorder.isRounded()))) {
1885             Path path;
1886             
1887             if (outerBorder.isRounded() && bleedAvoidance != BackgroundBleedUseTransparencyLayer)
1888                 path.addRoundedRect(outerBorder);
1889             else
1890                 path.addRect(outerBorder.rect());
1891
1892             if (haveAllDoubleEdges) {
1893                 IntRect innerThirdRect = outerBorder.rect();
1894                 IntRect outerThirdRect = outerBorder.rect();
1895                 for (int side = BSTop; side <= BSLeft; ++side) {
1896                     int outerWidth;
1897                     int innerWidth;
1898                     edges[side].getDoubleBorderStripeWidths(outerWidth, innerWidth);
1899
1900                     if (side == BSTop) {
1901                         innerThirdRect.shiftYEdgeTo(innerThirdRect.y() + innerWidth);
1902                         outerThirdRect.shiftYEdgeTo(outerThirdRect.y() + outerWidth);
1903                     } else if (side == BSBottom) {
1904                         innerThirdRect.setHeight(innerThirdRect.height() - innerWidth);
1905                         outerThirdRect.setHeight(outerThirdRect.height() - outerWidth);
1906                     } else if (side == BSLeft) {
1907                         innerThirdRect.shiftXEdgeTo(innerThirdRect.x() + innerWidth);
1908                         outerThirdRect.shiftXEdgeTo(outerThirdRect.x() + outerWidth);
1909                     } else {
1910                         innerThirdRect.setWidth(innerThirdRect.width() - innerWidth);
1911                         outerThirdRect.setWidth(outerThirdRect.width() - outerWidth);
1912                     }
1913                 }
1914
1915                 RoundedRect outerThird = outerBorder;
1916                 RoundedRect innerThird = innerBorder;
1917                 innerThird.setRect(innerThirdRect);
1918                 outerThird.setRect(outerThirdRect);
1919
1920                 if (outerThird.isRounded() && bleedAvoidance != BackgroundBleedUseTransparencyLayer)
1921                     path.addRoundedRect(outerThird);
1922                 else
1923                     path.addRect(outerThird.rect());
1924
1925                 if (innerThird.isRounded() && bleedAvoidance != BackgroundBleedUseTransparencyLayer)
1926                     path.addRoundedRect(innerThird);
1927                 else
1928                     path.addRect(innerThird.rect());
1929             }
1930
1931             if (innerBorder.isRounded())
1932                 path.addRoundedRect(innerBorder);
1933             else
1934                 path.addRect(innerBorder.rect());
1935             
1936             graphicsContext->setFillRule(RULE_EVENODD);
1937             graphicsContext->setFillColor(edges[firstVisibleEdge].color, style->colorSpace());
1938             graphicsContext->fillPath(path);
1939             return;
1940         } 
1941         // Avoid creating transparent layers
1942         if (haveAllSolidEdges && numEdgesVisible != 4 && !outerBorder.isRounded() && haveAlphaColor) {
1943             Path path;
1944
1945             for (int i = BSTop; i <= BSLeft; ++i) {
1946                 const BorderEdge& currEdge = edges[i];
1947                 if (currEdge.shouldRender()) {
1948                     IntRect sideRect = calculateSideRect(outerBorder, edges, i);
1949                     path.addRect(sideRect);
1950                 }
1951             }
1952
1953             graphicsContext->setFillRule(RULE_NONZERO);
1954             graphicsContext->setFillColor(edges[firstVisibleEdge].color, style->colorSpace());
1955             graphicsContext->fillPath(path);
1956             return;
1957         }
1958     }
1959
1960     bool clipToOuterBorder = outerBorder.isRounded();
1961     GraphicsContextStateSaver stateSaver(*graphicsContext, clipToOuterBorder);
1962     if (clipToOuterBorder) {
1963         // Clip to the inner and outer radii rects.
1964         if (bleedAvoidance != BackgroundBleedUseTransparencyLayer)
1965             graphicsContext->clipRoundedRect(outerBorder);
1966         // isRenderable() check avoids issue described in https://bugs.webkit.org/show_bug.cgi?id=38787
1967         // The inside will be clipped out later (in clipBorderSideForComplexInnerPath)
1968         if (innerBorder.isRenderable())
1969             graphicsContext->clipOutRoundedRect(innerBorder);
1970     }
1971
1972     // If only one edge visible antialiasing doesn't create seams
1973     bool antialias = shouldAntialiasLines(graphicsContext) || numEdgesVisible == 1;
1974     RoundedRect unadjustedInnerBorder = (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? style->getRoundedInnerBorderFor(rect, includeLogicalLeftEdge, includeLogicalRightEdge) : innerBorder;
1975     IntPoint innerBorderAdjustment(innerBorder.rect().x() - unadjustedInnerBorder.rect().x(), innerBorder.rect().y() - unadjustedInnerBorder.rect().y());
1976     if (haveAlphaColor)
1977         paintTranslucentBorderSides(graphicsContext, style, outerBorder, unadjustedInnerBorder, innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias);
1978     else
1979         paintBorderSides(graphicsContext, style, outerBorder, unadjustedInnerBorder, innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias);
1980 }
1981
1982 void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext* graphicsContext, const LayoutRect& borderRect, const Path& borderPath, const BorderEdge edges[],
1983                                     float thickness, float drawThickness, BoxSide side, const RenderStyle* style, 
1984                                     Color color, EBorderStyle borderStyle, BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
1985 {
1986     if (thickness <= 0)
1987         return;
1988
1989     if (borderStyle == DOUBLE && thickness < 3)
1990         borderStyle = SOLID;
1991
1992     switch (borderStyle) {
1993     case BNONE:
1994     case BHIDDEN:
1995         return;
1996     case DOTTED:
1997     case DASHED: {
1998         graphicsContext->setStrokeColor(color, style->colorSpace());
1999
2000         // The stroke is doubled here because the provided path is the 
2001         // outside edge of the border so half the stroke is clipped off. 
2002         // The extra multiplier is so that the clipping mask can antialias
2003         // the edges to prevent jaggies.
2004         graphicsContext->setStrokeThickness(drawThickness * 2 * 1.1f);
2005         graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke : DottedStroke);
2006
2007         // If the number of dashes that fit in the path is odd and non-integral then we
2008         // will have an awkwardly-sized dash at the end of the path. To try to avoid that
2009         // here, we simply make the whitespace dashes ever so slightly bigger.
2010         // FIXME: This could be even better if we tried to manipulate the dash offset
2011         // and possibly the gapLength to get the corners dash-symmetrical.
2012         float dashLength = thickness * ((borderStyle == DASHED) ? 3.0f : 1.0f);
2013         float gapLength = dashLength;
2014         float numberOfDashes = borderPath.length() / dashLength;
2015         // Don't try to show dashes if we have less than 2 dashes + 2 gaps.
2016         // FIXME: should do this test per side.
2017         if (numberOfDashes >= 4) {
2018             bool evenNumberOfFullDashes = !((int)numberOfDashes % 2);
2019             bool integralNumberOfDashes = !(numberOfDashes - (int)numberOfDashes);
2020             if (!evenNumberOfFullDashes && !integralNumberOfDashes) {
2021                 float numberOfGaps = numberOfDashes / 2;
2022                 gapLength += (dashLength  / numberOfGaps);
2023             }
2024
2025             DashArray lineDash;
2026             lineDash.append(dashLength);
2027             lineDash.append(gapLength);
2028             graphicsContext->setLineDash(lineDash, dashLength);
2029         }
2030         
2031         // FIXME: stroking the border path causes issues with tight corners:
2032         // https://bugs.webkit.org/show_bug.cgi?id=58711
2033         // Also, to get the best appearance we should stroke a path between the two borders.
2034         graphicsContext->strokePath(borderPath);
2035         return;
2036     }
2037     case DOUBLE: {
2038         // Get the inner border rects for both the outer border line and the inner border line
2039         int outerBorderTopWidth;
2040         int innerBorderTopWidth;
2041         edges[BSTop].getDoubleBorderStripeWidths(outerBorderTopWidth, innerBorderTopWidth);
2042
2043         int outerBorderRightWidth;
2044         int innerBorderRightWidth;
2045         edges[BSRight].getDoubleBorderStripeWidths(outerBorderRightWidth, innerBorderRightWidth);
2046
2047         int outerBorderBottomWidth;
2048         int innerBorderBottomWidth;
2049         edges[BSBottom].getDoubleBorderStripeWidths(outerBorderBottomWidth, innerBorderBottomWidth);
2050
2051         int outerBorderLeftWidth;
2052         int innerBorderLeftWidth;
2053         edges[BSLeft].getDoubleBorderStripeWidths(outerBorderLeftWidth, innerBorderLeftWidth);
2054
2055         // Draw inner border line
2056         {
2057             GraphicsContextStateSaver stateSaver(*graphicsContext);
2058             RoundedRect innerClip = style->getRoundedInnerBorderFor(borderRect,
2059                 innerBorderTopWidth, innerBorderBottomWidth, innerBorderLeftWidth, innerBorderRightWidth,
2060                 includeLogicalLeftEdge, includeLogicalRightEdge);
2061             
2062             graphicsContext->clipRoundedRect(innerClip);
2063             drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
2064         }
2065
2066         // Draw outer border line
2067         {
2068             GraphicsContextStateSaver stateSaver(*graphicsContext);
2069             LayoutRect outerRect = borderRect;
2070             if (bleedAvoidance == BackgroundBleedUseTransparencyLayer) {
2071                 outerRect.inflate(1);
2072                 ++outerBorderTopWidth;
2073                 ++outerBorderBottomWidth;
2074                 ++outerBorderLeftWidth;
2075                 ++outerBorderRightWidth;
2076             }
2077                 
2078             RoundedRect outerClip = style->getRoundedInnerBorderFor(outerRect,
2079                 outerBorderTopWidth, outerBorderBottomWidth, outerBorderLeftWidth, outerBorderRightWidth,
2080                 includeLogicalLeftEdge, includeLogicalRightEdge);
2081             graphicsContext->clipOutRoundedRect(outerClip);
2082             drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
2083         }
2084         return;
2085     }
2086     case RIDGE:
2087     case GROOVE:
2088     {
2089         EBorderStyle s1;
2090         EBorderStyle s2;
2091         if (borderStyle == GROOVE) {
2092             s1 = INSET;
2093             s2 = OUTSET;
2094         } else {
2095             s1 = OUTSET;
2096             s2 = INSET;
2097         }
2098         
2099         // Paint full border
2100         drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s1, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
2101
2102         // Paint inner only
2103         GraphicsContextStateSaver stateSaver(*graphicsContext);
2104         LayoutUnit topWidth = edges[BSTop].usedWidth() / 2;
2105         LayoutUnit bottomWidth = edges[BSBottom].usedWidth() / 2;
2106         LayoutUnit leftWidth = edges[BSLeft].usedWidth() / 2;
2107         LayoutUnit rightWidth = edges[BSRight].usedWidth() / 2;
2108
2109         RoundedRect clipRect = style->getRoundedInnerBorderFor(borderRect,
2110             topWidth, bottomWidth, leftWidth, rightWidth,
2111             includeLogicalLeftEdge, includeLogicalRightEdge);
2112
2113         graphicsContext->clipRoundedRect(clipRect);
2114         drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thickness, drawThickness, side, style, color, s2, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge);
2115         return;
2116     }
2117     case INSET:
2118         if (side == BSTop || side == BSLeft)
2119             color = color.dark();
2120         break;
2121     case OUTSET:
2122         if (side == BSBottom || side == BSRight)
2123             color = color.dark();
2124         break;
2125     default:
2126         break;
2127     }
2128
2129     graphicsContext->setStrokeStyle(NoStroke);
2130     graphicsContext->setFillColor(color, style->colorSpace());
2131     graphicsContext->drawRect(pixelSnappedIntRect(borderRect));
2132 }
2133
2134 static void findInnerVertex(const FloatPoint& outerCorner, const FloatPoint& innerCorner, const FloatPoint& centerPoint, FloatPoint& result)
2135 {
2136     // If the line between outer and inner corner is towards the horizontal, intersect with a vertical line through the center,
2137     // otherwise with a horizontal line through the center. The points that form this line are arbitrary (we use 0, 100).
2138     // Note that if findIntersection fails, it will leave result untouched.
2139     float diffInnerOuterX = fabs(innerCorner.x() - outerCorner.x());
2140     float diffInnerOuterY = fabs(innerCorner.y() - outerCorner.y());
2141     float diffCenterOuterX = fabs(centerPoint.x() - outerCorner.x());
2142     float diffCenterOuterY = fabs(centerPoint.y() - outerCorner.y());
2143     if (diffInnerOuterY * diffCenterOuterX < diffCenterOuterY * diffInnerOuterX)
2144         findIntersection(outerCorner, innerCorner, FloatPoint(centerPoint.x(), 0), FloatPoint(centerPoint.x(), 100), result);
2145     else
2146         findIntersection(outerCorner, innerCorner, FloatPoint(0, centerPoint.y()), FloatPoint(100, centerPoint.y()), result);
2147 }
2148
2149 void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext* graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
2150                                                  BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches)
2151 {
2152     FloatPoint quad[4];
2153
2154     const LayoutRect& outerRect = outerBorder.rect();
2155     const LayoutRect& innerRect = innerBorder.rect();
2156
2157     FloatPoint centerPoint(innerRect.location().x() + static_cast<float>(innerRect.width()) / 2, innerRect.location().y() + static_cast<float>(innerRect.height()) / 2);
2158
2159     // For each side, create a quad that encompasses all parts of that side that may draw,
2160     // including areas inside the innerBorder.
2161     //
2162     //         0----------------3
2163     //       0  \              /  0
2164     //       |\  1----------- 2  /|
2165     //       | 1                1 |   
2166     //       | |                | |
2167     //       | |                | |  
2168     //       | 2                2 |  
2169     //       |/  1------------2  \| 
2170     //       3  /              \  3   
2171     //         0----------------3
2172     //
2173     switch (side) {
2174     case BSTop:
2175         quad[0] = outerRect.minXMinYCorner();
2176         quad[1] = innerRect.minXMinYCorner();
2177         quad[2] = innerRect.maxXMinYCorner();
2178         quad[3] = outerRect.maxXMinYCorner();
2179
2180         if (!innerBorder.radii().topLeft().isZero())
2181             findInnerVertex(outerRect.minXMinYCorner(), innerRect.minXMinYCorner(), centerPoint, quad[1]);
2182
2183         if (!innerBorder.radii().topRight().isZero())
2184             findInnerVertex(outerRect.maxXMinYCorner(), innerRect.maxXMinYCorner(), centerPoint, quad[2]);
2185         break;
2186
2187     case BSLeft:
2188         quad[0] = outerRect.minXMinYCorner();
2189         quad[1] = innerRect.minXMinYCorner();
2190         quad[2] = innerRect.minXMaxYCorner();
2191         quad[3] = outerRect.minXMaxYCorner();
2192
2193         if (!innerBorder.radii().topLeft().isZero())
2194             findInnerVertex(outerRect.minXMinYCorner(), innerRect.minXMinYCorner(), centerPoint, quad[1]);
2195
2196         if (!innerBorder.radii().bottomLeft().isZero())
2197             findInnerVertex(outerRect.minXMaxYCorner(), innerRect.minXMaxYCorner(), centerPoint, quad[2]);
2198         break;
2199
2200     case BSBottom:
2201         quad[0] = outerRect.minXMaxYCorner();
2202         quad[1] = innerRect.minXMaxYCorner();
2203         quad[2] = innerRect.maxXMaxYCorner();
2204         quad[3] = outerRect.maxXMaxYCorner();
2205
2206         if (!innerBorder.radii().bottomLeft().isZero())
2207             findInnerVertex(outerRect.minXMaxYCorner(), innerRect.minXMaxYCorner(), centerPoint, quad[1]);
2208
2209         if (!innerBorder.radii().bottomRight().isZero())
2210             findInnerVertex(outerRect.maxXMaxYCorner(), innerRect.maxXMaxYCorner(), centerPoint, quad[2]);
2211         break;
2212
2213     case BSRight:
2214         quad[0] = outerRect.maxXMinYCorner();
2215         quad[1] = innerRect.maxXMinYCorner();
2216         quad[2] = innerRect.maxXMaxYCorner();
2217         quad[3] = outerRect.maxXMaxYCorner();
2218
2219         if (!innerBorder.radii().topRight().isZero())
2220             findInnerVertex(outerRect.maxXMinYCorner(), innerRect.maxXMinYCorner(), centerPoint, quad[1]);
2221
2222         if (!innerBorder.radii().bottomRight().isZero())
2223             findInnerVertex(outerRect.maxXMaxYCorner(), innerRect.maxXMaxYCorner(), centerPoint, quad[2]);
2224         break;
2225     }
2226
2227     // If the border matches both of its adjacent sides, don't anti-alias the clip, and
2228     // if neither side matches, anti-alias the clip.
2229     if (firstEdgeMatches == secondEdgeMatches) {
2230         graphicsContext->clipConvexPolygon(4, quad, !firstEdgeMatches);
2231         return;
2232     }
2233
2234     // Square off the end which shouldn't be affected by antialiasing, and clip.
2235     FloatPoint firstQuad[4];
2236     firstQuad[0] = quad[0];
2237     firstQuad[1] = quad[1];
2238     firstQuad[2] = side == BSTop || side == BSBottom ? FloatPoint(quad[3].x(), quad[2].y())
2239         : FloatPoint(quad[2].x(), quad[3].y());
2240     firstQuad[3] = quad[3];
2241     graphicsContext->clipConvexPolygon(4, firstQuad, !firstEdgeMatches);
2242
2243     FloatPoint secondQuad[4];
2244     secondQuad[0] = quad[0];
2245     secondQuad[1] = side == BSTop || side == BSBottom ? FloatPoint(quad[0].x(), quad[1].y())
2246         : FloatPoint(quad[1].x(), quad[0].y());
2247     secondQuad[2] = quad[2];
2248     secondQuad[3] = quad[3];
2249     // Antialiasing affects the second side.
2250     graphicsContext->clipConvexPolygon(4, secondQuad, !secondEdgeMatches);
2251 }
2252
2253 static IntRect calculateSideRectIncludingInner(const RoundedRect& outerBorder, const BorderEdge edges[], BoxSide side)
2254 {
2255     IntRect sideRect = outerBorder.rect();
2256     int width;
2257
2258     switch (side) {
2259     case BSTop:
2260         width = sideRect.height() - edges[BSBottom].width;
2261         sideRect.setHeight(width);
2262         break;
2263     case BSBottom:
2264         width = sideRect.height() - edges[BSTop].width;
2265         sideRect.shiftYEdgeTo(sideRect.maxY() - width);
2266         break;
2267     case BSLeft:
2268         width = sideRect.width() - edges[BSRight].width;
2269         sideRect.setWidth(width);
2270         break;
2271     case BSRight:
2272         width = sideRect.width() - edges[BSLeft].width;
2273         sideRect.shiftXEdgeTo(sideRect.maxX() - width);
2274         break;
2275     }
2276
2277     return sideRect;
2278 }
2279
2280 static RoundedRect calculateAdjustedInnerBorder(const RoundedRect&innerBorder, BoxSide side)
2281 {
2282     // Expand the inner border as necessary to make it a rounded rect (i.e. radii contained within each edge).
2283     // This function relies on the fact we only get radii not contained within each edge if one of the radii
2284     // for an edge is zero, so we can shift the arc towards the zero radius corner.
2285     RoundedRect::Radii newRadii = innerBorder.radii();
2286     IntRect newRect = innerBorder.rect();
2287
2288     float overshoot;
2289     float maxRadii;
2290
2291     switch (side) {
2292     case BSTop:
2293         overshoot = newRadii.topLeft().width() + newRadii.topRight().width() - newRect.width();
2294         if (overshoot > 0) {
2295             ASSERT(!(newRadii.topLeft().width() && newRadii.topRight().width()));
2296             newRect.setWidth(newRect.width() + overshoot);
2297             if (!newRadii.topLeft().width())
2298                 newRect.move(-overshoot, 0);
2299         }
2300         newRadii.setBottomLeft(IntSize(0, 0));
2301         newRadii.setBottomRight(IntSize(0, 0));
2302         maxRadii = std::max(newRadii.topLeft().height(), newRadii.topRight().height());
2303         if (maxRadii > newRect.height())
2304             newRect.setHeight(maxRadii);
2305         break;
2306
2307     case BSBottom:
2308         overshoot = newRadii.bottomLeft().width() + newRadii.bottomRight().width() - newRect.width();
2309         if (overshoot > 0) {
2310             ASSERT(!(newRadii.bottomLeft().width() && newRadii.bottomRight().width()));
2311             newRect.setWidth(newRect.width() + overshoot);
2312             if (!newRadii.bottomLeft().width())
2313                 newRect.move(-overshoot, 0);
2314         }
2315         newRadii.setTopLeft(IntSize(0, 0));
2316         newRadii.setTopRight(IntSize(0, 0));
2317         maxRadii = std::max(newRadii.bottomLeft().height(), newRadii.bottomRight().height());
2318         if (maxRadii > newRect.height()) {
2319             newRect.move(0, newRect.height() - maxRadii);
2320             newRect.setHeight(maxRadii);
2321         }
2322         break;
2323
2324     case BSLeft:
2325         overshoot = newRadii.topLeft().height() + newRadii.bottomLeft().height() - newRect.height();
2326         if (overshoot > 0) {
2327             ASSERT(!(newRadii.topLeft().height() && newRadii.bottomLeft().height()));
2328             newRect.setHeight(newRect.height() + overshoot);
2329             if (!newRadii.topLeft().height())
2330                 newRect.move(0, -overshoot);
2331         }
2332         newRadii.setTopRight(IntSize(0, 0));
2333         newRadii.setBottomRight(IntSize(0, 0));
2334         maxRadii = std::max(newRadii.topLeft().width(), newRadii.bottomLeft().width());
2335         if (maxRadii > newRect.width())
2336             newRect.setWidth(maxRadii);
2337         break;
2338
2339     case BSRight:
2340         overshoot = newRadii.topRight().height() + newRadii.bottomRight().height() - newRect.height();
2341         if (overshoot > 0) {
2342             ASSERT(!(newRadii.topRight().height() && newRadii.bottomRight().height()));
2343             newRect.setHeight(newRect.height() + overshoot);
2344             if (!newRadii.topRight().height())
2345                 newRect.move(0, -overshoot);
2346         }
2347         newRadii.setTopLeft(IntSize(0, 0));
2348         newRadii.setBottomLeft(IntSize(0, 0));
2349         maxRadii = std::max(newRadii.topRight().width(), newRadii.bottomRight().width());
2350         if (maxRadii > newRect.width()) {
2351             newRect.move(newRect.width() - maxRadii, 0);
2352             newRect.setWidth(maxRadii);
2353         }
2354         break;
2355     }
2356
2357     return RoundedRect(newRect, newRadii);
2358 }
2359
2360 void RenderBoxModelObject::clipBorderSideForComplexInnerPath(GraphicsContext* graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
2361     BoxSide side, const class BorderEdge edges[])
2362 {
2363     graphicsContext->clip(calculateSideRectIncludingInner(outerBorder, edges, side));
2364     graphicsContext->clipOutRoundedRect(calculateAdjustedInnerBorder(innerBorder, side));
2365 }
2366
2367 void RenderBoxModelObject::getBorderEdgeInfo(BorderEdge edges[], const RenderStyle* style, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const
2368 {
2369     bool horizontal = style->isHorizontalWritingMode();
2370
2371     edges[BSTop] = BorderEdge(style->borderTopWidth(),
2372         style->visitedDependentColor(CSSPropertyBorderTopColor),
2373         style->borderTopStyle(),
2374         style->borderTopIsTransparent(),
2375         horizontal || includeLogicalLeftEdge);
2376
2377     edges[BSRight] = BorderEdge(style->borderRightWidth(),
2378         style->visitedDependentColor(CSSPropertyBorderRightColor),
2379         style->borderRightStyle(),
2380         style->borderRightIsTransparent(),
2381         !horizontal || includeLogicalRightEdge);
2382
2383     edges[BSBottom] = BorderEdge(style->borderBottomWidth(),
2384         style->visitedDependentColor(CSSPropertyBorderBottomColor),
2385         style->borderBottomStyle(),
2386         style->borderBottomIsTransparent(),
2387         horizontal || includeLogicalRightEdge);
2388
2389     edges[BSLeft] = BorderEdge(style->borderLeftWidth(),
2390         style->visitedDependentColor(CSSPropertyBorderLeftColor),
2391         style->borderLeftStyle(),
2392         style->borderLeftIsTransparent(),
2393         !horizontal || includeLogicalLeftEdge);
2394 }
2395
2396 bool RenderBoxModelObject::borderObscuresBackgroundEdge(const FloatSize& contextScale) const
2397 {
2398     BorderEdge edges[4];
2399     getBorderEdgeInfo(edges, &style());
2400
2401     for (int i = BSTop; i <= BSLeft; ++i) {
2402         const BorderEdge& currEdge = edges[i];
2403         // FIXME: for vertical text
2404         float axisScale = (i == BSTop || i == BSBottom) ? contextScale.height() : contextScale.width();
2405         if (!currEdge.obscuresBackgroundEdge(axisScale))
2406             return false;
2407     }
2408
2409     return true;
2410 }
2411
2412 bool RenderBoxModelObject::borderObscuresBackground() const
2413 {
2414     if (!style().hasBorder())
2415         return false;
2416
2417     // Bail if we have any border-image for now. We could look at the image alpha to improve this.
2418     if (style().borderImage().image())
2419         return false;
2420
2421     BorderEdge edges[4];
2422     getBorderEdgeInfo(edges, &style());
2423
2424     for (int i = BSTop; i <= BSLeft; ++i) {
2425         const BorderEdge& currEdge = edges[i];
2426         if (!currEdge.obscuresBackground())
2427             return false;
2428     }
2429
2430     return true;
2431 }
2432
2433 bool RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* inlineFlowBox) const
2434 {
2435     if (bleedAvoidance != BackgroundBleedNone)
2436         return false;
2437
2438     if (style().hasAppearance())
2439         return false;
2440
2441     bool hasOneNormalBoxShadow = false;
2442     for (const ShadowData* currentShadow = style().boxShadow(); currentShadow; currentShadow = currentShadow->next()) {
2443         if (currentShadow->style() != Normal)
2444             continue;
2445
2446         if (hasOneNormalBoxShadow)
2447             return false;
2448         hasOneNormalBoxShadow = true;
2449
2450         if (currentShadow->spread())
2451             return false;
2452     }
2453
2454     if (!hasOneNormalBoxShadow)
2455         return false;
2456
2457     Color backgroundColor = style().visitedDependentColor(CSSPropertyBackgroundColor);
2458     if (!backgroundColor.isValid() || backgroundColor.hasAlpha())
2459         return false;
2460
2461     const FillLayer* lastBackgroundLayer = style().backgroundLayers();
2462     for (const FillLayer* next = lastBackgroundLayer->next(); next; next = lastBackgroundLayer->next())
2463         lastBackgroundLayer = next;
2464
2465     if (lastBackgroundLayer->clip() != BorderFillBox)
2466         return false;
2467
2468     if (lastBackgroundLayer->image() && style().hasBorderRadius())
2469         return false;
2470
2471     if (inlineFlowBox && !inlineFlowBox->boxShadowCanBeAppliedToBackground(*lastBackgroundLayer))
2472         return false;
2473
2474     if (hasOverflowClip() && lastBackgroundLayer->attachment() == LocalBackgroundAttachment)
2475         return false;
2476
2477     return true;
2478 }
2479
2480 static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowExtent, int shadowSpread, const IntSize& shadowOffset)
2481 {
2482     IntRect bounds(holeRect);
2483     
2484     bounds.inflate(shadowExtent);
2485
2486     if (shadowSpread < 0)
2487         bounds.inflate(-shadowSpread);
2488     
2489     IntRect offsetBounds = bounds;
2490     offsetBounds.move(-shadowOffset);
2491     return unionRect(bounds, offsetBounds);
2492 }
2493
2494 void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRect& paintRect, const RenderStyle* s, ShadowStyle shadowStyle, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
2495 {
2496     // FIXME: Deal with border-image.  Would be great to use border-image as a mask.
2497     GraphicsContext* context = info.context;
2498     if (context->paintingDisabled() || !s->boxShadow())
2499         return;
2500
2501     RoundedRect border = (shadowStyle == Inset)
2502         ? s->getRoundedInnerBorderFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge)
2503         : s->getRoundedBorderFor(paintRect, &view(), includeLogicalLeftEdge, includeLogicalRightEdge);
2504
2505     bool hasBorderRadius = s->hasBorderRadius();
2506     bool isHorizontal = s->isHorizontalWritingMode();
2507     
2508     bool hasOpaqueBackground = s->visitedDependentColor(CSSPropertyBackgroundColor).isValid() && s->visitedDependentColor(CSSPropertyBackgroundColor).alpha() == 255;
2509     for (const ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next()) {
2510         if (shadow->style() != shadowStyle)
2511             continue;
2512
2513         IntSize shadowOffset(shadow->x(), shadow->y());
2514         int shadowRadius = shadow->radius();
2515         int shadowPaintingExtent = shadow->paintingExtent();
2516         int shadowSpread = shadow->spread();
2517         
2518         if (shadowOffset.isZero() && !shadowRadius && !shadowSpread)
2519             continue;
2520         
2521         const Color& shadowColor = shadow->color();
2522
2523         if (shadow->style() == Normal) {
2524             RoundedRect fillRect = border;
2525             fillRect.inflate(shadowSpread);
2526             if (fillRect.isEmpty())
2527                 continue;
2528
2529             IntRect shadowRect(border.rect());
2530             shadowRect.inflate(shadowPaintingExtent + shadowSpread);
2531             shadowRect.move(shadowOffset);
2532
2533             GraphicsContextStateSaver stateSaver(*context);
2534             context->clip(shadowRect);
2535
2536             // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not
2537             // bleed in (due to antialiasing) if the context is transformed.
2538             IntSize extraOffset(paintRect.pixelSnappedWidth() + std::max(0, shadowOffset.width()) + shadowPaintingExtent + 2 * shadowSpread + 1, 0);
2539             shadowOffset -= extraOffset;
2540             fillRect.move(extraOffset);
2541
2542             if (shadow->isWebkitBoxShadow())
2543                 context->setLegacyShadow(shadowOffset, shadowRadius, shadowColor, s->colorSpace());
2544             else
2545                 context->setShadow(shadowOffset, shadowRadius, shadowColor, s->colorSpace());
2546
2547             if (hasBorderRadius) {
2548                 RoundedRect rectToClipOut = border;
2549
2550                 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
2551                 // when painting the shadow. On the other hand, it introduces subpixel gaps along the
2552                 // corners. Those are avoided by insetting the clipping path by one pixel.
2553                 if (hasOpaqueBackground) {
2554                     rectToClipOut.inflateWithRadii(-1);
2555                 }
2556
2557                 if (!rectToClipOut.isEmpty())
2558                     context->clipOutRoundedRect(rectToClipOut);
2559
2560                 RoundedRect influenceRect(shadowRect, border.radii());
2561                 influenceRect.expandRadii(2 * shadowPaintingExtent + shadowSpread);
2562                 if (allCornersClippedOut(influenceRect, info.rect))
2563                     context->fillRect(fillRect.rect(), Color::black, s->colorSpace());
2564                 else {
2565                     fillRect.expandRadii(shadowSpread);
2566                     if (!fillRect.isRenderable())
2567                         fillRect.adjustRadii();
2568                     context->fillRoundedRect(fillRect, Color::black, s->colorSpace());
2569                 }
2570             } else {
2571                 IntRect rectToClipOut = border.rect();
2572
2573                 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
2574                 // when painting the shadow. On the other hand, it introduces subpixel gaps along the
2575                 // edges if they are not pixel-aligned. Those are avoided by insetting the clipping path
2576                 // by one pixel.
2577                 if (hasOpaqueBackground) {
2578                     // FIXME: The function to decide on the policy based on the transform should be a named function.
2579                     // FIXME: It's not clear if this check is right. What about integral scale factors?
2580                     AffineTransform transform = context->getCTM();
2581                     if (transform.a() != 1 || (transform.d() != 1 && transform.d() != -1) || transform.b() || transform.c())
2582                         rectToClipOut.inflate(-1);
2583                 }
2584
2585                 if (!rectToClipOut.isEmpty())
2586                     context->clipOut(rectToClipOut);
2587                 context->fillRect(fillRect.rect(), Color::black, s->colorSpace());
2588             }
2589         } else {
2590             // Inset shadow.
2591             IntRect holeRect(border.rect());
2592             holeRect.inflate(-shadowSpread);
2593
2594             if (holeRect.isEmpty()) {
2595                 if (hasBorderRadius)
2596                     context->fillRoundedRect(border, shadowColor, s->colorSpace());
2597                 else
2598                     context->fillRect(border.rect(), shadowColor, s->colorSpace());
2599                 continue;
2600             }
2601
2602             if (!includeLogicalLeftEdge) {
2603                 if (isHorizontal) {
2604                     holeRect.move(-std::max(shadowOffset.width(), 0) - shadowPaintingExtent, 0);
2605                     holeRect.setWidth(holeRect.width() + std::max(shadowOffset.width(), 0) + shadowPaintingExtent);
2606                 } else {
2607                     holeRect.move(0, -std::max(shadowOffset.height(), 0) - shadowPaintingExtent);
2608                     holeRect.setHeight(holeRect.height() + std::max(shadowOffset.height(), 0) + shadowPaintingExtent);
2609                 }
2610             }
2611             if (!includeLogicalRightEdge) {
2612                 if (isHorizontal)
2613                     holeRect.setWidth(holeRect.width() - std::min(shadowOffset.width(), 0) + shadowPaintingExtent);
2614                 else
2615                     holeRect.setHeight(holeRect.height() - std::min(shadowOffset.height(), 0) + shadowPaintingExtent);
2616             }
2617
2618             Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255);
2619
2620             IntRect outerRect = areaCastingShadowInHole(border.rect(), shadowPaintingExtent, shadowSpread, shadowOffset);
2621             RoundedRect roundedHole(holeRect, border.radii());
2622
2623             GraphicsContextStateSaver stateSaver(*context);
2624             if (hasBorderRadius) {
2625                 Path path;
2626                 path.addRoundedRect(border);
2627                 context->clip(path);
2628                 roundedHole.shrinkRadii(shadowSpread);
2629             } else
2630                 context->clip(border.rect());
2631
2632             IntSize extraOffset(2 * paintRect.pixelSnappedWidth() + std::max(0, shadowOffset.width()) + shadowPaintingExtent - 2 * shadowSpread + 1, 0);
2633             context->translate(extraOffset.width(), extraOffset.height());
2634             shadowOffset -= extraOffset;
2635
2636             if (shadow->isWebkitBoxShadow())
2637                 context->setLegacyShadow(shadowOffset, shadowRadius, shadowColor, s->colorSpace());
2638             else
2639                 context->setShadow(shadowOffset, shadowRadius, shadowColor, s->colorSpace());
2640
2641             context->fillRectWithRoundedHole(outerRect, roundedHole, fillColor, s->colorSpace());
2642         }
2643     }
2644 }
2645
2646 LayoutUnit RenderBoxModelObject::containingBlockLogicalWidthForContent() const
2647 {
2648     return containingBlock()->availableLogicalWidth();
2649 }
2650
2651 RenderBoxModelObject* RenderBoxModelObject::continuation() const
2652 {
2653     if (!continuationMap)
2654         return 0;
2655     return continuationMap->get(this);
2656 }
2657
2658 void RenderBoxModelObject::setContinuation(RenderBoxModelObject* continuation)
2659 {
2660     if (continuation) {
2661         if (!continuationMap)
2662             continuationMap = new ContinuationMap;
2663         continuationMap->set(this, continuation);
2664     } else {
2665         if (continuationMap)
2666             continuationMap->remove(this);
2667     }
2668 }
2669
2670 RenderTextFragment* RenderBoxModelObject::firstLetterRemainingText() const
2671 {
2672     if (!firstLetterRemainingTextMap)
2673         return 0;
2674     return firstLetterRemainingTextMap->get(this);
2675 }
2676
2677 void RenderBoxModelObject::setFirstLetterRemainingText(RenderTextFragment* remainingText)
2678 {
2679     if (remainingText) {
2680         if (!firstLetterRemainingTextMap)
2681             firstLetterRemainingTextMap = new FirstLetterRemainingTextMap;
2682         firstLetterRemainingTextMap->set(this, remainingText);
2683     } else if (firstLetterRemainingTextMap)
2684         firstLetterRemainingTextMap->remove(this);
2685 }
2686
2687 LayoutRect RenderBoxModelObject::localCaretRectForEmptyElement(LayoutUnit width, LayoutUnit textIndentOffset)
2688 {
2689     ASSERT(!firstChild());
2690
2691     // FIXME: This does not take into account either :first-line or :first-letter
2692     // However, as soon as some content is entered, the line boxes will be
2693     // constructed and this kludge is not called any more. So only the caret size
2694     // of an empty :first-line'd block is wrong. I think we can live with that.
2695     const RenderStyle& currentStyle = firstLineStyle();
2696     LayoutUnit height = lineHeight(true, currentStyle.isHorizontalWritingMode() ? HorizontalLine : VerticalLine);
2697
2698     enum CaretAlignment { alignLeft, alignRight, alignCenter };
2699
2700     CaretAlignment alignment = alignLeft;
2701
2702     switch (currentStyle.textAlign()) {
2703     case LEFT:
2704     case WEBKIT_LEFT:
2705         break;
2706     case CENTER:
2707     case WEBKIT_CENTER:
2708         alignment = alignCenter;
2709         break;
2710     case RIGHT:
2711     case WEBKIT_RIGHT:
2712         alignment = alignRight;
2713         break;
2714     case JUSTIFY:
2715     case TASTART:
2716         if (!currentStyle.isLeftToRightDirection())
2717             alignment = alignRight;
2718         break;
2719     case TAEND:
2720         if (currentStyle.isLeftToRightDirection())
2721             alignment = alignRight;
2722         break;
2723     }
2724
2725     LayoutUnit x = borderLeft() + paddingLeft();
2726     LayoutUnit maxX = width - borderRight() - paddingRight();
2727
2728     switch (alignment) {
2729     case alignLeft:
2730         if (currentStyle.isLeftToRightDirection())
2731             x += textIndentOffset;
2732         break;
2733     case alignCenter:
2734         x = (x + maxX) / 2;
2735         if (currentStyle.isLeftToRightDirection())
2736             x += textIndentOffset / 2;
2737         else
2738             x -= textIndentOffset / 2;
2739         break;
2740     case alignRight:
2741         x = maxX - caretWidth;
2742         if (!currentStyle.isLeftToRightDirection())
2743             x -= textIndentOffset;
2744         break;
2745     }
2746     x = std::min(x, std::max<LayoutUnit>(maxX - caretWidth, 0));
2747
2748     LayoutUnit y = paddingTop() + borderTop();
2749
2750     return currentStyle.isHorizontalWritingMode() ? LayoutRect(x, y, caretWidth, height) : LayoutRect(y, x, height, caretWidth);
2751 }
2752
2753 bool RenderBoxModelObject::shouldAntialiasLines(GraphicsContext* context)
2754 {
2755     // FIXME: We may want to not antialias when scaled by an integral value,
2756     // and we may want to antialias when translated by a non-integral value.
2757     return !context->getCTM().isIdentityOrTranslationOrFlipped();
2758 }
2759
2760 void RenderBoxModelObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const
2761 {
2762     auto o = container();
2763     if (!o)
2764         return;
2765
2766     // The point inside a box that's inside a region has its coordinates relative to the region,
2767     // not the FlowThread that is its container in the RenderObject tree.
2768     if (o->isRenderFlowThread() && isRenderBlock()) {
2769         // FIXME (CSSREGIONS): switch to Box instead of Block when we'll have range information
2770         // for boxes as well, not just for blocks.
2771         RenderRegion* startRegion;
2772         RenderRegion* endRegion;
2773         toRenderFlowThread(o)->getRegionRangeForBox(toRenderBlock(this), startRegion, endRegion);
2774         if (startRegion)
2775             o = startRegion;
2776     }
2777
2778     o->mapAbsoluteToLocalPoint(mode, transformState);
2779
2780     LayoutSize containerOffset = offsetFromContainer(o, LayoutPoint());
2781
2782     if (!style().hasOutOfFlowPosition() && o->hasColumns()) {
2783         RenderBlock* block = toRenderBlock(o);
2784         LayoutPoint point(roundedLayoutPoint(transformState.mappedPoint()));
2785         point -= containerOffset;
2786         block->adjustForColumnRect(containerOffset, point);
2787     }
2788
2789     bool preserve3D = mode & UseTransforms && (o->style().preserves3D() || style().preserves3D());
2790     if (mode & UseTransforms && shouldUseTransformFromContainer(o)) {
2791         TransformationMatrix t;
2792         getTransformFromContainer(o, containerOffset, t);
2793         transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
2794     } else
2795         transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
2796 }
2797
2798 void RenderBoxModelObject::moveChildTo(RenderBoxModelObject* toBoxModelObject, RenderObject* child, RenderObject* beforeChild, bool fullRemoveInsert)
2799 {
2800     // We assume that callers have cleared their positioned objects list for child moves (!fullRemoveInsert) so the
2801     // positioned renderer maps don't become stale. It would be too slow to do the map lookup on each call.
2802     ASSERT(!fullRemoveInsert || !isRenderBlock() || !toRenderBlock(this)->hasPositionedObjects());
2803
2804     ASSERT(this == child->parent());
2805     ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent());
2806     if (fullRemoveInsert && (toBoxModelObject->isRenderBlock() || toBoxModelObject->isRenderInline())) {
2807         // Takes care of adding the new child correctly if toBlock and fromBlock
2808         // have different kind of children (block vs inline).
2809         removeChildInternal(*child, NotifyChildren);
2810         toBoxModelObject->addChild(child, beforeChild);
2811     } else {
2812         NotifyChildrenType notifyType = fullRemoveInsert ? NotifyChildren : DontNotifyChildren;
2813         removeChildInternal(*child, notifyType);
2814         toBoxModelObject->insertChildInternal(child, beforeChild, notifyType);
2815     }
2816 }
2817
2818 void RenderBoxModelObject::moveChildrenTo(RenderBoxModelObject* toBoxModelObject, RenderObject* startChild, RenderObject* endChild, RenderObject* beforeChild, bool fullRemoveInsert)
2819 {
2820     // This condition is rarely hit since this function is usually called on
2821     // anonymous blocks which can no longer carry positioned objects (see r120761)
2822     // or when fullRemoveInsert is false.
2823     if (fullRemoveInsert && isRenderBlock()) {
2824         toRenderBlock(this)->removePositionedObjects(0);
2825         if (isRenderBlockFlow())
2826             toRenderBlockFlow(this)->removeFloatingObjects(); 
2827     }
2828
2829     ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent());
2830     for (RenderObject* child = startChild; child && child != endChild; ) {
2831         // Save our next sibling as moveChildTo will clear it.
2832         RenderObject* nextSibling = child->nextSibling();
2833         moveChildTo(toBoxModelObject, child, beforeChild, fullRemoveInsert);
2834         child = nextSibling;
2835     }
2836 }
2837
2838 } // namespace WebCore