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