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