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