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