1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2005 Allan Sandfeld Jensen (kde (at) carewolf.com) 5 * (C) 2005, 2006 Samuel Weinig (sam.weinig (at) gmail.com) 6 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 * 23 */ 24 25 #include "config.h" 26 #include "RenderBox.h" 27 28 #include "CachedImage.h" 29 #include "Chrome.h" 30 #include "ChromeClient.h" 31 #include "Document.h" 32 #include "FrameView.h" 33 #include "GraphicsContext.h" 34 #include "htmlediting.h" 35 #include "HTMLElement.h" 36 #include "HTMLNames.h" 37 #include "ImageBuffer.h" 38 #include "FloatQuad.h" 39 #include "Frame.h" 40 #include "Page.h" 41 #include "RenderArena.h" 42 #include "RenderFlexibleBox.h" 43 #include "RenderInline.h" 44 #include "RenderLayer.h" 45 #include "RenderTableCell.h" 46 #include "RenderTheme.h" 47 #ifdef ANDROID_LAYOUT 48 #include "Settings.h" 49 #endif 50 #include "RenderView.h" 51 #include "TransformState.h" 52 #include <algorithm> 53 #include <math.h> 54 55 #if ENABLE(WML) 56 #include "WMLNames.h" 57 #endif 58 59 using namespace std; 60 61 namespace WebCore { 62 63 using namespace HTMLNames; 64 65 // Used by flexible boxes when flexing this element. 66 typedef WTF::HashMap<const RenderBox*, int> OverrideSizeMap; 67 static OverrideSizeMap* gOverrideSizeMap = 0; 68 69 bool RenderBox::s_hadOverflowClip = false; 70 71 RenderBox::RenderBox(Node* node) 72 : RenderBoxModelObject(node) 73 #ifdef ANDROID_LAYOUT 74 , m_visibleWidth(0) 75 #endif 76 , m_marginLeft(0) 77 , m_marginRight(0) 78 , m_marginTop(0) 79 , m_marginBottom(0) 80 , m_minPrefWidth(-1) 81 , m_maxPrefWidth(-1) 82 , m_inlineBoxWrapper(0) 83 { 84 setIsBox(); 85 } 86 87 RenderBox::~RenderBox() 88 { 89 } 90 91 void RenderBox::destroy() 92 { 93 // A lot of the code in this function is just pasted into 94 // RenderWidget::destroy. If anything in this function changes, 95 // be sure to fix RenderWidget::destroy() as well. 96 if (hasOverrideSize()) 97 gOverrideSizeMap->remove(this); 98 99 if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent())) 100 RenderBlock::removePercentHeightDescendant(this); 101 102 RenderBoxModelObject::destroy(); 103 } 104 105 void RenderBox::removeFloatingOrPositionedChildFromBlockLists() 106 { 107 ASSERT(isFloatingOrPositioned()); 108 109 if (documentBeingDestroyed()) 110 return; 111 112 if (isFloating()) { 113 RenderBlock* outermostBlock = containingBlock(); 114 for (RenderBlock* p = outermostBlock; p && !p->isRenderView(); p = p->containingBlock()) { 115 if (p->containsFloat(this)) 116 outermostBlock = p; 117 } 118 119 if (outermostBlock) { 120 RenderObject* parent = outermostBlock->parent(); 121 if (parent && parent->isFlexibleBox()) 122 outermostBlock = toRenderBlock(parent); 123 124 outermostBlock->markAllDescendantsWithFloatsForLayout(this, false); 125 } 126 } 127 128 if (isPositioned()) { 129 RenderObject* p; 130 for (p = parent(); p; p = p->parent()) { 131 if (p->isRenderBlock()) 132 toRenderBlock(p)->removePositionedObject(this); 133 } 134 } 135 } 136 137 void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) 138 { 139 s_hadOverflowClip = hasOverflowClip(); 140 141 if (style()) { 142 // The background of the root element or the body element could propagate up to 143 // the canvas. Just dirty the entire canvas when our style changes substantially. 144 if (diff >= StyleDifferenceRepaint && node() && 145 (node()->hasTagName(htmlTag) || node()->hasTagName(bodyTag))) 146 view()->repaint(); 147 148 // When a layout hint happens and an object's position style changes, we have to do a layout 149 // to dirty the render tree using the old position value now. 150 if (diff == StyleDifferenceLayout && parent() && style()->position() != newStyle->position()) { 151 markContainingBlocksForLayout(); 152 if (style()->position() == StaticPosition) 153 repaint(); 154 else if (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition) 155 parent()->setChildNeedsLayout(true); 156 if (isFloating() && !isPositioned() && (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition)) 157 removeFloatingOrPositionedChildFromBlockLists(); 158 } 159 } 160 161 RenderBoxModelObject::styleWillChange(diff, newStyle); 162 } 163 164 void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 165 { 166 RenderBoxModelObject::styleDidChange(diff, oldStyle); 167 168 if (needsLayout() && oldStyle && (oldStyle->height().isPercent() || oldStyle->minHeight().isPercent() || oldStyle->maxHeight().isPercent())) 169 RenderBlock::removePercentHeightDescendant(this); 170 171 // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the 172 // new zoomed coordinate space. 173 if (hasOverflowClip() && oldStyle && style() && oldStyle->effectiveZoom() != style()->effectiveZoom()) { 174 int left = scrollLeft(); 175 if (left) { 176 left = (left / oldStyle->effectiveZoom()) * style()->effectiveZoom(); 177 setScrollLeft(left); 178 } 179 int top = scrollTop(); 180 if (top) { 181 top = (top / oldStyle->effectiveZoom()) * style()->effectiveZoom(); 182 setScrollTop(top); 183 } 184 } 185 186 // Set the text color if we're the body. 187 if (isBody()) 188 document()->setTextColor(style()->color()); 189 } 190 191 void RenderBox::updateBoxModelInfoFromStyle() 192 { 193 RenderBoxModelObject::updateBoxModelInfoFromStyle(); 194 195 bool isRootObject = isRoot(); 196 bool isViewObject = isRenderView(); 197 198 // The root and the RenderView always paint their backgrounds/borders. 199 if (isRootObject || isViewObject) 200 setHasBoxDecorations(true); 201 202 setPositioned(style()->position() == AbsolutePosition || style()->position() == FixedPosition); 203 setFloating(!isPositioned() && style()->isFloating()); 204 205 // We also handle <body> and <html>, whose overflow applies to the viewport. 206 if (style()->overflowX() != OVISIBLE && !isRootObject && (isRenderBlock() || isTableRow() || isTableSection())) { 207 bool boxHasOverflowClip = true; 208 if (isBody()) { 209 // Overflow on the body can propagate to the viewport under the following conditions. 210 // (1) The root element is <html>. 211 // (2) We are the primary <body> (can be checked by looking at document.body). 212 // (3) The root element has visible overflow. 213 if (document()->documentElement()->hasTagName(htmlTag) && 214 document()->body() == node() && 215 document()->documentElement()->renderer()->style()->overflowX() == OVISIBLE) 216 boxHasOverflowClip = false; 217 } 218 219 // Check for overflow clip. 220 // It's sufficient to just check one direction, since it's illegal to have visible on only one overflow value. 221 if (boxHasOverflowClip) { 222 if (!s_hadOverflowClip) 223 // Erase the overflow 224 repaint(); 225 setHasOverflowClip(); 226 } 227 } 228 229 setHasTransform(style()->hasTransformRelatedProperty()); 230 setHasReflection(style()->boxReflect()); 231 } 232 233 void RenderBox::layout() 234 { 235 ASSERT(needsLayout()); 236 237 RenderObject* child = firstChild(); 238 if (!child) { 239 setNeedsLayout(false); 240 return; 241 } 242 243 LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y())); 244 while (child) { 245 child->layoutIfNeeded(); 246 ASSERT(!child->needsLayout()); 247 child = child->nextSibling(); 248 } 249 statePusher.pop(); 250 setNeedsLayout(false); 251 } 252 253 // More IE extensions. clientWidth and clientHeight represent the interior of an object 254 // excluding border and scrollbar. 255 int RenderBox::clientWidth() const 256 { 257 return width() - borderLeft() - borderRight() - verticalScrollbarWidth(); 258 } 259 260 int RenderBox::clientHeight() const 261 { 262 return height() - borderTop() - borderBottom() - horizontalScrollbarHeight(); 263 } 264 265 int RenderBox::scrollWidth() const 266 { 267 if (hasOverflowClip()) 268 return layer()->scrollWidth(); 269 // For objects with visible overflow, this matches IE. 270 if (style()->direction() == LTR) 271 return max(clientWidth(), rightmostPosition(true, false) - borderLeft()); 272 return clientWidth() - min(0, leftmostPosition(true, false) - borderLeft()); 273 } 274 275 int RenderBox::scrollHeight() const 276 { 277 if (hasOverflowClip()) 278 return layer()->scrollHeight(); 279 // For objects with visible overflow, this matches IE. 280 return max(clientHeight(), lowestPosition(true, false) - borderTop()); 281 } 282 283 int RenderBox::scrollLeft() const 284 { 285 return hasOverflowClip() ? layer()->scrollXOffset() : 0; 286 } 287 288 int RenderBox::scrollTop() const 289 { 290 return hasOverflowClip() ? layer()->scrollYOffset() : 0; 291 } 292 293 void RenderBox::setScrollLeft(int newLeft) 294 { 295 if (hasOverflowClip()) 296 layer()->scrollToXOffset(newLeft); 297 } 298 299 void RenderBox::setScrollTop(int newTop) 300 { 301 if (hasOverflowClip()) 302 layer()->scrollToYOffset(newTop); 303 } 304 305 void RenderBox::absoluteRects(Vector<IntRect>& rects, int tx, int ty) 306 { 307 rects.append(IntRect(tx, ty, width(), height())); 308 } 309 310 void RenderBox::absoluteQuads(Vector<FloatQuad>& quads) 311 { 312 quads.append(localToAbsoluteQuad(FloatRect(0, 0, width(), height()))); 313 } 314 315 IntRect RenderBox::absoluteContentBox() const 316 { 317 IntRect rect = contentBoxRect(); 318 FloatPoint absPos = localToAbsolute(FloatPoint()); 319 rect.move(absPos.x(), absPos.y()); 320 return rect; 321 } 322 323 FloatQuad RenderBox::absoluteContentQuad() const 324 { 325 IntRect rect = contentBoxRect(); 326 return localToAbsoluteQuad(FloatRect(rect)); 327 } 328 329 IntRect RenderBox::outlineBoundsForRepaint(RenderBoxModelObject* repaintContainer) const 330 { 331 IntRect box = borderBoundingBox(); 332 adjustRectForOutlineAndShadow(box); 333 334 FloatQuad containerRelativeQuad = localToContainerQuad(FloatRect(box), repaintContainer); 335 box = containerRelativeQuad.enclosingBoundingBox(); 336 337 // FIXME: layoutDelta needs to be applied in parts before/after transforms and 338 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 339 box.move(view()->layoutDelta()); 340 341 return box; 342 } 343 344 void RenderBox::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) 345 { 346 if (width() && height()) 347 rects.append(IntRect(tx, ty, width(), height())); 348 } 349 350 IntRect RenderBox::reflectionBox() const 351 { 352 IntRect result; 353 if (!style()->boxReflect()) 354 return result; 355 IntRect box = borderBoxRect(); 356 result = box; 357 switch (style()->boxReflect()->direction()) { 358 case ReflectionBelow: 359 result.move(0, box.height() + reflectionOffset()); 360 break; 361 case ReflectionAbove: 362 result.move(0, -box.height() - reflectionOffset()); 363 break; 364 case ReflectionLeft: 365 result.move(-box.width() - reflectionOffset(), 0); 366 break; 367 case ReflectionRight: 368 result.move(box.width() + reflectionOffset(), 0); 369 break; 370 } 371 return result; 372 } 373 374 int RenderBox::reflectionOffset() const 375 { 376 if (!style()->boxReflect()) 377 return 0; 378 if (style()->boxReflect()->direction() == ReflectionLeft || style()->boxReflect()->direction() == ReflectionRight) 379 return style()->boxReflect()->offset().calcValue(borderBoxRect().width()); 380 return style()->boxReflect()->offset().calcValue(borderBoxRect().height()); 381 } 382 383 IntRect RenderBox::reflectedRect(const IntRect& r) const 384 { 385 if (!style()->boxReflect()) 386 return IntRect(); 387 388 IntRect box = borderBoxRect(); 389 IntRect result = r; 390 switch (style()->boxReflect()->direction()) { 391 case ReflectionBelow: 392 result.setY(box.bottom() + reflectionOffset() + (box.bottom() - r.bottom())); 393 break; 394 case ReflectionAbove: 395 result.setY(box.y() - reflectionOffset() - box.height() + (box.bottom() - r.bottom())); 396 break; 397 case ReflectionLeft: 398 result.setX(box.x() - reflectionOffset() - box.width() + (box.right() - r.right())); 399 break; 400 case ReflectionRight: 401 result.setX(box.right() + reflectionOffset() + (box.right() - r.right())); 402 break; 403 } 404 return result; 405 } 406 407 int RenderBox::verticalScrollbarWidth() const 408 { 409 return includeVerticalScrollbarSize() ? layer()->verticalScrollbarWidth() : 0; 410 } 411 412 int RenderBox::horizontalScrollbarHeight() const 413 { 414 return includeHorizontalScrollbarSize() ? layer()->horizontalScrollbarHeight() : 0; 415 } 416 417 bool RenderBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) 418 { 419 RenderLayer* l = layer(); 420 if (l && l->scroll(direction, granularity, multiplier)) { 421 if (stopNode) 422 *stopNode = node(); 423 return true; 424 } 425 426 if (stopNode && *stopNode && *stopNode == node()) 427 return true; 428 429 RenderBlock* b = containingBlock(); 430 if (b && !b->isRenderView()) 431 return b->scroll(direction, granularity, multiplier, stopNode); 432 return false; 433 } 434 435 bool RenderBox::canBeScrolledAndHasScrollableArea() const 436 { 437 return canBeProgramaticallyScrolled(false) && (scrollHeight() != clientHeight() || scrollWidth() != clientWidth()); 438 } 439 440 bool RenderBox::canBeProgramaticallyScrolled(bool) const 441 { 442 return (hasOverflowClip() && (scrollsOverflow() || (node() && node()->isContentEditable()))) || (node() && node()->isDocumentNode()); 443 } 444 445 void RenderBox::autoscroll() 446 { 447 if (layer()) 448 layer()->autoscroll(); 449 } 450 451 void RenderBox::panScroll(const IntPoint& source) 452 { 453 if (layer()) 454 layer()->panScrollFromPoint(source); 455 } 456 457 int RenderBox::minPrefWidth() const 458 { 459 if (prefWidthsDirty()) 460 const_cast<RenderBox*>(this)->calcPrefWidths(); 461 462 return m_minPrefWidth; 463 } 464 465 int RenderBox::maxPrefWidth() const 466 { 467 if (prefWidthsDirty()) 468 const_cast<RenderBox*>(this)->calcPrefWidths(); 469 470 return m_maxPrefWidth; 471 } 472 473 int RenderBox::overrideSize() const 474 { 475 if (!hasOverrideSize()) 476 return -1; 477 return gOverrideSizeMap->get(this); 478 } 479 480 void RenderBox::setOverrideSize(int s) 481 { 482 if (s == -1) { 483 if (hasOverrideSize()) { 484 setHasOverrideSize(false); 485 gOverrideSizeMap->remove(this); 486 } 487 } else { 488 if (!gOverrideSizeMap) 489 gOverrideSizeMap = new OverrideSizeMap(); 490 setHasOverrideSize(true); 491 gOverrideSizeMap->set(this, s); 492 } 493 } 494 495 int RenderBox::overrideWidth() const 496 { 497 return hasOverrideSize() ? overrideSize() : width(); 498 } 499 500 int RenderBox::overrideHeight() const 501 { 502 return hasOverrideSize() ? overrideSize() : height(); 503 } 504 505 int RenderBox::calcBorderBoxWidth(int width) const 506 { 507 int bordersPlusPadding = borderLeft() + borderRight() + paddingLeft() + paddingRight(); 508 if (style()->boxSizing() == CONTENT_BOX) 509 return width + bordersPlusPadding; 510 return max(width, bordersPlusPadding); 511 } 512 513 int RenderBox::calcBorderBoxHeight(int height) const 514 { 515 int bordersPlusPadding = borderTop() + borderBottom() + paddingTop() + paddingBottom(); 516 if (style()->boxSizing() == CONTENT_BOX) 517 return height + bordersPlusPadding; 518 return max(height, bordersPlusPadding); 519 } 520 521 int RenderBox::calcContentBoxWidth(int width) const 522 { 523 if (style()->boxSizing() == BORDER_BOX) 524 width -= (borderLeft() + borderRight() + paddingLeft() + paddingRight()); 525 return max(0, width); 526 } 527 528 int RenderBox::calcContentBoxHeight(int height) const 529 { 530 if (style()->boxSizing() == BORDER_BOX) 531 height -= (borderTop() + borderBottom() + paddingTop() + paddingBottom()); 532 return max(0, height); 533 } 534 535 // Hit Testing 536 bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action) 537 { 538 tx += x(); 539 ty += y(); 540 541 // Check kids first. 542 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { 543 if (!child->hasLayer() && child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) { 544 updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty)); 545 return true; 546 } 547 } 548 549 // Check our bounds next. For this purpose always assume that we can only be hit in the 550 // foreground phase (which is true for replaced elements like images). 551 if (visibleToHitTesting() && action == HitTestForeground && IntRect(tx, ty, width(), height()).contains(xPos, yPos)) { 552 updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty)); 553 return true; 554 } 555 556 return false; 557 } 558 559 // --------------------- painting stuff ------------------------------- 560 561 void RenderBox::paint(PaintInfo& paintInfo, int tx, int ty) 562 { 563 tx += x(); 564 ty += y(); 565 566 // default implementation. Just pass paint through to the children 567 PaintInfo childInfo(paintInfo); 568 childInfo.paintingRoot = paintingRootForChildren(paintInfo); 569 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) 570 child->paint(childInfo, tx, ty); 571 } 572 573 void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int tx, int ty) 574 { 575 const FillLayer* bgLayer = style()->backgroundLayers(); 576 Color bgColor = style()->backgroundColor(); 577 RenderObject* bodyObject = 0; 578 if (!style()->hasBackground() && node() && node()->hasTagName(HTMLNames::htmlTag)) { 579 // Locate the <body> element using the DOM. This is easier than trying 580 // to crawl around a render tree with potential :before/:after content and 581 // anonymous blocks created by inline <body> tags etc. We can locate the <body> 582 // render object very easily via the DOM. 583 HTMLElement* body = document()->body(); 584 bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0; 585 if (bodyObject) { 586 bgLayer = bodyObject->style()->backgroundLayers(); 587 bgColor = bodyObject->style()->backgroundColor(); 588 } 589 } 590 591 int w = width(); 592 int h = height(); 593 594 int rw; 595 int rh; 596 if (view()->frameView()) { 597 rw = view()->frameView()->contentsWidth(); 598 rh = view()->frameView()->contentsHeight(); 599 } else { 600 rw = view()->width(); 601 rh = view()->height(); 602 } 603 604 // CSS2 14.2: 605 // The background of the box generated by the root element covers the entire canvas including 606 // its margins. 607 int bx = tx - marginLeft(); 608 int by = ty - marginTop(); 609 int bw = max(w + marginLeft() + marginRight() + borderLeft() + borderRight(), rw); 610 int bh = max(h + marginTop() + marginBottom() + borderTop() + borderBottom(), rh); 611 612 paintFillLayers(paintInfo, bgColor, bgLayer, bx, by, bw, bh, CompositeSourceOver, bodyObject); 613 614 if (style()->hasBorder() && style()->display() != INLINE) 615 paintBorder(paintInfo.context, tx, ty, w, h, style()); 616 } 617 618 void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) 619 { 620 if (!shouldPaintWithinRoot(paintInfo)) 621 return; 622 623 if (isRoot()) { 624 paintRootBoxDecorations(paintInfo, tx, ty); 625 return; 626 } 627 628 int w = width(); 629 int h = height(); 630 631 // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat 632 // balloon layout is an example of this). 633 borderFitAdjust(tx, w); 634 635 // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have 636 // custom shadows of their own. 637 paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Normal); 638 639 // If we have a native theme appearance, paint that before painting our background. 640 // The theme will tell us whether or not we should also paint the CSS background. 641 bool themePainted = style()->hasAppearance() && !theme()->paint(this, paintInfo, IntRect(tx, ty, w, h)); 642 if (!themePainted) { 643 // The <body> only paints its background if the root element has defined a background 644 // independent of the body. Go through the DOM to get to the root element's render object, 645 // since the root could be inline and wrapped in an anonymous block. 646 if (!isBody() || document()->documentElement()->renderer()->style()->hasBackground()) 647 paintFillLayers(paintInfo, style()->backgroundColor(), style()->backgroundLayers(), tx, ty, w, h); 648 if (style()->hasAppearance()) 649 theme()->paintDecorations(this, paintInfo, IntRect(tx, ty, w, h)); 650 } 651 paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Inset); 652 653 // The theme will tell us whether or not we should also paint the CSS border. 654 if ((!style()->hasAppearance() || (!themePainted && theme()->paintBorderOnly(this, paintInfo, IntRect(tx, ty, w, h)))) && style()->hasBorder()) 655 paintBorder(paintInfo.context, tx, ty, w, h, style()); 656 } 657 658 void RenderBox::paintMask(PaintInfo& paintInfo, int tx, int ty) 659 { 660 if (!shouldPaintWithinRoot(paintInfo) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) 661 return; 662 663 int w = width(); 664 int h = height(); 665 666 // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat 667 // balloon layout is an example of this). 668 borderFitAdjust(tx, w); 669 670 paintMaskImages(paintInfo, tx, ty, w, h); 671 } 672 673 void RenderBox::paintMaskImages(const PaintInfo& paintInfo, int tx, int ty, int w, int h) 674 { 675 // Figure out if we need to push a transparency layer to render our mask. 676 bool pushTransparencyLayer = false; 677 bool compositedMask = hasLayer() && layer()->hasCompositedMask(); 678 CompositeOperator compositeOp = CompositeSourceOver; 679 680 bool allMaskImagesLoaded = true; 681 682 if (!compositedMask) { 683 StyleImage* maskBoxImage = style()->maskBoxImage().image(); 684 const FillLayer* maskLayers = style()->maskLayers(); 685 686 // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content. 687 if (maskBoxImage) 688 allMaskImagesLoaded &= maskBoxImage->isLoaded(); 689 690 if (maskLayers) 691 allMaskImagesLoaded &= maskLayers->imagesAreLoaded(); 692 693 // Before all images have loaded, just use an empty transparency layer as the mask. 694 if (!allMaskImagesLoaded) 695 pushTransparencyLayer = true; 696 697 if (maskBoxImage && maskLayers->hasImage()) { 698 // We have a mask-box-image and mask-image, so need to composite them together before using the result as a mask. 699 pushTransparencyLayer = true; 700 } else { 701 // We have to use an extra image buffer to hold the mask. Multiple mask images need 702 // to composite together using source-over so that they can then combine into a single unified mask that 703 // can be composited with the content using destination-in. SVG images need to be able to set compositing modes 704 // as they draw images contained inside their sub-document, so we paint all our images into a separate buffer 705 // and composite that buffer as the mask. 706 // We have to check that the mask images to be rendered contain at least one image that can be actually used in rendering 707 // before pushing the transparency layer. 708 for (const FillLayer* fillLayer = maskLayers->next(); fillLayer; fillLayer = fillLayer->next()) { 709 if (fillLayer->hasImage() && fillLayer->image()->canRender(style()->effectiveZoom())) { 710 pushTransparencyLayer = true; 711 // We found one image that can be used in rendering, exit the loop 712 break; 713 } 714 } 715 } 716 717 compositeOp = CompositeDestinationIn; 718 if (pushTransparencyLayer) { 719 paintInfo.context->setCompositeOperation(CompositeDestinationIn); 720 paintInfo.context->beginTransparencyLayer(1.0f); 721 compositeOp = CompositeSourceOver; 722 } 723 } 724 725 if (allMaskImagesLoaded) { 726 paintFillLayers(paintInfo, Color(), style()->maskLayers(), tx, ty, w, h, compositeOp); 727 paintNinePieceImage(paintInfo.context, tx, ty, w, h, style(), style()->maskBoxImage(), compositeOp); 728 } 729 730 if (pushTransparencyLayer) 731 paintInfo.context->endTransparencyLayer(); 732 } 733 734 IntRect RenderBox::maskClipRect() 735 { 736 IntRect bbox = borderBoxRect(); 737 if (style()->maskBoxImage().image()) 738 return bbox; 739 740 IntRect result; 741 for (const FillLayer* maskLayer = style()->maskLayers(); maskLayer; maskLayer = maskLayer->next()) { 742 if (maskLayer->image()) { 743 IntRect maskRect; 744 IntPoint phase; 745 IntSize tileSize; 746 calculateBackgroundImageGeometry(maskLayer, bbox.x(), bbox.y(), bbox.width(), bbox.height(), maskRect, phase, tileSize); 747 result.unite(maskRect); 748 } 749 } 750 return result; 751 } 752 753 void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject) 754 { 755 if (!fillLayer) 756 return; 757 758 paintFillLayers(paintInfo, c, fillLayer->next(), tx, ty, width, height, op, backgroundObject); 759 paintFillLayer(paintInfo, c, fillLayer, tx, ty, width, height, op, backgroundObject); 760 } 761 762 void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject) 763 { 764 paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, width, height, 0, op, backgroundObject); 765 } 766 767 void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*) 768 { 769 if (!parent()) 770 return; 771 772 if ((style()->borderImage().image() && style()->borderImage().image()->data() == image) || 773 (style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image)) { 774 repaint(); 775 return; 776 } 777 778 bool didFullRepaint = repaintLayerRectsForImage(image, style()->backgroundLayers(), true); 779 if (!didFullRepaint) 780 repaintLayerRectsForImage(image, style()->maskLayers(), false); 781 } 782 783 bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground) 784 { 785 IntRect rendererRect; 786 RenderBox* layerRenderer = 0; 787 788 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) { 789 if (curLayer->image() && image == curLayer->image()->data() && curLayer->image()->canRender(style()->effectiveZoom())) { 790 // Now that we know this image is being used, compute the renderer and the rect 791 // if we haven't already 792 if (!layerRenderer) { 793 bool drawingRootBackground = drawingBackground && (isRoot() || (isBody() && !document()->documentElement()->renderer()->style()->hasBackground())); 794 if (drawingRootBackground) { 795 layerRenderer = view(); 796 797 int rw; 798 int rh; 799 800 if (FrameView* frameView = toRenderView(layerRenderer)->frameView()) { 801 rw = frameView->contentsWidth(); 802 rh = frameView->contentsHeight(); 803 } else { 804 rw = layerRenderer->width(); 805 rh = layerRenderer->height(); 806 } 807 rendererRect = IntRect(-layerRenderer->marginLeft(), 808 -layerRenderer->marginTop(), 809 max(layerRenderer->width() + layerRenderer->marginLeft() + layerRenderer->marginRight() + layerRenderer->borderLeft() + layerRenderer->borderRight(), rw), 810 max(layerRenderer->height() + layerRenderer->marginTop() + layerRenderer->marginBottom() + layerRenderer->borderTop() + layerRenderer->borderBottom(), rh)); 811 } else { 812 layerRenderer = this; 813 rendererRect = borderBoxRect(); 814 } 815 } 816 817 IntRect repaintRect; 818 IntPoint phase; 819 IntSize tileSize; 820 layerRenderer->calculateBackgroundImageGeometry(curLayer, rendererRect.x(), rendererRect.y(), rendererRect.width(), rendererRect.height(), repaintRect, phase, tileSize); 821 layerRenderer->repaintRectangle(repaintRect); 822 if (repaintRect == rendererRect) 823 return true; 824 } 825 } 826 return false; 827 } 828 829 #if PLATFORM(MAC) 830 831 void RenderBox::paintCustomHighlight(int tx, int ty, const AtomicString& type, bool behindText) 832 { 833 Frame* frame = document()->frame(); 834 if (!frame) 835 return; 836 Page* page = frame->page(); 837 if (!page) 838 return; 839 840 InlineBox* boxWrap = inlineBoxWrapper(); 841 RootInlineBox* r = boxWrap ? boxWrap->root() : 0; 842 if (r) { 843 FloatRect rootRect(tx + r->x(), ty + r->selectionTop(), r->width(), r->selectionHeight()); 844 FloatRect imageRect(tx + x(), rootRect.y(), width(), rootRect.height()); 845 page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, rootRect, behindText, false); 846 } else { 847 FloatRect imageRect(tx + x(), ty + y(), width(), height()); 848 page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, imageRect, behindText, false); 849 } 850 } 851 852 #endif 853 854 bool RenderBox::pushContentsClip(PaintInfo& paintInfo, int tx, int ty) 855 { 856 if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseSelfOutline || paintInfo.phase == PaintPhaseMask) 857 return false; 858 859 bool isControlClip = hasControlClip(); 860 bool isOverflowClip = hasOverflowClip() && !layer()->isSelfPaintingLayer(); 861 862 if (!isControlClip && !isOverflowClip) 863 return false; 864 865 if (paintInfo.phase == PaintPhaseOutline) 866 paintInfo.phase = PaintPhaseChildOutlines; 867 else if (paintInfo.phase == PaintPhaseChildBlockBackground) { 868 paintInfo.phase = PaintPhaseBlockBackground; 869 paintObject(paintInfo, tx, ty); 870 paintInfo.phase = PaintPhaseChildBlockBackgrounds; 871 } 872 IntRect clipRect(isControlClip ? controlClipRect(tx, ty) : overflowClipRect(tx, ty)); 873 paintInfo.context->save(); 874 if (style()->hasBorderRadius()) { 875 IntSize topLeft, topRight, bottomLeft, bottomRight; 876 IntRect borderRect = IntRect(tx, ty, width(), height()); 877 style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight); 878 879 paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); 880 } 881 882 paintInfo.context->clip(clipRect); 883 return true; 884 } 885 886 void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, int tx, int ty) 887 { 888 ASSERT(hasControlClip() || (hasOverflowClip() && !layer()->isSelfPaintingLayer())); 889 890 paintInfo.context->restore(); 891 if (originalPhase == PaintPhaseOutline) { 892 paintInfo.phase = PaintPhaseSelfOutline; 893 paintObject(paintInfo, tx, ty); 894 paintInfo.phase = originalPhase; 895 } else if (originalPhase == PaintPhaseChildBlockBackground) 896 paintInfo.phase = originalPhase; 897 } 898 899 IntRect RenderBox::overflowClipRect(int tx, int ty) 900 { 901 // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property 902 // here. 903 904 int bLeft = borderLeft(); 905 int bTop = borderTop(); 906 907 int clipX = tx + bLeft; 908 int clipY = ty + bTop; 909 int clipWidth = width() - bLeft - borderRight(); 910 int clipHeight = height() - bTop - borderBottom(); 911 912 // Subtract out scrollbars if we have them. 913 if (layer()) { 914 clipWidth -= layer()->verticalScrollbarWidth(); 915 clipHeight -= layer()->horizontalScrollbarHeight(); 916 } 917 918 return IntRect(clipX, clipY, clipWidth, clipHeight); 919 } 920 921 IntRect RenderBox::clipRect(int tx, int ty) 922 { 923 int clipX = tx; 924 int clipY = ty; 925 int clipWidth = width(); 926 int clipHeight = height(); 927 928 if (!style()->clipLeft().isAuto()) { 929 int c = style()->clipLeft().calcValue(width()); 930 clipX += c; 931 clipWidth -= c; 932 } 933 934 if (!style()->clipRight().isAuto()) 935 clipWidth -= width() - style()->clipRight().calcValue(width()); 936 937 if (!style()->clipTop().isAuto()) { 938 int c = style()->clipTop().calcValue(height()); 939 clipY += c; 940 clipHeight -= c; 941 } 942 943 if (!style()->clipBottom().isAuto()) 944 clipHeight -= height() - style()->clipBottom().calcValue(height()); 945 946 return IntRect(clipX, clipY, clipWidth, clipHeight); 947 } 948 949 int RenderBox::containingBlockWidthForContent() const 950 { 951 RenderBlock* cb = containingBlock(); 952 if (shrinkToAvoidFloats()) 953 return cb->lineWidth(y(), false); 954 return cb->availableWidth(); 955 } 956 957 void RenderBox::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const 958 { 959 if (repaintContainer == this) 960 return; 961 962 if (RenderView* v = view()) { 963 if (v->layoutStateEnabled() && !repaintContainer) { 964 LayoutState* layoutState = v->layoutState(); 965 IntSize offset = layoutState->m_offset; 966 offset.expand(x(), y()); 967 if (style()->position() == RelativePosition && layer()) 968 offset += layer()->relativePositionOffset(); 969 transformState.move(offset); 970 return; 971 } 972 } 973 974 bool containerSkipped; 975 RenderObject* o = container(repaintContainer, &containerSkipped); 976 if (!o) 977 return; 978 979 bool isFixedPos = style()->position() == FixedPosition; 980 bool hasTransform = hasLayer() && layer()->transform(); 981 if (hasTransform) { 982 // If this box has a transform, it acts as a fixed position container for fixed descendants, 983 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. 984 fixed &= isFixedPos; 985 } else 986 fixed |= isFixedPos; 987 988 IntSize containerOffset = offsetFromContainer(o); 989 990 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); 991 if (useTransforms && shouldUseTransformFromContainer(o)) { 992 TransformationMatrix t; 993 getTransformFromContainer(o, containerOffset, t); 994 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 995 } else 996 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 997 998 if (containerSkipped) { 999 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe 1000 // to just subtract the delta between the repaintContainer and o. 1001 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); 1002 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1003 return; 1004 } 1005 1006 o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); 1007 } 1008 1009 void RenderBox::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const 1010 { 1011 // We don't expect absoluteToLocal() to be called during layout (yet) 1012 ASSERT(!view() || !view()->layoutStateEnabled()); 1013 1014 bool isFixedPos = style()->position() == FixedPosition; 1015 bool hasTransform = hasLayer() && layer()->transform(); 1016 if (hasTransform) { 1017 // If this box has a transform, it acts as a fixed position container for fixed descendants, 1018 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. 1019 fixed &= isFixedPos; 1020 } else 1021 fixed |= isFixedPos; 1022 1023 RenderObject* o = container(); 1024 if (!o) 1025 return; 1026 1027 o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState); 1028 1029 IntSize containerOffset = offsetFromContainer(o); 1030 1031 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); 1032 if (useTransforms && shouldUseTransformFromContainer(o)) { 1033 TransformationMatrix t; 1034 getTransformFromContainer(o, containerOffset, t); 1035 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1036 } else 1037 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1038 } 1039 1040 IntSize RenderBox::offsetFromContainer(RenderObject* o) const 1041 { 1042 ASSERT(o == container()); 1043 1044 IntSize offset; 1045 if (isRelPositioned()) 1046 offset += relativePositionOffset(); 1047 1048 if (!isInline() || isReplaced()) { 1049 RenderBlock* cb; 1050 if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition 1051 && (cb = toRenderBlock(o))->hasColumns()) { 1052 IntRect rect(x(), y(), 1, 1); 1053 cb->adjustRectForColumns(rect); 1054 offset.expand(rect.x(), rect.y()); 1055 } else 1056 offset.expand(x(), y()); 1057 } 1058 1059 if (o->hasOverflowClip()) 1060 offset -= toRenderBox(o)->layer()->scrolledContentOffset(); 1061 1062 if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isRenderInline()) 1063 offset += toRenderInline(o)->relativePositionedInlineOffset(this); 1064 1065 return offset; 1066 } 1067 1068 InlineBox* RenderBox::createInlineBox() 1069 { 1070 return new (renderArena()) InlineBox(this); 1071 } 1072 1073 void RenderBox::dirtyLineBoxes(bool fullLayout) 1074 { 1075 if (m_inlineBoxWrapper) { 1076 if (fullLayout) { 1077 m_inlineBoxWrapper->destroy(renderArena()); 1078 m_inlineBoxWrapper = 0; 1079 } else 1080 m_inlineBoxWrapper->dirtyLineBoxes(); 1081 } 1082 } 1083 1084 void RenderBox::positionLineBox(InlineBox* box) 1085 { 1086 if (isPositioned()) { 1087 // Cache the x position only if we were an INLINE type originally. 1088 bool wasInline = style()->isOriginalDisplayInlineType(); 1089 if (wasInline && style()->hasStaticX()) { 1090 // The value is cached in the xPos of the box. We only need this value if 1091 // our object was inline originally, since otherwise it would have ended up underneath 1092 // the inlines. 1093 layer()->setStaticX(box->x()); 1094 setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. 1095 } else if (!wasInline && style()->hasStaticY()) { 1096 // Our object was a block originally, so we make our normal flow position be 1097 // just below the line box (as though all the inlines that came before us got 1098 // wrapped in an anonymous block, which is what would have happened had we been 1099 // in flow). This value was cached in the y() of the box. 1100 layer()->setStaticY(box->y()); 1101 setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. 1102 } 1103 1104 // Nuke the box. 1105 box->remove(); 1106 box->destroy(renderArena()); 1107 } else if (isReplaced()) { 1108 setLocation(box->x(), box->y()); 1109 m_inlineBoxWrapper = box; 1110 } 1111 } 1112 1113 void RenderBox::deleteLineBoxWrapper() 1114 { 1115 if (m_inlineBoxWrapper) { 1116 if (!documentBeingDestroyed()) 1117 m_inlineBoxWrapper->remove(); 1118 m_inlineBoxWrapper->destroy(renderArena()); 1119 m_inlineBoxWrapper = 0; 1120 } 1121 } 1122 1123 IntRect RenderBox::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) 1124 { 1125 if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent()) 1126 return IntRect(); 1127 1128 IntRect r = visibleOverflowRect(); 1129 1130 RenderView* v = view(); 1131 if (v) { 1132 // FIXME: layoutDelta needs to be applied in parts before/after transforms and 1133 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 1134 r.move(v->layoutDelta()); 1135 } 1136 1137 if (style()) { 1138 if (style()->hasAppearance()) 1139 // The theme may wish to inflate the rect used when repainting. 1140 theme()->adjustRepaintRect(this, r); 1141 1142 // We have to use maximalOutlineSize() because a child might have an outline 1143 // that projects outside of our overflowRect. 1144 if (v) { 1145 ASSERT(style()->outlineSize() <= v->maximalOutlineSize()); 1146 r.inflate(v->maximalOutlineSize()); 1147 } 1148 } 1149 computeRectForRepaint(repaintContainer, r); 1150 return r; 1151 } 1152 1153 void RenderBox::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) 1154 { 1155 if (RenderView* v = view()) { 1156 // LayoutState is only valid for root-relative repainting 1157 if (v->layoutStateEnabled() && !repaintContainer) { 1158 LayoutState* layoutState = v->layoutState(); 1159 1160 if (layer() && layer()->transform()) 1161 rect = layer()->transform()->mapRect(rect); 1162 1163 if (style()->position() == RelativePosition && layer()) 1164 rect.move(layer()->relativePositionOffset()); 1165 1166 rect.move(x(), y()); 1167 rect.move(layoutState->m_offset); 1168 if (layoutState->m_clipped) 1169 rect.intersect(layoutState->m_clipRect); 1170 return; 1171 } 1172 } 1173 1174 if (hasReflection()) 1175 rect.unite(reflectedRect(rect)); 1176 1177 if (repaintContainer == this) 1178 return; 1179 1180 bool containerSkipped; 1181 RenderObject* o = container(repaintContainer, &containerSkipped); 1182 if (!o) 1183 return; 1184 1185 IntPoint topLeft = rect.location(); 1186 topLeft.move(x(), y()); 1187 1188 if (style()->position() == FixedPosition) 1189 fixed = true; 1190 1191 if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition) { 1192 RenderBlock* cb = toRenderBlock(o); 1193 if (cb->hasColumns()) { 1194 IntRect repaintRect(topLeft, rect.size()); 1195 cb->adjustRectForColumns(repaintRect); 1196 topLeft = repaintRect.location(); 1197 rect = repaintRect; 1198 } 1199 } 1200 1201 // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box 1202 // in the parent's coordinate space that encloses us. 1203 if (layer() && layer()->transform()) { 1204 fixed = false; 1205 rect = layer()->transform()->mapRect(rect); 1206 // FIXME: this clobbers topLeft adjustment done for multicol above 1207 topLeft = rect.location(); 1208 topLeft.move(x(), y()); 1209 } 1210 1211 if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isRenderInline()) 1212 topLeft += toRenderInline(o)->relativePositionedInlineOffset(this); 1213 else if (style()->position() == RelativePosition && layer()) { 1214 // Apply the relative position offset when invalidating a rectangle. The layer 1215 // is translated, but the render box isn't, so we need to do this to get the 1216 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position 1217 // flag on the RenderObject has been cleared, so use the one on the style(). 1218 topLeft += layer()->relativePositionOffset(); 1219 } 1220 1221 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout, 1222 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer. 1223 if (o->hasOverflowClip()) { 1224 RenderBox* containerBox = toRenderBox(o); 1225 1226 // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the 1227 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint 1228 // anyway if its size does change. 1229 topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden. 1230 1231 IntRect repaintRect(topLeft, rect.size()); 1232 IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height()); 1233 rect = intersection(repaintRect, boxRect); 1234 if (rect.isEmpty()) 1235 return; 1236 } else 1237 rect.setLocation(topLeft); 1238 1239 if (containerSkipped) { 1240 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates. 1241 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); 1242 rect.move(-containerOffset); 1243 return; 1244 } 1245 1246 o->computeRectForRepaint(repaintContainer, rect, fixed); 1247 } 1248 1249 void RenderBox::repaintDuringLayoutIfMoved(const IntRect& rect) 1250 { 1251 int newX = x(); 1252 int newY = y(); 1253 int newWidth = width(); 1254 int newHeight = height(); 1255 if (rect.x() != newX || rect.y() != newY) { 1256 // The child moved. Invalidate the object's old and new positions. We have to do this 1257 // since the object may not have gotten a layout. 1258 m_frameRect = rect; 1259 repaint(); 1260 repaintOverhangingFloats(true); 1261 m_frameRect = IntRect(newX, newY, newWidth, newHeight); 1262 repaint(); 1263 repaintOverhangingFloats(true); 1264 } 1265 } 1266 1267 void RenderBox::calcWidth() 1268 { 1269 #ifdef ANDROID_LAYOUT 1270 if (view()->frameView()) { 1271 const Settings* settings = document()->settings(); 1272 ASSERT(settings); 1273 if (settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) { 1274 m_visibleWidth = view()->frameView()->screenWidth(); 1275 } 1276 } 1277 #endif 1278 1279 if (isPositioned()) { 1280 calcAbsoluteHorizontal(); 1281 return; 1282 } 1283 1284 // If layout is limited to a subtree, the subtree root's width does not change. 1285 if (node() && view()->frameView() && view()->frameView()->layoutRoot(true) == this) 1286 return; 1287 1288 // The parent box is flexing us, so it has increased or decreased our 1289 // width. Use the width from the style context. 1290 if (hasOverrideSize() && parent()->style()->boxOrient() == HORIZONTAL 1291 && parent()->isFlexibleBox() && parent()->isFlexingChildren()) { 1292 setWidth(overrideSize()); 1293 return; 1294 } 1295 1296 bool inVerticalBox = parent()->isFlexibleBox() && (parent()->style()->boxOrient() == VERTICAL); 1297 bool stretching = (parent()->style()->boxAlign() == BSTRETCH); 1298 bool treatAsReplaced = shouldCalculateSizeAsReplaced() && (!inVerticalBox || !stretching); 1299 1300 Length w = (treatAsReplaced) ? Length(calcReplacedWidth(), Fixed) : style()->width(); 1301 1302 RenderBlock* cb = containingBlock(); 1303 int containerWidth = max(0, containingBlockWidthForContent()); 1304 1305 Length marginLeft = style()->marginLeft(); 1306 Length marginRight = style()->marginRight(); 1307 1308 if (isInline() && !isInlineBlockOrInlineTable()) { 1309 // just calculate margins 1310 m_marginLeft = marginLeft.calcMinValue(containerWidth); 1311 m_marginRight = marginRight.calcMinValue(containerWidth); 1312 #ifdef ANDROID_LAYOUT 1313 if (treatAsReplaced) { 1314 #else 1315 if (treatAsReplaced) 1316 #endif 1317 setWidth(max(w.value() + borderLeft() + borderRight() + paddingLeft() + paddingRight(), minPrefWidth())); 1318 1319 #ifdef ANDROID_LAYOUT 1320 // in SSR mode with replaced box, if the box width is wider than the container width, 1321 // it will be shrinked to fit to the container. 1322 if (containerWidth && (width() + m_marginLeft + m_marginRight) > containerWidth && 1323 document()->frame()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { 1324 m_marginLeft = m_marginRight = 0; 1325 setWidth(containerWidth); 1326 m_minPrefWidth = m_maxPrefWidth = containerWidth; 1327 } 1328 } 1329 #endif 1330 return; 1331 } 1332 1333 // Width calculations 1334 if (treatAsReplaced) 1335 setWidth(w.value() + borderLeft() + borderRight() + paddingLeft() + paddingRight()); 1336 else { 1337 // Calculate Width 1338 setWidth(calcWidthUsing(Width, containerWidth)); 1339 1340 // Calculate MaxWidth 1341 if (!style()->maxWidth().isUndefined()) { 1342 int maxW = calcWidthUsing(MaxWidth, containerWidth); 1343 if (width() > maxW) { 1344 setWidth(maxW); 1345 w = style()->maxWidth(); 1346 } 1347 } 1348 1349 // Calculate MinWidth 1350 int minW = calcWidthUsing(MinWidth, containerWidth); 1351 if (width() < minW) { 1352 setWidth(minW); 1353 w = style()->minWidth(); 1354 } 1355 } 1356 1357 if (stretchesToMinIntrinsicWidth()) { 1358 setWidth(max(width(), minPrefWidth())); 1359 w = Length(width(), Fixed); 1360 } 1361 1362 // Margin calculations 1363 if (w.isAuto()) { 1364 m_marginLeft = marginLeft.calcMinValue(containerWidth); 1365 m_marginRight = marginRight.calcMinValue(containerWidth); 1366 } else { 1367 m_marginLeft = 0; 1368 m_marginRight = 0; 1369 calcHorizontalMargins(marginLeft, marginRight, containerWidth); 1370 } 1371 #ifdef ANDROID_LAYOUT 1372 // in SSR mode with non-replaced box, we use ANDROID_SSR_MARGIN_PADDING for left/right margin. 1373 // If the box width is wider than the container width, it will be shrinked to fit to the container. 1374 if (containerWidth && !treatAsReplaced && 1375 document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { 1376 setWidth(width() + m_marginLeft + m_marginRight); 1377 m_marginLeft = m_marginLeft > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginLeft; 1378 m_marginRight = m_marginRight > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginRight; 1379 if (width() > containerWidth) { 1380 m_minPrefWidth = m_maxPrefWidth = containerWidth-(m_marginLeft + m_marginRight); 1381 setWidth(m_minPrefWidth); 1382 } else 1383 setWidth(width() -(m_marginLeft + m_marginRight)); 1384 } 1385 #endif 1386 1387 if (containerWidth && containerWidth != (width() + m_marginLeft + m_marginRight) 1388 && !isFloating() && !isInline() && !cb->isFlexibleBox()) { 1389 if (cb->style()->direction() == LTR) 1390 m_marginRight = containerWidth - width() - m_marginLeft; 1391 else 1392 m_marginLeft = containerWidth - width() - m_marginRight; 1393 } 1394 } 1395 1396 int RenderBox::calcWidthUsing(WidthType widthType, int cw) 1397 { 1398 int widthResult = width(); 1399 Length w; 1400 if (widthType == Width) 1401 w = style()->width(); 1402 else if (widthType == MinWidth) 1403 w = style()->minWidth(); 1404 else 1405 w = style()->maxWidth(); 1406 1407 if (w.isIntrinsicOrAuto()) { 1408 int marginLeft = style()->marginLeft().calcMinValue(cw); 1409 int marginRight = style()->marginRight().calcMinValue(cw); 1410 if (cw) 1411 widthResult = cw - marginLeft - marginRight; 1412 1413 if (sizesToIntrinsicWidth(widthType)) { 1414 widthResult = max(widthResult, minPrefWidth()); 1415 widthResult = min(widthResult, maxPrefWidth()); 1416 } 1417 } else 1418 widthResult = calcBorderBoxWidth(w.calcValue(cw)); 1419 1420 return widthResult; 1421 } 1422 1423 bool RenderBox::sizesToIntrinsicWidth(WidthType widthType) const 1424 { 1425 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks, 1426 // but they allow text to sit on the same line as the marquee. 1427 if (isFloating() || (isInlineBlockOrInlineTable() && !isHTMLMarquee())) 1428 return true; 1429 1430 // This code may look a bit strange. Basically width:intrinsic should clamp the size when testing both 1431 // min-width and width. max-width is only clamped if it is also intrinsic. 1432 Length width = (widthType == MaxWidth) ? style()->maxWidth() : style()->width(); 1433 if (width.type() == Intrinsic) 1434 return true; 1435 1436 // Children of a horizontal marquee do not fill the container by default. 1437 // FIXME: Need to deal with MAUTO value properly. It could be vertical. 1438 if (parent()->style()->overflowX() == OMARQUEE) { 1439 EMarqueeDirection dir = parent()->style()->marqueeDirection(); 1440 if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT) 1441 return true; 1442 } 1443 1444 // Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes 1445 // that don't stretch their kids lay out their children at their intrinsic widths. 1446 if (parent()->isFlexibleBox() 1447 && (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH)) 1448 return true; 1449 1450 return false; 1451 } 1452 1453 void RenderBox::calcHorizontalMargins(const Length& marginLeft, const Length& marginRight, int containerWidth) 1454 { 1455 if (isFloating() || isInline()) { 1456 // Inline blocks/tables and floats don't have their margins increased. 1457 m_marginLeft = marginLeft.calcMinValue(containerWidth); 1458 m_marginRight = marginRight.calcMinValue(containerWidth); 1459 return; 1460 } 1461 1462 if ((marginLeft.isAuto() && marginRight.isAuto() && width() < containerWidth) 1463 || (!marginLeft.isAuto() && !marginRight.isAuto() && containingBlock()->style()->textAlign() == WEBKIT_CENTER)) { 1464 m_marginLeft = max(0, (containerWidth - width()) / 2); 1465 m_marginRight = containerWidth - width() - m_marginLeft; 1466 } else if ((marginRight.isAuto() && width() < containerWidth) 1467 || (!marginLeft.isAuto() && containingBlock()->style()->direction() == RTL && containingBlock()->style()->textAlign() == WEBKIT_LEFT)) { 1468 m_marginLeft = marginLeft.calcValue(containerWidth); 1469 m_marginRight = containerWidth - width() - m_marginLeft; 1470 } else if ((marginLeft.isAuto() && width() < containerWidth) 1471 || (!marginRight.isAuto() && containingBlock()->style()->direction() == LTR && containingBlock()->style()->textAlign() == WEBKIT_RIGHT)) { 1472 m_marginRight = marginRight.calcValue(containerWidth); 1473 m_marginLeft = containerWidth - width() - m_marginRight; 1474 } else { 1475 // This makes auto margins 0 if we failed a width() < containerWidth test above (css2.1, 10.3.3). 1476 m_marginLeft = marginLeft.calcMinValue(containerWidth); 1477 m_marginRight = marginRight.calcMinValue(containerWidth); 1478 } 1479 } 1480 1481 void RenderBox::calcHeight() 1482 { 1483 // Cell height is managed by the table and inline non-replaced elements do not support a height property. 1484 if (isTableCell() || (isInline() && !isReplaced())) 1485 return; 1486 1487 Length h; 1488 if (isPositioned()) 1489 calcAbsoluteVertical(); 1490 else { 1491 calcVerticalMargins(); 1492 1493 // For tables, calculate margins only. 1494 if (isTable()) 1495 return; 1496 1497 bool inHorizontalBox = parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL; 1498 bool stretching = parent()->style()->boxAlign() == BSTRETCH; 1499 bool treatAsReplaced = shouldCalculateSizeAsReplaced() && (!inHorizontalBox || !stretching); 1500 bool checkMinMaxHeight = false; 1501 1502 // The parent box is flexing us, so it has increased or decreased our height. We have to 1503 // grab our cached flexible height. 1504 if (hasOverrideSize() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL 1505 && parent()->isFlexingChildren()) 1506 h = Length(overrideSize() - borderTop() - borderBottom() - paddingTop() - paddingBottom(), Fixed); 1507 else if (treatAsReplaced) 1508 h = Length(calcReplacedHeight(), Fixed); 1509 else { 1510 h = style()->height(); 1511 checkMinMaxHeight = true; 1512 } 1513 1514 // Block children of horizontal flexible boxes fill the height of the box. 1515 if (h.isAuto() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL 1516 && parent()->isStretchingChildren()) { 1517 h = Length(parentBox()->contentHeight() - marginTop() - marginBottom() - 1518 borderTop() - paddingTop() - borderBottom() - paddingBottom(), Fixed); 1519 checkMinMaxHeight = false; 1520 } 1521 1522 int heightResult; 1523 if (checkMinMaxHeight) { 1524 #ifdef ANDROID_LAYOUT 1525 // in SSR mode, ignore CSS height as layout is so different 1526 if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) 1527 heightResult = -1; 1528 else 1529 #endif 1530 heightResult = calcHeightUsing(style()->height()); 1531 if (heightResult == -1) 1532 heightResult = height(); 1533 int minH = calcHeightUsing(style()->minHeight()); // Leave as -1 if unset. 1534 int maxH = style()->maxHeight().isUndefined() ? heightResult : calcHeightUsing(style()->maxHeight()); 1535 if (maxH == -1) 1536 maxH = heightResult; 1537 heightResult = min(maxH, heightResult); 1538 heightResult = max(minH, heightResult); 1539 } else { 1540 // The only times we don't check min/max height are when a fixed length has 1541 // been given as an override. Just use that. The value has already been adjusted 1542 // for box-sizing. 1543 heightResult = h.value() + borderTop() + borderBottom() + paddingTop() + paddingBottom(); 1544 } 1545 1546 setHeight(heightResult); 1547 } 1548 1549 // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the 1550 // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height 1551 // is specified. When we're printing, we also need this quirk if the body or root has a percentage 1552 // height since we don't set a height in RenderView when we're printing. So without this quirk, the 1553 // height has nothing to be a percentage of, and it ends up being 0. That is bad. 1554 bool printingNeedsBaseHeight = document()->printing() && h.isPercent() 1555 && (isRoot() || (isBody() && document()->documentElement()->renderer()->style()->height().isPercent())); 1556 if (stretchesToViewHeight() || printingNeedsBaseHeight) { 1557 int margins = collapsedMarginTop() + collapsedMarginBottom(); 1558 int visHeight = document()->printing() ? view()->frameView()->visibleHeight() : view()->viewHeight(); 1559 if (isRoot()) 1560 setHeight(max(height(), visHeight - margins)); 1561 else { 1562 int marginsBordersPadding = margins + parentBox()->marginTop() + parentBox()->marginBottom() 1563 + parentBox()->borderTop() + parentBox()->borderBottom() 1564 + parentBox()->paddingTop() + parentBox()->paddingBottom(); 1565 setHeight(max(height(), visHeight - marginsBordersPadding)); 1566 } 1567 } 1568 } 1569 1570 int RenderBox::calcHeightUsing(const Length& h) 1571 { 1572 int height = -1; 1573 if (!h.isAuto()) { 1574 if (h.isFixed()) 1575 height = h.value(); 1576 else if (h.isPercent()) 1577 height = calcPercentageHeight(h); 1578 if (height != -1) { 1579 height = calcBorderBoxHeight(height); 1580 return height; 1581 } 1582 } 1583 return height; 1584 } 1585 1586 int RenderBox::calcPercentageHeight(const Length& height) 1587 { 1588 int result = -1; 1589 bool skippedAutoHeightContainingBlock = false; 1590 RenderBlock* cb = containingBlock(); 1591 if (style()->htmlHacks()) { 1592 // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing 1593 // block that may have a specified height and then use it. In strict mode, this violates the 1594 // specification, which states that percentage heights just revert to auto if the containing 1595 // block has an auto height. 1596 while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() && cb->style()->height().isAuto()) { 1597 skippedAutoHeightContainingBlock = true; 1598 cb = cb->containingBlock(); 1599 cb->addPercentHeightDescendant(this); 1600 } 1601 } 1602 1603 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height 1604 // explicitly specified that can be used for any percentage computations. 1605 bool isPositionedWithSpecifiedHeight = cb->isPositioned() && (!cb->style()->height().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto())); 1606 1607 bool includeBorderPadding = isTable(); 1608 1609 // Table cells violate what the CSS spec says to do with heights. Basically we 1610 // don't care if the cell specified a height or not. We just always make ourselves 1611 // be a percentage of the cell's current content height. 1612 if (cb->isTableCell()) { 1613 if (!skippedAutoHeightContainingBlock) { 1614 result = cb->overrideSize(); 1615 if (result == -1) { 1616 // Normally we would let the cell size intrinsically, but scrolling overflow has to be 1617 // treated differently, since WinIE lets scrolled overflow regions shrink as needed. 1618 // While we can't get all cases right, we can at least detect when the cell has a specified 1619 // height or when the table has a specified height. In these cases we want to initially have 1620 // no size and allow the flexing of the table or the cell to its specified height to cause us 1621 // to grow to fill the space. This could end up being wrong in some cases, but it is 1622 // preferable to the alternative (sizing intrinsically and making the row end up too big). 1623 RenderTableCell* cell = toRenderTableCell(cb); 1624 if (scrollsOverflowY() && (!cell->style()->height().isAuto() || !cell->table()->style()->height().isAuto())) 1625 return 0; 1626 return -1; 1627 } 1628 includeBorderPadding = true; 1629 } 1630 } 1631 // Otherwise we only use our percentage height if our containing block had a specified 1632 // height. 1633 else if (cb->style()->height().isFixed()) 1634 result = cb->calcContentBoxHeight(cb->style()->height().value()); 1635 else if (cb->style()->height().isPercent() && !isPositionedWithSpecifiedHeight) { 1636 // We need to recur and compute the percentage height for our containing block. 1637 result = cb->calcPercentageHeight(cb->style()->height()); 1638 if (result != -1) 1639 result = cb->calcContentBoxHeight(result); 1640 } else if (cb->isRenderView() || (cb->isBody() && style()->htmlHacks()) || isPositionedWithSpecifiedHeight) { 1641 // Don't allow this to affect the block' height() member variable, since this 1642 // can get called while the block is still laying out its kids. 1643 int oldHeight = cb->height(); 1644 cb->calcHeight(); 1645 result = cb->contentHeight(); 1646 cb->setHeight(oldHeight); 1647 } else if (cb->isRoot() && isPositioned()) 1648 // Match the positioned objects behavior, which is that positioned objects will fill their viewport 1649 // always. Note we could only hit this case by recurring into calcPercentageHeight on a positioned containing block. 1650 result = cb->calcContentBoxHeight(cb->availableHeight()); 1651 1652 if (result != -1) { 1653 result = height.calcValue(result); 1654 if (includeBorderPadding) { 1655 // It is necessary to use the border-box to match WinIE's broken 1656 // box model. This is essential for sizing inside 1657 // table cells using percentage heights. 1658 result -= (borderTop() + paddingTop() + borderBottom() + paddingBottom()); 1659 result = max(0, result); 1660 } 1661 } 1662 return result; 1663 } 1664 1665 int RenderBox::calcReplacedWidth(bool includeMaxWidth) const 1666 { 1667 int width = calcReplacedWidthUsing(style()->width()); 1668 int minW = calcReplacedWidthUsing(style()->minWidth()); 1669 int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth()); 1670 1671 return max(minW, min(width, maxW)); 1672 } 1673 1674 int RenderBox::calcReplacedWidthUsing(Length width) const 1675 { 1676 switch (width.type()) { 1677 case Fixed: 1678 return calcContentBoxWidth(width.value()); 1679 case Percent: { 1680 const int cw = isPositioned() ? containingBlockWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockWidthForContent(); 1681 if (cw > 0) 1682 return calcContentBoxWidth(width.calcMinValue(cw)); 1683 } 1684 // fall through 1685 default: 1686 return intrinsicSize().width(); 1687 } 1688 } 1689 1690 int RenderBox::calcReplacedHeight() const 1691 { 1692 int height = calcReplacedHeightUsing(style()->height()); 1693 int minH = calcReplacedHeightUsing(style()->minHeight()); 1694 int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight()); 1695 1696 return max(minH, min(height, maxH)); 1697 } 1698 1699 int RenderBox::calcReplacedHeightUsing(Length height) const 1700 { 1701 switch (height.type()) { 1702 case Fixed: 1703 return calcContentBoxHeight(height.value()); 1704 case Percent: 1705 { 1706 RenderObject* cb = isPositioned() ? container() : containingBlock(); 1707 while (cb->isAnonymous()) { 1708 cb = cb->containingBlock(); 1709 toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this)); 1710 } 1711 1712 if (cb->isPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) { 1713 ASSERT(cb->isRenderBlock()); 1714 RenderBlock* block = toRenderBlock(cb); 1715 int oldHeight = block->height(); 1716 block->calcHeight(); 1717 int newHeight = block->calcContentBoxHeight(block->contentHeight()); 1718 block->setHeight(oldHeight); 1719 return calcContentBoxHeight(height.calcValue(newHeight)); 1720 } 1721 1722 int availableHeight = isPositioned() ? containingBlockHeightForPositioned(toRenderBoxModelObject(cb)) : toRenderBox(cb)->availableHeight(); 1723 1724 // It is necessary to use the border-box to match WinIE's broken 1725 // box model. This is essential for sizing inside 1726 // table cells using percentage heights. 1727 if (cb->isTableCell() && (cb->style()->height().isAuto() || cb->style()->height().isPercent())) { 1728 // Don't let table cells squeeze percent-height replaced elements 1729 // <http://bugs.webkit.org/show_bug.cgi?id=15359> 1730 availableHeight = max(availableHeight, intrinsicSize().height()); 1731 return height.calcValue(availableHeight - (borderTop() + borderBottom() 1732 + paddingTop() + paddingBottom())); 1733 } 1734 1735 return calcContentBoxHeight(height.calcValue(availableHeight)); 1736 } 1737 default: 1738 return intrinsicSize().height(); 1739 } 1740 } 1741 1742 int RenderBox::availableHeight() const 1743 { 1744 return availableHeightUsing(style()->height()); 1745 } 1746 1747 int RenderBox::availableHeightUsing(const Length& h) const 1748 { 1749 if (h.isFixed()) 1750 return calcContentBoxHeight(h.value()); 1751 1752 if (isRenderView()) 1753 return toRenderView(this)->frameView()->visibleHeight(); 1754 1755 // We need to stop here, since we don't want to increase the height of the table 1756 // artificially. We're going to rely on this cell getting expanded to some new 1757 // height, and then when we lay out again we'll use the calculation below. 1758 if (isTableCell() && (h.isAuto() || h.isPercent())) 1759 return overrideSize() - (borderLeft() + borderRight() + paddingLeft() + paddingRight()); 1760 1761 if (h.isPercent()) 1762 return calcContentBoxHeight(h.calcValue(containingBlock()->availableHeight())); 1763 1764 if (isRenderBlock() && isPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) { 1765 RenderBlock* block = const_cast<RenderBlock*>(toRenderBlock(this)); 1766 int oldHeight = block->height(); 1767 block->calcHeight(); 1768 int newHeight = block->calcContentBoxHeight(block->contentHeight()); 1769 block->setHeight(oldHeight); 1770 return calcContentBoxHeight(newHeight); 1771 } 1772 1773 return containingBlock()->availableHeight(); 1774 } 1775 1776 void RenderBox::calcVerticalMargins() 1777 { 1778 if (isTableCell()) { 1779 m_marginTop = 0; 1780 m_marginBottom = 0; 1781 return; 1782 } 1783 1784 // margins are calculated with respect to the _width_ of 1785 // the containing block (8.3) 1786 int cw = containingBlock()->contentWidth(); 1787 1788 m_marginTop = style()->marginTop().calcMinValue(cw); 1789 m_marginBottom = style()->marginBottom().calcMinValue(cw); 1790 } 1791 1792 int RenderBox::containingBlockWidthForPositioned(const RenderBoxModelObject* containingBlock) const 1793 { 1794 if (containingBlock->isBox()) { 1795 const RenderBox* containingBlockBox = toRenderBox(containingBlock); 1796 return containingBlockBox->width() - containingBlockBox->borderLeft() - containingBlockBox->borderRight() - containingBlockBox->verticalScrollbarWidth(); 1797 } 1798 1799 ASSERT(containingBlock->isRenderInline() && containingBlock->isRelPositioned()); 1800 1801 const RenderInline* flow = toRenderInline(containingBlock); 1802 InlineFlowBox* first = flow->firstLineBox(); 1803 InlineFlowBox* last = flow->lastLineBox(); 1804 1805 // If the containing block is empty, return a width of 0. 1806 if (!first || !last) 1807 return 0; 1808 1809 int fromLeft; 1810 int fromRight; 1811 if (containingBlock->style()->direction() == LTR) { 1812 fromLeft = first->x() + first->borderLeft(); 1813 fromRight = last->x() + last->width() - last->borderRight(); 1814 } else { 1815 fromRight = first->x() + first->width() - first->borderRight(); 1816 fromLeft = last->x() + last->borderLeft(); 1817 } 1818 1819 return max(0, (fromRight - fromLeft)); 1820 } 1821 1822 int RenderBox::containingBlockHeightForPositioned(const RenderBoxModelObject* containingBlock) const 1823 { 1824 int heightResult = 0; 1825 if (containingBlock->isBox()) 1826 heightResult = toRenderBox(containingBlock)->height(); 1827 else if (containingBlock->isRenderInline()) { 1828 ASSERT(containingBlock->isRelPositioned()); 1829 heightResult = toRenderInline(containingBlock)->linesBoundingBox().height(); 1830 } 1831 return heightResult - containingBlock->borderTop() - containingBlock->borderBottom(); 1832 } 1833 1834 void RenderBox::calcAbsoluteHorizontal() 1835 { 1836 if (isReplaced()) { 1837 calcAbsoluteHorizontalReplaced(); 1838 return; 1839 } 1840 1841 // QUESTIONS 1842 // FIXME 1: Which RenderObject's 'direction' property should used: the 1843 // containing block (cb) as the spec seems to imply, the parent (parent()) as 1844 // was previously done in calculating the static distances, or ourself, which 1845 // was also previously done for deciding what to override when you had 1846 // over-constrained margins? Also note that the container block is used 1847 // in similar situations in other parts of the RenderBox class (see calcWidth() 1848 // and calcHorizontalMargins()). For now we are using the parent for quirks 1849 // mode and the containing block for strict mode. 1850 1851 // FIXME 2: Should we still deal with these the cases of 'left' or 'right' having 1852 // the type 'static' in determining whether to calculate the static distance? 1853 // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1. 1854 1855 // FIXME 3: Can perhaps optimize out cases when max-width/min-width are greater 1856 // than or less than the computed width(). Be careful of box-sizing and 1857 // percentage issues. 1858 1859 // The following is based off of the W3C Working Draft from April 11, 2006 of 1860 // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements" 1861 // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width> 1862 // (block-style-comments in this function and in calcAbsoluteHorizontalValues() 1863 // correspond to text from the spec) 1864 1865 1866 // We don't use containingBlock(), since we may be positioned by an enclosing 1867 // relative positioned inline. 1868 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 1869 1870 const int containerWidth = containingBlockWidthForPositioned(containerBlock); 1871 1872 // To match WinIE, in quirks mode use the parent's 'direction' property 1873 // instead of the the container block's. 1874 TextDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction(); 1875 1876 const int bordersPlusPadding = borderLeft() + borderRight() + paddingLeft() + paddingRight(); 1877 const Length marginLeft = style()->marginLeft(); 1878 const Length marginRight = style()->marginRight(); 1879 Length left = style()->left(); 1880 Length right = style()->right(); 1881 1882 /*---------------------------------------------------------------------------*\ 1883 * For the purposes of this section and the next, the term "static position" 1884 * (of an element) refers, roughly, to the position an element would have had 1885 * in the normal flow. More precisely: 1886 * 1887 * * The static position for 'left' is the distance from the left edge of the 1888 * containing block to the left margin edge of a hypothetical box that would 1889 * have been the first box of the element if its 'position' property had 1890 * been 'static' and 'float' had been 'none'. The value is negative if the 1891 * hypothetical box is to the left of the containing block. 1892 * * The static position for 'right' is the distance from the right edge of the 1893 * containing block to the right margin edge of the same hypothetical box as 1894 * above. The value is positive if the hypothetical box is to the left of the 1895 * containing block's edge. 1896 * 1897 * But rather than actually calculating the dimensions of that hypothetical box, 1898 * user agents are free to make a guess at its probable position. 1899 * 1900 * For the purposes of calculating the static position, the containing block of 1901 * fixed positioned elements is the initial containing block instead of the 1902 * viewport, and all scrollable boxes should be assumed to be scrolled to their 1903 * origin. 1904 \*---------------------------------------------------------------------------*/ 1905 1906 // see FIXME 2 1907 // Calculate the static distance if needed. 1908 if (left.isAuto() && right.isAuto()) { 1909 if (containerDirection == LTR) { 1910 // 'staticX' should already have been set through layout of the parent. 1911 int staticPosition = layer()->staticX() - containerBlock->borderLeft(); 1912 for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) { 1913 if (po->isBox()) 1914 staticPosition += toRenderBox(po)->x(); 1915 } 1916 left.setValue(Fixed, staticPosition); 1917 } else { 1918 RenderObject* po = parent(); 1919 // 'staticX' should already have been set through layout of the parent. 1920 int staticPosition = layer()->staticX() + containerWidth + containerBlock->borderRight(); 1921 if (po->isBox()) 1922 staticPosition -= toRenderBox(po)->width(); 1923 for (; po && po != containerBlock; po = po->parent()) { 1924 if (po->isBox()) 1925 staticPosition -= toRenderBox(po)->x(); 1926 } 1927 right.setValue(Fixed, staticPosition); 1928 } 1929 } 1930 1931 // Calculate constraint equation values for 'width' case. 1932 int widthResult; 1933 int xResult; 1934 calcAbsoluteHorizontalValues(style()->width(), containerBlock, containerDirection, 1935 containerWidth, bordersPlusPadding, 1936 left, right, marginLeft, marginRight, 1937 widthResult, m_marginLeft, m_marginRight, xResult); 1938 setWidth(widthResult); 1939 setX(xResult); 1940 1941 // Calculate constraint equation values for 'max-width' case. 1942 if (!style()->maxWidth().isUndefined()) { 1943 int maxWidth; 1944 int maxMarginLeft; 1945 int maxMarginRight; 1946 int maxXPos; 1947 1948 calcAbsoluteHorizontalValues(style()->maxWidth(), containerBlock, containerDirection, 1949 containerWidth, bordersPlusPadding, 1950 left, right, marginLeft, marginRight, 1951 maxWidth, maxMarginLeft, maxMarginRight, maxXPos); 1952 1953 if (width() > maxWidth) { 1954 setWidth(maxWidth); 1955 m_marginLeft = maxMarginLeft; 1956 m_marginRight = maxMarginRight; 1957 m_frameRect.setX(maxXPos); 1958 } 1959 } 1960 1961 // Calculate constraint equation values for 'min-width' case. 1962 if (!style()->minWidth().isZero()) { 1963 int minWidth; 1964 int minMarginLeft; 1965 int minMarginRight; 1966 int minXPos; 1967 1968 calcAbsoluteHorizontalValues(style()->minWidth(), containerBlock, containerDirection, 1969 containerWidth, bordersPlusPadding, 1970 left, right, marginLeft, marginRight, 1971 minWidth, minMarginLeft, minMarginRight, minXPos); 1972 1973 if (width() < minWidth) { 1974 setWidth(minWidth); 1975 m_marginLeft = minMarginLeft; 1976 m_marginRight = minMarginRight; 1977 m_frameRect.setX(minXPos); 1978 } 1979 } 1980 1981 if (stretchesToMinIntrinsicWidth() && width() < minPrefWidth() - bordersPlusPadding) { 1982 calcAbsoluteHorizontalValues(Length(minPrefWidth() - bordersPlusPadding, Fixed), containerBlock, containerDirection, 1983 containerWidth, bordersPlusPadding, 1984 left, right, marginLeft, marginRight, 1985 widthResult, m_marginLeft, m_marginRight, xResult); 1986 setWidth(widthResult); 1987 setX(xResult); 1988 } 1989 1990 // Put width() into correct form. 1991 setWidth(width() + bordersPlusPadding); 1992 } 1993 1994 void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderBoxModelObject* containerBlock, TextDirection containerDirection, 1995 const int containerWidth, const int bordersPlusPadding, 1996 const Length left, const Length right, const Length marginLeft, const Length marginRight, 1997 int& widthValue, int& marginLeftValue, int& marginRightValue, int& xPos) 1998 { 1999 // 'left' and 'right' cannot both be 'auto' because one would of been 2000 // converted to the static position already 2001 ASSERT(!(left.isAuto() && right.isAuto())); 2002 2003 int leftValue = 0; 2004 2005 bool widthIsAuto = width.isIntrinsicOrAuto(); 2006 bool leftIsAuto = left.isAuto(); 2007 bool rightIsAuto = right.isAuto(); 2008 2009 if (!leftIsAuto && !widthIsAuto && !rightIsAuto) { 2010 /*-----------------------------------------------------------------------*\ 2011 * If none of the three is 'auto': If both 'margin-left' and 'margin- 2012 * right' are 'auto', solve the equation under the extra constraint that 2013 * the two margins get equal values, unless this would make them negative, 2014 * in which case when direction of the containing block is 'ltr' ('rtl'), 2015 * set 'margin-left' ('margin-right') to zero and solve for 'margin-right' 2016 * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', 2017 * solve the equation for that value. If the values are over-constrained, 2018 * ignore the value for 'left' (in case the 'direction' property of the 2019 * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') 2020 * and solve for that value. 2021 \*-----------------------------------------------------------------------*/ 2022 // NOTE: It is not necessary to solve for 'right' in the over constrained 2023 // case because the value is not used for any further calculations. 2024 2025 leftValue = left.calcValue(containerWidth); 2026 widthValue = calcContentBoxWidth(width.calcValue(containerWidth)); 2027 2028 const int availableSpace = containerWidth - (leftValue + widthValue + right.calcValue(containerWidth) + bordersPlusPadding); 2029 2030 // Margins are now the only unknown 2031 if (marginLeft.isAuto() && marginRight.isAuto()) { 2032 // Both margins auto, solve for equality 2033 if (availableSpace >= 0) { 2034 marginLeftValue = availableSpace / 2; // split the difference 2035 marginRightValue = availableSpace - marginLeftValue; // account for odd valued differences 2036 } else { 2037 // see FIXME 1 2038 if (containerDirection == LTR) { 2039 marginLeftValue = 0; 2040 marginRightValue = availableSpace; // will be negative 2041 } else { 2042 marginLeftValue = availableSpace; // will be negative 2043 marginRightValue = 0; 2044 } 2045 } 2046 } else if (marginLeft.isAuto()) { 2047 // Solve for left margin 2048 marginRightValue = marginRight.calcValue(containerWidth); 2049 marginLeftValue = availableSpace - marginRightValue; 2050 } else if (marginRight.isAuto()) { 2051 // Solve for right margin 2052 marginLeftValue = marginLeft.calcValue(containerWidth); 2053 marginRightValue = availableSpace - marginLeftValue; 2054 } else { 2055 // Over-constrained, solve for left if direction is RTL 2056 marginLeftValue = marginLeft.calcValue(containerWidth); 2057 marginRightValue = marginRight.calcValue(containerWidth); 2058 2059 // see FIXME 1 -- used to be "this->style()->direction()" 2060 if (containerDirection == RTL) 2061 leftValue = (availableSpace + leftValue) - marginLeftValue - marginRightValue; 2062 } 2063 } else { 2064 /*--------------------------------------------------------------------*\ 2065 * Otherwise, set 'auto' values for 'margin-left' and 'margin-right' 2066 * to 0, and pick the one of the following six rules that applies. 2067 * 2068 * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the 2069 * width is shrink-to-fit. Then solve for 'left' 2070 * 2071 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT 2072 * ------------------------------------------------------------------ 2073 * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if 2074 * the 'direction' property of the containing block is 'ltr' set 2075 * 'left' to the static position, otherwise set 'right' to the 2076 * static position. Then solve for 'left' (if 'direction is 'rtl') 2077 * or 'right' (if 'direction' is 'ltr'). 2078 * ------------------------------------------------------------------ 2079 * 2080 * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the 2081 * width is shrink-to-fit . Then solve for 'right' 2082 * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve 2083 * for 'left' 2084 * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve 2085 * for 'width' 2086 * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve 2087 * for 'right' 2088 * 2089 * Calculation of the shrink-to-fit width is similar to calculating the 2090 * width of a table cell using the automatic table layout algorithm. 2091 * Roughly: calculate the preferred width by formatting the content 2092 * without breaking lines other than where explicit line breaks occur, 2093 * and also calculate the preferred minimum width, e.g., by trying all 2094 * possible line breaks. CSS 2.1 does not define the exact algorithm. 2095 * Thirdly, calculate the available width: this is found by solving 2096 * for 'width' after setting 'left' (in case 1) or 'right' (in case 3) 2097 * to 0. 2098 * 2099 * Then the shrink-to-fit width is: 2100 * min(max(preferred minimum width, available width), preferred width). 2101 \*--------------------------------------------------------------------*/ 2102 // NOTE: For rules 3 and 6 it is not necessary to solve for 'right' 2103 // because the value is not used for any further calculations. 2104 2105 // Calculate margins, 'auto' margins are ignored. 2106 marginLeftValue = marginLeft.calcMinValue(containerWidth); 2107 marginRightValue = marginRight.calcMinValue(containerWidth); 2108 2109 const int availableSpace = containerWidth - (marginLeftValue + marginRightValue + bordersPlusPadding); 2110 2111 // FIXME: Is there a faster way to find the correct case? 2112 // Use rule/case that applies. 2113 if (leftIsAuto && widthIsAuto && !rightIsAuto) { 2114 // RULE 1: (use shrink-to-fit for width, and solve of left) 2115 int rightValue = right.calcValue(containerWidth); 2116 2117 // FIXME: would it be better to have shrink-to-fit in one step? 2118 int preferredWidth = maxPrefWidth() - bordersPlusPadding; 2119 int preferredMinWidth = minPrefWidth() - bordersPlusPadding; 2120 int availableWidth = availableSpace - rightValue; 2121 widthValue = min(max(preferredMinWidth, availableWidth), preferredWidth); 2122 leftValue = availableSpace - (widthValue + rightValue); 2123 } else if (!leftIsAuto && widthIsAuto && rightIsAuto) { 2124 // RULE 3: (use shrink-to-fit for width, and no need solve of right) 2125 leftValue = left.calcValue(containerWidth); 2126 2127 // FIXME: would it be better to have shrink-to-fit in one step? 2128 int preferredWidth = maxPrefWidth() - bordersPlusPadding; 2129 int preferredMinWidth = minPrefWidth() - bordersPlusPadding; 2130 int availableWidth = availableSpace - leftValue; 2131 widthValue = min(max(preferredMinWidth, availableWidth), preferredWidth); 2132 } else if (leftIsAuto && !width.isAuto() && !rightIsAuto) { 2133 // RULE 4: (solve for left) 2134 widthValue = calcContentBoxWidth(width.calcValue(containerWidth)); 2135 leftValue = availableSpace - (widthValue + right.calcValue(containerWidth)); 2136 } else if (!leftIsAuto && widthIsAuto && !rightIsAuto) { 2137 // RULE 5: (solve for width) 2138 leftValue = left.calcValue(containerWidth); 2139 widthValue = availableSpace - (leftValue + right.calcValue(containerWidth)); 2140 } else if (!leftIsAuto&& !widthIsAuto && rightIsAuto) { 2141 // RULE 6: (no need solve for right) 2142 leftValue = left.calcValue(containerWidth); 2143 widthValue = calcContentBoxWidth(width.calcValue(containerWidth)); 2144 } 2145 } 2146 2147 // Use computed values to calculate the horizontal position. 2148 2149 // FIXME: This hack is needed to calculate the xPos for a 'rtl' relatively 2150 // positioned, inline because right now, it is using the xPos 2151 // of the first line box when really it should use the last line box. When 2152 // this is fixed elsewhere, this block should be removed. 2153 if (containerBlock->isRenderInline() && containerBlock->style()->direction() == RTL) { 2154 const RenderInline* flow = toRenderInline(containerBlock); 2155 InlineFlowBox* firstLine = flow->firstLineBox(); 2156 InlineFlowBox* lastLine = flow->lastLineBox(); 2157 if (firstLine && lastLine && firstLine != lastLine) { 2158 xPos = leftValue + marginLeftValue + lastLine->borderLeft() + (lastLine->x() - firstLine->x()); 2159 return; 2160 } 2161 } 2162 2163 xPos = leftValue + marginLeftValue + containerBlock->borderLeft(); 2164 } 2165 2166 void RenderBox::calcAbsoluteVertical() 2167 { 2168 if (isReplaced()) { 2169 calcAbsoluteVerticalReplaced(); 2170 return; 2171 } 2172 2173 // The following is based off of the W3C Working Draft from April 11, 2006 of 2174 // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements" 2175 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height> 2176 // (block-style-comments in this function and in calcAbsoluteVerticalValues() 2177 // correspond to text from the spec) 2178 2179 2180 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. 2181 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 2182 2183 const int containerHeight = containingBlockHeightForPositioned(containerBlock); 2184 2185 const int bordersPlusPadding = borderTop() + borderBottom() + paddingTop() + paddingBottom(); 2186 const Length marginTop = style()->marginTop(); 2187 const Length marginBottom = style()->marginBottom(); 2188 Length top = style()->top(); 2189 Length bottom = style()->bottom(); 2190 2191 /*---------------------------------------------------------------------------*\ 2192 * For the purposes of this section and the next, the term "static position" 2193 * (of an element) refers, roughly, to the position an element would have had 2194 * in the normal flow. More precisely, the static position for 'top' is the 2195 * distance from the top edge of the containing block to the top margin edge 2196 * of a hypothetical box that would have been the first box of the element if 2197 * its 'position' property had been 'static' and 'float' had been 'none'. The 2198 * value is negative if the hypothetical box is above the containing block. 2199 * 2200 * But rather than actually calculating the dimensions of that hypothetical 2201 * box, user agents are free to make a guess at its probable position. 2202 * 2203 * For the purposes of calculating the static position, the containing block 2204 * of fixed positioned elements is the initial containing block instead of 2205 * the viewport. 2206 \*---------------------------------------------------------------------------*/ 2207 2208 // see FIXME 2 2209 // Calculate the static distance if needed. 2210 if (top.isAuto() && bottom.isAuto()) { 2211 // staticY should already have been set through layout of the parent() 2212 int staticTop = layer()->staticY() - containerBlock->borderTop(); 2213 for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) { 2214 if (po->isBox() && !po->isTableRow()) 2215 staticTop += toRenderBox(po)->y(); 2216 } 2217 top.setValue(Fixed, staticTop); 2218 } 2219 2220 2221 int h; // Needed to compute overflow. 2222 int y; 2223 2224 // Calculate constraint equation values for 'height' case. 2225 calcAbsoluteVerticalValues(style()->height(), containerBlock, containerHeight, bordersPlusPadding, 2226 top, bottom, marginTop, marginBottom, 2227 h, m_marginTop, m_marginBottom, y); 2228 setY(y); 2229 2230 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults). 2231 // see FIXME 3 2232 2233 // Calculate constraint equation values for 'max-height' case. 2234 if (!style()->maxHeight().isUndefined()) { 2235 int maxHeight; 2236 int maxMarginTop; 2237 int maxMarginBottom; 2238 int maxYPos; 2239 2240 calcAbsoluteVerticalValues(style()->maxHeight(), containerBlock, containerHeight, bordersPlusPadding, 2241 top, bottom, marginTop, marginBottom, 2242 maxHeight, maxMarginTop, maxMarginBottom, maxYPos); 2243 2244 if (h > maxHeight) { 2245 h = maxHeight; 2246 m_marginTop = maxMarginTop; 2247 m_marginBottom = maxMarginBottom; 2248 m_frameRect.setY(maxYPos); 2249 } 2250 } 2251 2252 // Calculate constraint equation values for 'min-height' case. 2253 if (!style()->minHeight().isZero()) { 2254 int minHeight; 2255 int minMarginTop; 2256 int minMarginBottom; 2257 int minYPos; 2258 2259 calcAbsoluteVerticalValues(style()->minHeight(), containerBlock, containerHeight, bordersPlusPadding, 2260 top, bottom, marginTop, marginBottom, 2261 minHeight, minMarginTop, minMarginBottom, minYPos); 2262 2263 if (h < minHeight) { 2264 h = minHeight; 2265 m_marginTop = minMarginTop; 2266 m_marginBottom = minMarginBottom; 2267 m_frameRect.setY(minYPos); 2268 } 2269 } 2270 2271 // Set final height value. 2272 setHeight(h + bordersPlusPadding); 2273 } 2274 2275 void RenderBox::calcAbsoluteVerticalValues(Length h, const RenderBoxModelObject* containerBlock, 2276 const int containerHeight, const int bordersPlusPadding, 2277 const Length top, const Length bottom, const Length marginTop, const Length marginBottom, 2278 int& heightValue, int& marginTopValue, int& marginBottomValue, int& yPos) 2279 { 2280 // 'top' and 'bottom' cannot both be 'auto' because 'top would of been 2281 // converted to the static position in calcAbsoluteVertical() 2282 ASSERT(!(top.isAuto() && bottom.isAuto())); 2283 2284 int contentHeight = height() - bordersPlusPadding; 2285 2286 int topValue = 0; 2287 2288 bool heightIsAuto = h.isAuto(); 2289 bool topIsAuto = top.isAuto(); 2290 bool bottomIsAuto = bottom.isAuto(); 2291 2292 // Height is never unsolved for tables. 2293 if (isTable()) { 2294 h.setValue(Fixed, contentHeight); 2295 heightIsAuto = false; 2296 } 2297 2298 if (!topIsAuto && !heightIsAuto && !bottomIsAuto) { 2299 /*-----------------------------------------------------------------------*\ 2300 * If none of the three are 'auto': If both 'margin-top' and 'margin- 2301 * bottom' are 'auto', solve the equation under the extra constraint that 2302 * the two margins get equal values. If one of 'margin-top' or 'margin- 2303 * bottom' is 'auto', solve the equation for that value. If the values 2304 * are over-constrained, ignore the value for 'bottom' and solve for that 2305 * value. 2306 \*-----------------------------------------------------------------------*/ 2307 // NOTE: It is not necessary to solve for 'bottom' in the over constrained 2308 // case because the value is not used for any further calculations. 2309 2310 heightValue = calcContentBoxHeight(h.calcValue(containerHeight)); 2311 topValue = top.calcValue(containerHeight); 2312 2313 const int availableSpace = containerHeight - (topValue + heightValue + bottom.calcValue(containerHeight) + bordersPlusPadding); 2314 2315 // Margins are now the only unknown 2316 if (marginTop.isAuto() && marginBottom.isAuto()) { 2317 // Both margins auto, solve for equality 2318 // NOTE: This may result in negative values. 2319 marginTopValue = availableSpace / 2; // split the difference 2320 marginBottomValue = availableSpace - marginTopValue; // account for odd valued differences 2321 } else if (marginTop.isAuto()) { 2322 // Solve for top margin 2323 marginBottomValue = marginBottom.calcValue(containerHeight); 2324 marginTopValue = availableSpace - marginBottomValue; 2325 } else if (marginBottom.isAuto()) { 2326 // Solve for bottom margin 2327 marginTopValue = marginTop.calcValue(containerHeight); 2328 marginBottomValue = availableSpace - marginTopValue; 2329 } else { 2330 // Over-constrained, (no need solve for bottom) 2331 marginTopValue = marginTop.calcValue(containerHeight); 2332 marginBottomValue = marginBottom.calcValue(containerHeight); 2333 } 2334 } else { 2335 /*--------------------------------------------------------------------*\ 2336 * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom' 2337 * to 0, and pick the one of the following six rules that applies. 2338 * 2339 * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then 2340 * the height is based on the content, and solve for 'top'. 2341 * 2342 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT 2343 * ------------------------------------------------------------------ 2344 * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then 2345 * set 'top' to the static position, and solve for 'bottom'. 2346 * ------------------------------------------------------------------ 2347 * 2348 * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then 2349 * the height is based on the content, and solve for 'bottom'. 2350 * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and 2351 * solve for 'top'. 2352 * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and 2353 * solve for 'height'. 2354 * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and 2355 * solve for 'bottom'. 2356 \*--------------------------------------------------------------------*/ 2357 // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom' 2358 // because the value is not used for any further calculations. 2359 2360 // Calculate margins, 'auto' margins are ignored. 2361 marginTopValue = marginTop.calcMinValue(containerHeight); 2362 marginBottomValue = marginBottom.calcMinValue(containerHeight); 2363 2364 const int availableSpace = containerHeight - (marginTopValue + marginBottomValue + bordersPlusPadding); 2365 2366 // Use rule/case that applies. 2367 if (topIsAuto && heightIsAuto && !bottomIsAuto) { 2368 // RULE 1: (height is content based, solve of top) 2369 heightValue = contentHeight; 2370 topValue = availableSpace - (heightValue + bottom.calcValue(containerHeight)); 2371 } else if (!topIsAuto && heightIsAuto && bottomIsAuto) { 2372 // RULE 3: (height is content based, no need solve of bottom) 2373 topValue = top.calcValue(containerHeight); 2374 heightValue = contentHeight; 2375 } else if (topIsAuto && !heightIsAuto && !bottomIsAuto) { 2376 // RULE 4: (solve of top) 2377 heightValue = calcContentBoxHeight(h.calcValue(containerHeight)); 2378 topValue = availableSpace - (heightValue + bottom.calcValue(containerHeight)); 2379 } else if (!topIsAuto && heightIsAuto && !bottomIsAuto) { 2380 // RULE 5: (solve of height) 2381 topValue = top.calcValue(containerHeight); 2382 heightValue = max(0, availableSpace - (topValue + bottom.calcValue(containerHeight))); 2383 } else if (!topIsAuto && !heightIsAuto && bottomIsAuto) { 2384 // RULE 6: (no need solve of bottom) 2385 heightValue = calcContentBoxHeight(h.calcValue(containerHeight)); 2386 topValue = top.calcValue(containerHeight); 2387 } 2388 } 2389 2390 // Use computed values to calculate the vertical position. 2391 yPos = topValue + marginTopValue + containerBlock->borderTop(); 2392 } 2393 2394 void RenderBox::calcAbsoluteHorizontalReplaced() 2395 { 2396 // The following is based off of the W3C Working Draft from April 11, 2006 of 2397 // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements" 2398 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width> 2399 // (block-style-comments in this function correspond to text from the spec and 2400 // the numbers correspond to numbers in spec) 2401 2402 // We don't use containingBlock(), since we may be positioned by an enclosing 2403 // relative positioned inline. 2404 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 2405 2406 const int containerWidth = containingBlockWidthForPositioned(containerBlock); 2407 2408 // To match WinIE, in quirks mode use the parent's 'direction' property 2409 // instead of the the container block's. 2410 TextDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction(); 2411 2412 // Variables to solve. 2413 Length left = style()->left(); 2414 Length right = style()->right(); 2415 Length marginLeft = style()->marginLeft(); 2416 Length marginRight = style()->marginRight(); 2417 2418 2419 /*-----------------------------------------------------------------------*\ 2420 * 1. The used value of 'width' is determined as for inline replaced 2421 * elements. 2422 \*-----------------------------------------------------------------------*/ 2423 // NOTE: This value of width is FINAL in that the min/max width calculations 2424 // are dealt with in calcReplacedWidth(). This means that the steps to produce 2425 // correct max/min in the non-replaced version, are not necessary. 2426 setWidth(calcReplacedWidth() + borderLeft() + borderRight() + paddingLeft() + paddingRight()); 2427 const int availableSpace = containerWidth - width(); 2428 2429 /*-----------------------------------------------------------------------*\ 2430 * 2. If both 'left' and 'right' have the value 'auto', then if 'direction' 2431 * of the containing block is 'ltr', set 'left' to the static position; 2432 * else if 'direction' is 'rtl', set 'right' to the static position. 2433 \*-----------------------------------------------------------------------*/ 2434 // see FIXME 2 2435 if (left.isAuto() && right.isAuto()) { 2436 // see FIXME 1 2437 if (containerDirection == LTR) { 2438 // 'staticX' should already have been set through layout of the parent. 2439 int staticPosition = layer()->staticX() - containerBlock->borderLeft(); 2440 for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) { 2441 if (po->isBox()) 2442 staticPosition += toRenderBox(po)->x(); 2443 } 2444 left.setValue(Fixed, staticPosition); 2445 } else { 2446 RenderObject* po = parent(); 2447 // 'staticX' should already have been set through layout of the parent. 2448 int staticPosition = layer()->staticX() + containerWidth + containerBlock->borderRight(); 2449 for ( ; po && po != containerBlock; po = po->parent()) { 2450 if (po->isBox()) 2451 staticPosition += toRenderBox(po)->x(); 2452 } 2453 right.setValue(Fixed, staticPosition); 2454 } 2455 } 2456 2457 /*-----------------------------------------------------------------------*\ 2458 * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' 2459 * or 'margin-right' with '0'. 2460 \*-----------------------------------------------------------------------*/ 2461 if (left.isAuto() || right.isAuto()) { 2462 if (marginLeft.isAuto()) 2463 marginLeft.setValue(Fixed, 0); 2464 if (marginRight.isAuto()) 2465 marginRight.setValue(Fixed, 0); 2466 } 2467 2468 /*-----------------------------------------------------------------------*\ 2469 * 4. If at this point both 'margin-left' and 'margin-right' are still 2470 * 'auto', solve the equation under the extra constraint that the two 2471 * margins must get equal values, unless this would make them negative, 2472 * in which case when the direction of the containing block is 'ltr' 2473 * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for 2474 * 'margin-right' ('margin-left'). 2475 \*-----------------------------------------------------------------------*/ 2476 int leftValue = 0; 2477 int rightValue = 0; 2478 2479 if (marginLeft.isAuto() && marginRight.isAuto()) { 2480 // 'left' and 'right' cannot be 'auto' due to step 3 2481 ASSERT(!(left.isAuto() && right.isAuto())); 2482 2483 leftValue = left.calcValue(containerWidth); 2484 rightValue = right.calcValue(containerWidth); 2485 2486 int difference = availableSpace - (leftValue + rightValue); 2487 if (difference > 0) { 2488 m_marginLeft = difference / 2; // split the difference 2489 m_marginRight = difference - m_marginLeft; // account for odd valued differences 2490 } else { 2491 // see FIXME 1 2492 if (containerDirection == LTR) { 2493 m_marginLeft = 0; 2494 m_marginRight = difference; // will be negative 2495 } else { 2496 m_marginLeft = difference; // will be negative 2497 m_marginRight = 0; 2498 } 2499 } 2500 2501 /*-----------------------------------------------------------------------*\ 2502 * 5. If at this point there is an 'auto' left, solve the equation for 2503 * that value. 2504 \*-----------------------------------------------------------------------*/ 2505 } else if (left.isAuto()) { 2506 m_marginLeft = marginLeft.calcValue(containerWidth); 2507 m_marginRight = marginRight.calcValue(containerWidth); 2508 rightValue = right.calcValue(containerWidth); 2509 2510 // Solve for 'left' 2511 leftValue = availableSpace - (rightValue + m_marginLeft + m_marginRight); 2512 } else if (right.isAuto()) { 2513 m_marginLeft = marginLeft.calcValue(containerWidth); 2514 m_marginRight = marginRight.calcValue(containerWidth); 2515 leftValue = left.calcValue(containerWidth); 2516 2517 // Solve for 'right' 2518 rightValue = availableSpace - (leftValue + m_marginLeft + m_marginRight); 2519 } else if (marginLeft.isAuto()) { 2520 m_marginRight = marginRight.calcValue(containerWidth); 2521 leftValue = left.calcValue(containerWidth); 2522 rightValue = right.calcValue(containerWidth); 2523 2524 // Solve for 'margin-left' 2525 m_marginLeft = availableSpace - (leftValue + rightValue + m_marginRight); 2526 } else if (marginRight.isAuto()) { 2527 m_marginLeft = marginLeft.calcValue(containerWidth); 2528 leftValue = left.calcValue(containerWidth); 2529 rightValue = right.calcValue(containerWidth); 2530 2531 // Solve for 'margin-right' 2532 m_marginRight = availableSpace - (leftValue + rightValue + m_marginLeft); 2533 } else { 2534 // Nothing is 'auto', just calculate the values. 2535 m_marginLeft = marginLeft.calcValue(containerWidth); 2536 m_marginRight = marginRight.calcValue(containerWidth); 2537 rightValue = right.calcValue(containerWidth); 2538 leftValue = left.calcValue(containerWidth); 2539 } 2540 2541 /*-----------------------------------------------------------------------*\ 2542 * 6. If at this point the values are over-constrained, ignore the value 2543 * for either 'left' (in case the 'direction' property of the 2544 * containing block is 'rtl') or 'right' (in case 'direction' is 2545 * 'ltr') and solve for that value. 2546 \*-----------------------------------------------------------------------*/ 2547 // NOTE: It is not necessary to solve for 'right' when the direction is 2548 // LTR because the value is not used. 2549 int totalWidth = width() + leftValue + rightValue + m_marginLeft + m_marginRight; 2550 if (totalWidth > containerWidth && (containerDirection == RTL)) 2551 leftValue = containerWidth - (totalWidth - leftValue); 2552 2553 // Use computed values to calculate the horizontal position. 2554 2555 // FIXME: This hack is needed to calculate the xPos for a 'rtl' relatively 2556 // positioned, inline containing block because right now, it is using the xPos 2557 // of the first line box when really it should use the last line box. When 2558 // this is fixed elsewhere, this block should be removed. 2559 if (containerBlock->isRenderInline() && containerBlock->style()->direction() == RTL) { 2560 const RenderInline* flow = toRenderInline(containerBlock); 2561 InlineFlowBox* firstLine = flow->firstLineBox(); 2562 InlineFlowBox* lastLine = flow->lastLineBox(); 2563 if (firstLine && lastLine && firstLine != lastLine) { 2564 m_frameRect.setX(leftValue + m_marginLeft + lastLine->borderLeft() + (lastLine->x() - firstLine->x())); 2565 return; 2566 } 2567 } 2568 2569 m_frameRect.setX(leftValue + m_marginLeft + containerBlock->borderLeft()); 2570 } 2571 2572 void RenderBox::calcAbsoluteVerticalReplaced() 2573 { 2574 // The following is based off of the W3C Working Draft from April 11, 2006 of 2575 // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements" 2576 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height> 2577 // (block-style-comments in this function correspond to text from the spec and 2578 // the numbers correspond to numbers in spec) 2579 2580 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. 2581 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 2582 2583 const int containerHeight = containingBlockHeightForPositioned(containerBlock); 2584 2585 // Variables to solve. 2586 Length top = style()->top(); 2587 Length bottom = style()->bottom(); 2588 Length marginTop = style()->marginTop(); 2589 Length marginBottom = style()->marginBottom(); 2590 2591 2592 /*-----------------------------------------------------------------------*\ 2593 * 1. The used value of 'height' is determined as for inline replaced 2594 * elements. 2595 \*-----------------------------------------------------------------------*/ 2596 // NOTE: This value of height is FINAL in that the min/max height calculations 2597 // are dealt with in calcReplacedHeight(). This means that the steps to produce 2598 // correct max/min in the non-replaced version, are not necessary. 2599 setHeight(calcReplacedHeight() + borderTop() + borderBottom() + paddingTop() + paddingBottom()); 2600 const int availableSpace = containerHeight - height(); 2601 2602 /*-----------------------------------------------------------------------*\ 2603 * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top' 2604 * with the element's static position. 2605 \*-----------------------------------------------------------------------*/ 2606 // see FIXME 2 2607 if (top.isAuto() && bottom.isAuto()) { 2608 // staticY should already have been set through layout of the parent(). 2609 int staticTop = layer()->staticY() - containerBlock->borderTop(); 2610 for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) { 2611 if (po->isBox() && !po->isTableRow()) 2612 staticTop += toRenderBox(po)->y(); 2613 } 2614 top.setValue(Fixed, staticTop); 2615 } 2616 2617 /*-----------------------------------------------------------------------*\ 2618 * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or 2619 * 'margin-bottom' with '0'. 2620 \*-----------------------------------------------------------------------*/ 2621 // FIXME: The spec. says that this step should only be taken when bottom is 2622 // auto, but if only top is auto, this makes step 4 impossible. 2623 if (top.isAuto() || bottom.isAuto()) { 2624 if (marginTop.isAuto()) 2625 marginTop.setValue(Fixed, 0); 2626 if (marginBottom.isAuto()) 2627 marginBottom.setValue(Fixed, 0); 2628 } 2629 2630 /*-----------------------------------------------------------------------*\ 2631 * 4. If at this point both 'margin-top' and 'margin-bottom' are still 2632 * 'auto', solve the equation under the extra constraint that the two 2633 * margins must get equal values. 2634 \*-----------------------------------------------------------------------*/ 2635 int topValue = 0; 2636 int bottomValue = 0; 2637 2638 if (marginTop.isAuto() && marginBottom.isAuto()) { 2639 // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined. 2640 ASSERT(!(top.isAuto() || bottom.isAuto())); 2641 2642 topValue = top.calcValue(containerHeight); 2643 bottomValue = bottom.calcValue(containerHeight); 2644 2645 int difference = availableSpace - (topValue + bottomValue); 2646 // NOTE: This may result in negative values. 2647 m_marginTop = difference / 2; // split the difference 2648 m_marginBottom = difference - m_marginTop; // account for odd valued differences 2649 2650 /*-----------------------------------------------------------------------*\ 2651 * 5. If at this point there is only one 'auto' left, solve the equation 2652 * for that value. 2653 \*-----------------------------------------------------------------------*/ 2654 } else if (top.isAuto()) { 2655 m_marginTop = marginTop.calcValue(containerHeight); 2656 m_marginBottom = marginBottom.calcValue(containerHeight); 2657 bottomValue = bottom.calcValue(containerHeight); 2658 2659 // Solve for 'top' 2660 topValue = availableSpace - (bottomValue + m_marginTop + m_marginBottom); 2661 } else if (bottom.isAuto()) { 2662 m_marginTop = marginTop.calcValue(containerHeight); 2663 m_marginBottom = marginBottom.calcValue(containerHeight); 2664 topValue = top.calcValue(containerHeight); 2665 2666 // Solve for 'bottom' 2667 // NOTE: It is not necessary to solve for 'bottom' because we don't ever 2668 // use the value. 2669 } else if (marginTop.isAuto()) { 2670 m_marginBottom = marginBottom.calcValue(containerHeight); 2671 topValue = top.calcValue(containerHeight); 2672 bottomValue = bottom.calcValue(containerHeight); 2673 2674 // Solve for 'margin-top' 2675 m_marginTop = availableSpace - (topValue + bottomValue + m_marginBottom); 2676 } else if (marginBottom.isAuto()) { 2677 m_marginTop = marginTop.calcValue(containerHeight); 2678 topValue = top.calcValue(containerHeight); 2679 bottomValue = bottom.calcValue(containerHeight); 2680 2681 // Solve for 'margin-bottom' 2682 m_marginBottom = availableSpace - (topValue + bottomValue + m_marginTop); 2683 } else { 2684 // Nothing is 'auto', just calculate the values. 2685 m_marginTop = marginTop.calcValue(containerHeight); 2686 m_marginBottom = marginBottom.calcValue(containerHeight); 2687 topValue = top.calcValue(containerHeight); 2688 // NOTE: It is not necessary to solve for 'bottom' because we don't ever 2689 // use the value. 2690 } 2691 2692 /*-----------------------------------------------------------------------*\ 2693 * 6. If at this point the values are over-constrained, ignore the value 2694 * for 'bottom' and solve for that value. 2695 \*-----------------------------------------------------------------------*/ 2696 // NOTE: It is not necessary to do this step because we don't end up using 2697 // the value of 'bottom' regardless of whether the values are over-constrained 2698 // or not. 2699 2700 // Use computed values to calculate the vertical position. 2701 m_frameRect.setY(topValue + m_marginTop + containerBlock->borderTop()); 2702 } 2703 2704 IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWidthToEndOfLine) 2705 { 2706 // VisiblePositions at offsets inside containers either a) refer to the positions before/after 2707 // those containers (tables and select elements) or b) refer to the position inside an empty block. 2708 // They never refer to children. 2709 // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements. 2710 2711 // FIXME: What about border and padding? 2712 IntRect rect(x(), y(), caretWidth, height()); 2713 TextDirection direction = box ? box->direction() : style()->direction(); 2714 2715 if ((!caretOffset) ^ (direction == LTR)) 2716 rect.move(IntSize(width() - caretWidth, 0)); 2717 2718 if (box) { 2719 RootInlineBox* rootBox = box->root(); 2720 int top = rootBox->lineTop(); 2721 rect.setY(top); 2722 rect.setHeight(rootBox->lineBottom() - top); 2723 } 2724 2725 // If height of box is smaller than font height, use the latter one, 2726 // otherwise the caret might become invisible. 2727 // 2728 // Also, if the box is not a replaced element, always use the font height. 2729 // This prevents the "big caret" bug described in: 2730 // <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point 2731 // 2732 // FIXME: ignoring :first-line, missing good reason to take care of 2733 int fontHeight = style()->font().height(); 2734 if (fontHeight > rect.height() || (!isReplaced() && !isTable())) 2735 rect.setHeight(fontHeight); 2736 2737 if (extraWidthToEndOfLine) 2738 *extraWidthToEndOfLine = x() + width() - rect.right(); 2739 2740 // Move to local coords 2741 rect.move(-x(), -y()); 2742 return rect; 2743 } 2744 2745 int RenderBox::lowestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const 2746 { 2747 if (!includeSelf || !width()) 2748 return 0; 2749 int bottom = height(); 2750 if (isRelPositioned()) 2751 bottom += relativePositionOffsetY(); 2752 return bottom; 2753 } 2754 2755 int RenderBox::rightmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const 2756 { 2757 if (!includeSelf || !height()) 2758 return 0; 2759 int right = width(); 2760 if (isRelPositioned()) 2761 right += relativePositionOffsetX(); 2762 return right; 2763 } 2764 2765 int RenderBox::leftmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const 2766 { 2767 if (!includeSelf || !height()) 2768 return width(); 2769 int left = 0; 2770 if (isRelPositioned()) 2771 left += relativePositionOffsetX(); 2772 return left; 2773 } 2774 2775 VisiblePosition RenderBox::positionForPoint(const IntPoint& point) 2776 { 2777 // no children...return this render object's element, if there is one, and offset 0 2778 if (!firstChild()) 2779 return createVisiblePosition(node() ? firstDeepEditingPositionForNode(node()) : Position(0, 0)); 2780 2781 int xPos = point.x(); 2782 int yPos = point.y(); 2783 2784 if (isTable() && node()) { 2785 int right = contentWidth() + borderRight() + paddingRight() + borderLeft() + paddingLeft(); 2786 int bottom = contentHeight() + borderTop() + paddingTop() + borderBottom() + paddingBottom(); 2787 2788 if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) { 2789 if (xPos <= right / 2) 2790 return createVisiblePosition(firstDeepEditingPositionForNode(node())); 2791 return createVisiblePosition(lastDeepEditingPositionForNode(node())); 2792 } 2793 } 2794 2795 // Pass off to the closest child. 2796 int minDist = INT_MAX; 2797 RenderBox* closestRenderer = 0; 2798 int newX = xPos; 2799 int newY = yPos; 2800 if (isTableRow()) { 2801 newX += x(); 2802 newY += y(); 2803 } 2804 for (RenderObject* renderObject = firstChild(); renderObject; renderObject = renderObject->nextSibling()) { 2805 if ((!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isBlockFlow() ) 2806 || renderObject->style()->visibility() != VISIBLE) 2807 continue; 2808 2809 if (!renderObject->isBox()) 2810 continue; 2811 2812 RenderBox* renderer = toRenderBox(renderObject); 2813 2814 int top = renderer->borderTop() + renderer->paddingTop() + (isTableRow() ? 0 : renderer->y()); 2815 int bottom = top + renderer->contentHeight(); 2816 int left = renderer->borderLeft() + renderer->paddingLeft() + (isTableRow() ? 0 : renderer->x()); 2817 int right = left + renderer->contentWidth(); 2818 2819 if (xPos <= right && xPos >= left && yPos <= top && yPos >= bottom) { 2820 if (renderer->isTableRow()) 2821 return renderer->positionForCoordinates(xPos + newX - renderer->x(), yPos + newY - renderer->y()); 2822 return renderer->positionForCoordinates(xPos - renderer->x(), yPos - renderer->y()); 2823 } 2824 2825 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces 2826 // and use a different compare depending on which piece (x, y) is in. 2827 IntPoint cmp; 2828 if (xPos > right) { 2829 if (yPos < top) 2830 cmp = IntPoint(right, top); 2831 else if (yPos > bottom) 2832 cmp = IntPoint(right, bottom); 2833 else 2834 cmp = IntPoint(right, yPos); 2835 } else if (xPos < left) { 2836 if (yPos < top) 2837 cmp = IntPoint(left, top); 2838 else if (yPos > bottom) 2839 cmp = IntPoint(left, bottom); 2840 else 2841 cmp = IntPoint(left, yPos); 2842 } else { 2843 if (yPos < top) 2844 cmp = IntPoint(xPos, top); 2845 else 2846 cmp = IntPoint(xPos, bottom); 2847 } 2848 2849 int x1minusx2 = cmp.x() - xPos; 2850 int y1minusy2 = cmp.y() - yPos; 2851 2852 int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2; 2853 if (dist < minDist) { 2854 closestRenderer = renderer; 2855 minDist = dist; 2856 } 2857 } 2858 2859 if (closestRenderer) 2860 return closestRenderer->positionForCoordinates(newX - closestRenderer->x(), newY - closestRenderer->y()); 2861 2862 return createVisiblePosition(firstDeepEditingPositionForNode(node())); 2863 } 2864 2865 bool RenderBox::shrinkToAvoidFloats() const 2866 { 2867 // FIXME: Technically we should be able to shrink replaced elements on a line, but this is difficult to accomplish, since this 2868 // involves doing a relayout during findNextLineBreak and somehow overriding the containingBlockWidth method to return the 2869 // current remaining width on a line. 2870 if ((isInline() && !isHTMLMarquee()) || !avoidsFloats()) 2871 return false; 2872 2873 // All auto-width objects that avoid floats should always use lineWidth. 2874 return style()->width().isAuto(); 2875 } 2876 2877 bool RenderBox::avoidsFloats() const 2878 { 2879 return isReplaced() || hasOverflowClip() || isHR(); 2880 } 2881 2882 void RenderBox::addShadowOverflow() 2883 { 2884 int shadowLeft; 2885 int shadowRight; 2886 int shadowTop; 2887 int shadowBottom; 2888 style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft); 2889 IntRect borderBox = borderBoxRect(); 2890 int overflowLeft = borderBox.x() + shadowLeft; 2891 int overflowRight = borderBox.right() + shadowRight; 2892 int overflowTop = borderBox.y() + shadowTop; 2893 int overflowBottom = borderBox.bottom() + shadowBottom; 2894 addVisualOverflow(IntRect(overflowLeft, overflowTop, overflowRight - overflowLeft, overflowBottom - overflowTop)); 2895 } 2896 2897 void RenderBox::addOverflowFromChild(RenderBox* child, const IntSize& delta) 2898 { 2899 // Update our overflow in case the child spills out the block, but only if we were going to paint 2900 // the child block ourselves. 2901 if (child->hasSelfPaintingLayer()) 2902 return; 2903 2904 // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then 2905 // its overflow is internal to it, and we don't care about it. 2906 IntRect childLayoutOverflowRect = child->hasOverflowClip() ? child->borderBoxRect() : child->layoutOverflowRect(); 2907 childLayoutOverflowRect.move(delta); 2908 addLayoutOverflow(childLayoutOverflowRect); 2909 2910 // Add in visual overflow from the child. Even if the child clips its overflow, it may still 2911 // have visual overflow of its own set from box shadows or reflections. It is unnecessary to propagate this 2912 // overflow if we are clipping our own overflow. 2913 if (hasOverflowClip()) 2914 return; 2915 IntRect childVisualOverflowRect = child->visualOverflowRect(); 2916 childVisualOverflowRect.move(delta); 2917 addVisualOverflow(childVisualOverflowRect); 2918 } 2919 2920 void RenderBox::addLayoutOverflow(const IntRect& rect) 2921 { 2922 IntRect borderBox = borderBoxRect(); 2923 if (borderBox.contains(rect)) 2924 return; 2925 2926 if (!m_overflow) 2927 m_overflow.set(new RenderOverflow(borderBox)); 2928 2929 m_overflow->addLayoutOverflow(rect); 2930 } 2931 2932 void RenderBox::addVisualOverflow(const IntRect& rect) 2933 { 2934 IntRect borderBox = borderBoxRect(); 2935 if (borderBox.contains(rect)) 2936 return; 2937 2938 if (!m_overflow) 2939 m_overflow.set(new RenderOverflow(borderBox)); 2940 2941 m_overflow->addVisualOverflow(rect); 2942 } 2943 2944 void RenderBox::clearLayoutOverflow() 2945 { 2946 if (!m_overflow) 2947 return; 2948 2949 if (visualOverflowRect() == borderBoxRect()) { 2950 m_overflow.clear(); 2951 return; 2952 } 2953 2954 m_overflow->resetLayoutOverflow(borderBoxRect()); 2955 } 2956 2957 #if ENABLE(SVG) 2958 2959 AffineTransform RenderBox::localTransform() const 2960 { 2961 return AffineTransform(1, 0, 0, 1, x(), y()); 2962 } 2963 2964 #endif 2965 2966 } // namespace WebCore 2967