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 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25 #include "config.h"
26 #include "RenderBoxModelObject.h"
27
28 #include "GraphicsContext.h"
29 #include "HTMLElement.h"
30 #include "HTMLNames.h"
31 #include "ImageBuffer.h"
32 #include "RenderBlock.h"
33 #include "RenderInline.h"
34 #include "RenderLayer.h"
35 #include "RenderView.h"
36
37 using namespace std;
38
39 namespace WebCore {
40
41 using namespace HTMLNames;
42
43 bool RenderBoxModelObject::s_wasFloating = false;
44 bool RenderBoxModelObject::s_hadLayer = false;
45 bool RenderBoxModelObject::s_layerWasSelfPainting = false;
46
RenderBoxModelObject(Node * node)47 RenderBoxModelObject::RenderBoxModelObject(Node* node)
48 : RenderObject(node)
49 , m_layer(0)
50 {
51 }
52
~RenderBoxModelObject()53 RenderBoxModelObject::~RenderBoxModelObject()
54 {
55 // Our layer should have been destroyed and cleared by now
56 ASSERT(!hasLayer());
57 ASSERT(!m_layer);
58 }
59
destroyLayer()60 void RenderBoxModelObject::destroyLayer()
61 {
62 ASSERT(!hasLayer()); // Callers should have already called setHasLayer(false)
63 ASSERT(m_layer);
64 m_layer->destroy(renderArena());
65 m_layer = 0;
66 }
67
destroy()68 void RenderBoxModelObject::destroy()
69 {
70 // This must be done before we destroy the RenderObject.
71 if (m_layer)
72 m_layer->clearClipRects();
73
74 // RenderObject::destroy calls back to destroyLayer() for layer destruction
75 RenderObject::destroy();
76 }
77
hasSelfPaintingLayer() const78 bool RenderBoxModelObject::hasSelfPaintingLayer() const
79 {
80 return m_layer && m_layer->isSelfPaintingLayer();
81 }
82
styleWillChange(StyleDifference diff,const RenderStyle * newStyle)83 void RenderBoxModelObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
84 {
85 s_wasFloating = isFloating();
86 s_hadLayer = hasLayer();
87 if (s_hadLayer)
88 s_layerWasSelfPainting = layer()->isSelfPaintingLayer();
89
90 // If our z-index changes value or our visibility changes,
91 // we need to dirty our stacking context's z-order list.
92 if (style() && newStyle) {
93 if (parent()) {
94 // Do a repaint with the old style first, e.g., for example if we go from
95 // having an outline to not having an outline.
96 if (diff == StyleDifferenceRepaintLayer) {
97 layer()->repaintIncludingDescendants();
98 if (!(style()->clip() == newStyle->clip()))
99 layer()->clearClipRectsIncludingDescendants();
100 } else if (diff == StyleDifferenceRepaint || newStyle->outlineSize() < style()->outlineSize())
101 repaint();
102 }
103
104 if (diff == StyleDifferenceLayout) {
105 // When a layout hint happens, we go ahead and do a repaint of the layer, since the layer could
106 // end up being destroyed.
107 if (hasLayer()) {
108 if (style()->position() != newStyle->position() ||
109 style()->zIndex() != newStyle->zIndex() ||
110 style()->hasAutoZIndex() != newStyle->hasAutoZIndex() ||
111 !(style()->clip() == newStyle->clip()) ||
112 style()->hasClip() != newStyle->hasClip() ||
113 style()->opacity() != newStyle->opacity() ||
114 style()->transform() != newStyle->transform())
115 layer()->repaintIncludingDescendants();
116 } else if (newStyle->hasTransform() || newStyle->opacity() < 1) {
117 // If we don't have a layer yet, but we are going to get one because of transform or opacity,
118 // then we need to repaint the old position of the object.
119 repaint();
120 }
121 }
122
123 if (hasLayer() && (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() ||
124 style()->zIndex() != newStyle->zIndex() ||
125 style()->visibility() != newStyle->visibility())) {
126 layer()->dirtyStackingContextZOrderLists();
127 if (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() || style()->visibility() != newStyle->visibility())
128 layer()->dirtyZOrderLists();
129 }
130 }
131
132 RenderObject::styleWillChange(diff, newStyle);
133 }
134
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)135 void RenderBoxModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
136 {
137 RenderObject::styleDidChange(diff, oldStyle);
138 updateBoxModelInfoFromStyle();
139
140 if (requiresLayer()) {
141 if (!layer()) {
142 if (s_wasFloating && isFloating())
143 setChildNeedsLayout(true);
144 m_layer = new (renderArena()) RenderLayer(this);
145 setHasLayer(true);
146 m_layer->insertOnlyThisLayer();
147 if (parent() && !needsLayout() && containingBlock())
148 m_layer->updateLayerPositions();
149 }
150 } else if (layer() && layer()->parent()) {
151 setHasTransform(false); // Either a transform wasn't specified or the object doesn't support transforms, so just null out the bit.
152 setHasReflection(false);
153 m_layer->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer
154 if (s_wasFloating && isFloating())
155 setChildNeedsLayout(true);
156 }
157
158 if (layer()) {
159 layer()->styleChanged(diff, oldStyle);
160 if (s_hadLayer && layer()->isSelfPaintingLayer() != s_layerWasSelfPainting)
161 setChildNeedsLayout(true);
162 }
163 }
164
updateBoxModelInfoFromStyle()165 void RenderBoxModelObject::updateBoxModelInfoFromStyle()
166 {
167 // Set the appropriate bits for a box model object. Since all bits are cleared in styleWillChange,
168 // we only check for bits that could possibly be set to true.
169 setHasBoxDecorations(style()->hasBorder() || style()->hasBackground() || style()->hasAppearance() || style()->boxShadow());
170 setInline(style()->isDisplayInlineType());
171 setRelPositioned(style()->position() == RelativePosition);
172 }
173
relativePositionOffsetX() const174 int RenderBoxModelObject::relativePositionOffsetX() const
175 {
176 // Objects that shrink to avoid floats normally use available line width when computing containing block width. However
177 // in the case of relative positioning using percentages, we can't do this. The offset should always be resolved using the
178 // available width of the containing block. Therefore we don't use containingBlockWidthForContent() here, but instead explicitly
179 // call availableWidth on our containing block.
180 if (!style()->left().isAuto()) {
181 RenderBlock* cb = containingBlock();
182 if (!style()->right().isAuto() && containingBlock()->style()->direction() == RTL)
183 return -style()->right().calcValue(cb->availableWidth());
184 return style()->left().calcValue(cb->availableWidth());
185 }
186 if (!style()->right().isAuto()) {
187 RenderBlock* cb = containingBlock();
188 return -style()->right().calcValue(cb->availableWidth());
189 }
190 return 0;
191 }
192
relativePositionOffsetY() const193 int RenderBoxModelObject::relativePositionOffsetY() const
194 {
195 if (!style()->top().isAuto())
196 return style()->top().calcValue(containingBlock()->availableHeight());
197 else if (!style()->bottom().isAuto())
198 return -style()->bottom().calcValue(containingBlock()->availableHeight());
199
200 return 0;
201 }
202
offsetLeft() const203 int RenderBoxModelObject::offsetLeft() const
204 {
205 // If the element is the HTML body element or does not have an associated box
206 // return 0 and stop this algorithm.
207 if (isBody())
208 return 0;
209
210 RenderBoxModelObject* offsetPar = offsetParent();
211 int xPos = (isBox() ? toRenderBox(this)->x() : 0);
212
213 // If the offsetParent of the element is null, or is the HTML body element,
214 // return the distance between the canvas origin and the left border edge
215 // of the element and stop this algorithm.
216 if (offsetPar) {
217 if (offsetPar->isBox() && !offsetPar->isBody())
218 xPos -= toRenderBox(offsetPar)->borderLeft();
219 if (!isPositioned()) {
220 if (isRelPositioned())
221 xPos += relativePositionOffsetX();
222 RenderObject* curr = parent();
223 while (curr && curr != offsetPar) {
224 // FIXME: What are we supposed to do inside SVG content?
225 if (curr->isBox() && !curr->isTableRow())
226 xPos += toRenderBox(curr)->x();
227 curr = curr->parent();
228 }
229 if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned())
230 xPos += toRenderBox(offsetPar)->x();
231 }
232 }
233
234 return xPos;
235 }
236
offsetTop() const237 int RenderBoxModelObject::offsetTop() const
238 {
239 // If the element is the HTML body element or does not have an associated box
240 // return 0 and stop this algorithm.
241 if (isBody())
242 return 0;
243
244 RenderBoxModelObject* offsetPar = offsetParent();
245 int yPos = (isBox() ? toRenderBox(this)->y() : 0);
246
247 // If the offsetParent of the element is null, or is the HTML body element,
248 // return the distance between the canvas origin and the top border edge
249 // of the element and stop this algorithm.
250 if (offsetPar) {
251 if (offsetPar->isBox() && !offsetPar->isBody())
252 yPos -= toRenderBox(offsetPar)->borderTop();
253 if (!isPositioned()) {
254 if (isRelPositioned())
255 yPos += relativePositionOffsetY();
256 RenderObject* curr = parent();
257 while (curr && curr != offsetPar) {
258 // FIXME: What are we supposed to do inside SVG content?
259 if (curr->isBox() && !curr->isTableRow())
260 yPos += toRenderBox(curr)->y();
261 curr = curr->parent();
262 }
263 if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned())
264 yPos += toRenderBox(offsetPar)->y();
265 }
266 }
267 return yPos;
268 }
269
paddingTop(bool) const270 int RenderBoxModelObject::paddingTop(bool) const
271 {
272 int w = 0;
273 Length padding = style()->paddingTop();
274 if (padding.isPercent())
275 w = containingBlock()->availableWidth();
276 return padding.calcMinValue(w);
277 }
278
paddingBottom(bool) const279 int RenderBoxModelObject::paddingBottom(bool) const
280 {
281 int w = 0;
282 Length padding = style()->paddingBottom();
283 if (padding.isPercent())
284 w = containingBlock()->availableWidth();
285 return padding.calcMinValue(w);
286 }
287
paddingLeft(bool) const288 int RenderBoxModelObject::paddingLeft(bool) const
289 {
290 int w = 0;
291 Length padding = style()->paddingLeft();
292 if (padding.isPercent())
293 w = containingBlock()->availableWidth();
294 return padding.calcMinValue(w);
295 }
296
paddingRight(bool) const297 int RenderBoxModelObject::paddingRight(bool) const
298 {
299 int w = 0;
300 Length padding = style()->paddingRight();
301 if (padding.isPercent())
302 w = containingBlock()->availableWidth();
303 return padding.calcMinValue(w);
304 }
305
306
paintFillLayerExtended(const PaintInfo & paintInfo,const Color & c,const FillLayer * bgLayer,int tx,int ty,int w,int h,InlineFlowBox * box,CompositeOperator op)307 void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& c, const FillLayer* bgLayer, int tx, int ty, int w, int h, InlineFlowBox* box, CompositeOperator op)
308 {
309 GraphicsContext* context = paintInfo.context;
310 bool includeLeftEdge = box ? box->includeLeftEdge() : true;
311 bool includeRightEdge = box ? box->includeRightEdge() : true;
312 int bLeft = includeLeftEdge ? borderLeft() : 0;
313 int bRight = includeRightEdge ? borderRight() : 0;
314 int pLeft = includeLeftEdge ? paddingLeft() : 0;
315 int pRight = includeRightEdge ? paddingRight() : 0;
316
317 bool clippedToBorderRadius = false;
318 if (style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge)) {
319 context->save();
320
321 IntSize topLeft, topRight, bottomLeft, bottomRight;
322 IntRect borderRect(tx, ty, w, h);
323 style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight);
324
325 context->addRoundedRectClip(borderRect, includeLeftEdge ? topLeft : IntSize(),
326 includeRightEdge ? topRight : IntSize(),
327 includeLeftEdge ? bottomLeft : IntSize(),
328 includeRightEdge ? bottomRight : IntSize());
329 clippedToBorderRadius = true;
330 }
331
332 bool clippedWithLocalScrolling = hasOverflowClip() && bgLayer->attachment() == LocalBackgroundAttachment;
333 if (clippedWithLocalScrolling) {
334 // Clip to the overflow area.
335 context->save();
336 context->clip(toRenderBox(this)->overflowClipRect(tx, ty));
337
338 // Now adjust our tx, ty, w, h to reflect a scrolled content box with borders at the ends.
339 layer()->subtractScrolledContentOffset(tx, ty);
340 w = bLeft + layer()->scrollWidth() + bRight;
341 h = borderTop() + layer()->scrollHeight() + borderBottom();
342 }
343
344 if (bgLayer->clip() == PaddingFillBox || bgLayer->clip() == ContentFillBox) {
345 // Clip to the padding or content boxes as necessary.
346 bool includePadding = bgLayer->clip() == ContentFillBox;
347 int x = tx + bLeft + (includePadding ? pLeft : 0);
348 int y = ty + borderTop() + (includePadding ? paddingTop() : 0);
349 int width = w - bLeft - bRight - (includePadding ? pLeft + pRight : 0);
350 int height = h - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : 0);
351 context->save();
352 context->clip(IntRect(x, y, width, height));
353 } else if (bgLayer->clip() == TextFillBox) {
354 // We have to draw our text into a mask that can then be used to clip background drawing.
355 // First figure out how big the mask has to be. It should be no bigger than what we need
356 // to actually render, so we should intersect the dirty rect with the border box of the background.
357 IntRect maskRect(tx, ty, w, h);
358 maskRect.intersect(paintInfo.rect);
359
360 // Now create the mask.
361 OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size());
362 if (!maskImage)
363 return;
364
365 GraphicsContext* maskImageContext = maskImage->context();
366 maskImageContext->translate(-maskRect.x(), -maskRect.y());
367
368 // Now add the text to the clip. We do this by painting using a special paint phase that signals to
369 // InlineTextBoxes that they should just add their contents to the clip.
370 PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, true, 0, 0);
371 if (box)
372 box->paint(info, tx - box->x(), ty - box->y());
373 else {
374 int x = isBox() ? toRenderBox(this)->x() : 0;
375 int y = isBox() ? toRenderBox(this)->y() : 0;
376 paint(info, tx - x, ty - y);
377 }
378
379 // The mask has been created. Now we just need to clip to it.
380 context->save();
381 context->clipToImageBuffer(maskRect, maskImage.get());
382 }
383
384 StyleImage* bg = bgLayer->image();
385 bool shouldPaintBackgroundImage = bg && bg->canRender(style()->effectiveZoom());
386 Color bgColor = c;
387
388 // When this style flag is set, change existing background colors and images to a solid white background.
389 // If there's no bg color or image, leave it untouched to avoid affecting transparency.
390 // We don't try to avoid loading the background images, because this style flag is only set
391 // when printing, and at that point we've already loaded the background images anyway. (To avoid
392 // loading the background images we'd have to do this check when applying styles rather than
393 // while rendering.)
394 if (style()->forceBackgroundsToWhite()) {
395 // Note that we can't reuse this variable below because the bgColor might be changed
396 bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && bgColor.alpha() > 0;
397 if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) {
398 bgColor = Color::white;
399 shouldPaintBackgroundImage = false;
400 }
401 }
402
403 bool isRoot = this->isRoot();
404
405 // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with
406 // no background in the child document should show the parent's background.
407 bool isOpaqueRoot = false;
408 if (isRoot) {
409 isOpaqueRoot = true;
410 if (!bgLayer->next() && !(bgColor.isValid() && bgColor.alpha() == 255) && view()->frameView()) {
411 Element* ownerElement = document()->ownerElement();
412 if (ownerElement) {
413 if (!ownerElement->hasTagName(frameTag)) {
414 // Locate the <body> element using the DOM. This is easier than trying
415 // to crawl around a render tree with potential :before/:after content and
416 // anonymous blocks created by inline <body> tags etc. We can locate the <body>
417 // render object very easily via the DOM.
418 HTMLElement* body = document()->body();
419 if (body) {
420 // Can't scroll a frameset document anyway.
421 isOpaqueRoot = body->hasLocalName(framesetTag);
422 }
423 }
424 } else
425 isOpaqueRoot = !view()->frameView()->isTransparent();
426 }
427 view()->frameView()->setContentIsOpaque(isOpaqueRoot);
428 }
429
430 // Paint the color first underneath all images.
431 if (!bgLayer->next()) {
432 IntRect rect(tx, ty, w, h);
433 rect.intersect(paintInfo.rect);
434 // If we have an alpha and we are painting the root element, go ahead and blend with the base background color.
435 if (isOpaqueRoot) {
436 Color baseColor = view()->frameView()->baseBackgroundColor();
437 if (baseColor.alpha() > 0) {
438 context->save();
439 context->setCompositeOperation(CompositeCopy);
440 context->fillRect(rect, baseColor);
441 context->restore();
442 #ifdef ANDROID_ALLOW_TRANSPARENT_BACKGROUNDS
443 }
444 #else
445 } else
446 context->clearRect(rect);
447 #endif
448 }
449
450 if (bgColor.isValid() && bgColor.alpha() > 0)
451 context->fillRect(rect, bgColor);
452 }
453
454 // no progressive loading of the background image
455 if (shouldPaintBackgroundImage) {
456 IntRect destRect;
457 IntPoint phase;
458 IntSize tileSize;
459
460 calculateBackgroundImageGeometry(bgLayer, tx, ty, w, h, destRect, phase, tileSize);
461 IntPoint destOrigin = destRect.location();
462 destRect.intersect(paintInfo.rect);
463 if (!destRect.isEmpty()) {
464 phase += destRect.location() - destOrigin;
465 CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op;
466 RenderObject* clientForBackgroundImage = this;
467 // Check if this is the root element painting a background layer propagated from <body>,
468 // and pass the body's renderer as the client in that case.
469 if (isRoot && !style()->hasBackground()) {
470 ASSERT(node()->hasTagName(htmlTag));
471 HTMLElement* body = document()->body();
472 ASSERT(body);
473 ASSERT(body->hasLocalName(bodyTag));
474 ASSERT(body->renderer());
475 if (body) {
476 if (RenderObject* bodyRenderer = body->renderer())
477 clientForBackgroundImage = bodyRenderer;
478 }
479 }
480 context->drawTiledImage(bg->image(clientForBackgroundImage, tileSize), destRect, phase, tileSize, compositeOp);
481 }
482 }
483
484 if (bgLayer->clip() != BorderFillBox)
485 // Undo the background clip
486 context->restore();
487
488 if (clippedToBorderRadius)
489 // Undo the border radius clip
490 context->restore();
491
492 if (clippedWithLocalScrolling) // Undo the clip for local background attachments.
493 context->restore();
494 }
495
calculateBackgroundSize(const FillLayer * bgLayer,int scaledWidth,int scaledHeight) const496 IntSize RenderBoxModelObject::calculateBackgroundSize(const FillLayer* bgLayer, int scaledWidth, int scaledHeight) const
497 {
498 StyleImage* bg = bgLayer->image();
499 bg->setImageContainerSize(IntSize(scaledWidth, scaledHeight)); // Use the box established by background-origin.
500
501 if (bgLayer->isSizeSet()) {
502 int w = scaledWidth;
503 int h = scaledHeight;
504 Length bgWidth = bgLayer->size().width();
505 Length bgHeight = bgLayer->size().height();
506
507 if (bgWidth.isFixed())
508 w = bgWidth.value();
509 else if (bgWidth.isPercent())
510 w = bgWidth.calcValue(scaledWidth);
511
512 if (bgHeight.isFixed())
513 h = bgHeight.value();
514 else if (bgHeight.isPercent())
515 h = bgHeight.calcValue(scaledHeight);
516
517 // If one of the values is auto we have to use the appropriate
518 // scale to maintain our aspect ratio.
519 if (bgWidth.isAuto() && !bgHeight.isAuto())
520 w = bg->imageSize(this, style()->effectiveZoom()).width() * h / bg->imageSize(this, style()->effectiveZoom()).height();
521 else if (!bgWidth.isAuto() && bgHeight.isAuto())
522 h = bg->imageSize(this, style()->effectiveZoom()).height() * w / bg->imageSize(this, style()->effectiveZoom()).width();
523 else if (bgWidth.isAuto() && bgHeight.isAuto()) {
524 // If both width and height are auto, we just want to use the image's
525 // intrinsic size.
526 w = bg->imageSize(this, style()->effectiveZoom()).width();
527 h = bg->imageSize(this, style()->effectiveZoom()).height();
528 }
529
530 return IntSize(max(1, w), max(1, h));
531 } else
532 return bg->imageSize(this, style()->effectiveZoom());
533 }
534
calculateBackgroundImageGeometry(const FillLayer * bgLayer,int tx,int ty,int w,int h,IntRect & destRect,IntPoint & phase,IntSize & tileSize)535 void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* bgLayer, int tx, int ty, int w, int h,
536 IntRect& destRect, IntPoint& phase, IntSize& tileSize)
537 {
538 int pw;
539 int ph;
540 int left = 0;
541 int right = 0;
542 int top = 0;
543 int bottom = 0;
544 int cx;
545 int cy;
546 int rw = 0;
547 int rh = 0;
548
549 // CSS2 chapter 14.2.1
550 bool fixedAttachment = bgLayer->attachment() == FixedBackgroundAttachment;
551 if (!fixedAttachment) {
552 // Scroll and Local
553 if (bgLayer->origin() != BorderFillBox) {
554 left = borderLeft();
555 right = borderRight();
556 top = borderTop();
557 bottom = borderBottom();
558 if (bgLayer->origin() == ContentFillBox) {
559 left += paddingLeft();
560 right += paddingRight();
561 top += paddingTop();
562 bottom += paddingBottom();
563 }
564 }
565
566 // The background of the box generated by the root element covers the entire canvas including
567 // its margins. Since those were added in already, we have to factor them out when computing the
568 // box used by background-origin/size/position.
569 if (isRoot()) {
570 rw = toRenderBox(this)->width() - left - right;
571 rh = toRenderBox(this)->height() - top - bottom;
572 left += marginLeft();
573 right += marginRight();
574 top += marginTop();
575 bottom += marginBottom();
576 }
577 cx = tx;
578 cy = ty;
579 pw = w - left - right;
580 ph = h - top - bottom;
581 } else {
582 // Fixed background attachment.
583 IntRect vr = viewRect();
584 cx = vr.x();
585 cy = vr.y();
586 pw = vr.width();
587 ph = vr.height();
588 }
589
590 int sx = 0;
591 int sy = 0;
592 int cw;
593 int ch;
594
595 IntSize scaledImageSize;
596 if (isRoot() && !fixedAttachment)
597 scaledImageSize = calculateBackgroundSize(bgLayer, rw, rh);
598 else
599 scaledImageSize = calculateBackgroundSize(bgLayer, pw, ph);
600
601 int scaledImageWidth = scaledImageSize.width();
602 int scaledImageHeight = scaledImageSize.height();
603
604 EFillRepeat backgroundRepeat = bgLayer->repeat();
605
606 int xPosition;
607 if (isRoot() && !fixedAttachment)
608 xPosition = bgLayer->xPosition().calcMinValue(rw - scaledImageWidth, true);
609 else
610 xPosition = bgLayer->xPosition().calcMinValue(pw - scaledImageWidth, true);
611 if (backgroundRepeat == RepeatFill || backgroundRepeat == RepeatXFill) {
612 cw = pw + left + right;
613 sx = scaledImageWidth ? scaledImageWidth - (xPosition + left) % scaledImageWidth : 0;
614 } else {
615 cx += max(xPosition + left, 0);
616 sx = -min(xPosition + left, 0);
617 cw = scaledImageWidth + min(xPosition + left, 0);
618 }
619
620 int yPosition;
621 if (isRoot() && !fixedAttachment)
622 yPosition = bgLayer->yPosition().calcMinValue(rh - scaledImageHeight, true);
623 else
624 yPosition = bgLayer->yPosition().calcMinValue(ph - scaledImageHeight, true);
625 if (backgroundRepeat == RepeatFill || backgroundRepeat == RepeatYFill) {
626 ch = ph + top + bottom;
627 sy = scaledImageHeight ? scaledImageHeight - (yPosition + top) % scaledImageHeight : 0;
628 } else {
629 cy += max(yPosition + top, 0);
630 sy = -min(yPosition + top, 0);
631 ch = scaledImageHeight + min(yPosition + top, 0);
632 }
633
634 if (fixedAttachment) {
635 sx += max(tx - cx, 0);
636 sy += max(ty - cy, 0);
637 }
638
639 destRect = IntRect(cx, cy, cw, ch);
640 destRect.intersect(IntRect(tx, ty, w, h));
641 phase = IntPoint(sx, sy);
642 tileSize = IntSize(scaledImageWidth, scaledImageHeight);
643 }
644
verticalPosition(bool firstLine) const645 int RenderBoxModelObject::verticalPosition(bool firstLine) const
646 {
647 // This method determines the vertical position for inline elements.
648 ASSERT(isInline());
649 if (!isInline())
650 return 0;
651
652 int vpos = 0;
653 EVerticalAlign va = style()->verticalAlign();
654 if (va == TOP)
655 vpos = PositionTop;
656 else if (va == BOTTOM)
657 vpos = PositionBottom;
658 else {
659 bool checkParent = parent()->isRenderInline() && parent()->style()->verticalAlign() != TOP && parent()->style()->verticalAlign() != BOTTOM;
660 vpos = checkParent ? toRenderInline(parent())->verticalPositionFromCache(firstLine) : 0;
661 // don't allow elements nested inside text-top to have a different valignment.
662 if (va == BASELINE)
663 return vpos;
664
665 const Font& f = parent()->style(firstLine)->font();
666 int fontsize = f.pixelSize();
667
668 if (va == SUB)
669 vpos += fontsize / 5 + 1;
670 else if (va == SUPER)
671 vpos -= fontsize / 3 + 1;
672 else if (va == TEXT_TOP)
673 vpos += baselinePosition(firstLine) - f.ascent();
674 else if (va == MIDDLE)
675 vpos += -static_cast<int>(f.xHeight() / 2) - lineHeight(firstLine) / 2 + baselinePosition(firstLine);
676 else if (va == TEXT_BOTTOM) {
677 vpos += f.descent();
678 if (!isReplaced()) // lineHeight - baselinePosition is always 0 for replaced elements, so don't bother wasting time in that case.
679 vpos -= (lineHeight(firstLine) - baselinePosition(firstLine));
680 } else if (va == BASELINE_MIDDLE)
681 vpos += -lineHeight(firstLine) / 2 + baselinePosition(firstLine);
682 else if (va == LENGTH)
683 vpos -= style()->verticalAlignLength().calcValue(lineHeight(firstLine));
684 }
685
686 return vpos;
687 }
688
paintNinePieceImage(GraphicsContext * graphicsContext,int tx,int ty,int w,int h,const RenderStyle * style,const NinePieceImage & ninePieceImage,CompositeOperator op)689 bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style,
690 const NinePieceImage& ninePieceImage, CompositeOperator op)
691 {
692 StyleImage* styleImage = ninePieceImage.image();
693 if (!styleImage)
694 return false;
695
696 if (!styleImage->isLoaded())
697 return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either.
698
699 if (!styleImage->canRender(style->effectiveZoom()))
700 return false;
701
702 // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function
703 // doesn't have any understanding of the zoom that is in effect on the tile.
704 styleImage->setImageContainerSize(IntSize(w, h));
705 IntSize imageSize = styleImage->imageSize(this, 1.0f);
706 int imageWidth = imageSize.width();
707 int imageHeight = imageSize.height();
708
709 int topSlice = min(imageHeight, ninePieceImage.m_slices.top().calcValue(imageHeight));
710 int bottomSlice = min(imageHeight, ninePieceImage.m_slices.bottom().calcValue(imageHeight));
711 int leftSlice = min(imageWidth, ninePieceImage.m_slices.left().calcValue(imageWidth));
712 int rightSlice = min(imageWidth, ninePieceImage.m_slices.right().calcValue(imageWidth));
713
714 ENinePieceImageRule hRule = ninePieceImage.horizontalRule();
715 ENinePieceImageRule vRule = ninePieceImage.verticalRule();
716
717 bool fitToBorder = style->borderImage() == ninePieceImage;
718
719 int leftWidth = fitToBorder ? style->borderLeftWidth() : leftSlice;
720 int topWidth = fitToBorder ? style->borderTopWidth() : topSlice;
721 int rightWidth = fitToBorder ? style->borderRightWidth() : rightSlice;
722 int bottomWidth = fitToBorder ? style->borderBottomWidth() : bottomSlice;
723
724 bool drawLeft = leftSlice > 0 && leftWidth > 0;
725 bool drawTop = topSlice > 0 && topWidth > 0;
726 bool drawRight = rightSlice > 0 && rightWidth > 0;
727 bool drawBottom = bottomSlice > 0 && bottomWidth > 0;
728 bool drawMiddle = (imageWidth - leftSlice - rightSlice) > 0 && (w - leftWidth - rightWidth) > 0 &&
729 (imageHeight - topSlice - bottomSlice) > 0 && (h - topWidth - bottomWidth) > 0;
730
731 Image* image = styleImage->image(this, imageSize);
732
733 if (drawLeft) {
734 // Paint the top and bottom left corners.
735
736 // The top left corner rect is (tx, ty, leftWidth, topWidth)
737 // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice)
738 if (drawTop)
739 graphicsContext->drawImage(image, IntRect(tx, ty, leftWidth, topWidth),
740 IntRect(0, 0, leftSlice, topSlice), op);
741
742 // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth)
743 // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice)
744 if (drawBottom)
745 graphicsContext->drawImage(image, IntRect(tx, ty + h - bottomWidth, leftWidth, bottomWidth),
746 IntRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op);
747
748 // Paint the left edge.
749 // Have to scale and tile into the border rect.
750 graphicsContext->drawTiledImage(image, IntRect(tx, ty + topWidth, leftWidth,
751 h - topWidth - bottomWidth),
752 IntRect(0, topSlice, leftSlice, imageHeight - topSlice - bottomSlice),
753 Image::StretchTile, (Image::TileRule)vRule, op);
754 }
755
756 if (drawRight) {
757 // Paint the top and bottom right corners
758 // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth)
759 // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice)
760 if (drawTop)
761 graphicsContext->drawImage(image, IntRect(tx + w - rightWidth, ty, rightWidth, topWidth),
762 IntRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op);
763
764 // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth)
765 // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice)
766 if (drawBottom)
767 graphicsContext->drawImage(image, IntRect(tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth),
768 IntRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op);
769
770 // Paint the right edge.
771 graphicsContext->drawTiledImage(image, IntRect(tx + w - rightWidth, ty + topWidth, rightWidth,
772 h - topWidth - bottomWidth),
773 IntRect(imageWidth - rightSlice, topSlice, rightSlice, imageHeight - topSlice - bottomSlice),
774 Image::StretchTile, (Image::TileRule)vRule, op);
775 }
776
777 // Paint the top edge.
778 if (drawTop)
779 graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty, w - leftWidth - rightWidth, topWidth),
780 IntRect(leftSlice, 0, imageWidth - rightSlice - leftSlice, topSlice),
781 (Image::TileRule)hRule, Image::StretchTile, op);
782
783 // Paint the bottom edge.
784 if (drawBottom)
785 graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty + h - bottomWidth,
786 w - leftWidth - rightWidth, bottomWidth),
787 IntRect(leftSlice, imageHeight - bottomSlice, imageWidth - rightSlice - leftSlice, bottomSlice),
788 (Image::TileRule)hRule, Image::StretchTile, op);
789
790 // Paint the middle.
791 if (drawMiddle)
792 graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty + topWidth, w - leftWidth - rightWidth,
793 h - topWidth - bottomWidth),
794 IntRect(leftSlice, topSlice, imageWidth - rightSlice - leftSlice, imageHeight - topSlice - bottomSlice),
795 (Image::TileRule)hRule, (Image::TileRule)vRule, op);
796
797 return true;
798 }
799
paintBorder(GraphicsContext * graphicsContext,int tx,int ty,int w,int h,const RenderStyle * style,bool begin,bool end)800 void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h,
801 const RenderStyle* style, bool begin, bool end)
802 {
803 if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage()))
804 return;
805
806 const Color& topColor = style->borderTopColor();
807 const Color& bottomColor = style->borderBottomColor();
808 const Color& leftColor = style->borderLeftColor();
809 const Color& rightColor = style->borderRightColor();
810
811 bool topTransparent = style->borderTopIsTransparent();
812 bool bottomTransparent = style->borderBottomIsTransparent();
813 bool rightTransparent = style->borderRightIsTransparent();
814 bool leftTransparent = style->borderLeftIsTransparent();
815
816 EBorderStyle topStyle = style->borderTopStyle();
817 EBorderStyle bottomStyle = style->borderBottomStyle();
818 EBorderStyle leftStyle = style->borderLeftStyle();
819 EBorderStyle rightStyle = style->borderRightStyle();
820
821 bool renderTop = topStyle > BHIDDEN && !topTransparent;
822 bool renderLeft = leftStyle > BHIDDEN && begin && !leftTransparent;
823 bool renderRight = rightStyle > BHIDDEN && end && !rightTransparent;
824 bool renderBottom = bottomStyle > BHIDDEN && !bottomTransparent;
825
826 bool renderRadii = false;
827 IntSize topLeft, topRight, bottomLeft, bottomRight;
828
829 if (style->hasBorderRadius()) {
830 IntRect borderRect = IntRect(tx, ty, w, h);
831
832 IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius;
833 style->getBorderRadiiForRect(borderRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
834
835 if (begin) {
836 topLeft = topLeftRadius;
837 bottomLeft = bottomLeftRadius;
838 }
839 if (end) {
840 topRight = topRightRadius;
841 bottomRight = bottomRightRadius;
842 }
843
844 renderRadii = true;
845
846 // Clip to the rounded rectangle.
847 graphicsContext->save();
848 graphicsContext->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight);
849 }
850
851 int firstAngleStart, secondAngleStart, firstAngleSpan, secondAngleSpan;
852 float thickness;
853 bool upperLeftBorderStylesMatch = renderLeft && (topStyle == leftStyle) && (topColor == leftColor);
854 bool upperRightBorderStylesMatch = renderRight && (topStyle == rightStyle) && (topColor == rightColor) && (topStyle != OUTSET) && (topStyle != RIDGE) && (topStyle != INSET) && (topStyle != GROOVE);
855 bool lowerLeftBorderStylesMatch = renderLeft && (bottomStyle == leftStyle) && (bottomColor == leftColor) && (bottomStyle != OUTSET) && (bottomStyle != RIDGE) && (bottomStyle != INSET) && (bottomStyle != GROOVE);
856 bool lowerRightBorderStylesMatch = renderRight && (bottomStyle == rightStyle) && (bottomColor == rightColor);
857
858 if (renderTop) {
859 bool ignore_left = (renderRadii && topLeft.width() > 0) ||
860 (topColor == leftColor && topTransparent == leftTransparent && topStyle >= OUTSET &&
861 (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET));
862
863 bool ignore_right = (renderRadii && topRight.width() > 0) ||
864 (topColor == rightColor && topTransparent == rightTransparent && topStyle >= OUTSET &&
865 (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET));
866
867 int x = tx;
868 int x2 = tx + w;
869 if (renderRadii) {
870 x += topLeft.width();
871 x2 -= topRight.width();
872 }
873
874 drawLineForBoxSide(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, topColor, style->color(), topStyle,
875 ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth());
876
877 if (renderRadii) {
878 int leftY = ty;
879
880 // We make the arc double thick and let the clip rect take care of clipping the extra off.
881 // We're doing this because it doesn't seem possible to match the curve of the clip exactly
882 // with the arc-drawing function.
883 thickness = style->borderTopWidth() * 2;
884
885 if (topLeft.width()) {
886 int leftX = tx;
887 // The inner clip clips inside the arc. This is especially important for 1px borders.
888 bool applyLeftInnerClip = (style->borderLeftWidth() < topLeft.width())
889 && (style->borderTopWidth() < topLeft.height())
890 && (topStyle != DOUBLE || style->borderTopWidth() > 6);
891 if (applyLeftInnerClip) {
892 graphicsContext->save();
893 graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, topLeft.width() * 2, topLeft.height() * 2),
894 style->borderTopWidth());
895 }
896
897 firstAngleStart = 90;
898 firstAngleSpan = upperLeftBorderStylesMatch ? 90 : 45;
899
900 // Draw upper left arc
901 drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, topLeft, firstAngleStart, firstAngleSpan,
902 BSTop, topColor, style->color(), topStyle, true);
903 if (applyLeftInnerClip)
904 graphicsContext->restore();
905 }
906
907 if (topRight.width()) {
908 int rightX = tx + w - topRight.width() * 2;
909 bool applyRightInnerClip = (style->borderRightWidth() < topRight.width())
910 && (style->borderTopWidth() < topRight.height())
911 && (topStyle != DOUBLE || style->borderTopWidth() > 6);
912 if (applyRightInnerClip) {
913 graphicsContext->save();
914 graphicsContext->addInnerRoundedRectClip(IntRect(rightX, leftY, topRight.width() * 2, topRight.height() * 2),
915 style->borderTopWidth());
916 }
917
918 if (upperRightBorderStylesMatch) {
919 secondAngleStart = 0;
920 secondAngleSpan = 90;
921 } else {
922 secondAngleStart = 45;
923 secondAngleSpan = 45;
924 }
925
926 // Draw upper right arc
927 drawArcForBoxSide(graphicsContext, rightX, leftY, thickness, topRight, secondAngleStart, secondAngleSpan,
928 BSTop, topColor, style->color(), topStyle, false);
929 if (applyRightInnerClip)
930 graphicsContext->restore();
931 }
932 }
933 }
934
935 if (renderBottom) {
936 bool ignore_left = (renderRadii && bottomLeft.width() > 0) ||
937 (bottomColor == leftColor && bottomTransparent == leftTransparent && bottomStyle >= OUTSET &&
938 (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET));
939
940 bool ignore_right = (renderRadii && bottomRight.width() > 0) ||
941 (bottomColor == rightColor && bottomTransparent == rightTransparent && bottomStyle >= OUTSET &&
942 (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET));
943
944 int x = tx;
945 int x2 = tx + w;
946 if (renderRadii) {
947 x += bottomLeft.width();
948 x2 -= bottomRight.width();
949 }
950
951 drawLineForBoxSide(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bottomColor, style->color(), bottomStyle,
952 ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth());
953
954 if (renderRadii) {
955 thickness = style->borderBottomWidth() * 2;
956
957 if (bottomLeft.width()) {
958 int leftX = tx;
959 int leftY = ty + h - bottomLeft.height() * 2;
960 bool applyLeftInnerClip = (style->borderLeftWidth() < bottomLeft.width())
961 && (style->borderBottomWidth() < bottomLeft.height())
962 && (bottomStyle != DOUBLE || style->borderBottomWidth() > 6);
963 if (applyLeftInnerClip) {
964 graphicsContext->save();
965 graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, bottomLeft.width() * 2, bottomLeft.height() * 2),
966 style->borderBottomWidth());
967 }
968
969 if (lowerLeftBorderStylesMatch) {
970 firstAngleStart = 180;
971 firstAngleSpan = 90;
972 } else {
973 firstAngleStart = 225;
974 firstAngleSpan = 45;
975 }
976
977 // Draw lower left arc
978 drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, bottomLeft, firstAngleStart, firstAngleSpan,
979 BSBottom, bottomColor, style->color(), bottomStyle, true);
980 if (applyLeftInnerClip)
981 graphicsContext->restore();
982 }
983
984 if (bottomRight.width()) {
985 int rightY = ty + h - bottomRight.height() * 2;
986 int rightX = tx + w - bottomRight.width() * 2;
987 bool applyRightInnerClip = (style->borderRightWidth() < bottomRight.width())
988 && (style->borderBottomWidth() < bottomRight.height())
989 && (bottomStyle != DOUBLE || style->borderBottomWidth() > 6);
990 if (applyRightInnerClip) {
991 graphicsContext->save();
992 graphicsContext->addInnerRoundedRectClip(IntRect(rightX, rightY, bottomRight.width() * 2, bottomRight.height() * 2),
993 style->borderBottomWidth());
994 }
995
996 secondAngleStart = 270;
997 secondAngleSpan = lowerRightBorderStylesMatch ? 90 : 45;
998
999 // Draw lower right arc
1000 drawArcForBoxSide(graphicsContext, rightX, rightY, thickness, bottomRight, secondAngleStart, secondAngleSpan,
1001 BSBottom, bottomColor, style->color(), bottomStyle, false);
1002 if (applyRightInnerClip)
1003 graphicsContext->restore();
1004 }
1005 }
1006 }
1007
1008 if (renderLeft) {
1009 bool ignore_top = (renderRadii && topLeft.height() > 0) ||
1010 (topColor == leftColor && topTransparent == leftTransparent && leftStyle >= OUTSET &&
1011 (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET));
1012
1013 bool ignore_bottom = (renderRadii && bottomLeft.height() > 0) ||
1014 (bottomColor == leftColor && bottomTransparent == leftTransparent && leftStyle >= OUTSET &&
1015 (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET));
1016
1017 int y = ty;
1018 int y2 = ty + h;
1019 if (renderRadii) {
1020 y += topLeft.height();
1021 y2 -= bottomLeft.height();
1022 }
1023
1024 drawLineForBoxSide(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, leftColor, style->color(), leftStyle,
1025 ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth());
1026
1027 if (renderRadii && (!upperLeftBorderStylesMatch || !lowerLeftBorderStylesMatch)) {
1028 int topX = tx;
1029 thickness = style->borderLeftWidth() * 2;
1030
1031 if (!upperLeftBorderStylesMatch && topLeft.width()) {
1032 int topY = ty;
1033 bool applyTopInnerClip = (style->borderLeftWidth() < topLeft.width())
1034 && (style->borderTopWidth() < topLeft.height())
1035 && (leftStyle != DOUBLE || style->borderLeftWidth() > 6);
1036 if (applyTopInnerClip) {
1037 graphicsContext->save();
1038 graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topLeft.width() * 2, topLeft.height() * 2),
1039 style->borderLeftWidth());
1040 }
1041
1042 firstAngleStart = 135;
1043 firstAngleSpan = 45;
1044
1045 // Draw top left arc
1046 drawArcForBoxSide(graphicsContext, topX, topY, thickness, topLeft, firstAngleStart, firstAngleSpan,
1047 BSLeft, leftColor, style->color(), leftStyle, true);
1048 if (applyTopInnerClip)
1049 graphicsContext->restore();
1050 }
1051
1052 if (!lowerLeftBorderStylesMatch && bottomLeft.width()) {
1053 int bottomY = ty + h - bottomLeft.height() * 2;
1054 bool applyBottomInnerClip = (style->borderLeftWidth() < bottomLeft.width())
1055 && (style->borderBottomWidth() < bottomLeft.height())
1056 && (leftStyle != DOUBLE || style->borderLeftWidth() > 6);
1057 if (applyBottomInnerClip) {
1058 graphicsContext->save();
1059 graphicsContext->addInnerRoundedRectClip(IntRect(topX, bottomY, bottomLeft.width() * 2, bottomLeft.height() * 2),
1060 style->borderLeftWidth());
1061 }
1062
1063 secondAngleStart = 180;
1064 secondAngleSpan = 45;
1065
1066 // Draw bottom left arc
1067 drawArcForBoxSide(graphicsContext, topX, bottomY, thickness, bottomLeft, secondAngleStart, secondAngleSpan,
1068 BSLeft, leftColor, style->color(), leftStyle, false);
1069 if (applyBottomInnerClip)
1070 graphicsContext->restore();
1071 }
1072 }
1073 }
1074
1075 if (renderRight) {
1076 bool ignore_top = (renderRadii && topRight.height() > 0) ||
1077 ((topColor == rightColor) && (topTransparent == rightTransparent) &&
1078 (rightStyle >= DOTTED || rightStyle == INSET) &&
1079 (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET));
1080
1081 bool ignore_bottom = (renderRadii && bottomRight.height() > 0) ||
1082 ((bottomColor == rightColor) && (bottomTransparent == rightTransparent) &&
1083 (rightStyle >= DOTTED || rightStyle == INSET) &&
1084 (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET));
1085
1086 int y = ty;
1087 int y2 = ty + h;
1088 if (renderRadii) {
1089 y += topRight.height();
1090 y2 -= bottomRight.height();
1091 }
1092
1093 drawLineForBoxSide(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rightColor, style->color(), rightStyle,
1094 ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth());
1095
1096 if (renderRadii && (!upperRightBorderStylesMatch || !lowerRightBorderStylesMatch)) {
1097 thickness = style->borderRightWidth() * 2;
1098
1099 if (!upperRightBorderStylesMatch && topRight.width()) {
1100 int topX = tx + w - topRight.width() * 2;
1101 int topY = ty;
1102 bool applyTopInnerClip = (style->borderRightWidth() < topRight.width())
1103 && (style->borderTopWidth() < topRight.height())
1104 && (rightStyle != DOUBLE || style->borderRightWidth() > 6);
1105 if (applyTopInnerClip) {
1106 graphicsContext->save();
1107 graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topRight.width() * 2, topRight.height() * 2),
1108 style->borderRightWidth());
1109 }
1110
1111 firstAngleStart = 0;
1112 firstAngleSpan = 45;
1113
1114 // Draw top right arc
1115 drawArcForBoxSide(graphicsContext, topX, topY, thickness, topRight, firstAngleStart, firstAngleSpan,
1116 BSRight, rightColor, style->color(), rightStyle, true);
1117 if (applyTopInnerClip)
1118 graphicsContext->restore();
1119 }
1120
1121 if (!lowerRightBorderStylesMatch && bottomRight.width()) {
1122 int bottomX = tx + w - bottomRight.width() * 2;
1123 int bottomY = ty + h - bottomRight.height() * 2;
1124 bool applyBottomInnerClip = (style->borderRightWidth() < bottomRight.width())
1125 && (style->borderBottomWidth() < bottomRight.height())
1126 && (rightStyle != DOUBLE || style->borderRightWidth() > 6);
1127 if (applyBottomInnerClip) {
1128 graphicsContext->save();
1129 graphicsContext->addInnerRoundedRectClip(IntRect(bottomX, bottomY, bottomRight.width() * 2, bottomRight.height() * 2),
1130 style->borderRightWidth());
1131 }
1132
1133 secondAngleStart = 315;
1134 secondAngleSpan = 45;
1135
1136 // Draw bottom right arc
1137 drawArcForBoxSide(graphicsContext, bottomX, bottomY, thickness, bottomRight, secondAngleStart, secondAngleSpan,
1138 BSRight, rightColor, style->color(), rightStyle, false);
1139 if (applyBottomInnerClip)
1140 graphicsContext->restore();
1141 }
1142 }
1143 }
1144
1145 if (renderRadii)
1146 graphicsContext->restore();
1147 }
1148
paintBoxShadow(GraphicsContext * context,int tx,int ty,int w,int h,const RenderStyle * s,ShadowStyle shadowStyle,bool begin,bool end)1149 void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int ty, int w, int h, const RenderStyle* s, ShadowStyle shadowStyle, bool begin, bool end)
1150 {
1151 // FIXME: Deal with border-image. Would be great to use border-image as a mask.
1152
1153 if (context->paintingDisabled())
1154 return;
1155
1156 IntRect rect(tx, ty, w, h);
1157 IntSize topLeft;
1158 IntSize topRight;
1159 IntSize bottomLeft;
1160 IntSize bottomRight;
1161
1162 bool hasBorderRadius = s->hasBorderRadius();
1163 if (hasBorderRadius && (begin || end)) {
1164 IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius;
1165 s->getBorderRadiiForRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
1166
1167 if (begin) {
1168 if (shadowStyle == Inset) {
1169 topLeftRadius.expand(-borderLeft(), -borderTop());
1170 topLeftRadius.clampNegativeToZero();
1171 bottomLeftRadius.expand(-borderLeft(), -borderBottom());
1172 bottomLeftRadius.clampNegativeToZero();
1173 }
1174 topLeft = topLeftRadius;
1175 bottomLeft = bottomLeftRadius;
1176 }
1177 if (end) {
1178 if (shadowStyle == Inset) {
1179 topRightRadius.expand(-borderRight(), -borderTop());
1180 topRightRadius.clampNegativeToZero();
1181 bottomRightRadius.expand(-borderRight(), -borderBottom());
1182 bottomRightRadius.clampNegativeToZero();
1183 }
1184 topRight = topRightRadius;
1185 bottomRight = bottomRightRadius;
1186 }
1187 }
1188
1189 if (shadowStyle == Inset) {
1190 rect.move(begin ? borderLeft() : 0, borderTop());
1191 rect.setWidth(rect.width() - (begin ? borderLeft() : 0) - (end ? borderRight() : 0));
1192 rect.setHeight(rect.height() - borderTop() - borderBottom());
1193 }
1194
1195 bool hasOpaqueBackground = s->backgroundColor().isValid() && s->backgroundColor().alpha() == 255;
1196 for (ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next) {
1197 if (shadow->style != shadowStyle)
1198 continue;
1199
1200 IntSize shadowOffset(shadow->x, shadow->y);
1201 int shadowBlur = shadow->blur;
1202 int shadowSpread = shadow->spread;
1203 Color& shadowColor = shadow->color;
1204
1205 if (shadow->style == Normal) {
1206 IntRect fillRect(rect);
1207 fillRect.inflate(shadowSpread);
1208 if (fillRect.isEmpty())
1209 continue;
1210
1211 IntRect shadowRect(rect);
1212 shadowRect.inflate(shadowBlur + shadowSpread);
1213 shadowRect.move(shadowOffset);
1214
1215 context->save();
1216 context->clip(shadowRect);
1217
1218 // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not
1219 // bleed in (due to antialiasing) if the context is transformed.
1220 IntSize extraOffset(w + max(0, shadowOffset.width()) + shadowBlur + 2 * shadowSpread + 1, 0);
1221 shadowOffset -= extraOffset;
1222 fillRect.move(extraOffset);
1223
1224 context->setShadow(shadowOffset, shadowBlur, shadowColor);
1225 if (hasBorderRadius) {
1226 IntRect rectToClipOut = rect;
1227 IntSize topLeftToClipOut = topLeft;
1228 IntSize topRightToClipOut = topRight;
1229 IntSize bottomLeftToClipOut = bottomLeft;
1230 IntSize bottomRightToClipOut = bottomRight;
1231
1232 if (shadowSpread < 0) {
1233 topLeft.expand(shadowSpread, shadowSpread);
1234 topLeft.clampNegativeToZero();
1235
1236 topRight.expand(shadowSpread, shadowSpread);
1237 topRight.clampNegativeToZero();
1238
1239 bottomLeft.expand(shadowSpread, shadowSpread);
1240 bottomLeft.clampNegativeToZero();
1241
1242 bottomRight.expand(shadowSpread, shadowSpread);
1243 bottomRight.clampNegativeToZero();
1244 }
1245
1246 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
1247 // when painting the shadow. On the other hand, it introduces subpixel gaps along the
1248 // corners. Those are avoided by insetting the clipping path by one pixel.
1249 if (hasOpaqueBackground) {
1250 rectToClipOut.inflate(-1);
1251
1252 topLeftToClipOut.expand(-1, -1);
1253 topLeftToClipOut.clampNegativeToZero();
1254
1255 topRightToClipOut.expand(-1, -1);
1256 topRightToClipOut.clampNegativeToZero();
1257
1258 bottomLeftToClipOut.expand(-1, -1);
1259 bottomLeftToClipOut.clampNegativeToZero();
1260
1261 bottomRightToClipOut.expand(-1, -1);
1262 bottomRightToClipOut.clampNegativeToZero();
1263 }
1264
1265 if (!rectToClipOut.isEmpty())
1266 context->clipOutRoundedRect(rectToClipOut, topLeftToClipOut, topRightToClipOut, bottomLeftToClipOut, bottomRightToClipOut);
1267 context->fillRoundedRect(fillRect, topLeft, topRight, bottomLeft, bottomRight, Color::black);
1268 } else {
1269 IntRect rectToClipOut = rect;
1270
1271 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time
1272 // when painting the shadow. On the other hand, it introduces subpixel gaps along the
1273 // edges if they are not pixel-aligned. Those are avoided by insetting the clipping path
1274 // by one pixel.
1275 if (hasOpaqueBackground) {
1276 TransformationMatrix currentTransformation = context->getCTM();
1277 if (currentTransformation.a() != 1 || (currentTransformation.d() != 1 && currentTransformation.d() != -1)
1278 || currentTransformation.b() || currentTransformation.c())
1279 rectToClipOut.inflate(-1);
1280 }
1281
1282 if (!rectToClipOut.isEmpty())
1283 context->clipOut(rectToClipOut);
1284 context->fillRect(fillRect, Color::black);
1285 }
1286
1287 context->restore();
1288 } else {
1289 // Inset shadow.
1290 IntRect holeRect(rect);
1291 holeRect.inflate(-shadowSpread);
1292
1293 if (holeRect.isEmpty()) {
1294 if (hasBorderRadius)
1295 context->fillRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight, shadowColor);
1296 else
1297 context->fillRect(rect, shadowColor);
1298 continue;
1299 }
1300 if (!begin) {
1301 holeRect.move(-max(shadowOffset.width(), 0) - shadowBlur, 0);
1302 holeRect.setWidth(holeRect.width() + max(shadowOffset.width(), 0) + shadowBlur);
1303 }
1304 if (!end)
1305 holeRect.setWidth(holeRect.width() - min(shadowOffset.width(), 0) + shadowBlur);
1306
1307 Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255);
1308
1309 IntRect outerRect(rect);
1310 outerRect.inflateX(w - 2 * shadowSpread);
1311 outerRect.inflateY(h - 2 * shadowSpread);
1312
1313 context->save();
1314
1315 if (hasBorderRadius)
1316 context->clip(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight));
1317 else
1318 context->clip(rect);
1319
1320 IntSize extraOffset(2 * w + max(0, shadowOffset.width()) + shadowBlur - 2 * shadowSpread + 1, 0);
1321 context->translate(extraOffset.width(), extraOffset.height());
1322 shadowOffset -= extraOffset;
1323
1324 context->beginPath();
1325 context->addPath(Path::createRectangle(outerRect));
1326
1327 if (hasBorderRadius) {
1328 if (shadowSpread > 0) {
1329 topLeft.expand(-shadowSpread, -shadowSpread);
1330 topLeft.clampNegativeToZero();
1331
1332 topRight.expand(-shadowSpread, -shadowSpread);
1333 topRight.clampNegativeToZero();
1334
1335 bottomLeft.expand(-shadowSpread, -shadowSpread);
1336 bottomLeft.clampNegativeToZero();
1337
1338 bottomRight.expand(-shadowSpread, -shadowSpread);
1339 bottomRight.clampNegativeToZero();
1340 }
1341 context->addPath(Path::createRoundedRectangle(holeRect, topLeft, topRight, bottomLeft, bottomRight));
1342 } else
1343 context->addPath(Path::createRectangle(holeRect));
1344
1345 context->setFillRule(RULE_EVENODD);
1346 context->setFillColor(fillColor);
1347 context->setShadow(shadowOffset, shadowBlur, shadowColor);
1348 context->fillPath();
1349
1350 context->restore();
1351 }
1352 }
1353 }
1354
containingBlockWidthForContent() const1355 int RenderBoxModelObject::containingBlockWidthForContent() const
1356 {
1357 return containingBlock()->availableWidth();
1358 }
1359
1360 } // namespace WebCore
1361