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