RenderBoxModelObject.cpp revision cad810f21b803229eb11403f9209855525a25d57
1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) 5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com) 6 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 7 * Copyright (C) 2010 Google Inc. All rights reserved. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Library General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Library General Public License for more details. 18 * 19 * You should have received a copy of the GNU Library General Public License 20 * along with this library; see the file COPYING.LIB. If not, write to 21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110-1301, USA. 23 * 24 */ 25 26#include "config.h" 27#include "RenderBoxModelObject.h" 28 29#include "GraphicsContext.h" 30#include "HTMLFrameOwnerElement.h" 31#include "HTMLNames.h" 32#include "ImageBuffer.h" 33#include "Path.h" 34#include "RenderBlock.h" 35#include "RenderInline.h" 36#include "RenderLayer.h" 37#include "RenderView.h" 38#include <wtf/CurrentTime.h> 39 40using namespace std; 41 42namespace WebCore { 43 44using namespace HTMLNames; 45 46bool RenderBoxModelObject::s_wasFloating = false; 47bool RenderBoxModelObject::s_hadLayer = false; 48bool RenderBoxModelObject::s_layerWasSelfPainting = false; 49 50static const double cInterpolationCutoff = 800. * 800.; 51static const double cLowQualityTimeThreshold = 0.500; // 500 ms 52 53typedef pair<RenderBoxModelObject*, const void*> LastPaintSizeMapKey; 54typedef HashMap<LastPaintSizeMapKey, IntSize> LastPaintSizeMap; 55 56// The HashMap for storing continuation pointers. 57// An inline can be split with blocks occuring in between the inline content. 58// When this occurs we need a pointer to the next object. We can basically be 59// split into a sequence of inlines and blocks. The continuation will either be 60// an anonymous block (that houses other blocks) or it will be an inline flow. 61// <b><i><p>Hello</p></i></b>. In this example the <i> will have a block as 62// its continuation but the <b> will just have an inline as its continuation. 63typedef HashMap<const RenderBoxModelObject*, RenderBoxModelObject*> ContinuationMap; 64static ContinuationMap* continuationMap = 0; 65 66class ImageQualityController : public Noncopyable { 67public: 68 ImageQualityController(); 69 bool shouldPaintAtLowQuality(GraphicsContext*, RenderBoxModelObject*, Image*, const void* layer, const IntSize&); 70 void keyDestroyed(LastPaintSizeMapKey key); 71 void objectDestroyed(RenderBoxModelObject*); 72 73private: 74 void highQualityRepaintTimerFired(Timer<ImageQualityController>*); 75 void restartTimer(); 76 77 LastPaintSizeMap m_lastPaintSizeMap; 78 Timer<ImageQualityController> m_timer; 79 bool m_animatedResizeIsActive; 80}; 81 82ImageQualityController::ImageQualityController() 83 : m_timer(this, &ImageQualityController::highQualityRepaintTimerFired) 84 , m_animatedResizeIsActive(false) 85{ 86} 87 88void ImageQualityController::keyDestroyed(LastPaintSizeMapKey key) 89{ 90 m_lastPaintSizeMap.remove(key); 91 if (m_lastPaintSizeMap.isEmpty()) { 92 m_animatedResizeIsActive = false; 93 m_timer.stop(); 94 } 95} 96 97void ImageQualityController::objectDestroyed(RenderBoxModelObject* object) 98{ 99 Vector<LastPaintSizeMapKey> keysToDie; 100 for (LastPaintSizeMap::iterator it = m_lastPaintSizeMap.begin(); it != m_lastPaintSizeMap.end(); ++it) 101 if (it->first.first == object) 102 keysToDie.append(it->first); 103 for (Vector<LastPaintSizeMapKey>::iterator it = keysToDie.begin(); it != keysToDie.end(); ++it) 104 keyDestroyed(*it); 105} 106 107void ImageQualityController::highQualityRepaintTimerFired(Timer<ImageQualityController>*) 108{ 109 if (m_animatedResizeIsActive) { 110 m_animatedResizeIsActive = false; 111 for (LastPaintSizeMap::iterator it = m_lastPaintSizeMap.begin(); it != m_lastPaintSizeMap.end(); ++it) 112 it->first.first->repaint(); 113 } 114} 115 116void ImageQualityController::restartTimer() 117{ 118 m_timer.startOneShot(cLowQualityTimeThreshold); 119} 120 121bool ImageQualityController::shouldPaintAtLowQuality(GraphicsContext* context, RenderBoxModelObject* object, Image* image, const void *layer, const IntSize& size) 122{ 123 // If the image is not a bitmap image, then none of this is relevant and we just paint at high 124 // quality. 125 if (!image || !image->isBitmapImage() || context->paintingDisabled()) 126 return false; 127 128 // Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image 129 // is actually being scaled. 130 IntSize imageSize(image->width(), image->height()); 131 132 // Look ourselves up in the hashtable. 133 LastPaintSizeMapKey key(object, layer); 134 LastPaintSizeMap::iterator i = m_lastPaintSizeMap.find(key); 135 136 const AffineTransform& currentTransform = context->getCTM(); 137 bool contextIsScaled = !currentTransform.isIdentityOrTranslationOrFlipped(); 138 if (!contextIsScaled && imageSize == size) { 139 // There is no scale in effect. If we had a scale in effect before, we can just remove this object from the list. 140 if (i != m_lastPaintSizeMap.end()) 141 m_lastPaintSizeMap.remove(key); 142 143 return false; 144 } 145 146 // There is no need to hash scaled images that always use low quality mode when the page demands it. This is the iChat case. 147 if (object->document()->page()->inLowQualityImageInterpolationMode()) { 148 double totalPixels = static_cast<double>(image->width()) * static_cast<double>(image->height()); 149 if (totalPixels > cInterpolationCutoff) 150 return true; 151 } 152 // If an animated resize is active, paint in low quality and kick the timer ahead. 153 if (m_animatedResizeIsActive) { 154 m_lastPaintSizeMap.set(key, size); 155 restartTimer(); 156 return true; 157 } 158 // If this is the first time resizing this image, or its size is the 159 // same as the last resize, draw at high res, but record the paint 160 // size and set the timer. 161 if (i == m_lastPaintSizeMap.end() || size == i->second) { 162 restartTimer(); 163 m_lastPaintSizeMap.set(key, size); 164 return false; 165 } 166 // If the timer is no longer active, draw at high quality and don't 167 // set the timer. 168 if (!m_timer.isActive()) { 169 keyDestroyed(key); 170 return false; 171 } 172 // This object has been resized to two different sizes while the timer 173 // is active, so draw at low quality, set the flag for animated resizes and 174 // the object to the list for high quality redraw. 175 m_lastPaintSizeMap.set(key, size); 176 m_animatedResizeIsActive = true; 177 restartTimer(); 178 return true; 179} 180 181static ImageQualityController* imageQualityController() 182{ 183 static ImageQualityController* controller = new ImageQualityController; 184 return controller; 185} 186 187void RenderBoxModelObject::setSelectionState(SelectionState s) 188{ 189 if (selectionState() == s) 190 return; 191 192 if (s == SelectionInside && selectionState() != SelectionNone) 193 return; 194 195 if ((s == SelectionStart && selectionState() == SelectionEnd) 196 || (s == SelectionEnd && selectionState() == SelectionStart)) 197 RenderObject::setSelectionState(SelectionBoth); 198 else 199 RenderObject::setSelectionState(s); 200 201 // FIXME: 202 // We should consider whether it is OK propagating to ancestor RenderInlines. 203 // This is a workaround for http://webkit.org/b/32123 204 RenderBlock* cb = containingBlock(); 205 if (cb && !cb->isRenderView()) 206 cb->setSelectionState(s); 207} 208 209bool RenderBoxModelObject::shouldPaintAtLowQuality(GraphicsContext* context, Image* image, const void* layer, const IntSize& size) 210{ 211 return imageQualityController()->shouldPaintAtLowQuality(context, this, image, layer, size); 212} 213 214RenderBoxModelObject::RenderBoxModelObject(Node* node) 215 : RenderObject(node) 216 , m_layer(0) 217{ 218} 219 220RenderBoxModelObject::~RenderBoxModelObject() 221{ 222 // Our layer should have been destroyed and cleared by now 223 ASSERT(!hasLayer()); 224 ASSERT(!m_layer); 225 imageQualityController()->objectDestroyed(this); 226} 227 228void RenderBoxModelObject::destroyLayer() 229{ 230 ASSERT(!hasLayer()); // Callers should have already called setHasLayer(false) 231 ASSERT(m_layer); 232 m_layer->destroy(renderArena()); 233 m_layer = 0; 234} 235 236void RenderBoxModelObject::destroy() 237{ 238 // This must be done before we destroy the RenderObject. 239 if (m_layer) 240 m_layer->clearClipRects(); 241 242 // A continuation of this RenderObject should be destroyed at subclasses. 243 ASSERT(!continuation()); 244 245 // RenderObject::destroy calls back to destroyLayer() for layer destruction 246 RenderObject::destroy(); 247} 248 249bool RenderBoxModelObject::hasSelfPaintingLayer() const 250{ 251 return m_layer && m_layer->isSelfPaintingLayer(); 252} 253 254void RenderBoxModelObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) 255{ 256 s_wasFloating = isFloating(); 257 s_hadLayer = hasLayer(); 258 if (s_hadLayer) 259 s_layerWasSelfPainting = layer()->isSelfPaintingLayer(); 260 261 // If our z-index changes value or our visibility changes, 262 // we need to dirty our stacking context's z-order list. 263 if (style() && newStyle) { 264 if (parent()) { 265 // Do a repaint with the old style first, e.g., for example if we go from 266 // having an outline to not having an outline. 267 if (diff == StyleDifferenceRepaintLayer) { 268 layer()->repaintIncludingDescendants(); 269 if (!(style()->clip() == newStyle->clip())) 270 layer()->clearClipRectsIncludingDescendants(); 271 } else if (diff == StyleDifferenceRepaint || newStyle->outlineSize() < style()->outlineSize()) 272 repaint(); 273 } 274 275 if (diff == StyleDifferenceLayout) { 276 // When a layout hint happens, we go ahead and do a repaint of the layer, since the layer could 277 // end up being destroyed. 278 if (hasLayer()) { 279 if (style()->position() != newStyle->position() || 280 style()->zIndex() != newStyle->zIndex() || 281 style()->hasAutoZIndex() != newStyle->hasAutoZIndex() || 282 !(style()->clip() == newStyle->clip()) || 283 style()->hasClip() != newStyle->hasClip() || 284 style()->opacity() != newStyle->opacity() || 285 style()->transform() != newStyle->transform()) 286 layer()->repaintIncludingDescendants(); 287 } else if (newStyle->hasTransform() || newStyle->opacity() < 1) { 288 // If we don't have a layer yet, but we are going to get one because of transform or opacity, 289 // then we need to repaint the old position of the object. 290 repaint(); 291 } 292 } 293 294 if (hasLayer() && (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() || 295 style()->zIndex() != newStyle->zIndex() || 296 style()->visibility() != newStyle->visibility())) { 297 layer()->dirtyStackingContextZOrderLists(); 298 if (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() || style()->visibility() != newStyle->visibility()) 299 layer()->dirtyZOrderLists(); 300 } 301 } 302 303 RenderObject::styleWillChange(diff, newStyle); 304} 305 306void RenderBoxModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 307{ 308 RenderObject::styleDidChange(diff, oldStyle); 309 updateBoxModelInfoFromStyle(); 310 311 if (requiresLayer()) { 312 if (!layer()) { 313 if (s_wasFloating && isFloating()) 314 setChildNeedsLayout(true); 315 m_layer = new (renderArena()) RenderLayer(this); 316 setHasLayer(true); 317 m_layer->insertOnlyThisLayer(); 318 if (parent() && !needsLayout() && containingBlock()) 319 m_layer->updateLayerPositions(); 320 } 321 } else if (layer() && layer()->parent()) { 322 setHasTransform(false); // Either a transform wasn't specified or the object doesn't support transforms, so just null out the bit. 323 setHasReflection(false); 324 m_layer->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer 325 if (s_wasFloating && isFloating()) 326 setChildNeedsLayout(true); 327 } 328 329 if (layer()) { 330 layer()->styleChanged(diff, oldStyle); 331 if (s_hadLayer && layer()->isSelfPaintingLayer() != s_layerWasSelfPainting) 332 setChildNeedsLayout(true); 333 } 334} 335 336void RenderBoxModelObject::updateBoxModelInfoFromStyle() 337{ 338 // Set the appropriate bits for a box model object. Since all bits are cleared in styleWillChange, 339 // we only check for bits that could possibly be set to true. 340 setHasBoxDecorations(hasBackground() || style()->hasBorder() || style()->hasAppearance() || style()->boxShadow()); 341 setInline(style()->isDisplayInlineType()); 342 setRelPositioned(style()->position() == RelativePosition); 343} 344 345int RenderBoxModelObject::relativePositionOffsetX() const 346{ 347 // Objects that shrink to avoid floats normally use available line width when computing containing block width. However 348 // in the case of relative positioning using percentages, we can't do this. The offset should always be resolved using the 349 // available width of the containing block. Therefore we don't use containingBlockLogicalWidthForContent() here, but instead explicitly 350 // call availableWidth on our containing block. 351 if (!style()->left().isAuto()) { 352 RenderBlock* cb = containingBlock(); 353 if (!style()->right().isAuto() && !containingBlock()->style()->isLeftToRightDirection()) 354 return -style()->right().calcValue(cb->availableWidth()); 355 return style()->left().calcValue(cb->availableWidth()); 356 } 357 if (!style()->right().isAuto()) { 358 RenderBlock* cb = containingBlock(); 359 return -style()->right().calcValue(cb->availableWidth()); 360 } 361 return 0; 362} 363 364int RenderBoxModelObject::relativePositionOffsetY() const 365{ 366 RenderBlock* containingBlock = this->containingBlock(); 367 368 // If the containing block of a relatively positioned element does not 369 // specify a height, a percentage top or bottom offset should be resolved as 370 // auto. An exception to this is if the containing block has the WinIE quirk 371 // where <html> and <body> assume the size of the viewport. In this case, 372 // calculate the percent offset based on this height. 373 // See <https://bugs.webkit.org/show_bug.cgi?id=26396>. 374 if (!style()->top().isAuto() 375 && (!containingBlock->style()->height().isAuto() 376 || !style()->top().isPercent() 377 || containingBlock->stretchesToViewport())) 378 return style()->top().calcValue(containingBlock->availableHeight()); 379 380 if (!style()->bottom().isAuto() 381 && (!containingBlock->style()->height().isAuto() 382 || !style()->bottom().isPercent() 383 || containingBlock->stretchesToViewport())) 384 return -style()->bottom().calcValue(containingBlock->availableHeight()); 385 386 return 0; 387} 388 389int RenderBoxModelObject::offsetLeft() const 390{ 391 // If the element is the HTML body element or does not have an associated box 392 // return 0 and stop this algorithm. 393 if (isBody()) 394 return 0; 395 396 RenderBoxModelObject* offsetPar = offsetParent(); 397 int xPos = (isBox() ? toRenderBox(this)->x() : 0); 398 399 // If the offsetParent of the element is null, or is the HTML body element, 400 // return the distance between the canvas origin and the left border edge 401 // of the element and stop this algorithm. 402 if (offsetPar) { 403 if (offsetPar->isBox() && !offsetPar->isBody()) 404 xPos -= toRenderBox(offsetPar)->borderLeft(); 405 if (!isPositioned()) { 406 if (isRelPositioned()) 407 xPos += relativePositionOffsetX(); 408 RenderObject* curr = parent(); 409 while (curr && curr != offsetPar) { 410 // FIXME: What are we supposed to do inside SVG content? 411 if (curr->isBox() && !curr->isTableRow()) 412 xPos += toRenderBox(curr)->x(); 413 curr = curr->parent(); 414 } 415 if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned()) 416 xPos += toRenderBox(offsetPar)->x(); 417 } 418 } 419 420 return xPos; 421} 422 423int RenderBoxModelObject::offsetTop() const 424{ 425 // If the element is the HTML body element or does not have an associated box 426 // return 0 and stop this algorithm. 427 if (isBody()) 428 return 0; 429 430 RenderBoxModelObject* offsetPar = offsetParent(); 431 int yPos = (isBox() ? toRenderBox(this)->y() : 0); 432 433 // If the offsetParent of the element is null, or is the HTML body element, 434 // return the distance between the canvas origin and the top border edge 435 // of the element and stop this algorithm. 436 if (offsetPar) { 437 if (offsetPar->isBox() && !offsetPar->isBody()) 438 yPos -= toRenderBox(offsetPar)->borderTop(); 439 if (!isPositioned()) { 440 if (isRelPositioned()) 441 yPos += relativePositionOffsetY(); 442 RenderObject* curr = parent(); 443 while (curr && curr != offsetPar) { 444 // FIXME: What are we supposed to do inside SVG content? 445 if (curr->isBox() && !curr->isTableRow()) 446 yPos += toRenderBox(curr)->y(); 447 curr = curr->parent(); 448 } 449 if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned()) 450 yPos += toRenderBox(offsetPar)->y(); 451 } 452 } 453 return yPos; 454} 455 456int RenderBoxModelObject::paddingTop(bool) const 457{ 458 int w = 0; 459 Length padding = style()->paddingTop(); 460 if (padding.isPercent()) 461 w = containingBlock()->availableLogicalWidth(); 462 return padding.calcMinValue(w); 463} 464 465int RenderBoxModelObject::paddingBottom(bool) const 466{ 467 int w = 0; 468 Length padding = style()->paddingBottom(); 469 if (padding.isPercent()) 470 w = containingBlock()->availableLogicalWidth(); 471 return padding.calcMinValue(w); 472} 473 474int RenderBoxModelObject::paddingLeft(bool) const 475{ 476 int w = 0; 477 Length padding = style()->paddingLeft(); 478 if (padding.isPercent()) 479 w = containingBlock()->availableLogicalWidth(); 480 return padding.calcMinValue(w); 481} 482 483int RenderBoxModelObject::paddingRight(bool) const 484{ 485 int w = 0; 486 Length padding = style()->paddingRight(); 487 if (padding.isPercent()) 488 w = containingBlock()->availableLogicalWidth(); 489 return padding.calcMinValue(w); 490} 491 492int RenderBoxModelObject::paddingBefore(bool) const 493{ 494 int w = 0; 495 Length padding = style()->paddingBefore(); 496 if (padding.isPercent()) 497 w = containingBlock()->availableLogicalWidth(); 498 return padding.calcMinValue(w); 499} 500 501int RenderBoxModelObject::paddingAfter(bool) const 502{ 503 int w = 0; 504 Length padding = style()->paddingAfter(); 505 if (padding.isPercent()) 506 w = containingBlock()->availableLogicalWidth(); 507 return padding.calcMinValue(w); 508} 509 510int RenderBoxModelObject::paddingStart(bool) const 511{ 512 int w = 0; 513 Length padding = style()->paddingStart(); 514 if (padding.isPercent()) 515 w = containingBlock()->availableLogicalWidth(); 516 return padding.calcMinValue(w); 517} 518 519int RenderBoxModelObject::paddingEnd(bool) const 520{ 521 int w = 0; 522 Length padding = style()->paddingEnd(); 523 if (padding.isPercent()) 524 w = containingBlock()->availableLogicalWidth(); 525 return padding.calcMinValue(w); 526} 527 528void 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) 529{ 530 GraphicsContext* context = paintInfo.context; 531 if (context->paintingDisabled()) 532 return; 533 534 bool includeLeftEdge = box ? box->includeLogicalLeftEdge() : true; 535 bool includeRightEdge = box ? box->includeLogicalRightEdge() : true; 536 int bLeft = includeLeftEdge ? borderLeft() : 0; 537 int bRight = includeRightEdge ? borderRight() : 0; 538 int pLeft = includeLeftEdge ? paddingLeft() : 0; 539 int pRight = includeRightEdge ? paddingRight() : 0; 540 541 bool clippedToBorderRadius = false; 542 if (style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge)) { 543 IntRect borderRect(tx, ty, w, h); 544 545 if (borderRect.isEmpty()) 546 return; 547 548 context->save(); 549 550 IntSize topLeft, topRight, bottomLeft, bottomRight; 551 style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight); 552 553 if (!includeLeftEdge) { 554 topLeft = IntSize(); 555 if (box->isHorizontal()) 556 bottomLeft = IntSize(); 557 else 558 topRight = IntSize(); 559 } 560 561 if (!includeRightEdge) { 562 if (box->isHorizontal()) 563 topRight = IntSize(); 564 else 565 bottomLeft = IntSize(); 566 bottomRight = IntSize(); 567 } 568 569 context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); 570 clippedToBorderRadius = true; 571 } 572 573 bool clippedWithLocalScrolling = hasOverflowClip() && bgLayer->attachment() == LocalBackgroundAttachment; 574 if (clippedWithLocalScrolling) { 575 // Clip to the overflow area. 576 context->save(); 577 context->clip(toRenderBox(this)->overflowClipRect(tx, ty)); 578 579 // Now adjust our tx, ty, w, h to reflect a scrolled content box with borders at the ends. 580 IntSize offset = layer()->scrolledContentOffset(); 581 tx -= offset.width(); 582 ty -= offset.height(); 583 w = bLeft + layer()->scrollWidth() + bRight; 584 h = borderTop() + layer()->scrollHeight() + borderBottom(); 585 } 586 587 if (bgLayer->clip() == PaddingFillBox || bgLayer->clip() == ContentFillBox) { 588 // Clip to the padding or content boxes as necessary. 589 bool includePadding = bgLayer->clip() == ContentFillBox; 590 int x = tx + bLeft + (includePadding ? pLeft : 0); 591 int y = ty + borderTop() + (includePadding ? paddingTop() : 0); 592 int width = w - bLeft - bRight - (includePadding ? pLeft + pRight : 0); 593 int height = h - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : 0); 594 context->save(); 595 context->clip(IntRect(x, y, width, height)); 596 } else if (bgLayer->clip() == TextFillBox) { 597 // We have to draw our text into a mask that can then be used to clip background drawing. 598 // First figure out how big the mask has to be. It should be no bigger than what we need 599 // to actually render, so we should intersect the dirty rect with the border box of the background. 600 IntRect maskRect(tx, ty, w, h); 601 maskRect.intersect(paintInfo.rect); 602 603 // Now create the mask. 604 OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size()); 605 if (!maskImage) 606 return; 607 608 GraphicsContext* maskImageContext = maskImage->context(); 609 maskImageContext->translate(-maskRect.x(), -maskRect.y()); 610 611 // Now add the text to the clip. We do this by painting using a special paint phase that signals to 612 // InlineTextBoxes that they should just add their contents to the clip. 613 PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, true, 0, 0); 614 if (box) 615 box->paint(info, tx - box->x(), ty - box->y()); 616 else { 617 int x = isBox() ? toRenderBox(this)->x() : 0; 618 int y = isBox() ? toRenderBox(this)->y() : 0; 619 paint(info, tx - x, ty - y); 620 } 621 622 // The mask has been created. Now we just need to clip to it. 623 context->save(); 624 context->clipToImageBuffer(maskImage.get(), maskRect); 625 } 626 627 StyleImage* bg = bgLayer->image(); 628 bool shouldPaintBackgroundImage = bg && bg->canRender(style()->effectiveZoom()); 629 Color bgColor = c; 630 631 // When this style flag is set, change existing background colors and images to a solid white background. 632 // If there's no bg color or image, leave it untouched to avoid affecting transparency. 633 // We don't try to avoid loading the background images, because this style flag is only set 634 // when printing, and at that point we've already loaded the background images anyway. (To avoid 635 // loading the background images we'd have to do this check when applying styles rather than 636 // while rendering.) 637 if (style()->forceBackgroundsToWhite()) { 638 // Note that we can't reuse this variable below because the bgColor might be changed 639 bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && bgColor.alpha() > 0; 640 if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) { 641 bgColor = Color::white; 642 shouldPaintBackgroundImage = false; 643 } 644 } 645 646 bool isRoot = this->isRoot(); 647 648 // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with 649 // no background in the child document should show the parent's background. 650 bool isOpaqueRoot = false; 651 if (isRoot) { 652 isOpaqueRoot = true; 653 if (!bgLayer->next() && !(bgColor.isValid() && bgColor.alpha() == 255) && view()->frameView()) { 654 Element* ownerElement = document()->ownerElement(); 655 if (ownerElement) { 656 if (!ownerElement->hasTagName(frameTag)) { 657 // Locate the <body> element using the DOM. This is easier than trying 658 // to crawl around a render tree with potential :before/:after content and 659 // anonymous blocks created by inline <body> tags etc. We can locate the <body> 660 // render object very easily via the DOM. 661 HTMLElement* body = document()->body(); 662 if (body) { 663 // Can't scroll a frameset document anyway. 664 isOpaqueRoot = body->hasLocalName(framesetTag); 665 } 666#if ENABLE(SVG) 667 else { 668 // SVG documents and XML documents with SVG root nodes are transparent. 669 isOpaqueRoot = !document()->hasSVGRootNode(); 670 } 671#endif 672 } 673 } else 674 isOpaqueRoot = !view()->frameView()->isTransparent(); 675 } 676 view()->frameView()->setContentIsOpaque(isOpaqueRoot); 677 } 678 679 // Paint the color first underneath all images. 680 if (!bgLayer->next()) { 681 IntRect rect(tx, ty, w, h); 682 rect.intersect(paintInfo.rect); 683 // If we have an alpha and we are painting the root element, go ahead and blend with the base background color. 684 if (isOpaqueRoot) { 685 Color baseColor = view()->frameView()->baseBackgroundColor(); 686 if (baseColor.alpha() > 0) { 687 CompositeOperator previousOperator = context->compositeOperation(); 688 context->setCompositeOperation(CompositeCopy); 689 context->fillRect(rect, baseColor, style()->colorSpace()); 690 context->setCompositeOperation(previousOperator); 691 } else 692 context->clearRect(rect); 693 } 694 695 if (bgColor.isValid() && bgColor.alpha() > 0) 696 context->fillRect(rect, bgColor, style()->colorSpace()); 697 } 698 699 // no progressive loading of the background image 700 if (shouldPaintBackgroundImage) { 701 IntRect destRect; 702 IntPoint phase; 703 IntSize tileSize; 704 705 calculateBackgroundImageGeometry(bgLayer, tx, ty, w, h, destRect, phase, tileSize); 706 IntPoint destOrigin = destRect.location(); 707 destRect.intersect(paintInfo.rect); 708 if (!destRect.isEmpty()) { 709 phase += destRect.location() - destOrigin; 710 CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op; 711 RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this; 712 Image* image = bg->image(clientForBackgroundImage, tileSize); 713 bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, bgLayer, tileSize); 714 context->drawTiledImage(image, style()->colorSpace(), destRect, phase, tileSize, compositeOp, useLowQualityScaling); 715 } 716 } 717 718 if (bgLayer->clip() != BorderFillBox) 719 // Undo the background clip 720 context->restore(); 721 722 if (clippedToBorderRadius) 723 // Undo the border radius clip 724 context->restore(); 725 726 if (clippedWithLocalScrolling) // Undo the clip for local background attachments. 727 context->restore(); 728} 729 730IntSize RenderBoxModelObject::calculateFillTileSize(const FillLayer* fillLayer, IntSize positioningAreaSize) const 731{ 732 StyleImage* image = fillLayer->image(); 733 image->setImageContainerSize(positioningAreaSize); // Use the box established by background-origin. 734 735 EFillSizeType type = fillLayer->size().type; 736 737 switch (type) { 738 case SizeLength: { 739 int w = positioningAreaSize.width(); 740 int h = positioningAreaSize.height(); 741 742 Length layerWidth = fillLayer->size().size.width(); 743 Length layerHeight = fillLayer->size().size.height(); 744 745 if (layerWidth.isFixed()) 746 w = layerWidth.value(); 747 else if (layerWidth.isPercent()) 748 w = layerWidth.calcValue(positioningAreaSize.width()); 749 750 if (layerHeight.isFixed()) 751 h = layerHeight.value(); 752 else if (layerHeight.isPercent()) 753 h = layerHeight.calcValue(positioningAreaSize.height()); 754 755 // If one of the values is auto we have to use the appropriate 756 // scale to maintain our aspect ratio. 757 if (layerWidth.isAuto() && !layerHeight.isAuto()) { 758 IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom()); 759 if (imageIntrinsicSize.height()) 760 w = imageIntrinsicSize.width() * h / imageIntrinsicSize.height(); 761 } else if (!layerWidth.isAuto() && layerHeight.isAuto()) { 762 IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom()); 763 if (imageIntrinsicSize.width()) 764 h = imageIntrinsicSize.height() * w / imageIntrinsicSize.width(); 765 } else if (layerWidth.isAuto() && layerHeight.isAuto()) { 766 // If both width and height are auto, use the image's intrinsic size. 767 IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom()); 768 w = imageIntrinsicSize.width(); 769 h = imageIntrinsicSize.height(); 770 } 771 772 return IntSize(max(1, w), max(1, h)); 773 } 774 case Contain: 775 case Cover: { 776 IntSize imageIntrinsicSize = image->imageSize(this, 1); 777 float horizontalScaleFactor = imageIntrinsicSize.width() 778 ? static_cast<float>(positioningAreaSize.width()) / imageIntrinsicSize.width() : 1; 779 float verticalScaleFactor = imageIntrinsicSize.height() 780 ? static_cast<float>(positioningAreaSize.height()) / imageIntrinsicSize.height() : 1; 781 float scaleFactor = type == Contain ? min(horizontalScaleFactor, verticalScaleFactor) : max(horizontalScaleFactor, verticalScaleFactor); 782 return IntSize(max<int>(1, imageIntrinsicSize.width() * scaleFactor), max<int>(1, imageIntrinsicSize.height() * scaleFactor)); 783 } 784 case SizeNone: 785 break; 786 } 787 788 return image->imageSize(this, style()->effectiveZoom()); 789} 790 791void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* fillLayer, int tx, int ty, int w, int h, 792 IntRect& destRect, IntPoint& phase, IntSize& tileSize) 793{ 794 int left = 0; 795 int top = 0; 796 IntSize positioningAreaSize; 797 798 // Determine the background positioning area and set destRect to the background painting area. 799 // destRect will be adjusted later if the background is non-repeating. 800 bool fixedAttachment = fillLayer->attachment() == FixedBackgroundAttachment; 801 802#if ENABLE(FAST_MOBILE_SCROLLING) 803 if (view()->frameView() && view()->frameView()->canBlitOnScroll()) { 804 // As a side effect of an optimization to blit on scroll, we do not honor the CSS 805 // property "background-attachment: fixed" because it may result in rendering 806 // artifacts. Note, these artifacts only appear if we are blitting on scroll of 807 // a page that has fixed background images. 808 fixedAttachment = false; 809 } 810#endif 811 812 if (!fixedAttachment) { 813 destRect = IntRect(tx, ty, w, h); 814 815 int right = 0; 816 int bottom = 0; 817 // Scroll and Local. 818 if (fillLayer->origin() != BorderFillBox) { 819 left = borderLeft(); 820 right = borderRight(); 821 top = borderTop(); 822 bottom = borderBottom(); 823 if (fillLayer->origin() == ContentFillBox) { 824 left += paddingLeft(); 825 right += paddingRight(); 826 top += paddingTop(); 827 bottom += paddingBottom(); 828 } 829 } 830 831 // The background of the box generated by the root element covers the entire canvas including 832 // its margins. Since those were added in already, we have to factor them out when computing 833 // the background positioning area. 834 if (isRoot()) { 835 positioningAreaSize = IntSize(toRenderBox(this)->width() - left - right, toRenderBox(this)->height() - top - bottom); 836 left += marginLeft(); 837 top += marginTop(); 838 } else 839 positioningAreaSize = IntSize(w - left - right, h - top - bottom); 840 } else { 841 destRect = viewRect(); 842 positioningAreaSize = destRect.size(); 843 } 844 845 tileSize = calculateFillTileSize(fillLayer, positioningAreaSize); 846 847 EFillRepeat backgroundRepeatX = fillLayer->repeatX(); 848 EFillRepeat backgroundRepeatY = fillLayer->repeatY(); 849 850 int xPosition = fillLayer->xPosition().calcMinValue(positioningAreaSize.width() - tileSize.width(), true); 851 if (backgroundRepeatX == RepeatFill) 852 phase.setX(tileSize.width() ? tileSize.width() - (xPosition + left) % tileSize.width() : 0); 853 else { 854 destRect.move(max(xPosition + left, 0), 0); 855 phase.setX(-min(xPosition + left, 0)); 856 destRect.setWidth(tileSize.width() + min(xPosition + left, 0)); 857 } 858 859 int yPosition = fillLayer->yPosition().calcMinValue(positioningAreaSize.height() - tileSize.height(), true); 860 if (backgroundRepeatY == RepeatFill) 861 phase.setY(tileSize.height() ? tileSize.height() - (yPosition + top) % tileSize.height() : 0); 862 else { 863 destRect.move(0, max(yPosition + top, 0)); 864 phase.setY(-min(yPosition + top, 0)); 865 destRect.setHeight(tileSize.height() + min(yPosition + top, 0)); 866 } 867 868 if (fixedAttachment) 869 phase.move(max(tx - destRect.x(), 0), max(ty - destRect.y(), 0)); 870 871 destRect.intersect(IntRect(tx, ty, w, h)); 872} 873 874bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style, 875 const NinePieceImage& ninePieceImage, CompositeOperator op) 876{ 877 StyleImage* styleImage = ninePieceImage.image(); 878 if (!styleImage) 879 return false; 880 881 if (!styleImage->isLoaded()) 882 return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either. 883 884 if (!styleImage->canRender(style->effectiveZoom())) 885 return false; 886 887 // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function 888 // doesn't have any understanding of the zoom that is in effect on the tile. 889 styleImage->setImageContainerSize(IntSize(w, h)); 890 IntSize imageSize = styleImage->imageSize(this, 1.0f); 891 int imageWidth = imageSize.width(); 892 int imageHeight = imageSize.height(); 893 894 int topSlice = min(imageHeight, ninePieceImage.slices().top().calcValue(imageHeight)); 895 int bottomSlice = min(imageHeight, ninePieceImage.slices().bottom().calcValue(imageHeight)); 896 int leftSlice = min(imageWidth, ninePieceImage.slices().left().calcValue(imageWidth)); 897 int rightSlice = min(imageWidth, ninePieceImage.slices().right().calcValue(imageWidth)); 898 899 ENinePieceImageRule hRule = ninePieceImage.horizontalRule(); 900 ENinePieceImageRule vRule = ninePieceImage.verticalRule(); 901 902 bool fitToBorder = style->borderImage() == ninePieceImage; 903 904 int leftWidth = fitToBorder ? style->borderLeftWidth() : leftSlice; 905 int topWidth = fitToBorder ? style->borderTopWidth() : topSlice; 906 int rightWidth = fitToBorder ? style->borderRightWidth() : rightSlice; 907 int bottomWidth = fitToBorder ? style->borderBottomWidth() : bottomSlice; 908 909 bool drawLeft = leftSlice > 0 && leftWidth > 0; 910 bool drawTop = topSlice > 0 && topWidth > 0; 911 bool drawRight = rightSlice > 0 && rightWidth > 0; 912 bool drawBottom = bottomSlice > 0 && bottomWidth > 0; 913 bool drawMiddle = (imageWidth - leftSlice - rightSlice) > 0 && (w - leftWidth - rightWidth) > 0 && 914 (imageHeight - topSlice - bottomSlice) > 0 && (h - topWidth - bottomWidth) > 0; 915 916 Image* image = styleImage->image(this, imageSize); 917 ColorSpace colorSpace = style->colorSpace(); 918 919 if (drawLeft) { 920 // Paint the top and bottom left corners. 921 922 // The top left corner rect is (tx, ty, leftWidth, topWidth) 923 // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice) 924 if (drawTop) 925 graphicsContext->drawImage(image, colorSpace, IntRect(tx, ty, leftWidth, topWidth), 926 IntRect(0, 0, leftSlice, topSlice), op); 927 928 // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth) 929 // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice) 930 if (drawBottom) 931 graphicsContext->drawImage(image, colorSpace, IntRect(tx, ty + h - bottomWidth, leftWidth, bottomWidth), 932 IntRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op); 933 934 // Paint the left edge. 935 // Have to scale and tile into the border rect. 936 graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx, ty + topWidth, leftWidth, 937 h - topWidth - bottomWidth), 938 IntRect(0, topSlice, leftSlice, imageHeight - topSlice - bottomSlice), 939 Image::StretchTile, (Image::TileRule)vRule, op); 940 } 941 942 if (drawRight) { 943 // Paint the top and bottom right corners 944 // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth) 945 // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice) 946 if (drawTop) 947 graphicsContext->drawImage(image, colorSpace, IntRect(tx + w - rightWidth, ty, rightWidth, topWidth), 948 IntRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op); 949 950 // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth) 951 // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice) 952 if (drawBottom) 953 graphicsContext->drawImage(image, colorSpace, IntRect(tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth), 954 IntRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op); 955 956 // Paint the right edge. 957 graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + w - rightWidth, ty + topWidth, rightWidth, 958 h - topWidth - bottomWidth), 959 IntRect(imageWidth - rightSlice, topSlice, rightSlice, imageHeight - topSlice - bottomSlice), 960 Image::StretchTile, (Image::TileRule)vRule, op); 961 } 962 963 // Paint the top edge. 964 if (drawTop) 965 graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + leftWidth, ty, w - leftWidth - rightWidth, topWidth), 966 IntRect(leftSlice, 0, imageWidth - rightSlice - leftSlice, topSlice), 967 (Image::TileRule)hRule, Image::StretchTile, op); 968 969 // Paint the bottom edge. 970 if (drawBottom) 971 graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + leftWidth, ty + h - bottomWidth, 972 w - leftWidth - rightWidth, bottomWidth), 973 IntRect(leftSlice, imageHeight - bottomSlice, imageWidth - rightSlice - leftSlice, bottomSlice), 974 (Image::TileRule)hRule, Image::StretchTile, op); 975 976 // Paint the middle. 977 if (drawMiddle) 978 graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + leftWidth, ty + topWidth, w - leftWidth - rightWidth, 979 h - topWidth - bottomWidth), 980 IntRect(leftSlice, topSlice, imageWidth - rightSlice - leftSlice, imageHeight - topSlice - bottomSlice), 981 (Image::TileRule)hRule, (Image::TileRule)vRule, op); 982 983 return true; 984} 985 986#if HAVE(PATH_BASED_BORDER_RADIUS_DRAWING) 987static bool borderWillArcInnerEdge(const IntSize& firstRadius, const IntSize& secondRadius, int firstBorderWidth, int secondBorderWidth, int middleBorderWidth) 988{ 989 // FIXME: This test is insufficient. We need to take border style into account. 990 return (!firstRadius.width() || firstRadius.width() >= firstBorderWidth) 991 && (!firstRadius.height() || firstRadius.height() >= middleBorderWidth) 992 && (!secondRadius.width() || secondRadius.width() >= secondBorderWidth) 993 && (!secondRadius.height() || secondRadius.height() >= middleBorderWidth); 994} 995 996void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, 997 const RenderStyle* style, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) 998{ 999 if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage())) 1000 return; 1001 1002 if (graphicsContext->paintingDisabled()) 1003 return; 1004 1005 const Color& topColor = style->visitedDependentColor(CSSPropertyBorderTopColor); 1006 const Color& bottomColor = style->visitedDependentColor(CSSPropertyBorderBottomColor); 1007 const Color& leftColor = style->visitedDependentColor(CSSPropertyBorderLeftColor); 1008 const Color& rightColor = style->visitedDependentColor(CSSPropertyBorderRightColor); 1009 1010 bool topTransparent = style->borderTopIsTransparent(); 1011 bool bottomTransparent = style->borderBottomIsTransparent(); 1012 bool rightTransparent = style->borderRightIsTransparent(); 1013 bool leftTransparent = style->borderLeftIsTransparent(); 1014 1015 EBorderStyle topStyle = style->borderTopStyle(); 1016 EBorderStyle bottomStyle = style->borderBottomStyle(); 1017 EBorderStyle leftStyle = style->borderLeftStyle(); 1018 EBorderStyle rightStyle = style->borderRightStyle(); 1019 1020 bool horizontal = style->isHorizontalWritingMode(); 1021 1022 bool renderTop = topStyle > BHIDDEN && !topTransparent && (horizontal || includeLogicalLeftEdge); 1023 bool renderLeft = leftStyle > BHIDDEN && !leftTransparent && (!horizontal || includeLogicalLeftEdge); 1024 bool renderRight = rightStyle > BHIDDEN && !rightTransparent && (!horizontal || includeLogicalRightEdge); 1025 bool renderBottom = bottomStyle > BHIDDEN && !bottomTransparent && (horizontal || includeLogicalRightEdge); 1026 1027 bool renderRadii = false; 1028 Path roundedPath; 1029 IntSize topLeft, topRight, bottomLeft, bottomRight; 1030 IntRect borderRect(tx, ty, w, h); 1031 1032 if (style->hasBorderRadius()) { 1033 IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius; 1034 style->getBorderRadiiForRect(borderRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); 1035 1036 int leftWidth = (!horizontal || includeLogicalLeftEdge) ? style->borderLeftWidth() : 0; 1037 int rightWidth = (!horizontal || includeLogicalRightEdge) ? style->borderRightWidth() : 0; 1038 int topWidth = (horizontal || includeLogicalLeftEdge) ? style->borderTopWidth() : 0; 1039 int bottomWidth = (horizontal || includeLogicalRightEdge) ? style->borderBottomWidth() : 0; 1040 1041 IntRect innerBorderRect = borderInnerRect(borderRect, topWidth, bottomWidth, leftWidth, rightWidth); 1042 IntSize innerTopLeftRadius, innerTopRightRadius, innerBottomLeftRadius, innerBottomRightRadius; 1043 1044 style->getInnerBorderRadiiForRectWithBorderWidths(innerBorderRect, topWidth, bottomWidth, leftWidth, rightWidth, innerTopLeftRadius, innerTopRightRadius, innerBottomLeftRadius, innerBottomRightRadius); 1045 1046 IntSize innerTopLeft, innerTopRight, innerBottomLeft, innerBottomRight; 1047 1048 if (includeLogicalLeftEdge) { 1049 topLeft = topLeftRadius; 1050 innerTopLeft = innerTopLeftRadius; 1051 if (horizontal) { 1052 bottomLeft = bottomLeftRadius; 1053 innerBottomLeft = innerBottomLeftRadius; 1054 } else { 1055 topRight = topRightRadius; 1056 innerTopRight = innerTopRightRadius; 1057 } 1058 } 1059 1060 if (includeLogicalRightEdge) { 1061 if (horizontal) { 1062 topRight = topRightRadius; 1063 innerTopRight = innerTopRightRadius; 1064 } else { 1065 bottomLeft = bottomLeftRadius; 1066 innerBottomLeft = innerBottomLeftRadius; 1067 } 1068 bottomRight = bottomRightRadius; 1069 innerBottomRight = innerBottomRightRadius; 1070 } 1071 1072 renderRadii = !topLeft.isZero() || !topRight.isZero() || !bottomLeft.isZero() || !bottomRight.isZero(); 1073 1074 if (renderRadii) { 1075 // Clip to the inner and outer radii rects. 1076 graphicsContext->save(); 1077 graphicsContext->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); 1078 graphicsContext->clipOutRoundedRect(innerBorderRect, innerTopLeft, innerTopRight, innerBottomLeft, innerBottomRight); 1079 roundedPath.addRoundedRect(borderRect, topLeft, topRight, bottomLeft, bottomRight); 1080 } 1081 } 1082 1083 bool upperLeftBorderStylesMatch = renderLeft && (topStyle == leftStyle) && (topColor == leftColor); 1084 bool upperRightBorderStylesMatch = renderRight && (topStyle == rightStyle) && (topColor == rightColor) && (topStyle != OUTSET) && (topStyle != RIDGE) && (topStyle != INSET) && (topStyle != GROOVE); 1085 bool lowerLeftBorderStylesMatch = renderLeft && (bottomStyle == leftStyle) && (bottomColor == leftColor) && (bottomStyle != OUTSET) && (bottomStyle != RIDGE) && (bottomStyle != INSET) && (bottomStyle != GROOVE); 1086 bool lowerRightBorderStylesMatch = renderRight && (bottomStyle == rightStyle) && (bottomColor == rightColor); 1087 1088 if (renderTop) { 1089 int x = tx; 1090 int x2 = tx + w; 1091 1092 if (renderRadii && borderWillArcInnerEdge(topLeft, topRight, style->borderLeftWidth(), style->borderRightWidth(), style->borderTopWidth())) { 1093 graphicsContext->save(); 1094 clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSTop, upperLeftBorderStylesMatch, upperRightBorderStylesMatch, style, includeLogicalLeftEdge, includeLogicalRightEdge); 1095 float thickness = max(max(style->borderTopWidth(), style->borderLeftWidth()), style->borderRightWidth()); 1096 drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderTopWidth(), thickness, BSTop, style, topColor, topStyle); 1097 graphicsContext->restore(); 1098 } else { 1099 bool ignoreLeft = (topColor == leftColor && topTransparent == leftTransparent && topStyle >= OUTSET 1100 && (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET)); 1101 bool ignoreRight = (topColor == rightColor && topTransparent == rightTransparent && topStyle >= OUTSET 1102 && (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET)); 1103 1104 drawLineForBoxSide(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, topColor, topStyle, 1105 ignoreLeft ? 0 : style->borderLeftWidth(), ignoreRight ? 0 : style->borderRightWidth()); 1106 } 1107 } 1108 1109 if (renderBottom) { 1110 int x = tx; 1111 int x2 = tx + w; 1112 1113 if (renderRadii && borderWillArcInnerEdge(bottomLeft, bottomRight, style->borderLeftWidth(), style->borderRightWidth(), style->borderBottomWidth())) { 1114 graphicsContext->save(); 1115 clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSBottom, lowerLeftBorderStylesMatch, lowerRightBorderStylesMatch, style, includeLogicalLeftEdge, includeLogicalRightEdge); 1116 float thickness = max(max(style->borderBottomWidth(), style->borderLeftWidth()), style->borderRightWidth()); 1117 drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderBottomWidth(), thickness, BSBottom, style, bottomColor, bottomStyle); 1118 graphicsContext->restore(); 1119 } else { 1120 bool ignoreLeft = (bottomColor == leftColor && bottomTransparent == leftTransparent && bottomStyle >= OUTSET 1121 && (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET)); 1122 1123 bool ignoreRight = (bottomColor == rightColor && bottomTransparent == rightTransparent && bottomStyle >= OUTSET 1124 && (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET)); 1125 1126 drawLineForBoxSide(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bottomColor, 1127 bottomStyle, ignoreLeft ? 0 : style->borderLeftWidth(), 1128 ignoreRight ? 0 : style->borderRightWidth()); 1129 } 1130 } 1131 1132 if (renderLeft) { 1133 int y = ty; 1134 int y2 = ty + h; 1135 1136 if (renderRadii && borderWillArcInnerEdge(bottomLeft, topLeft, style->borderBottomWidth(), style->borderTopWidth(), style->borderLeftWidth())) { 1137 graphicsContext->save(); 1138 clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSLeft, upperLeftBorderStylesMatch, lowerLeftBorderStylesMatch, style, includeLogicalLeftEdge, includeLogicalRightEdge); 1139 float thickness = max(max(style->borderLeftWidth(), style->borderTopWidth()), style->borderBottomWidth()); 1140 drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderLeftWidth(), thickness, BSLeft, style, leftColor, leftStyle); 1141 graphicsContext->restore(); 1142 } else { 1143 bool ignoreTop = (topColor == leftColor && topTransparent == leftTransparent && leftStyle >= OUTSET 1144 && (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET)); 1145 1146 bool ignoreBottom = (bottomColor == leftColor && bottomTransparent == leftTransparent && leftStyle >= OUTSET 1147 && (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET)); 1148 1149 drawLineForBoxSide(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, leftColor, 1150 leftStyle, ignoreTop ? 0 : style->borderTopWidth(), ignoreBottom ? 0 : style->borderBottomWidth()); 1151 } 1152 } 1153 1154 if (renderRight) { 1155 if (renderRadii && borderWillArcInnerEdge(bottomRight, topRight, style->borderBottomWidth(), style->borderTopWidth(), style->borderRightWidth())) { 1156 graphicsContext->save(); 1157 clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSRight, upperRightBorderStylesMatch, lowerRightBorderStylesMatch, style, includeLogicalLeftEdge, includeLogicalRightEdge); 1158 float thickness = max(max(style->borderRightWidth(), style->borderTopWidth()), style->borderBottomWidth()); 1159 drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderRightWidth(), thickness, BSRight, style, rightColor, rightStyle); 1160 graphicsContext->restore(); 1161 } else { 1162 bool ignoreTop = ((topColor == rightColor) && (topTransparent == rightTransparent) 1163 && (rightStyle >= DOTTED || rightStyle == INSET) 1164 && (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET)); 1165 1166 bool ignoreBottom = ((bottomColor == rightColor) && (bottomTransparent == rightTransparent) 1167 && (rightStyle >= DOTTED || rightStyle == INSET) 1168 && (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET)); 1169 1170 int y = ty; 1171 int y2 = ty + h; 1172 1173 drawLineForBoxSide(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rightColor, 1174 rightStyle, ignoreTop ? 0 : style->borderTopWidth(), 1175 ignoreBottom ? 0 : style->borderBottomWidth()); 1176 } 1177 } 1178 1179 if (renderRadii) 1180 graphicsContext->restore(); 1181} 1182#else 1183void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, 1184 const RenderStyle* style, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) 1185{ 1186 // FIXME: This old version of paintBorder should be removed when all ports implement 1187 // GraphicsContext::clipConvexPolygon()!! This should happen soon. 1188 if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage())) 1189 return; 1190 1191 const Color& topColor = style->visitedDependentColor(CSSPropertyBorderTopColor); 1192 const Color& bottomColor = style->visitedDependentColor(CSSPropertyBorderBottomColor); 1193 const Color& leftColor = style->visitedDependentColor(CSSPropertyBorderLeftColor); 1194 const Color& rightColor = style->visitedDependentColor(CSSPropertyBorderRightColor); 1195 1196 bool topTransparent = style->borderTopIsTransparent(); 1197 bool bottomTransparent = style->borderBottomIsTransparent(); 1198 bool rightTransparent = style->borderRightIsTransparent(); 1199 bool leftTransparent = style->borderLeftIsTransparent(); 1200 1201 EBorderStyle topStyle = style->borderTopStyle(); 1202 EBorderStyle bottomStyle = style->borderBottomStyle(); 1203 EBorderStyle leftStyle = style->borderLeftStyle(); 1204 EBorderStyle rightStyle = style->borderRightStyle(); 1205 1206 bool horizontal = style->isHorizontalWritingMode(); 1207 bool renderTop = topStyle > BHIDDEN && !topTransparent && (horizontal || includeLogicalLeftEdge); 1208 bool renderLeft = leftStyle > BHIDDEN && !leftTransparent && (!horizontal || includeLogicalLeftEdge); 1209 bool renderRight = rightStyle > BHIDDEN && !rightTransparent && (!horizontal || includeLogicalRightEdge); 1210 bool renderBottom = bottomStyle > BHIDDEN && !bottomTransparent && (horizontal || includeLogicalRightEdge); 1211 1212 bool renderRadii = false; 1213 IntSize topLeft, topRight, bottomLeft, bottomRight; 1214 1215 if (style->hasBorderRadius()) { 1216 IntRect borderRect = IntRect(tx, ty, w, h); 1217 1218 IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius; 1219 style->getBorderRadiiForRect(borderRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); 1220 1221 if (includeLogicalLeftEdge) { 1222 topLeft = topLeftRadius; 1223 if (horizontal) 1224 bottomLeft = bottomLeftRadius; 1225 else 1226 topRight = topRightRadius; 1227 } 1228 1229 if (includeLogicalRightEdge) { 1230 if (horizontal) 1231 topRight = topRightRadius; 1232 else 1233 bottomLeft = bottomLeftRadius; 1234 bottomRight = bottomRightRadius; 1235 } 1236 1237 renderRadii = !topLeft.isZero() || !topRight.isZero() || !bottomLeft.isZero() || !bottomRight.isZero(); 1238 1239 if (renderRadii) { 1240 // Clip to the rounded rectangle. 1241 graphicsContext->save(); 1242 graphicsContext->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); 1243 } 1244 } 1245 1246 int firstAngleStart, secondAngleStart, firstAngleSpan, secondAngleSpan; 1247 float thickness; 1248 bool upperLeftBorderStylesMatch = renderLeft && (topStyle == leftStyle) && (topColor == leftColor); 1249 bool upperRightBorderStylesMatch = renderRight && (topStyle == rightStyle) && (topColor == rightColor) && (topStyle != OUTSET) && (topStyle != RIDGE) && (topStyle != INSET) && (topStyle != GROOVE); 1250 bool lowerLeftBorderStylesMatch = renderLeft && (bottomStyle == leftStyle) && (bottomColor == leftColor) && (bottomStyle != OUTSET) && (bottomStyle != RIDGE) && (bottomStyle != INSET) && (bottomStyle != GROOVE); 1251 bool lowerRightBorderStylesMatch = renderRight && (bottomStyle == rightStyle) && (bottomColor == rightColor); 1252 1253 if (renderTop) { 1254 bool ignore_left = (renderRadii && topLeft.width() > 0) || 1255 (topColor == leftColor && topTransparent == leftTransparent && topStyle >= OUTSET && 1256 (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET)); 1257 1258 bool ignore_right = (renderRadii && topRight.width() > 0) || 1259 (topColor == rightColor && topTransparent == rightTransparent && topStyle >= OUTSET && 1260 (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET)); 1261 1262 int x = tx; 1263 int x2 = tx + w; 1264 if (renderRadii) { 1265 x += topLeft.width(); 1266 x2 -= topRight.width(); 1267 } 1268 1269 drawLineForBoxSide(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, topColor, topStyle, 1270 ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); 1271 1272 if (renderRadii) { 1273 int leftY = ty; 1274 1275 // We make the arc double thick and let the clip rect take care of clipping the extra off. 1276 // We're doing this because it doesn't seem possible to match the curve of the clip exactly 1277 // with the arc-drawing function. 1278 thickness = style->borderTopWidth() * 2; 1279 1280 if (topLeft.width()) { 1281 int leftX = tx; 1282 // The inner clip clips inside the arc. This is especially important for 1px borders. 1283 bool applyLeftInnerClip = (style->borderLeftWidth() < topLeft.width()) 1284 && (style->borderTopWidth() < topLeft.height()) 1285 && (topStyle != DOUBLE || style->borderTopWidth() > 6); 1286 if (applyLeftInnerClip) { 1287 graphicsContext->save(); 1288 graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, topLeft.width() * 2, topLeft.height() * 2), 1289 style->borderTopWidth()); 1290 } 1291 1292 firstAngleStart = 90; 1293 firstAngleSpan = upperLeftBorderStylesMatch ? 90 : 45; 1294 1295 // Draw upper left arc 1296 drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, topLeft, firstAngleStart, firstAngleSpan, 1297 BSTop, topColor, topStyle, true); 1298 if (applyLeftInnerClip) 1299 graphicsContext->restore(); 1300 } 1301 1302 if (topRight.width()) { 1303 int rightX = tx + w - topRight.width() * 2; 1304 bool applyRightInnerClip = (style->borderRightWidth() < topRight.width()) 1305 && (style->borderTopWidth() < topRight.height()) 1306 && (topStyle != DOUBLE || style->borderTopWidth() > 6); 1307 if (applyRightInnerClip) { 1308 graphicsContext->save(); 1309 graphicsContext->addInnerRoundedRectClip(IntRect(rightX, leftY, topRight.width() * 2, topRight.height() * 2), 1310 style->borderTopWidth()); 1311 } 1312 1313 if (upperRightBorderStylesMatch) { 1314 secondAngleStart = 0; 1315 secondAngleSpan = 90; 1316 } else { 1317 secondAngleStart = 45; 1318 secondAngleSpan = 45; 1319 } 1320 1321 // Draw upper right arc 1322 drawArcForBoxSide(graphicsContext, rightX, leftY, thickness, topRight, secondAngleStart, secondAngleSpan, 1323 BSTop, topColor, topStyle, false); 1324 if (applyRightInnerClip) 1325 graphicsContext->restore(); 1326 } 1327 } 1328 } 1329 1330 if (renderBottom) { 1331 bool ignore_left = (renderRadii && bottomLeft.width() > 0) || 1332 (bottomColor == leftColor && bottomTransparent == leftTransparent && bottomStyle >= OUTSET && 1333 (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET)); 1334 1335 bool ignore_right = (renderRadii && bottomRight.width() > 0) || 1336 (bottomColor == rightColor && bottomTransparent == rightTransparent && bottomStyle >= OUTSET && 1337 (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET)); 1338 1339 int x = tx; 1340 int x2 = tx + w; 1341 if (renderRadii) { 1342 x += bottomLeft.width(); 1343 x2 -= bottomRight.width(); 1344 } 1345 1346 drawLineForBoxSide(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bottomColor, bottomStyle, 1347 ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); 1348 1349 if (renderRadii) { 1350 thickness = style->borderBottomWidth() * 2; 1351 1352 if (bottomLeft.width()) { 1353 int leftX = tx; 1354 int leftY = ty + h - bottomLeft.height() * 2; 1355 bool applyLeftInnerClip = (style->borderLeftWidth() < bottomLeft.width()) 1356 && (style->borderBottomWidth() < bottomLeft.height()) 1357 && (bottomStyle != DOUBLE || style->borderBottomWidth() > 6); 1358 if (applyLeftInnerClip) { 1359 graphicsContext->save(); 1360 graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, bottomLeft.width() * 2, bottomLeft.height() * 2), 1361 style->borderBottomWidth()); 1362 } 1363 1364 if (lowerLeftBorderStylesMatch) { 1365 firstAngleStart = 180; 1366 firstAngleSpan = 90; 1367 } else { 1368 firstAngleStart = 225; 1369 firstAngleSpan = 45; 1370 } 1371 1372 // Draw lower left arc 1373 drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, bottomLeft, firstAngleStart, firstAngleSpan, 1374 BSBottom, bottomColor, bottomStyle, true); 1375 if (applyLeftInnerClip) 1376 graphicsContext->restore(); 1377 } 1378 1379 if (bottomRight.width()) { 1380 int rightY = ty + h - bottomRight.height() * 2; 1381 int rightX = tx + w - bottomRight.width() * 2; 1382 bool applyRightInnerClip = (style->borderRightWidth() < bottomRight.width()) 1383 && (style->borderBottomWidth() < bottomRight.height()) 1384 && (bottomStyle != DOUBLE || style->borderBottomWidth() > 6); 1385 if (applyRightInnerClip) { 1386 graphicsContext->save(); 1387 graphicsContext->addInnerRoundedRectClip(IntRect(rightX, rightY, bottomRight.width() * 2, bottomRight.height() * 2), 1388 style->borderBottomWidth()); 1389 } 1390 1391 secondAngleStart = 270; 1392 secondAngleSpan = lowerRightBorderStylesMatch ? 90 : 45; 1393 1394 // Draw lower right arc 1395 drawArcForBoxSide(graphicsContext, rightX, rightY, thickness, bottomRight, secondAngleStart, secondAngleSpan, 1396 BSBottom, bottomColor, bottomStyle, false); 1397 if (applyRightInnerClip) 1398 graphicsContext->restore(); 1399 } 1400 } 1401 } 1402 1403 if (renderLeft) { 1404 bool ignore_top = (renderRadii && topLeft.height() > 0) || 1405 (topColor == leftColor && topTransparent == leftTransparent && leftStyle >= OUTSET && 1406 (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET)); 1407 1408 bool ignore_bottom = (renderRadii && bottomLeft.height() > 0) || 1409 (bottomColor == leftColor && bottomTransparent == leftTransparent && leftStyle >= OUTSET && 1410 (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET)); 1411 1412 int y = ty; 1413 int y2 = ty + h; 1414 if (renderRadii) { 1415 y += topLeft.height(); 1416 y2 -= bottomLeft.height(); 1417 } 1418 1419 drawLineForBoxSide(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, leftColor, leftStyle, 1420 ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); 1421 1422 if (renderRadii && (!upperLeftBorderStylesMatch || !lowerLeftBorderStylesMatch)) { 1423 int topX = tx; 1424 thickness = style->borderLeftWidth() * 2; 1425 1426 if (!upperLeftBorderStylesMatch && topLeft.width()) { 1427 int topY = ty; 1428 bool applyTopInnerClip = (style->borderLeftWidth() < topLeft.width()) 1429 && (style->borderTopWidth() < topLeft.height()) 1430 && (leftStyle != DOUBLE || style->borderLeftWidth() > 6); 1431 if (applyTopInnerClip) { 1432 graphicsContext->save(); 1433 graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topLeft.width() * 2, topLeft.height() * 2), 1434 style->borderLeftWidth()); 1435 } 1436 1437 firstAngleStart = 135; 1438 firstAngleSpan = 45; 1439 1440 // Draw top left arc 1441 drawArcForBoxSide(graphicsContext, topX, topY, thickness, topLeft, firstAngleStart, firstAngleSpan, 1442 BSLeft, leftColor, leftStyle, true); 1443 if (applyTopInnerClip) 1444 graphicsContext->restore(); 1445 } 1446 1447 if (!lowerLeftBorderStylesMatch && bottomLeft.width()) { 1448 int bottomY = ty + h - bottomLeft.height() * 2; 1449 bool applyBottomInnerClip = (style->borderLeftWidth() < bottomLeft.width()) 1450 && (style->borderBottomWidth() < bottomLeft.height()) 1451 && (leftStyle != DOUBLE || style->borderLeftWidth() > 6); 1452 if (applyBottomInnerClip) { 1453 graphicsContext->save(); 1454 graphicsContext->addInnerRoundedRectClip(IntRect(topX, bottomY, bottomLeft.width() * 2, bottomLeft.height() * 2), 1455 style->borderLeftWidth()); 1456 } 1457 1458 secondAngleStart = 180; 1459 secondAngleSpan = 45; 1460 1461 // Draw bottom left arc 1462 drawArcForBoxSide(graphicsContext, topX, bottomY, thickness, bottomLeft, secondAngleStart, secondAngleSpan, 1463 BSLeft, leftColor, leftStyle, false); 1464 if (applyBottomInnerClip) 1465 graphicsContext->restore(); 1466 } 1467 } 1468 } 1469 1470 if (renderRight) { 1471 bool ignore_top = (renderRadii && topRight.height() > 0) || 1472 ((topColor == rightColor) && (topTransparent == rightTransparent) && 1473 (rightStyle >= DOTTED || rightStyle == INSET) && 1474 (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET)); 1475 1476 bool ignore_bottom = (renderRadii && bottomRight.height() > 0) || 1477 ((bottomColor == rightColor) && (bottomTransparent == rightTransparent) && 1478 (rightStyle >= DOTTED || rightStyle == INSET) && 1479 (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET)); 1480 1481 int y = ty; 1482 int y2 = ty + h; 1483 if (renderRadii) { 1484 y += topRight.height(); 1485 y2 -= bottomRight.height(); 1486 } 1487 1488 drawLineForBoxSide(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rightColor, rightStyle, 1489 ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); 1490 1491 if (renderRadii && (!upperRightBorderStylesMatch || !lowerRightBorderStylesMatch)) { 1492 thickness = style->borderRightWidth() * 2; 1493 1494 if (!upperRightBorderStylesMatch && topRight.width()) { 1495 int topX = tx + w - topRight.width() * 2; 1496 int topY = ty; 1497 bool applyTopInnerClip = (style->borderRightWidth() < topRight.width()) 1498 && (style->borderTopWidth() < topRight.height()) 1499 && (rightStyle != DOUBLE || style->borderRightWidth() > 6); 1500 if (applyTopInnerClip) { 1501 graphicsContext->save(); 1502 graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topRight.width() * 2, topRight.height() * 2), 1503 style->borderRightWidth()); 1504 } 1505 1506 firstAngleStart = 0; 1507 firstAngleSpan = 45; 1508 1509 // Draw top right arc 1510 drawArcForBoxSide(graphicsContext, topX, topY, thickness, topRight, firstAngleStart, firstAngleSpan, 1511 BSRight, rightColor, rightStyle, true); 1512 if (applyTopInnerClip) 1513 graphicsContext->restore(); 1514 } 1515 1516 if (!lowerRightBorderStylesMatch && bottomRight.width()) { 1517 int bottomX = tx + w - bottomRight.width() * 2; 1518 int bottomY = ty + h - bottomRight.height() * 2; 1519 bool applyBottomInnerClip = (style->borderRightWidth() < bottomRight.width()) 1520 && (style->borderBottomWidth() < bottomRight.height()) 1521 && (rightStyle != DOUBLE || style->borderRightWidth() > 6); 1522 if (applyBottomInnerClip) { 1523 graphicsContext->save(); 1524 graphicsContext->addInnerRoundedRectClip(IntRect(bottomX, bottomY, bottomRight.width() * 2, bottomRight.height() * 2), 1525 style->borderRightWidth()); 1526 } 1527 1528 secondAngleStart = 315; 1529 secondAngleSpan = 45; 1530 1531 // Draw bottom right arc 1532 drawArcForBoxSide(graphicsContext, bottomX, bottomY, thickness, bottomRight, secondAngleStart, secondAngleSpan, 1533 BSRight, rightColor, rightStyle, false); 1534 if (applyBottomInnerClip) 1535 graphicsContext->restore(); 1536 } 1537 } 1538 } 1539 1540 if (renderRadii) 1541 graphicsContext->restore(); 1542} 1543#endif 1544 1545void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext* graphicsContext, const IntRect& box, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, 1546 const BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches, const RenderStyle* style, 1547 bool includeLogicalLeftEdge, bool includeLogicalRightEdge) 1548{ 1549 FloatPoint quad[4]; 1550 int tx = box.x(); 1551 int ty = box.y(); 1552 int w = box.width(); 1553 int h = box.height(); 1554 1555 bool horizontal = style->isHorizontalWritingMode(); 1556 int leftWidth = (!horizontal || includeLogicalLeftEdge) ? style->borderLeftWidth() : 0; 1557 int rightWidth = (!horizontal || includeLogicalRightEdge) ? style->borderRightWidth() : 0; 1558 int topWidth = (horizontal || includeLogicalLeftEdge) ? style->borderTopWidth() : 0; 1559 int bottomWidth = (horizontal || includeLogicalRightEdge) ? style->borderBottomWidth() : 0; 1560 1561 // For each side, create an array of FloatPoints where each point is based on whichever value in each corner 1562 // is larger -- the radius width/height or the border width/height -- as appropriate. 1563 switch (side) { 1564 case BSTop: 1565 quad[0] = FloatPoint(tx, ty); 1566 quad[1] = FloatPoint(tx + max(topLeft.width(), leftWidth), ty + max(topLeft.height(), topWidth)); 1567 quad[2] = FloatPoint(tx + w - max(topRight.width(), rightWidth), ty + max(topRight.height(), topWidth)); 1568 quad[3] = FloatPoint(tx + w, ty); 1569 break; 1570 case BSLeft: 1571 quad[0] = FloatPoint(tx, ty); 1572 quad[1] = FloatPoint(tx + max(topLeft.width(), leftWidth), ty + max(topLeft.height(), topWidth)); 1573 quad[2] = FloatPoint(tx + max(bottomLeft.width(), leftWidth), ty + h - max(bottomLeft.height(), bottomWidth)); 1574 quad[3] = FloatPoint(tx, ty + h); 1575 break; 1576 case BSBottom: 1577 quad[0] = FloatPoint(tx, ty + h); 1578 quad[1] = FloatPoint(tx + max(bottomLeft.width(), leftWidth), ty + h - max(bottomLeft.height(), bottomWidth)); 1579 quad[2] = FloatPoint(tx + w - max(bottomRight.width(), rightWidth), ty + h - max(bottomRight.height(), bottomWidth)); 1580 quad[3] = FloatPoint(tx + w, ty + h); 1581 break; 1582 case BSRight: 1583 quad[0] = FloatPoint(tx + w, ty); 1584 quad[1] = FloatPoint(tx + w - max(topRight.width(), rightWidth), ty + max(topRight.height(), topWidth)); 1585 quad[2] = FloatPoint(tx + w - max(bottomRight.width(), rightWidth), ty + h - max(bottomRight.height(), bottomWidth)); 1586 quad[3] = FloatPoint(tx + w, ty + h); 1587 break; 1588 default: 1589 break; 1590 } 1591 1592 // If the border matches both of its adjacent sides, don't anti-alias the clip, and 1593 // if neither side matches, anti-alias the clip. 1594 if (firstEdgeMatches == secondEdgeMatches) { 1595 graphicsContext->clipConvexPolygon(4, quad, !firstEdgeMatches); 1596 return; 1597 } 1598 1599 FloatPoint firstQuad[4]; 1600 firstQuad[0] = quad[0]; 1601 firstQuad[1] = quad[1]; 1602 firstQuad[2] = side == BSTop || side == BSBottom ? FloatPoint(quad[3].x(), quad[2].y()) 1603 : FloatPoint(quad[2].x(), quad[3].y()); 1604 firstQuad[3] = quad[3]; 1605 graphicsContext->clipConvexPolygon(4, firstQuad, !firstEdgeMatches); 1606 1607 FloatPoint secondQuad[4]; 1608 secondQuad[0] = quad[0]; 1609 secondQuad[1] = side == BSTop || side == BSBottom ? FloatPoint(quad[0].x(), quad[1].y()) 1610 : FloatPoint(quad[1].x(), quad[0].y()); 1611 secondQuad[2] = quad[2]; 1612 secondQuad[3] = quad[3]; 1613 graphicsContext->clipConvexPolygon(4, secondQuad, !secondEdgeMatches); 1614} 1615 1616static inline void uniformlyExpandBorderRadii(int delta, IntSize& topLeft, IntSize& topRight, IntSize& bottomLeft, IntSize& bottomRight) 1617{ 1618 topLeft.expand(delta, delta); 1619 topLeft.clampNegativeToZero(); 1620 topRight.expand(delta, delta); 1621 topRight.clampNegativeToZero(); 1622 bottomLeft.expand(delta, delta); 1623 bottomLeft.clampNegativeToZero(); 1624 bottomRight.expand(delta, delta); 1625 bottomRight.clampNegativeToZero(); 1626} 1627 1628void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int ty, int w, int h, const RenderStyle* s, ShadowStyle shadowStyle, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) 1629{ 1630 // FIXME: Deal with border-image. Would be great to use border-image as a mask. 1631 1632 if (context->paintingDisabled()) 1633 return; 1634 1635 IntRect rect(tx, ty, w, h); 1636 IntSize topLeft; 1637 IntSize topRight; 1638 IntSize bottomLeft; 1639 IntSize bottomRight; 1640 1641 bool hasBorderRadius = s->hasBorderRadius(); 1642 bool isHorizontal = s->isHorizontalWritingMode(); 1643 if (hasBorderRadius && (includeLogicalLeftEdge || includeLogicalRightEdge)) { 1644 IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius; 1645 s->getBorderRadiiForRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); 1646 1647 if (includeLogicalLeftEdge) { 1648 if (shadowStyle == Inset) { 1649 topLeftRadius.expand(-borderLeft(), -borderTop()); 1650 topLeftRadius.clampNegativeToZero(); 1651 if (isHorizontal) { 1652 bottomLeftRadius.expand(-borderLeft(), -borderBottom()); 1653 bottomLeftRadius.clampNegativeToZero(); 1654 } else { 1655 topRightRadius.expand(-borderRight(), -borderTop()); 1656 topRightRadius.clampNegativeToZero(); 1657 } 1658 } 1659 topLeft = topLeftRadius; 1660 if (isHorizontal) 1661 bottomLeft = bottomLeftRadius; 1662 else 1663 topRight = topRightRadius; 1664 } 1665 if (includeLogicalRightEdge) { 1666 if (shadowStyle == Inset) { 1667 if (isHorizontal) { 1668 topRightRadius.expand(-borderRight(), -borderTop()); 1669 topRightRadius.clampNegativeToZero(); 1670 } else { 1671 bottomLeftRadius.expand(-borderLeft(), -borderBottom()); 1672 bottomLeftRadius.clampNegativeToZero(); 1673 } 1674 bottomRightRadius.expand(-borderRight(), -borderBottom()); 1675 bottomRightRadius.clampNegativeToZero(); 1676 } 1677 if (isHorizontal) 1678 topRight = topRightRadius; 1679 else 1680 bottomLeft = bottomLeftRadius; 1681 bottomRight = bottomRightRadius; 1682 } 1683 } 1684 1685 if (shadowStyle == Inset) { 1686 rect.move(includeLogicalLeftEdge || !isHorizontal ? borderLeft() : 0, includeLogicalLeftEdge || isHorizontal ? borderTop() : 0); 1687 rect.setWidth(rect.width() - ((includeLogicalLeftEdge || !isHorizontal) ? borderLeft() : 0) - ((includeLogicalRightEdge || !isHorizontal) ? borderRight() : 0)); 1688 rect.setHeight(rect.height() - ((includeLogicalLeftEdge || isHorizontal) ? borderTop() : 0) - ((includeLogicalRightEdge || isHorizontal) ? borderBottom() : 0)); 1689 } 1690 1691 bool hasOpaqueBackground = s->visitedDependentColor(CSSPropertyBackgroundColor).isValid() && s->visitedDependentColor(CSSPropertyBackgroundColor).alpha() == 255; 1692 for (const ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next()) { 1693 if (shadow->style() != shadowStyle) 1694 continue; 1695 1696 IntSize shadowOffset(shadow->x(), shadow->y()); 1697 int shadowBlur = shadow->blur(); 1698 int shadowSpread = shadow->spread(); 1699 const Color& shadowColor = shadow->color(); 1700 1701 if (shadow->style() == Normal) { 1702 IntRect fillRect(rect); 1703 fillRect.inflate(shadowSpread); 1704 if (fillRect.isEmpty()) 1705 continue; 1706 1707 IntRect shadowRect(rect); 1708 shadowRect.inflate(shadowBlur + shadowSpread); 1709 shadowRect.move(shadowOffset); 1710 1711 context->save(); 1712 context->clip(shadowRect); 1713 1714 // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not 1715 // bleed in (due to antialiasing) if the context is transformed. 1716 IntSize extraOffset(w + max(0, shadowOffset.width()) + shadowBlur + 2 * shadowSpread + 1, 0); 1717 shadowOffset -= extraOffset; 1718 fillRect.move(extraOffset); 1719 1720 context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace()); 1721 if (hasBorderRadius) { 1722 IntRect rectToClipOut = rect; 1723 IntSize topLeftToClipOut = topLeft; 1724 IntSize topRightToClipOut = topRight; 1725 IntSize bottomLeftToClipOut = bottomLeft; 1726 IntSize bottomRightToClipOut = bottomRight; 1727 1728 IntSize topLeftToFill = topLeft; 1729 IntSize topRightToFill = topRight; 1730 IntSize bottomLeftToFill = bottomLeft; 1731 IntSize bottomRightToFill = bottomRight; 1732 if (shadowSpread < 0) 1733 uniformlyExpandBorderRadii(shadowSpread, topLeftToFill, topRightToFill, bottomLeftToFill, bottomRightToFill); 1734 1735 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time 1736 // when painting the shadow. On the other hand, it introduces subpixel gaps along the 1737 // corners. Those are avoided by insetting the clipping path by one pixel. 1738 if (hasOpaqueBackground) { 1739 rectToClipOut.inflate(-1); 1740 uniformlyExpandBorderRadii(-1, topLeftToClipOut, topRightToClipOut, bottomLeftToClipOut, bottomRightToClipOut); 1741 } 1742 1743 if (!rectToClipOut.isEmpty()) 1744 context->clipOutRoundedRect(rectToClipOut, topLeftToClipOut, topRightToClipOut, bottomLeftToClipOut, bottomRightToClipOut); 1745 context->fillRoundedRect(fillRect, topLeftToFill, topRightToFill, bottomLeftToFill, bottomRightToFill, Color::black, s->colorSpace()); 1746 } else { 1747 IntRect rectToClipOut = rect; 1748 1749 // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time 1750 // when painting the shadow. On the other hand, it introduces subpixel gaps along the 1751 // edges if they are not pixel-aligned. Those are avoided by insetting the clipping path 1752 // by one pixel. 1753 if (hasOpaqueBackground) { 1754 AffineTransform currentTransformation = context->getCTM(); 1755 if (currentTransformation.a() != 1 || (currentTransformation.d() != 1 && currentTransformation.d() != -1) 1756 || currentTransformation.b() || currentTransformation.c()) 1757 rectToClipOut.inflate(-1); 1758 } 1759 1760 if (!rectToClipOut.isEmpty()) 1761 context->clipOut(rectToClipOut); 1762 context->fillRect(fillRect, Color::black, s->colorSpace()); 1763 } 1764 1765 context->restore(); 1766 } else { 1767 // Inset shadow. 1768 IntRect holeRect(rect); 1769 holeRect.inflate(-shadowSpread); 1770 1771 if (holeRect.isEmpty()) { 1772 if (hasBorderRadius) 1773 context->fillRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight, shadowColor, s->colorSpace()); 1774 else 1775 context->fillRect(rect, shadowColor, s->colorSpace()); 1776 continue; 1777 } 1778 1779 if (!includeLogicalLeftEdge) { 1780 if (isHorizontal) { 1781 holeRect.move(-max(shadowOffset.width(), 0) - shadowBlur, 0); 1782 holeRect.setWidth(holeRect.width() + max(shadowOffset.width(), 0) + shadowBlur); 1783 } else { 1784 holeRect.move(0, -max(shadowOffset.height(), 0) - shadowBlur); 1785 holeRect.setHeight(holeRect.height() + max(shadowOffset.height(), 0) + shadowBlur); 1786 } 1787 } 1788 if (!includeLogicalRightEdge) { 1789 if (isHorizontal) 1790 holeRect.setWidth(holeRect.width() - min(shadowOffset.width(), 0) + shadowBlur); 1791 else 1792 holeRect.setHeight(holeRect.height() - min(shadowOffset.height(), 0) + shadowBlur); 1793 } 1794 1795 Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255); 1796 1797 IntRect outerRect(rect); 1798 outerRect.inflateX(w - 2 * shadowSpread); 1799 outerRect.inflateY(h - 2 * shadowSpread); 1800 1801 context->save(); 1802 1803 Path path; 1804 if (hasBorderRadius) { 1805 path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); 1806 context->clip(path); 1807 path.clear(); 1808 } else 1809 context->clip(rect); 1810 1811 IntSize extraOffset(2 * w + max(0, shadowOffset.width()) + shadowBlur - 2 * shadowSpread + 1, 0); 1812 context->translate(extraOffset.width(), extraOffset.height()); 1813 shadowOffset -= extraOffset; 1814 1815 path.addRect(outerRect); 1816 1817 if (hasBorderRadius) { 1818 if (shadowSpread > 0) 1819 uniformlyExpandBorderRadii(-shadowSpread, topLeft, topRight, bottomLeft, bottomRight); 1820 path.addRoundedRect(holeRect, topLeft, topRight, bottomLeft, bottomRight); 1821 } else 1822 path.addRect(holeRect); 1823 1824 context->setFillRule(RULE_EVENODD); 1825 context->setFillColor(fillColor, s->colorSpace()); 1826 context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace()); 1827 context->fillPath(path); 1828 1829 context->restore(); 1830 } 1831 } 1832} 1833 1834int RenderBoxModelObject::containingBlockLogicalWidthForContent() const 1835{ 1836 return containingBlock()->availableLogicalWidth(); 1837} 1838 1839RenderBoxModelObject* RenderBoxModelObject::continuation() const 1840{ 1841 if (!continuationMap) 1842 return 0; 1843 return continuationMap->get(this); 1844} 1845 1846void RenderBoxModelObject::setContinuation(RenderBoxModelObject* continuation) 1847{ 1848 if (continuation) { 1849 if (!continuationMap) 1850 continuationMap = new ContinuationMap; 1851 continuationMap->set(this, continuation); 1852 } else { 1853 if (continuationMap) 1854 continuationMap->remove(this); 1855 } 1856} 1857 1858} // namespace WebCore 1859