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