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