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