OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2009 Apple Inc. All rights reserved. | 2 * Copyright (C) 2009 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * This library is free software; you can redistribute it and/or | 4 * This library is free software; you can redistribute it and/or |
5 * modify it under the terms of the GNU Library General Public | 5 * modify it under the terms of the GNU Library General Public |
6 * License as published by the Free Software Foundation; either | 6 * License as published by the Free Software Foundation; either |
7 * version 2 of the License, or (at your option) any later version. | 7 * version 2 of the License, or (at your option) any later version. |
8 * | 8 * |
9 * This library is distributed in the hope that it will be useful, | 9 * This library is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 * Library General Public License for more details. | 12 * Library General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU Library General Public License | 14 * You should have received a copy of the GNU Library General Public License |
15 * along with this library; see the file COPYING.LIB. If not, write to | 15 * along with this library; see the file COPYING.LIB. If not, write to |
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 * Boston, MA 02110-1301, USA. | 17 * Boston, MA 02110-1301, USA. |
18 * | 18 * |
19 */ | 19 */ |
20 | 20 |
21 #include "config.h" | 21 #include "config.h" |
22 #include "RenderBoxModelObject.h" | 22 #include "RenderBoxModelObject.h" |
23 | 23 |
| 24 #include "GraphicsContext.h" |
| 25 #include "HTMLElement.h" |
| 26 #include "HTMLNames.h" |
| 27 #include "ImageBuffer.h" |
24 #include "RenderBlock.h" | 28 #include "RenderBlock.h" |
25 #include "RenderLayer.h" | 29 #include "RenderLayer.h" |
26 #include "RenderView.h" | 30 #include "RenderView.h" |
27 | 31 |
| 32 using namespace std; |
| 33 |
28 namespace WebCore { | 34 namespace WebCore { |
29 | 35 |
| 36 using namespace HTMLNames; |
| 37 |
30 bool RenderBoxModelObject::s_wasFloating = false; | 38 bool RenderBoxModelObject::s_wasFloating = false; |
31 | 39 |
32 RenderBoxModelObject::RenderBoxModelObject(Node* node) | 40 RenderBoxModelObject::RenderBoxModelObject(Node* node) |
33 : RenderObject(node) | 41 : RenderObject(node) |
34 , m_layer(0) | 42 , m_layer(0) |
35 { | 43 { |
36 } | 44 } |
37 | 45 |
38 RenderBoxModelObject::~RenderBoxModelObject() | 46 RenderBoxModelObject::~RenderBoxModelObject() |
39 { | 47 { |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
110 if (!style()->top().isAuto()) { | 118 if (!style()->top().isAuto()) { |
111 if (!style()->top().isPercent() || containingBlock()->style()->height().
isFixed()) | 119 if (!style()->top().isPercent() || containingBlock()->style()->height().
isFixed()) |
112 return style()->top().calcValue(containingBlockHeight()); | 120 return style()->top().calcValue(containingBlockHeight()); |
113 } else if (!style()->bottom().isAuto()) { | 121 } else if (!style()->bottom().isAuto()) { |
114 if (!style()->bottom().isPercent() || containingBlock()->style()->height
().isFixed()) | 122 if (!style()->bottom().isPercent() || containingBlock()->style()->height
().isFixed()) |
115 return -style()->bottom().calcValue(containingBlockHeight()); | 123 return -style()->bottom().calcValue(containingBlockHeight()); |
116 } | 124 } |
117 return 0; | 125 return 0; |
118 } | 126 } |
119 | 127 |
| 128 int RenderBoxModelObject::offsetLeft() const |
| 129 { |
| 130 RenderBoxModelObject* offsetPar = offsetParent(); |
| 131 if (!offsetPar) |
| 132 return 0; |
| 133 int xPos = (isBox() ? toRenderBox(this)->x() : 0); |
| 134 if (offsetPar->isBox()) |
| 135 xPos -= toRenderBox(offsetPar)->borderLeft(); |
| 136 if (!isPositioned()) { |
| 137 if (isRelPositioned()) |
| 138 xPos += relativePositionOffsetX(); |
| 139 RenderObject* curr = parent(); |
| 140 while (curr && curr != offsetPar) { |
| 141 // FIXME: What are we supposed to do inside SVG content? |
| 142 if (curr->isBox() && !curr->isTableRow()) |
| 143 xPos += toRenderBox(curr)->x(); |
| 144 curr = curr->parent(); |
| 145 } |
| 146 if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositi
oned() && !offsetPar->isPositioned()) |
| 147 xPos += toRenderBox(offsetPar)->x(); |
| 148 } |
| 149 return xPos; |
| 150 } |
| 151 |
| 152 int RenderBoxModelObject::offsetTop() const |
| 153 { |
| 154 RenderBoxModelObject* offsetPar = offsetParent(); |
| 155 if (!offsetPar) |
| 156 return 0; |
| 157 int yPos = (isBox() ? toRenderBox(this)->y() : 0); |
| 158 if (offsetPar->isBox()) |
| 159 yPos -= toRenderBox(offsetPar)->borderTop(); |
| 160 if (!isPositioned()) { |
| 161 if (isRelPositioned()) |
| 162 yPos += relativePositionOffsetY(); |
| 163 RenderObject* curr = parent(); |
| 164 while (curr && curr != offsetPar) { |
| 165 // FIXME: What are we supposed to do inside SVG content? |
| 166 if (curr->isBox() && !curr->isTableRow()) |
| 167 yPos += toRenderBox(curr)->y(); |
| 168 curr = curr->parent(); |
| 169 } |
| 170 if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositi
oned() && !offsetPar->isPositioned()) |
| 171 yPos += toRenderBox(offsetPar)->y(); |
| 172 } |
| 173 return yPos; |
| 174 } |
| 175 |
| 176 int RenderBoxModelObject::paddingTop(bool) const |
| 177 { |
| 178 int w = 0; |
| 179 Length padding = style()->paddingTop(); |
| 180 if (padding.isPercent()) |
| 181 w = containingBlock()->availableWidth(); |
| 182 return padding.calcMinValue(w); |
| 183 } |
| 184 |
| 185 int RenderBoxModelObject::paddingBottom(bool) const |
| 186 { |
| 187 int w = 0; |
| 188 Length padding = style()->paddingBottom(); |
| 189 if (padding.isPercent()) |
| 190 w = containingBlock()->availableWidth(); |
| 191 return padding.calcMinValue(w); |
| 192 } |
| 193 |
| 194 int RenderBoxModelObject::paddingLeft(bool) const |
| 195 { |
| 196 int w = 0; |
| 197 Length padding = style()->paddingLeft(); |
| 198 if (padding.isPercent()) |
| 199 w = containingBlock()->availableWidth(); |
| 200 return padding.calcMinValue(w); |
| 201 } |
| 202 |
| 203 int RenderBoxModelObject::paddingRight(bool) const |
| 204 { |
| 205 int w = 0; |
| 206 Length padding = style()->paddingRight(); |
| 207 if (padding.isPercent()) |
| 208 w = containingBlock()->availableWidth(); |
| 209 return padding.calcMinValue(w); |
| 210 } |
| 211 |
| 212 |
| 213 void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co
nst Color& c, const FillLayer* bgLayer, int clipY, int clipH, |
| 214 int tx, int ty, int w, int h,
InlineFlowBox* box, CompositeOperator op) |
| 215 { |
| 216 GraphicsContext* context = paintInfo.context; |
| 217 bool includeLeftEdge = box ? box->includeLeftEdge() : true; |
| 218 bool includeRightEdge = box ? box->includeRightEdge() : true; |
| 219 int bLeft = includeLeftEdge ? borderLeft() : 0; |
| 220 int bRight = includeRightEdge ? borderRight() : 0; |
| 221 int pLeft = includeLeftEdge ? paddingLeft() : 0; |
| 222 int pRight = includeRightEdge ? paddingRight() : 0; |
| 223 |
| 224 bool clippedToBorderRadius = false; |
| 225 if (style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge)) { |
| 226 context->save(); |
| 227 context->addRoundedRectClip(IntRect(tx, ty, w, h), |
| 228 includeLeftEdge ? style()->borderTopLeftRadius() : IntSize(), |
| 229 includeRightEdge ? style()->borderTopRightRadius() : IntSize(), |
| 230 includeLeftEdge ? style()->borderBottomLeftRadius() : IntSize(), |
| 231 includeRightEdge ? style()->borderBottomRightRadius() : IntSize()); |
| 232 clippedToBorderRadius = true; |
| 233 } |
| 234 |
| 235 if (bgLayer->clip() == PaddingFillBox || bgLayer->clip() == ContentFillBox)
{ |
| 236 // Clip to the padding or content boxes as necessary. |
| 237 bool includePadding = bgLayer->clip() == ContentFillBox; |
| 238 int x = tx + bLeft + (includePadding ? pLeft : 0); |
| 239 int y = ty + borderTop() + (includePadding ? paddingTop() : 0); |
| 240 int width = w - bLeft - bRight - (includePadding ? pLeft + pRight : 0); |
| 241 int height = h - borderTop() - borderBottom() - (includePadding ? paddin
gTop() + paddingBottom() : 0); |
| 242 context->save(); |
| 243 context->clip(IntRect(x, y, width, height)); |
| 244 } else if (bgLayer->clip() == TextFillBox) { |
| 245 // We have to draw our text into a mask that can then be used to clip ba
ckground drawing. |
| 246 // First figure out how big the mask has to be. It should be no bigger
than what we need |
| 247 // to actually render, so we should intersect the dirty rect with the bo
rder box of the background. |
| 248 IntRect maskRect(tx, ty, w, h); |
| 249 maskRect.intersect(paintInfo.rect); |
| 250 |
| 251 // Now create the mask. |
| 252 auto_ptr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size(), f
alse); |
| 253 if (!maskImage.get()) |
| 254 return; |
| 255 |
| 256 GraphicsContext* maskImageContext = maskImage->context(); |
| 257 maskImageContext->translate(-maskRect.x(), -maskRect.y()); |
| 258 |
| 259 // Now add the text to the clip. We do this by painting using a special
paint phase that signals to |
| 260 // InlineTextBoxes that they should just add their contents to the clip. |
| 261 PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, true, 0,
0); |
| 262 if (box) |
| 263 box->paint(info, tx - box->xPos(), ty - box->yPos()); |
| 264 else |
| 265 paint(info, tx, ty); |
| 266 |
| 267 // The mask has been created. Now we just need to clip to it. |
| 268 context->save(); |
| 269 context->clipToImageBuffer(maskRect, maskImage.get()); |
| 270 } |
| 271 |
| 272 StyleImage* bg = bgLayer->image(); |
| 273 bool shouldPaintBackgroundImage = bg && bg->canRender(style()->effectiveZoom
()); |
| 274 Color bgColor = c; |
| 275 |
| 276 // When this style flag is set, change existing background colors and images
to a solid white background. |
| 277 // If there's no bg color or image, leave it untouched to avoid affecting tr
ansparency. |
| 278 // We don't try to avoid loading the background images, because this style f
lag is only set |
| 279 // when printing, and at that point we've already loaded the background imag
es anyway. (To avoid |
| 280 // loading the background images we'd have to do this check when applying st
yles rather than |
| 281 // while rendering.) |
| 282 if (style()->forceBackgroundsToWhite()) { |
| 283 // Note that we can't reuse this variable below because the bgColor migh
t be changed |
| 284 bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid()
&& bgColor.alpha() > 0; |
| 285 if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) { |
| 286 bgColor = Color::white; |
| 287 shouldPaintBackgroundImage = false; |
| 288 } |
| 289 } |
| 290 |
| 291 // Only fill with a base color (e.g., white) if we're the root document, sin
ce iframes/frames with |
| 292 // no background in the child document should show the parent's background. |
| 293 bool isTransparent = false; |
| 294 if (!bgLayer->next() && isRoot() && !(bgColor.isValid() && bgColor.alpha() >
0) && view()->frameView()) { |
| 295 Node* elt = document()->ownerElement(); |
| 296 if (elt) { |
| 297 if (!elt->hasTagName(frameTag)) { |
| 298 // Locate the <body> element using the DOM. This is easier than
trying |
| 299 // to crawl around a render tree with potential :before/:after c
ontent and |
| 300 // anonymous blocks created by inline <body> tags etc. We can l
ocate the <body> |
| 301 // render object very easily via the DOM. |
| 302 HTMLElement* body = document()->body(); |
| 303 isTransparent = !body || !body->hasLocalName(framesetTag); // Ca
n't scroll a frameset document anyway. |
| 304 } |
| 305 } else |
| 306 isTransparent = view()->frameView()->isTransparent(); |
| 307 |
| 308 // FIXME: This needs to be dynamic. We should be able to go back to bli
tting if we ever stop being transparent. |
| 309 if (isTransparent) |
| 310 view()->frameView()->setUseSlowRepaints(); // The parent must show b
ehind the child. |
| 311 } |
| 312 |
| 313 // Paint the color first underneath all images. |
| 314 if (!bgLayer->next()) { |
| 315 IntRect rect(tx, clipY, w, clipH); |
| 316 // If we have an alpha and we are painting the root element, go ahead an
d blend with the base background color. |
| 317 if (isRoot() && (!bgColor.isValid() || bgColor.alpha() < 0xFF) && !isTra
nsparent) { |
| 318 Color baseColor = view()->frameView()->baseBackgroundColor(); |
| 319 if (baseColor.alpha() > 0) { |
| 320 context->save(); |
| 321 context->setCompositeOperation(CompositeCopy); |
| 322 context->fillRect(rect, baseColor); |
| 323 context->restore(); |
| 324 } else |
| 325 context->clearRect(rect); |
| 326 } |
| 327 |
| 328 if (bgColor.isValid() && bgColor.alpha() > 0) |
| 329 context->fillRect(rect, bgColor); |
| 330 } |
| 331 |
| 332 // no progressive loading of the background image |
| 333 if (shouldPaintBackgroundImage) { |
| 334 IntRect destRect; |
| 335 IntPoint phase; |
| 336 IntSize tileSize; |
| 337 |
| 338 calculateBackgroundImageGeometry(bgLayer, tx, ty, w, h, destRect, phase,
tileSize); |
| 339 if (!destRect.isEmpty()) { |
| 340 CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer-
>composite() : op; |
| 341 context->drawTiledImage(bg->image(this, tileSize), destRect, phase,
tileSize, compositeOp); |
| 342 } |
| 343 } |
| 344 |
| 345 if (bgLayer->clip() != BorderFillBox) |
| 346 // Undo the background clip |
| 347 context->restore(); |
| 348 |
| 349 if (clippedToBorderRadius) |
| 350 // Undo the border radius clip |
| 351 context->restore(); |
| 352 } |
| 353 |
| 354 IntSize RenderBoxModelObject::calculateBackgroundSize(const FillLayer* bgLayer,
int scaledWidth, int scaledHeight) const |
| 355 { |
| 356 StyleImage* bg = bgLayer->image(); |
| 357 bg->setImageContainerSize(IntSize(scaledWidth, scaledHeight)); // Use the bo
x established by background-origin. |
| 358 |
| 359 if (bgLayer->isSizeSet()) { |
| 360 int w = scaledWidth; |
| 361 int h = scaledHeight; |
| 362 Length bgWidth = bgLayer->size().width(); |
| 363 Length bgHeight = bgLayer->size().height(); |
| 364 |
| 365 if (bgWidth.isFixed()) |
| 366 w = bgWidth.value(); |
| 367 else if (bgWidth.isPercent()) |
| 368 w = bgWidth.calcValue(scaledWidth); |
| 369 |
| 370 if (bgHeight.isFixed()) |
| 371 h = bgHeight.value(); |
| 372 else if (bgHeight.isPercent()) |
| 373 h = bgHeight.calcValue(scaledHeight); |
| 374 |
| 375 // If one of the values is auto we have to use the appropriate |
| 376 // scale to maintain our aspect ratio. |
| 377 if (bgWidth.isAuto() && !bgHeight.isAuto()) |
| 378 w = bg->imageSize(this, style()->effectiveZoom()).width() * h / bg->
imageSize(this, style()->effectiveZoom()).height(); |
| 379 else if (!bgWidth.isAuto() && bgHeight.isAuto()) |
| 380 h = bg->imageSize(this, style()->effectiveZoom()).height() * w / bg-
>imageSize(this, style()->effectiveZoom()).width(); |
| 381 else if (bgWidth.isAuto() && bgHeight.isAuto()) { |
| 382 // If both width and height are auto, we just want to use the image'
s |
| 383 // intrinsic size. |
| 384 w = bg->imageSize(this, style()->effectiveZoom()).width(); |
| 385 h = bg->imageSize(this, style()->effectiveZoom()).height(); |
| 386 } |
| 387 |
| 388 return IntSize(max(1, w), max(1, h)); |
| 389 } else |
| 390 return bg->imageSize(this, style()->effectiveZoom()); |
| 391 } |
| 392 |
| 393 void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* bgL
ayer, int tx, int ty, int w, int h, |
| 394 IntRect& destRect, I
ntPoint& phase, IntSize& tileSize) |
| 395 { |
| 396 int pw; |
| 397 int ph; |
| 398 int left = 0; |
| 399 int right = 0; |
| 400 int top = 0; |
| 401 int bottom = 0; |
| 402 int cx; |
| 403 int cy; |
| 404 int rw = 0; |
| 405 int rh = 0; |
| 406 |
| 407 // CSS2 chapter 14.2.1 |
| 408 |
| 409 if (bgLayer->attachment()) { |
| 410 // Scroll |
| 411 if (bgLayer->origin() != BorderFillBox) { |
| 412 left = borderLeft(); |
| 413 right = borderRight(); |
| 414 top = borderTop(); |
| 415 bottom = borderBottom(); |
| 416 if (bgLayer->origin() == ContentFillBox) { |
| 417 left += paddingLeft(); |
| 418 right += paddingRight(); |
| 419 top += paddingTop(); |
| 420 bottom += paddingBottom(); |
| 421 } |
| 422 } |
| 423 |
| 424 // The background of the box generated by the root element covers the en
tire canvas including |
| 425 // its margins. Since those were added in already, we have to factor th
em out when computing the |
| 426 // box used by background-origin/size/position. |
| 427 if (isRoot()) { |
| 428 rw = toRenderBox(this)->width() - left - right; |
| 429 rh = toRenderBox(this)->height() - top - bottom; |
| 430 left += marginLeft(); |
| 431 right += marginRight(); |
| 432 top += marginTop(); |
| 433 bottom += marginBottom(); |
| 434 } |
| 435 cx = tx; |
| 436 cy = ty; |
| 437 pw = w - left - right; |
| 438 ph = h - top - bottom; |
| 439 } else { |
| 440 // Fixed |
| 441 IntRect vr = viewRect(); |
| 442 cx = vr.x(); |
| 443 cy = vr.y(); |
| 444 pw = vr.width(); |
| 445 ph = vr.height(); |
| 446 } |
| 447 |
| 448 int sx = 0; |
| 449 int sy = 0; |
| 450 int cw; |
| 451 int ch; |
| 452 |
| 453 IntSize scaledImageSize; |
| 454 if (isRoot() && bgLayer->attachment()) |
| 455 scaledImageSize = calculateBackgroundSize(bgLayer, rw, rh); |
| 456 else |
| 457 scaledImageSize = calculateBackgroundSize(bgLayer, pw, ph); |
| 458 |
| 459 int scaledImageWidth = scaledImageSize.width(); |
| 460 int scaledImageHeight = scaledImageSize.height(); |
| 461 |
| 462 EFillRepeat backgroundRepeat = bgLayer->repeat(); |
| 463 |
| 464 int xPosition; |
| 465 if (isRoot() && bgLayer->attachment()) |
| 466 xPosition = bgLayer->xPosition().calcMinValue(rw - scaledImageWidth, tru
e); |
| 467 else |
| 468 xPosition = bgLayer->xPosition().calcMinValue(pw - scaledImageWidth, tru
e); |
| 469 if (backgroundRepeat == RepeatFill || backgroundRepeat == RepeatXFill) { |
| 470 cw = pw + left + right; |
| 471 sx = scaledImageWidth ? scaledImageWidth - (xPosition + left) % scaledIm
ageWidth : 0; |
| 472 } else { |
| 473 cx += max(xPosition + left, 0); |
| 474 sx = -min(xPosition + left, 0); |
| 475 cw = scaledImageWidth + min(xPosition + left, 0); |
| 476 } |
| 477 |
| 478 int yPosition; |
| 479 if (isRoot() && bgLayer->attachment()) |
| 480 yPosition = bgLayer->yPosition().calcMinValue(rh - scaledImageHeight, tr
ue); |
| 481 else |
| 482 yPosition = bgLayer->yPosition().calcMinValue(ph - scaledImageHeight, tr
ue); |
| 483 if (backgroundRepeat == RepeatFill || backgroundRepeat == RepeatYFill) { |
| 484 ch = ph + top + bottom; |
| 485 sy = scaledImageHeight ? scaledImageHeight - (yPosition + top) % scaledI
mageHeight : 0; |
| 486 } else { |
| 487 cy += max(yPosition + top, 0); |
| 488 sy = -min(yPosition + top, 0); |
| 489 ch = scaledImageHeight + min(yPosition + top, 0); |
| 490 } |
| 491 |
| 492 if (!bgLayer->attachment()) { |
| 493 sx += max(tx - cx, 0); |
| 494 sy += max(ty - cy, 0); |
| 495 } |
| 496 |
| 497 destRect = IntRect(cx, cy, cw, ch); |
| 498 destRect.intersect(IntRect(tx, ty, w, h)); |
| 499 phase = IntPoint(sx, sy); |
| 500 tileSize = IntSize(scaledImageWidth, scaledImageHeight); |
| 501 } |
| 502 |
120 } // namespace WebCore | 503 } // namespace WebCore |
OLD | NEW |