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, 2010 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 "HitTestResult.h" 35 #include "htmlediting.h" 36 #include "HTMLElement.h" 37 #include "HTMLNames.h" 38 #include "ImageBuffer.h" 39 #include "FloatQuad.h" 40 #include "Frame.h" 41 #include "Page.h" 42 #include "PaintInfo.h" 43 #include "RenderArena.h" 44 #include "RenderFlexibleBox.h" 45 #include "RenderInline.h" 46 #include "RenderLayer.h" 47 #include "RenderTableCell.h" 48 #include "RenderTheme.h" 49 #ifdef ANDROID_LAYOUT 50 #include "Settings.h" 51 #endif 52 #include "RenderView.h" 53 #include "ScrollbarTheme.h" 54 #include "TransformState.h" 55 #include <algorithm> 56 #include <math.h> 57 58 #if ENABLE(WML) 59 #include "WMLNames.h" 60 #endif 61 62 #if PLATFORM(ANDROID) 63 #include "PlatformBridge.h" 64 #endif 65 66 using namespace std; 67 68 namespace WebCore { 69 70 using namespace HTMLNames; 71 72 // Used by flexible boxes when flexing this element. 73 typedef WTF::HashMap<const RenderBox*, int> OverrideSizeMap; 74 static OverrideSizeMap* gOverrideSizeMap = 0; 75 76 bool RenderBox::s_hadOverflowClip = false; 77 78 RenderBox::RenderBox(Node* node) 79 : RenderBoxModelObject(node) 80 , m_marginLeft(0) 81 , m_marginRight(0) 82 , m_marginTop(0) 83 , m_marginBottom(0) 84 , m_minPreferredLogicalWidth(-1) 85 , m_maxPreferredLogicalWidth(-1) 86 , m_inlineBoxWrapper(0) 87 #ifdef ANDROID_LAYOUT 88 , m_visibleWidth(0) 89 , m_isVisibleWidthChangedBeforeLayout(false) 90 #endif 91 { 92 setIsBox(); 93 } 94 95 RenderBox::~RenderBox() 96 { 97 } 98 99 int RenderBox::marginBefore() const 100 { 101 switch (style()->writingMode()) { 102 case TopToBottomWritingMode: 103 return m_marginTop; 104 case BottomToTopWritingMode: 105 return m_marginBottom; 106 case LeftToRightWritingMode: 107 return m_marginLeft; 108 case RightToLeftWritingMode: 109 return m_marginRight; 110 } 111 ASSERT_NOT_REACHED(); 112 return m_marginTop; 113 } 114 115 int RenderBox::marginAfter() const 116 { 117 switch (style()->writingMode()) { 118 case TopToBottomWritingMode: 119 return m_marginBottom; 120 case BottomToTopWritingMode: 121 return m_marginTop; 122 case LeftToRightWritingMode: 123 return m_marginRight; 124 case RightToLeftWritingMode: 125 return m_marginLeft; 126 } 127 ASSERT_NOT_REACHED(); 128 return m_marginBottom; 129 } 130 131 int RenderBox::marginStart() const 132 { 133 if (isHorizontalWritingMode()) 134 return style()->isLeftToRightDirection() ? m_marginLeft : m_marginRight; 135 return style()->isLeftToRightDirection() ? m_marginTop : m_marginBottom; 136 } 137 138 int RenderBox::marginEnd() const 139 { 140 if (isHorizontalWritingMode()) 141 return style()->isLeftToRightDirection() ? m_marginRight : m_marginLeft; 142 return style()->isLeftToRightDirection() ? m_marginBottom : m_marginTop; 143 } 144 145 void RenderBox::setMarginStart(int margin) 146 { 147 if (isHorizontalWritingMode()) { 148 if (style()->isLeftToRightDirection()) 149 m_marginLeft = margin; 150 else 151 m_marginRight = margin; 152 } else { 153 if (style()->isLeftToRightDirection()) 154 m_marginTop = margin; 155 else 156 m_marginBottom = margin; 157 } 158 } 159 160 void RenderBox::setMarginEnd(int margin) 161 { 162 if (isHorizontalWritingMode()) { 163 if (style()->isLeftToRightDirection()) 164 m_marginRight = margin; 165 else 166 m_marginLeft = margin; 167 } else { 168 if (style()->isLeftToRightDirection()) 169 m_marginBottom = margin; 170 else 171 m_marginTop = margin; 172 } 173 } 174 175 void RenderBox::setMarginBefore(int margin) 176 { 177 switch (style()->writingMode()) { 178 case TopToBottomWritingMode: 179 m_marginTop = margin; 180 break; 181 case BottomToTopWritingMode: 182 m_marginBottom = margin; 183 break; 184 case LeftToRightWritingMode: 185 m_marginLeft = margin; 186 break; 187 case RightToLeftWritingMode: 188 m_marginRight = margin; 189 break; 190 } 191 } 192 193 void RenderBox::setMarginAfter(int margin) 194 { 195 switch (style()->writingMode()) { 196 case TopToBottomWritingMode: 197 m_marginBottom = margin; 198 break; 199 case BottomToTopWritingMode: 200 m_marginTop = margin; 201 break; 202 case LeftToRightWritingMode: 203 m_marginRight = margin; 204 break; 205 case RightToLeftWritingMode: 206 m_marginLeft = margin; 207 break; 208 } 209 } 210 211 void RenderBox::destroy() 212 { 213 // A lot of the code in this function is just pasted into 214 // RenderWidget::destroy. If anything in this function changes, 215 // be sure to fix RenderWidget::destroy() as well. 216 if (hasOverrideSize()) 217 gOverrideSizeMap->remove(this); 218 219 if (style() && (style()->logicalHeight().isPercent() || style()->logicalMinHeight().isPercent() || style()->logicalMaxHeight().isPercent())) 220 RenderBlock::removePercentHeightDescendant(this); 221 222 RenderBoxModelObject::destroy(); 223 } 224 225 void RenderBox::removeFloatingOrPositionedChildFromBlockLists() 226 { 227 ASSERT(isFloatingOrPositioned()); 228 229 if (documentBeingDestroyed()) 230 return; 231 232 if (isFloating()) { 233 RenderBlock* parentBlock = 0; 234 for (RenderObject* curr = parent(); curr && !curr->isRenderView(); curr = curr->parent()) { 235 if (curr->isRenderBlock()) { 236 RenderBlock* currBlock = toRenderBlock(curr); 237 if (!parentBlock || currBlock->containsFloat(this)) 238 parentBlock = currBlock; 239 } 240 } 241 242 if (parentBlock) { 243 RenderObject* parent = parentBlock->parent(); 244 if (parent && parent->isFlexibleBox()) 245 parentBlock = toRenderBlock(parent); 246 247 parentBlock->markAllDescendantsWithFloatsForLayout(this, false); 248 } 249 } 250 251 if (isPositioned()) { 252 for (RenderObject* curr = parent(); curr; curr = curr->parent()) { 253 if (curr->isRenderBlock()) 254 toRenderBlock(curr)->removePositionedObject(this); 255 } 256 } 257 } 258 259 void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) 260 { 261 s_hadOverflowClip = hasOverflowClip(); 262 263 if (style()) { 264 // The background of the root element or the body element could propagate up to 265 // the canvas. Just dirty the entire canvas when our style changes substantially. 266 if (diff >= StyleDifferenceRepaint && node() && 267 (node()->hasTagName(htmlTag) || node()->hasTagName(bodyTag))) 268 view()->repaint(); 269 270 // When a layout hint happens and an object's position style changes, we have to do a layout 271 // to dirty the render tree using the old position value now. 272 if (diff == StyleDifferenceLayout && parent() && style()->position() != newStyle->position()) { 273 markContainingBlocksForLayout(); 274 if (style()->position() == StaticPosition) 275 repaint(); 276 else if (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition) 277 parent()->setChildNeedsLayout(true); 278 if (isFloating() && !isPositioned() && (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition)) 279 removeFloatingOrPositionedChildFromBlockLists(); 280 } 281 } else if (newStyle && isBody()) 282 view()->repaint(); 283 284 if (FrameView *frameView = view()->frameView()) { 285 bool newStyleIsFixed = newStyle && newStyle->position() == FixedPosition; 286 bool oldStyleIsFixed = style() && style()->position() == FixedPosition; 287 if (newStyleIsFixed != oldStyleIsFixed) { 288 if (newStyleIsFixed) 289 frameView->addFixedObject(); 290 else 291 frameView->removeFixedObject(); 292 } 293 } 294 295 RenderBoxModelObject::styleWillChange(diff, newStyle); 296 } 297 298 void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 299 { 300 RenderBoxModelObject::styleDidChange(diff, oldStyle); 301 302 if (needsLayout() && oldStyle) { 303 if (oldStyle && (oldStyle->logicalHeight().isPercent() || oldStyle->logicalMinHeight().isPercent() || oldStyle->logicalMaxHeight().isPercent())) 304 RenderBlock::removePercentHeightDescendant(this); 305 306 // Normally we can do optimized positioning layout for absolute/fixed positioned objects. There is one special case, however, which is 307 // when the positioned object's margin-before is changed. In this case the parent has to get a layout in order to run margin collapsing 308 // to determine the new static position. 309 if (isPositioned() && style()->hasStaticBlockPosition(isHorizontalWritingMode()) && oldStyle->marginBefore() != style()->marginBefore() 310 && parent() && !parent()->normalChildNeedsLayout()) 311 parent()->setChildNeedsLayout(true); 312 } 313 314 // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the 315 // new zoomed coordinate space. 316 if (hasOverflowClip() && oldStyle && style() && oldStyle->effectiveZoom() != style()->effectiveZoom()) { 317 if (int left = layer()->scrollXOffset()) { 318 left = (left / oldStyle->effectiveZoom()) * style()->effectiveZoom(); 319 layer()->scrollToXOffset(left); 320 } 321 if (int top = layer()->scrollYOffset()) { 322 top = (top / oldStyle->effectiveZoom()) * style()->effectiveZoom(); 323 layer()->scrollToYOffset(top); 324 } 325 } 326 327 bool isBodyRenderer = isBody(); 328 bool isRootRenderer = isRoot(); 329 330 // Set the text color if we're the body. 331 if (isBodyRenderer) 332 document()->setTextColor(style()->visitedDependentColor(CSSPropertyColor)); 333 334 if (isRootRenderer || isBodyRenderer) { 335 // Propagate the new writing mode and direction up to the RenderView. 336 RenderView* viewRenderer = view(); 337 RenderStyle* viewStyle = viewRenderer->style(); 338 if (viewStyle->direction() != style()->direction() && (isRootRenderer || !document()->directionSetOnDocumentElement())) { 339 viewStyle->setDirection(style()->direction()); 340 if (isBodyRenderer) 341 document()->documentElement()->renderer()->style()->setDirection(style()->direction()); 342 setNeedsLayoutAndPrefWidthsRecalc(); 343 } 344 345 if (viewStyle->writingMode() != style()->writingMode() && (isRootRenderer || !document()->writingModeSetOnDocumentElement())) { 346 viewStyle->setWritingMode(style()->writingMode()); 347 viewRenderer->setHorizontalWritingMode(style()->isHorizontalWritingMode()); 348 if (isBodyRenderer) { 349 document()->documentElement()->renderer()->style()->setWritingMode(style()->writingMode()); 350 document()->documentElement()->renderer()->setHorizontalWritingMode(style()->isHorizontalWritingMode()); 351 } 352 setNeedsLayoutAndPrefWidthsRecalc(); 353 } 354 } 355 } 356 357 void RenderBox::updateBoxModelInfoFromStyle() 358 { 359 RenderBoxModelObject::updateBoxModelInfoFromStyle(); 360 361 bool isRootObject = isRoot(); 362 bool isViewObject = isRenderView(); 363 364 // The root and the RenderView always paint their backgrounds/borders. 365 if (isRootObject || isViewObject) 366 setHasBoxDecorations(true); 367 368 setPositioned(style()->position() == AbsolutePosition || style()->position() == FixedPosition); 369 setFloating(!isPositioned() && style()->isFloating()); 370 371 // We also handle <body> and <html>, whose overflow applies to the viewport. 372 if (style()->overflowX() != OVISIBLE && !isRootObject && (isRenderBlock() || isTableRow() || isTableSection())) { 373 bool boxHasOverflowClip = true; 374 if (isBody()) { 375 // Overflow on the body can propagate to the viewport under the following conditions. 376 // (1) The root element is <html>. 377 // (2) We are the primary <body> (can be checked by looking at document.body). 378 // (3) The root element has visible overflow. 379 if (document()->documentElement()->hasTagName(htmlTag) && 380 document()->body() == node() && 381 document()->documentElement()->renderer()->style()->overflowX() == OVISIBLE) 382 boxHasOverflowClip = false; 383 } 384 385 // Check for overflow clip. 386 // It's sufficient to just check one direction, since it's illegal to have visible on only one overflow value. 387 if (boxHasOverflowClip) { 388 if (!s_hadOverflowClip) 389 // Erase the overflow 390 repaint(); 391 setHasOverflowClip(); 392 } 393 } 394 395 setHasTransform(style()->hasTransformRelatedProperty()); 396 setHasReflection(style()->boxReflect()); 397 } 398 399 void RenderBox::layout() 400 { 401 ASSERT(needsLayout()); 402 403 RenderObject* child = firstChild(); 404 if (!child) { 405 setNeedsLayout(false); 406 return; 407 } 408 409 LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), style()->isFlippedBlocksWritingMode()); 410 while (child) { 411 child->layoutIfNeeded(); 412 ASSERT(!child->needsLayout()); 413 child = child->nextSibling(); 414 } 415 statePusher.pop(); 416 setNeedsLayout(false); 417 } 418 419 // More IE extensions. clientWidth and clientHeight represent the interior of an object 420 // excluding border and scrollbar. 421 int RenderBox::clientWidth() const 422 { 423 return width() - borderLeft() - borderRight() - verticalScrollbarWidth(); 424 } 425 426 int RenderBox::clientHeight() const 427 { 428 return height() - borderTop() - borderBottom() - horizontalScrollbarHeight(); 429 } 430 431 int RenderBox::scrollWidth() const 432 { 433 if (hasOverflowClip()) 434 return layer()->scrollWidth(); 435 // For objects with visible overflow, this matches IE. 436 // FIXME: Need to work right with writing modes. 437 if (style()->isLeftToRightDirection()) 438 return max(clientWidth(), maxXLayoutOverflow() - borderLeft()); 439 return clientWidth() - min(0, minXLayoutOverflow() - borderLeft()); 440 } 441 442 int RenderBox::scrollHeight() const 443 { 444 if (hasOverflowClip()) 445 return layer()->scrollHeight(); 446 // For objects with visible overflow, this matches IE. 447 // FIXME: Need to work right with writing modes. 448 return max(clientHeight(), maxYLayoutOverflow() - borderTop()); 449 } 450 451 int RenderBox::scrollLeft() const 452 { 453 return hasOverflowClip() ? layer()->scrollXOffset() : 0; 454 } 455 456 int RenderBox::scrollTop() const 457 { 458 return hasOverflowClip() ? layer()->scrollYOffset() : 0; 459 } 460 461 void RenderBox::setScrollLeft(int newLeft) 462 { 463 if (hasOverflowClip()) 464 layer()->scrollToXOffset(newLeft); 465 } 466 467 void RenderBox::setScrollTop(int newTop) 468 { 469 if (hasOverflowClip()) 470 layer()->scrollToYOffset(newTop); 471 } 472 473 void RenderBox::absoluteRects(Vector<IntRect>& rects, int tx, int ty) 474 { 475 rects.append(IntRect(tx, ty, width(), height())); 476 } 477 478 void RenderBox::absoluteQuads(Vector<FloatQuad>& quads) 479 { 480 quads.append(localToAbsoluteQuad(FloatRect(0, 0, width(), height()))); 481 } 482 483 void RenderBox::updateLayerTransform() 484 { 485 // Transform-origin depends on box size, so we need to update the layer transform after layout. 486 if (hasLayer()) 487 layer()->updateTransform(); 488 } 489 490 IntRect RenderBox::absoluteContentBox() const 491 { 492 IntRect rect = contentBoxRect(); 493 FloatPoint absPos = localToAbsolute(FloatPoint()); 494 rect.move(absPos.x(), absPos.y()); 495 return rect; 496 } 497 498 FloatQuad RenderBox::absoluteContentQuad() const 499 { 500 IntRect rect = contentBoxRect(); 501 return localToAbsoluteQuad(FloatRect(rect)); 502 } 503 504 IntRect RenderBox::outlineBoundsForRepaint(RenderBoxModelObject* repaintContainer, IntPoint* cachedOffsetToRepaintContainer) const 505 { 506 IntRect box = borderBoundingBox(); 507 adjustRectForOutlineAndShadow(box); 508 509 FloatQuad containerRelativeQuad = FloatRect(box); 510 if (cachedOffsetToRepaintContainer) 511 containerRelativeQuad.move(cachedOffsetToRepaintContainer->x(), cachedOffsetToRepaintContainer->y()); 512 else 513 containerRelativeQuad = localToContainerQuad(containerRelativeQuad, repaintContainer); 514 515 box = containerRelativeQuad.enclosingBoundingBox(); 516 517 // FIXME: layoutDelta needs to be applied in parts before/after transforms and 518 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 519 box.move(view()->layoutDelta()); 520 521 return box; 522 } 523 524 void RenderBox::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) 525 { 526 if (width() && height()) 527 rects.append(IntRect(tx, ty, width(), height())); 528 } 529 530 IntRect RenderBox::reflectionBox() const 531 { 532 IntRect result; 533 if (!style()->boxReflect()) 534 return result; 535 IntRect box = borderBoxRect(); 536 result = box; 537 switch (style()->boxReflect()->direction()) { 538 case ReflectionBelow: 539 result.move(0, box.height() + reflectionOffset()); 540 break; 541 case ReflectionAbove: 542 result.move(0, -box.height() - reflectionOffset()); 543 break; 544 case ReflectionLeft: 545 result.move(-box.width() - reflectionOffset(), 0); 546 break; 547 case ReflectionRight: 548 result.move(box.width() + reflectionOffset(), 0); 549 break; 550 } 551 return result; 552 } 553 554 int RenderBox::reflectionOffset() const 555 { 556 if (!style()->boxReflect()) 557 return 0; 558 if (style()->boxReflect()->direction() == ReflectionLeft || style()->boxReflect()->direction() == ReflectionRight) 559 return style()->boxReflect()->offset().calcValue(borderBoxRect().width()); 560 return style()->boxReflect()->offset().calcValue(borderBoxRect().height()); 561 } 562 563 IntRect RenderBox::reflectedRect(const IntRect& r) const 564 { 565 if (!style()->boxReflect()) 566 return IntRect(); 567 568 IntRect box = borderBoxRect(); 569 IntRect result = r; 570 switch (style()->boxReflect()->direction()) { 571 case ReflectionBelow: 572 result.setY(box.maxY() + reflectionOffset() + (box.maxY() - r.maxY())); 573 break; 574 case ReflectionAbove: 575 result.setY(box.y() - reflectionOffset() - box.height() + (box.maxY() - r.maxY())); 576 break; 577 case ReflectionLeft: 578 result.setX(box.x() - reflectionOffset() - box.width() + (box.maxX() - r.maxX())); 579 break; 580 case ReflectionRight: 581 result.setX(box.maxX() + reflectionOffset() + (box.maxX() - r.maxX())); 582 break; 583 } 584 return result; 585 } 586 587 bool RenderBox::includeVerticalScrollbarSize() const 588 { 589 return hasOverflowClip() && !layer()->hasOverlayScrollbars() 590 && (style()->overflowY() == OSCROLL || style()->overflowY() == OAUTO); 591 } 592 593 bool RenderBox::includeHorizontalScrollbarSize() const 594 { 595 return hasOverflowClip() && !layer()->hasOverlayScrollbars() 596 && (style()->overflowX() == OSCROLL || style()->overflowX() == OAUTO); 597 } 598 599 int RenderBox::verticalScrollbarWidth() const 600 { 601 return includeVerticalScrollbarSize() ? layer()->verticalScrollbarWidth() : 0; 602 } 603 604 int RenderBox::horizontalScrollbarHeight() const 605 { 606 return includeHorizontalScrollbarSize() ? layer()->horizontalScrollbarHeight() : 0; 607 } 608 609 bool RenderBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) 610 { 611 RenderLayer* l = layer(); 612 if (l && l->scroll(direction, granularity, multiplier)) { 613 if (stopNode) 614 *stopNode = node(); 615 return true; 616 } 617 618 if (stopNode && *stopNode && *stopNode == node()) 619 return true; 620 621 RenderBlock* b = containingBlock(); 622 if (b && !b->isRenderView()) 623 return b->scroll(direction, granularity, multiplier, stopNode); 624 return false; 625 } 626 627 bool RenderBox::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) 628 { 629 bool scrolled = false; 630 631 RenderLayer* l = layer(); 632 if (l) { 633 #if PLATFORM(MAC) 634 // On Mac only we reset the inline direction position when doing a document scroll (e.g., hitting Home/End). 635 if (granularity == ScrollByDocument) 636 scrolled = l->scroll(logicalToPhysical(ScrollInlineDirectionBackward, isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), ScrollByDocument, multiplier); 637 #endif 638 if (l->scroll(logicalToPhysical(direction, isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier)) 639 scrolled = true; 640 641 if (scrolled) { 642 if (stopNode) 643 *stopNode = node(); 644 return true; 645 } 646 } 647 648 if (stopNode && *stopNode && *stopNode == node()) 649 return true; 650 651 RenderBlock* b = containingBlock(); 652 if (b && !b->isRenderView()) 653 return b->logicalScroll(direction, granularity, multiplier, stopNode); 654 return false; 655 } 656 657 bool RenderBox::canBeScrolledAndHasScrollableArea() const 658 { 659 return canBeProgramaticallyScrolled(false) && (scrollHeight() != clientHeight() || scrollWidth() != clientWidth()); 660 } 661 662 bool RenderBox::canBeProgramaticallyScrolled(bool) const 663 { 664 return (hasOverflowClip() && (scrollsOverflow() || (node() && node()->rendererIsEditable()))) || (node() && node()->isDocumentNode()); 665 } 666 667 void RenderBox::autoscroll() 668 { 669 if (layer()) 670 layer()->autoscroll(); 671 } 672 673 void RenderBox::panScroll(const IntPoint& source) 674 { 675 if (layer()) 676 layer()->panScrollFromPoint(source); 677 } 678 679 int RenderBox::minPreferredLogicalWidth() const 680 { 681 if (preferredLogicalWidthsDirty()) 682 const_cast<RenderBox*>(this)->computePreferredLogicalWidths(); 683 684 return m_minPreferredLogicalWidth; 685 } 686 687 int RenderBox::maxPreferredLogicalWidth() const 688 { 689 if (preferredLogicalWidthsDirty()) 690 const_cast<RenderBox*>(this)->computePreferredLogicalWidths(); 691 692 return m_maxPreferredLogicalWidth; 693 } 694 695 int RenderBox::overrideSize() const 696 { 697 if (!hasOverrideSize()) 698 return -1; 699 return gOverrideSizeMap->get(this); 700 } 701 702 void RenderBox::setOverrideSize(int s) 703 { 704 if (s == -1) { 705 if (hasOverrideSize()) { 706 setHasOverrideSize(false); 707 gOverrideSizeMap->remove(this); 708 } 709 } else { 710 if (!gOverrideSizeMap) 711 gOverrideSizeMap = new OverrideSizeMap(); 712 setHasOverrideSize(true); 713 gOverrideSizeMap->set(this, s); 714 } 715 } 716 717 int RenderBox::overrideWidth() const 718 { 719 return hasOverrideSize() ? overrideSize() : width(); 720 } 721 722 int RenderBox::overrideHeight() const 723 { 724 return hasOverrideSize() ? overrideSize() : height(); 725 } 726 727 int RenderBox::computeBorderBoxLogicalWidth(int width) const 728 { 729 int bordersPlusPadding = borderAndPaddingLogicalWidth(); 730 if (style()->boxSizing() == CONTENT_BOX) 731 return width + bordersPlusPadding; 732 return max(width, bordersPlusPadding); 733 } 734 735 int RenderBox::computeBorderBoxLogicalHeight(int height) const 736 { 737 int bordersPlusPadding = borderAndPaddingLogicalHeight(); 738 if (style()->boxSizing() == CONTENT_BOX) 739 return height + bordersPlusPadding; 740 return max(height, bordersPlusPadding); 741 } 742 743 int RenderBox::computeContentBoxLogicalWidth(int width) const 744 { 745 if (style()->boxSizing() == BORDER_BOX) 746 width -= borderAndPaddingLogicalWidth(); 747 return max(0, width); 748 } 749 750 int RenderBox::computeContentBoxLogicalHeight(int height) const 751 { 752 if (style()->boxSizing() == BORDER_BOX) 753 height -= borderAndPaddingLogicalHeight(); 754 return max(0, height); 755 } 756 757 // Hit Testing 758 bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action) 759 { 760 tx += x(); 761 ty += y(); 762 763 // Check kids first. 764 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { 765 if (!child->hasLayer() && child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) { 766 updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty)); 767 return true; 768 } 769 } 770 771 // Check our bounds next. For this purpose always assume that we can only be hit in the 772 // foreground phase (which is true for replaced elements like images). 773 IntRect boundsRect = IntRect(tx, ty, width(), height()); 774 if (visibleToHitTesting() && action == HitTestForeground && boundsRect.intersects(result.rectForPoint(xPos, yPos))) { 775 updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty)); 776 if (!result.addNodeToRectBasedTestResult(node(), xPos, yPos, boundsRect)) 777 return true; 778 } 779 780 return false; 781 } 782 783 // --------------------- painting stuff ------------------------------- 784 785 void RenderBox::paint(PaintInfo& paintInfo, int tx, int ty) 786 { 787 tx += x(); 788 ty += y(); 789 790 // default implementation. Just pass paint through to the children 791 PaintInfo childInfo(paintInfo); 792 childInfo.updatePaintingRootForChildren(this); 793 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) 794 child->paint(childInfo, tx, ty); 795 } 796 797 void RenderBox::paintRootBoxFillLayers(const PaintInfo& paintInfo) 798 { 799 const FillLayer* bgLayer = style()->backgroundLayers(); 800 Color bgColor = style()->visitedDependentColor(CSSPropertyBackgroundColor); 801 RenderObject* bodyObject = 0; 802 if (!hasBackground() && node() && node()->hasTagName(HTMLNames::htmlTag)) { 803 // Locate the <body> element using the DOM. This is easier than trying 804 // to crawl around a render tree with potential :before/:after content and 805 // anonymous blocks created by inline <body> tags etc. We can locate the <body> 806 // render object very easily via the DOM. 807 HTMLElement* body = document()->body(); 808 bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0; 809 if (bodyObject) { 810 bgLayer = bodyObject->style()->backgroundLayers(); 811 bgColor = bodyObject->style()->visitedDependentColor(CSSPropertyBackgroundColor); 812 } 813 } 814 815 // The background of the box generated by the root element covers the entire canvas, so just use 816 // the RenderView's docTop/Left/Width/Height accessors. 817 paintFillLayers(paintInfo, bgColor, bgLayer, view()->docLeft(), view()->docTop(), view()->docWidth(), view()->docHeight(), CompositeSourceOver, bodyObject); 818 } 819 820 void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) 821 { 822 if (!paintInfo.shouldPaintWithinRoot(this)) 823 return; 824 return paintBoxDecorationsWithSize(paintInfo, tx, ty, width(), height()); 825 } 826 827 void RenderBox::paintBoxDecorationsWithSize(PaintInfo& paintInfo, int tx, int ty, int width, int height) 828 { 829 // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat 830 // balloon layout is an example of this). 831 borderFitAdjust(tx, width); 832 833 // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have 834 // custom shadows of their own. 835 paintBoxShadow(paintInfo.context, tx, ty, width, height, style(), Normal); 836 837 // If we have a native theme appearance, paint that before painting our background. 838 // The theme will tell us whether or not we should also paint the CSS background. 839 bool themePainted = style()->hasAppearance() && !theme()->paint(this, paintInfo, IntRect(tx, ty, width, height)); 840 if (!themePainted) { 841 if (isRoot()) 842 paintRootBoxFillLayers(paintInfo); 843 else if (!isBody() || document()->documentElement()->renderer()->hasBackground()) { 844 // The <body> only paints its background if the root element has defined a background 845 // independent of the body. 846 paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), tx, ty, width, height); 847 } 848 if (style()->hasAppearance()) 849 theme()->paintDecorations(this, paintInfo, IntRect(tx, ty, width, height)); 850 } 851 paintBoxShadow(paintInfo.context, tx, ty, width, height, style(), Inset); 852 853 // The theme will tell us whether or not we should also paint the CSS border. 854 if ((!style()->hasAppearance() || (!themePainted && theme()->paintBorderOnly(this, paintInfo, IntRect(tx, ty, width, height)))) && style()->hasBorder()) 855 paintBorder(paintInfo.context, tx, ty, width, height, style()); 856 } 857 858 void RenderBox::paintMask(PaintInfo& paintInfo, int tx, int ty) 859 { 860 if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask || paintInfo.context->paintingDisabled()) 861 return; 862 863 int w = width(); 864 int h = height(); 865 866 // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat 867 // balloon layout is an example of this). 868 borderFitAdjust(tx, w); 869 870 paintMaskImages(paintInfo, tx, ty, w, h); 871 } 872 873 void RenderBox::paintMaskImages(const PaintInfo& paintInfo, int tx, int ty, int w, int h) 874 { 875 // Figure out if we need to push a transparency layer to render our mask. 876 bool pushTransparencyLayer = false; 877 bool compositedMask = hasLayer() && layer()->hasCompositedMask(); 878 CompositeOperator compositeOp = CompositeSourceOver; 879 880 bool allMaskImagesLoaded = true; 881 882 if (!compositedMask) { 883 // If the context has a rotation, scale or skew, then use a transparency layer to avoid 884 // pixel cruft around the edge of the mask. 885 const AffineTransform& currentCTM = paintInfo.context->getCTM(); 886 pushTransparencyLayer = !currentCTM.isIdentityOrTranslationOrFlipped(); 887 888 StyleImage* maskBoxImage = style()->maskBoxImage().image(); 889 const FillLayer* maskLayers = style()->maskLayers(); 890 891 // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content. 892 if (maskBoxImage) 893 allMaskImagesLoaded &= maskBoxImage->isLoaded(); 894 895 if (maskLayers) 896 allMaskImagesLoaded &= maskLayers->imagesAreLoaded(); 897 898 // Before all images have loaded, just use an empty transparency layer as the mask. 899 if (!allMaskImagesLoaded) 900 pushTransparencyLayer = true; 901 902 if (maskBoxImage && maskLayers->hasImage()) { 903 // We have a mask-box-image and mask-image, so need to composite them together before using the result as a mask. 904 pushTransparencyLayer = true; 905 } else { 906 // We have to use an extra image buffer to hold the mask. Multiple mask images need 907 // to composite together using source-over so that they can then combine into a single unified mask that 908 // can be composited with the content using destination-in. SVG images need to be able to set compositing modes 909 // as they draw images contained inside their sub-document, so we paint all our images into a separate buffer 910 // and composite that buffer as the mask. 911 // We have to check that the mask images to be rendered contain at least one image that can be actually used in rendering 912 // before pushing the transparency layer. 913 for (const FillLayer* fillLayer = maskLayers->next(); fillLayer; fillLayer = fillLayer->next()) { 914 if (fillLayer->hasImage() && fillLayer->image()->canRender(style()->effectiveZoom())) { 915 pushTransparencyLayer = true; 916 // We found one image that can be used in rendering, exit the loop 917 break; 918 } 919 } 920 } 921 922 compositeOp = CompositeDestinationIn; 923 if (pushTransparencyLayer) { 924 paintInfo.context->setCompositeOperation(CompositeDestinationIn); 925 paintInfo.context->beginTransparencyLayer(1.0f); 926 compositeOp = CompositeSourceOver; 927 } 928 } 929 930 if (allMaskImagesLoaded) { 931 paintFillLayers(paintInfo, Color(), style()->maskLayers(), tx, ty, w, h, compositeOp); 932 paintNinePieceImage(paintInfo.context, tx, ty, w, h, style(), style()->maskBoxImage(), compositeOp); 933 } 934 935 if (pushTransparencyLayer) 936 paintInfo.context->endTransparencyLayer(); 937 } 938 939 IntRect RenderBox::maskClipRect() 940 { 941 IntRect bbox = borderBoxRect(); 942 if (style()->maskBoxImage().image()) 943 return bbox; 944 945 IntRect result; 946 for (const FillLayer* maskLayer = style()->maskLayers(); maskLayer; maskLayer = maskLayer->next()) { 947 if (maskLayer->image()) { 948 IntRect maskRect; 949 IntPoint phase; 950 IntSize tileSize; 951 calculateBackgroundImageGeometry(maskLayer, bbox.x(), bbox.y(), bbox.width(), bbox.height(), maskRect, phase, tileSize); 952 result.unite(maskRect); 953 } 954 } 955 return result; 956 } 957 958 void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject) 959 { 960 if (!fillLayer) 961 return; 962 963 paintFillLayers(paintInfo, c, fillLayer->next(), tx, ty, width, height, op, backgroundObject); 964 paintFillLayer(paintInfo, c, fillLayer, tx, ty, width, height, op, backgroundObject); 965 } 966 967 void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject) 968 { 969 paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, width, height, 0, 0, 0, op, backgroundObject); 970 } 971 972 #if USE(ACCELERATED_COMPOSITING) 973 static bool layersUseImage(WrappedImagePtr image, const FillLayer* layers) 974 { 975 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) { 976 if (curLayer->image() && image == curLayer->image()->data()) 977 return true; 978 } 979 980 return false; 981 } 982 #endif 983 984 void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*) 985 { 986 if (!parent()) 987 return; 988 989 if ((style()->borderImage().image() && style()->borderImage().image()->data() == image) || 990 (style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image)) { 991 repaint(); 992 return; 993 } 994 995 bool didFullRepaint = repaintLayerRectsForImage(image, style()->backgroundLayers(), true); 996 if (!didFullRepaint) 997 repaintLayerRectsForImage(image, style()->maskLayers(), false); 998 999 1000 #if USE(ACCELERATED_COMPOSITING) 1001 if (hasLayer() && layer()->hasCompositedMask() && layersUseImage(image, style()->maskLayers())) 1002 layer()->contentChanged(RenderLayer::MaskImageChanged); 1003 #endif 1004 } 1005 1006 bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground) 1007 { 1008 IntRect rendererRect; 1009 RenderBox* layerRenderer = 0; 1010 1011 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) { 1012 if (curLayer->image() && image == curLayer->image()->data() && curLayer->image()->canRender(style()->effectiveZoom())) { 1013 // Now that we know this image is being used, compute the renderer and the rect 1014 // if we haven't already 1015 if (!layerRenderer) { 1016 bool drawingRootBackground = drawingBackground && (isRoot() || (isBody() && !document()->documentElement()->renderer()->hasBackground())); 1017 if (drawingRootBackground) { 1018 layerRenderer = view(); 1019 1020 int rw; 1021 int rh; 1022 1023 if (FrameView* frameView = toRenderView(layerRenderer)->frameView()) { 1024 rw = frameView->contentsWidth(); 1025 rh = frameView->contentsHeight(); 1026 } else { 1027 rw = layerRenderer->width(); 1028 rh = layerRenderer->height(); 1029 } 1030 rendererRect = IntRect(-layerRenderer->marginLeft(), 1031 -layerRenderer->marginTop(), 1032 max(layerRenderer->width() + layerRenderer->marginLeft() + layerRenderer->marginRight() + layerRenderer->borderLeft() + layerRenderer->borderRight(), rw), 1033 max(layerRenderer->height() + layerRenderer->marginTop() + layerRenderer->marginBottom() + layerRenderer->borderTop() + layerRenderer->borderBottom(), rh)); 1034 } else { 1035 layerRenderer = this; 1036 rendererRect = borderBoxRect(); 1037 } 1038 } 1039 1040 IntRect repaintRect; 1041 IntPoint phase; 1042 IntSize tileSize; 1043 layerRenderer->calculateBackgroundImageGeometry(curLayer, rendererRect.x(), rendererRect.y(), rendererRect.width(), rendererRect.height(), repaintRect, phase, tileSize); 1044 layerRenderer->repaintRectangle(repaintRect); 1045 if (repaintRect == rendererRect) 1046 return true; 1047 } 1048 } 1049 return false; 1050 } 1051 1052 #if PLATFORM(MAC) 1053 1054 void RenderBox::paintCustomHighlight(int tx, int ty, const AtomicString& type, bool behindText) 1055 { 1056 Frame* frame = this->frame(); 1057 if (!frame) 1058 return; 1059 Page* page = frame->page(); 1060 if (!page) 1061 return; 1062 1063 InlineBox* boxWrap = inlineBoxWrapper(); 1064 RootInlineBox* r = boxWrap ? boxWrap->root() : 0; 1065 if (r) { 1066 FloatRect rootRect(tx + r->x(), ty + r->selectionTop(), r->logicalWidth(), r->selectionHeight()); 1067 FloatRect imageRect(tx + x(), rootRect.y(), width(), rootRect.height()); 1068 page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, rootRect, behindText, false); 1069 } else { 1070 FloatRect imageRect(tx + x(), ty + y(), width(), height()); 1071 page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, imageRect, behindText, false); 1072 } 1073 } 1074 1075 #endif 1076 1077 bool RenderBox::pushContentsClip(PaintInfo& paintInfo, int tx, int ty) 1078 { 1079 if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseSelfOutline || paintInfo.phase == PaintPhaseMask) 1080 return false; 1081 1082 bool isControlClip = hasControlClip(); 1083 bool isOverflowClip = hasOverflowClip() && !layer()->isSelfPaintingLayer(); 1084 1085 if (!isControlClip && !isOverflowClip) 1086 return false; 1087 1088 if (paintInfo.phase == PaintPhaseOutline) 1089 paintInfo.phase = PaintPhaseChildOutlines; 1090 else if (paintInfo.phase == PaintPhaseChildBlockBackground) { 1091 paintInfo.phase = PaintPhaseBlockBackground; 1092 paintObject(paintInfo, tx, ty); 1093 paintInfo.phase = PaintPhaseChildBlockBackgrounds; 1094 } 1095 IntRect clipRect(isControlClip ? controlClipRect(tx, ty) : overflowClipRect(tx, ty)); 1096 paintInfo.context->save(); 1097 if (style()->hasBorderRadius()) 1098 paintInfo.context->addRoundedRectClip(style()->getRoundedBorderFor(IntRect(tx, ty, width(), height()))); 1099 paintInfo.context->clip(clipRect); 1100 return true; 1101 } 1102 1103 void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, int tx, int ty) 1104 { 1105 ASSERT(hasControlClip() || (hasOverflowClip() && !layer()->isSelfPaintingLayer())); 1106 1107 paintInfo.context->restore(); 1108 if (originalPhase == PaintPhaseOutline) { 1109 paintInfo.phase = PaintPhaseSelfOutline; 1110 paintObject(paintInfo, tx, ty); 1111 paintInfo.phase = originalPhase; 1112 } else if (originalPhase == PaintPhaseChildBlockBackground) 1113 paintInfo.phase = originalPhase; 1114 } 1115 1116 IntRect RenderBox::overflowClipRect(int tx, int ty, OverlayScrollbarSizeRelevancy relevancy) 1117 { 1118 // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property 1119 // here. 1120 1121 int bLeft = borderLeft(); 1122 int bTop = borderTop(); 1123 1124 int clipX = tx + bLeft; 1125 int clipY = ty + bTop; 1126 int clipWidth = width() - bLeft - borderRight(); 1127 int clipHeight = height() - bTop - borderBottom(); 1128 1129 // Subtract out scrollbars if we have them. 1130 if (layer()) { 1131 clipWidth -= layer()->verticalScrollbarWidth(relevancy); 1132 clipHeight -= layer()->horizontalScrollbarHeight(relevancy); 1133 } 1134 1135 return IntRect(clipX, clipY, clipWidth, clipHeight); 1136 } 1137 1138 IntRect RenderBox::clipRect(int tx, int ty) 1139 { 1140 int clipX = tx; 1141 int clipY = ty; 1142 int clipWidth = width(); 1143 int clipHeight = height(); 1144 1145 if (!style()->clipLeft().isAuto()) { 1146 int c = style()->clipLeft().calcValue(width()); 1147 clipX += c; 1148 clipWidth -= c; 1149 } 1150 1151 if (!style()->clipRight().isAuto()) 1152 clipWidth -= width() - style()->clipRight().calcValue(width()); 1153 1154 if (!style()->clipTop().isAuto()) { 1155 int c = style()->clipTop().calcValue(height()); 1156 clipY += c; 1157 clipHeight -= c; 1158 } 1159 1160 if (!style()->clipBottom().isAuto()) 1161 clipHeight -= height() - style()->clipBottom().calcValue(height()); 1162 1163 return IntRect(clipX, clipY, clipWidth, clipHeight); 1164 } 1165 1166 int RenderBox::containingBlockLogicalWidthForContent() const 1167 { 1168 RenderBlock* cb = containingBlock(); 1169 if (shrinkToAvoidFloats()) 1170 return cb->availableLogicalWidthForLine(y(), false); 1171 return cb->availableLogicalWidth(); 1172 } 1173 1174 int RenderBox::perpendicularContainingBlockLogicalHeight() const 1175 { 1176 RenderBlock* cb = containingBlock(); 1177 RenderStyle* containingBlockStyle = cb->style(); 1178 Length logicalHeightLength = containingBlockStyle->logicalHeight(); 1179 1180 // FIXME: For now just support fixed heights. Eventually should support percentage heights as well. 1181 if (!logicalHeightLength.isFixed()) { 1182 // Rather than making the child be completely unconstrained, WinIE uses the viewport width and height 1183 // as a constraint. We do that for now as well even though it's likely being unconstrained is what the spec 1184 // will decide. 1185 return containingBlockStyle->isHorizontalWritingMode() ? view()->frameView()->visibleHeight() : view()->frameView()->visibleWidth(); 1186 } 1187 1188 // Use the content box logical height as specified by the style. 1189 return cb->computeContentBoxLogicalHeight(logicalHeightLength.value()); 1190 } 1191 1192 void RenderBox::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const 1193 { 1194 if (repaintContainer == this) 1195 return; 1196 1197 if (RenderView* v = view()) { 1198 if (v->layoutStateEnabled() && !repaintContainer) { 1199 LayoutState* layoutState = v->layoutState(); 1200 IntSize offset = layoutState->m_paintOffset; 1201 offset.expand(x(), y()); 1202 if (style()->position() == RelativePosition && layer()) 1203 offset += layer()->relativePositionOffset(); 1204 transformState.move(offset); 1205 return; 1206 } 1207 } 1208 1209 bool containerSkipped; 1210 RenderObject* o = container(repaintContainer, &containerSkipped); 1211 if (!o) 1212 return; 1213 1214 bool isFixedPos = style()->position() == FixedPosition; 1215 bool hasTransform = hasLayer() && layer()->transform(); 1216 if (hasTransform) { 1217 // If this box has a transform, it acts as a fixed position container for fixed descendants, 1218 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. 1219 fixed &= isFixedPos; 1220 } else 1221 fixed |= isFixedPos; 1222 1223 IntSize containerOffset = offsetFromContainer(o, roundedIntPoint(transformState.mappedPoint())); 1224 1225 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); 1226 if (useTransforms && shouldUseTransformFromContainer(o)) { 1227 TransformationMatrix t; 1228 getTransformFromContainer(o, containerOffset, t); 1229 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1230 } else 1231 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1232 1233 if (containerSkipped) { 1234 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe 1235 // to just subtract the delta between the repaintContainer and o. 1236 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); 1237 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1238 return; 1239 } 1240 1241 o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); 1242 } 1243 1244 void RenderBox::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const 1245 { 1246 // We don't expect absoluteToLocal() to be called during layout (yet) 1247 ASSERT(!view() || !view()->layoutStateEnabled()); 1248 1249 bool isFixedPos = style()->position() == FixedPosition; 1250 bool hasTransform = hasLayer() && layer()->transform(); 1251 if (hasTransform) { 1252 // If this box has a transform, it acts as a fixed position container for fixed descendants, 1253 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. 1254 fixed &= isFixedPos; 1255 } else 1256 fixed |= isFixedPos; 1257 1258 RenderObject* o = container(); 1259 if (!o) 1260 return; 1261 1262 o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState); 1263 1264 IntSize containerOffset = offsetFromContainer(o, IntPoint()); 1265 1266 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); 1267 if (useTransforms && shouldUseTransformFromContainer(o)) { 1268 TransformationMatrix t; 1269 getTransformFromContainer(o, containerOffset, t); 1270 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1271 } else 1272 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1273 } 1274 1275 IntSize RenderBox::offsetFromContainer(RenderObject* o, const IntPoint& point) const 1276 { 1277 ASSERT(o == container()); 1278 1279 IntSize offset; 1280 if (isRelPositioned()) 1281 offset += relativePositionOffset(); 1282 1283 if (!isInline() || isReplaced()) { 1284 if (style()->position() != AbsolutePosition && style()->position() != FixedPosition) { 1285 if (o->hasColumns()) { 1286 IntRect columnRect(frameRect()); 1287 toRenderBlock(o)->flipForWritingModeIncludingColumns(columnRect); 1288 offset += IntSize(columnRect.location().x(), columnRect.location().y()); 1289 columnRect.move(point.x(), point.y()); 1290 o->adjustForColumns(offset, columnRect.location()); 1291 } else 1292 offset += locationOffsetIncludingFlipping(); 1293 } else 1294 offset += locationOffsetIncludingFlipping(); 1295 } 1296 1297 if (o->hasOverflowClip()) 1298 offset -= toRenderBox(o)->layer()->scrolledContentOffset(); 1299 1300 if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isRenderInline()) 1301 offset += toRenderInline(o)->relativePositionedInlineOffset(this); 1302 1303 return offset; 1304 } 1305 1306 InlineBox* RenderBox::createInlineBox() 1307 { 1308 return new (renderArena()) InlineBox(this); 1309 } 1310 1311 void RenderBox::dirtyLineBoxes(bool fullLayout) 1312 { 1313 if (m_inlineBoxWrapper) { 1314 if (fullLayout) { 1315 m_inlineBoxWrapper->destroy(renderArena()); 1316 m_inlineBoxWrapper = 0; 1317 } else 1318 m_inlineBoxWrapper->dirtyLineBoxes(); 1319 } 1320 } 1321 1322 void RenderBox::positionLineBox(InlineBox* box) 1323 { 1324 if (isPositioned()) { 1325 // Cache the x position only if we were an INLINE type originally. 1326 bool wasInline = style()->isOriginalDisplayInlineType(); 1327 if (wasInline) { 1328 // The value is cached in the xPos of the box. We only need this value if 1329 // our object was inline originally, since otherwise it would have ended up underneath 1330 // the inlines. 1331 layer()->setStaticInlinePosition(lroundf(box->logicalLeft())); 1332 if (style()->hasStaticInlinePosition(box->isHorizontal())) 1333 setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. 1334 } else { 1335 // Our object was a block originally, so we make our normal flow position be 1336 // just below the line box (as though all the inlines that came before us got 1337 // wrapped in an anonymous block, which is what would have happened had we been 1338 // in flow). This value was cached in the y() of the box. 1339 layer()->setStaticBlockPosition(box->logicalTop()); 1340 if (style()->hasStaticBlockPosition(box->isHorizontal())) 1341 setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. 1342 } 1343 1344 // Nuke the box. 1345 box->remove(); 1346 box->destroy(renderArena()); 1347 } else if (isReplaced()) { 1348 setLocation(lroundf(box->x()), lroundf(box->y())); 1349 m_inlineBoxWrapper = box; 1350 } 1351 } 1352 1353 void RenderBox::deleteLineBoxWrapper() 1354 { 1355 if (m_inlineBoxWrapper) { 1356 if (!documentBeingDestroyed()) 1357 m_inlineBoxWrapper->remove(); 1358 m_inlineBoxWrapper->destroy(renderArena()); 1359 m_inlineBoxWrapper = 0; 1360 } 1361 } 1362 1363 IntRect RenderBox::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) 1364 { 1365 if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent()) 1366 return IntRect(); 1367 1368 IntRect r = visualOverflowRect(); 1369 1370 RenderView* v = view(); 1371 if (v) { 1372 // FIXME: layoutDelta needs to be applied in parts before/after transforms and 1373 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 1374 r.move(v->layoutDelta()); 1375 } 1376 1377 if (style()) { 1378 if (style()->hasAppearance()) 1379 // The theme may wish to inflate the rect used when repainting. 1380 theme()->adjustRepaintRect(this, r); 1381 1382 // We have to use maximalOutlineSize() because a child might have an outline 1383 // that projects outside of our overflowRect. 1384 if (v) { 1385 ASSERT(style()->outlineSize() <= v->maximalOutlineSize()); 1386 r.inflate(v->maximalOutlineSize()); 1387 } 1388 } 1389 1390 computeRectForRepaint(repaintContainer, r); 1391 return r; 1392 } 1393 1394 void RenderBox::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) 1395 { 1396 // The rect we compute at each step is shifted by our x/y offset in the parent container's coordinate space. 1397 // Only when we cross a writing mode boundary will we have to possibly flipForWritingMode (to convert into a more appropriate 1398 // offset corner for the enclosing container). This allows for a fully RL or BT document to repaint 1399 // properly even during layout, since the rect remains flipped all the way until the end. 1400 // 1401 // RenderView::computeRectForRepaint then converts the rect to physical coordinates. We also convert to 1402 // physical when we hit a repaintContainer boundary. Therefore the final rect returned is always in the 1403 // physical coordinate space of the repaintContainer. 1404 if (RenderView* v = view()) { 1405 // LayoutState is only valid for root-relative repainting 1406 if (v->layoutStateEnabled() && !repaintContainer) { 1407 LayoutState* layoutState = v->layoutState(); 1408 1409 if (layer() && layer()->transform()) 1410 rect = layer()->transform()->mapRect(rect); 1411 1412 if (style()->position() == RelativePosition && layer()) 1413 rect.move(layer()->relativePositionOffset()); 1414 1415 rect.move(x(), y()); 1416 rect.move(layoutState->m_paintOffset); 1417 if (layoutState->m_clipped) 1418 rect.intersect(layoutState->m_clipRect); 1419 return; 1420 } 1421 } 1422 1423 if (hasReflection()) 1424 rect.unite(reflectedRect(rect)); 1425 1426 if (repaintContainer == this) { 1427 if (repaintContainer->style()->isFlippedBlocksWritingMode()) 1428 flipForWritingMode(rect); 1429 return; 1430 } 1431 1432 bool containerSkipped; 1433 RenderObject* o = container(repaintContainer, &containerSkipped); 1434 if (!o) 1435 return; 1436 1437 if (isWritingModeRoot() && !isPositioned()) 1438 flipForWritingMode(rect); 1439 IntPoint topLeft = rect.location(); 1440 topLeft.move(x(), y()); 1441 1442 EPosition position = style()->position(); 1443 1444 // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box 1445 // in the parent's coordinate space that encloses us. 1446 if (layer() && layer()->transform()) { 1447 fixed = position == FixedPosition; 1448 rect = layer()->transform()->mapRect(rect); 1449 topLeft = rect.location(); 1450 topLeft.move(x(), y()); 1451 } else if (position == FixedPosition) 1452 fixed = true; 1453 1454 if (position == AbsolutePosition && o->isRelPositioned() && o->isRenderInline()) 1455 topLeft += toRenderInline(o)->relativePositionedInlineOffset(this); 1456 else if (position == RelativePosition && layer()) { 1457 // Apply the relative position offset when invalidating a rectangle. The layer 1458 // is translated, but the render box isn't, so we need to do this to get the 1459 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position 1460 // flag on the RenderObject has been cleared, so use the one on the style(). 1461 topLeft += layer()->relativePositionOffset(); 1462 } 1463 1464 if (o->isBlockFlow() && position != AbsolutePosition && position != FixedPosition) { 1465 RenderBlock* cb = toRenderBlock(o); 1466 if (cb->hasColumns()) { 1467 IntRect repaintRect(topLeft, rect.size()); 1468 cb->adjustRectForColumns(repaintRect); 1469 topLeft = repaintRect.location(); 1470 rect = repaintRect; 1471 } 1472 } 1473 1474 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout, 1475 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer. 1476 if (o->hasOverflowClip()) { 1477 RenderBox* containerBox = toRenderBox(o); 1478 1479 // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the 1480 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint 1481 // anyway if its size does change. 1482 topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden. 1483 1484 IntRect repaintRect(topLeft, rect.size()); 1485 IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height()); 1486 rect = intersection(repaintRect, boxRect); 1487 if (rect.isEmpty()) 1488 return; 1489 } else 1490 rect.setLocation(topLeft); 1491 1492 if (containerSkipped) { 1493 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates. 1494 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); 1495 rect.move(-containerOffset); 1496 return; 1497 } 1498 1499 o->computeRectForRepaint(repaintContainer, rect, fixed); 1500 } 1501 1502 void RenderBox::repaintDuringLayoutIfMoved(const IntRect& rect) 1503 { 1504 int newX = x(); 1505 int newY = y(); 1506 int newWidth = width(); 1507 int newHeight = height(); 1508 if (rect.x() != newX || rect.y() != newY) { 1509 // The child moved. Invalidate the object's old and new positions. We have to do this 1510 // since the object may not have gotten a layout. 1511 m_frameRect = rect; 1512 repaint(); 1513 repaintOverhangingFloats(true); 1514 m_frameRect = IntRect(newX, newY, newWidth, newHeight); 1515 repaint(); 1516 repaintOverhangingFloats(true); 1517 } 1518 } 1519 1520 #ifdef ANDROID_LAYOUT 1521 void RenderBox::setVisibleWidth(int newWidth) { 1522 const Settings* settings = document()->settings(); 1523 ASSERT(settings); 1524 if (settings->layoutAlgorithm() != Settings::kLayoutFitColumnToScreen 1525 || m_visibleWidth == newWidth) 1526 return; 1527 m_isVisibleWidthChangedBeforeLayout = true; 1528 m_visibleWidth = newWidth; 1529 } 1530 1531 bool RenderBox::checkAndSetRelayoutChildren(bool* relayoutChildren) { 1532 if (m_isVisibleWidthChangedBeforeLayout) { 1533 m_isVisibleWidthChangedBeforeLayout = false; 1534 *relayoutChildren = true; 1535 return true; 1536 } 1537 return false; 1538 } 1539 #endif 1540 1541 void RenderBox::computeLogicalWidth() 1542 { 1543 #ifdef ANDROID_LAYOUT 1544 if (view()->frameView()) 1545 setVisibleWidth(view()->frameView()->textWrapWidth()); 1546 #endif 1547 1548 if (isPositioned()) { 1549 // FIXME: This calculation is not patched for block-flow yet. 1550 // https://bugs.webkit.org/show_bug.cgi?id=46500 1551 computePositionedLogicalWidth(); 1552 return; 1553 } 1554 1555 // If layout is limited to a subtree, the subtree root's logical width does not change. 1556 if (node() && view()->frameView() && view()->frameView()->layoutRoot(true) == this) 1557 return; 1558 1559 // The parent box is flexing us, so it has increased or decreased our 1560 // width. Use the width from the style context. 1561 // FIXME: Account for block-flow in flexible boxes. 1562 // https://bugs.webkit.org/show_bug.cgi?id=46418 1563 if (hasOverrideSize() && parent()->style()->boxOrient() == HORIZONTAL 1564 && parent()->isFlexibleBox() && parent()->isFlexingChildren()) { 1565 setLogicalWidth(overrideSize()); 1566 return; 1567 } 1568 1569 // FIXME: Account for block-flow in flexible boxes. 1570 // https://bugs.webkit.org/show_bug.cgi?id=46418 1571 bool inVerticalBox = parent()->isFlexibleBox() && (parent()->style()->boxOrient() == VERTICAL); 1572 bool stretching = (parent()->style()->boxAlign() == BSTRETCH); 1573 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inVerticalBox || !stretching); 1574 1575 Length logicalWidthLength = (treatAsReplaced) ? Length(computeReplacedLogicalWidth(), Fixed) : style()->logicalWidth(); 1576 1577 RenderBlock* cb = containingBlock(); 1578 int containerLogicalWidth = max(0, containingBlockLogicalWidthForContent()); 1579 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode(); 1580 int containerWidthInInlineDirection = containerLogicalWidth; 1581 if (hasPerpendicularContainingBlock) 1582 containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight(); 1583 1584 if (isInline() && !isInlineBlockOrInlineTable()) { 1585 // just calculate margins 1586 setMarginStart(style()->marginStart().calcMinValue(containerLogicalWidth)); 1587 setMarginEnd(style()->marginEnd().calcMinValue(containerLogicalWidth)); 1588 #ifdef ANDROID_LAYOUT 1589 if (treatAsReplaced) { 1590 #else 1591 if (treatAsReplaced) 1592 #endif 1593 setLogicalWidth(max(logicalWidthLength.value() + borderAndPaddingLogicalWidth(), minPreferredLogicalWidth())); 1594 1595 #ifdef ANDROID_LAYOUT 1596 // in SSR mode with replaced box, if the box width is wider than the container width, 1597 // it will be shrinked to fit to the container. 1598 if (containerLogicalWidth && (width() + m_marginLeft + m_marginRight) > containerLogicalWidth && 1599 document()->frame()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { 1600 m_marginLeft = m_marginRight = 0; 1601 setWidth(containerLogicalWidth); 1602 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = containerLogicalWidth; 1603 } 1604 } 1605 #endif 1606 return; 1607 } 1608 1609 // Width calculations 1610 if (treatAsReplaced) 1611 setLogicalWidth(logicalWidthLength.value() + borderAndPaddingLogicalWidth()); 1612 else { 1613 // Calculate LogicalWidth 1614 setLogicalWidth(computeLogicalWidthUsing(LogicalWidth, containerWidthInInlineDirection)); 1615 1616 // Calculate MaxLogicalWidth 1617 if (!style()->logicalMaxWidth().isUndefined()) { 1618 int maxLogicalWidth = computeLogicalWidthUsing(MaxLogicalWidth, containerWidthInInlineDirection); 1619 if (logicalWidth() > maxLogicalWidth) { 1620 setLogicalWidth(maxLogicalWidth); 1621 logicalWidthLength = style()->logicalMaxWidth(); 1622 } 1623 } 1624 1625 // Calculate MinLogicalWidth 1626 int minLogicalWidth = computeLogicalWidthUsing(MinLogicalWidth, containerWidthInInlineDirection); 1627 if (logicalWidth() < minLogicalWidth) { 1628 setLogicalWidth(minLogicalWidth); 1629 logicalWidthLength = style()->logicalMinWidth(); 1630 } 1631 } 1632 1633 // Fieldsets are currently the only objects that stretch to their minimum width. 1634 if (stretchesToMinIntrinsicLogicalWidth()) { 1635 setLogicalWidth(max(logicalWidth(), minPreferredLogicalWidth())); 1636 logicalWidthLength = Length(logicalWidth(), Fixed); 1637 } 1638 1639 // Margin calculations. 1640 if (logicalWidthLength.isAuto() || hasPerpendicularContainingBlock || isFloating() || isInline()) { 1641 setMarginStart(style()->marginStart().calcMinValue(containerLogicalWidth)); 1642 setMarginEnd(style()->marginEnd().calcMinValue(containerLogicalWidth)); 1643 } else 1644 computeInlineDirectionMargins(cb, containerLogicalWidth, logicalWidth()); 1645 1646 #ifdef ANDROID_LAYOUT 1647 // in SSR mode with non-replaced box, we use ANDROID_SSR_MARGIN_PADDING for left/right margin. 1648 // If the box width is wider than the container width, it will be shrinked to fit to the container. 1649 if (containerLogicalWidth && !treatAsReplaced && 1650 document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { 1651 setWidth(width() + m_marginLeft + m_marginRight); 1652 m_marginLeft = m_marginLeft > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginLeft; 1653 m_marginRight = m_marginRight > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginRight; 1654 if (width() > containerLogicalWidth) { 1655 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = containerLogicalWidth-(m_marginLeft + m_marginRight); 1656 setWidth(m_minPreferredLogicalWidth); 1657 } else 1658 setWidth(width() -(m_marginLeft + m_marginRight)); 1659 } 1660 #endif 1661 1662 if (!hasPerpendicularContainingBlock && containerLogicalWidth && containerLogicalWidth != (logicalWidth() + marginStart() + marginEnd()) 1663 && !isFloating() && !isInline() && !cb->isFlexibleBox()) 1664 cb->setMarginEndForChild(this, containerLogicalWidth - logicalWidth() - cb->marginStartForChild(this)); 1665 } 1666 1667 int RenderBox::computeLogicalWidthUsing(LogicalWidthType widthType, int availableLogicalWidth) 1668 { 1669 int logicalWidthResult = logicalWidth(); 1670 Length logicalWidth; 1671 if (widthType == LogicalWidth) 1672 logicalWidth = style()->logicalWidth(); 1673 else if (widthType == MinLogicalWidth) 1674 logicalWidth = style()->logicalMinWidth(); 1675 else 1676 logicalWidth = style()->logicalMaxWidth(); 1677 1678 if (logicalWidth.isIntrinsicOrAuto()) { 1679 int marginStart = style()->marginStart().calcMinValue(availableLogicalWidth); 1680 int marginEnd = style()->marginEnd().calcMinValue(availableLogicalWidth); 1681 if (availableLogicalWidth) 1682 logicalWidthResult = availableLogicalWidth - marginStart - marginEnd; 1683 1684 if (sizesToIntrinsicLogicalWidth(widthType)) { 1685 logicalWidthResult = max(logicalWidthResult, minPreferredLogicalWidth()); 1686 logicalWidthResult = min(logicalWidthResult, maxPreferredLogicalWidth()); 1687 } 1688 } else // FIXME: If the containing block flow is perpendicular to our direction we need to use the available logical height instead. 1689 logicalWidthResult = computeBorderBoxLogicalWidth(logicalWidth.calcValue(availableLogicalWidth)); 1690 1691 return logicalWidthResult; 1692 } 1693 1694 bool RenderBox::sizesToIntrinsicLogicalWidth(LogicalWidthType widthType) const 1695 { 1696 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks, 1697 // but they allow text to sit on the same line as the marquee. 1698 if (isFloating() || (isInlineBlockOrInlineTable() && !isHTMLMarquee())) 1699 return true; 1700 1701 // This code may look a bit strange. Basically width:intrinsic should clamp the size when testing both 1702 // min-width and width. max-width is only clamped if it is also intrinsic. 1703 Length logicalWidth = (widthType == MaxLogicalWidth) ? style()->logicalMaxWidth() : style()->logicalWidth(); 1704 if (logicalWidth.type() == Intrinsic) 1705 return true; 1706 1707 // Children of a horizontal marquee do not fill the container by default. 1708 // FIXME: Need to deal with MAUTO value properly. It could be vertical. 1709 // FIXME: Think about block-flow here. Need to find out how marquee direction relates to 1710 // block-flow (as well as how marquee overflow should relate to block flow). 1711 // https://bugs.webkit.org/show_bug.cgi?id=46472 1712 if (parent()->style()->overflowX() == OMARQUEE) { 1713 EMarqueeDirection dir = parent()->style()->marqueeDirection(); 1714 if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT) 1715 return true; 1716 } 1717 1718 // Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes 1719 // that don't stretch their kids lay out their children at their intrinsic widths. 1720 // FIXME: Think about block-flow here. 1721 // https://bugs.webkit.org/show_bug.cgi?id=46473 1722 if (parent()->isFlexibleBox() 1723 && (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH)) 1724 return true; 1725 1726 // Button, input, select, textarea, legend and datagrid treat 1727 // width value of 'auto' as 'intrinsic' unless it's in a 1728 // stretching vertical flexbox. 1729 // FIXME: Think about block-flow here. 1730 // https://bugs.webkit.org/show_bug.cgi?id=46473 1731 if (logicalWidth.type() == Auto && !(parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL && parent()->style()->boxAlign() == BSTRETCH) && node() && (node()->hasTagName(inputTag) || node()->hasTagName(selectTag) || node()->hasTagName(buttonTag) || node()->hasTagName(textareaTag) || node()->hasTagName(legendTag) || node()->hasTagName(datagridTag))) 1732 return true; 1733 1734 return false; 1735 } 1736 1737 void RenderBox::computeInlineDirectionMargins(RenderBlock* containingBlock, int containerWidth, int childWidth) 1738 { 1739 const RenderStyle* containingBlockStyle = containingBlock->style(); 1740 Length marginStartLength = style()->marginStartUsing(containingBlockStyle); 1741 Length marginEndLength = style()->marginEndUsing(containingBlockStyle); 1742 1743 // Case One: The object is being centered in the containing block's available logical width. 1744 if ((marginStartLength.isAuto() && marginEndLength.isAuto() && childWidth < containerWidth) 1745 || (!marginStartLength.isAuto() && !marginEndLength.isAuto() && containingBlock->style()->textAlign() == WEBKIT_CENTER)) { 1746 containingBlock->setMarginStartForChild(this, max(0, (containerWidth - childWidth) / 2)); 1747 containingBlock->setMarginEndForChild(this, containerWidth - childWidth - containingBlock->marginStartForChild(this)); 1748 return; 1749 } 1750 1751 // Case Two: The object is being pushed to the start of the containing block's available logical width. 1752 if (marginEndLength.isAuto() && childWidth < containerWidth) { 1753 containingBlock->setMarginStartForChild(this, marginStartLength.calcValue(containerWidth)); 1754 containingBlock->setMarginEndForChild(this, containerWidth - childWidth - containingBlock->marginStartForChild(this)); 1755 return; 1756 } 1757 1758 // Case Three: The object is being pushed to the end of the containing block's available logical width. 1759 bool pushToEndFromTextAlign = !marginEndLength.isAuto() && ((!containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_LEFT) 1760 || (containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_RIGHT)); 1761 if ((marginStartLength.isAuto() && childWidth < containerWidth) || pushToEndFromTextAlign) { 1762 containingBlock->setMarginEndForChild(this, marginEndLength.calcValue(containerWidth)); 1763 containingBlock->setMarginStartForChild(this, containerWidth - childWidth - containingBlock->marginEndForChild(this)); 1764 return; 1765 } 1766 1767 // Case Four: Either no auto margins, or our width is >= the container width (css2.1, 10.3.3). In that case 1768 // auto margins will just turn into 0. 1769 containingBlock->setMarginStartForChild(this, marginStartLength.calcMinValue(containerWidth)); 1770 containingBlock->setMarginEndForChild(this, marginEndLength.calcMinValue(containerWidth)); 1771 } 1772 1773 void RenderBox::computeLogicalHeight() 1774 { 1775 // Cell height is managed by the table and inline non-replaced elements do not support a height property. 1776 if (isTableCell() || (isInline() && !isReplaced())) 1777 return; 1778 1779 Length h; 1780 if (isPositioned()) { 1781 // FIXME: This calculation is not patched for block-flow yet. 1782 // https://bugs.webkit.org/show_bug.cgi?id=46500 1783 computePositionedLogicalHeight(); 1784 } else { 1785 RenderBlock* cb = containingBlock(); 1786 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode(); 1787 1788 if (!hasPerpendicularContainingBlock) 1789 computeBlockDirectionMargins(cb); 1790 1791 // For tables, calculate margins only. 1792 if (isTable()) { 1793 if (hasPerpendicularContainingBlock) 1794 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), logicalHeight()); 1795 return; 1796 } 1797 1798 // FIXME: Account for block-flow in flexible boxes. 1799 // https://bugs.webkit.org/show_bug.cgi?id=46418 1800 bool inHorizontalBox = parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL; 1801 bool stretching = parent()->style()->boxAlign() == BSTRETCH; 1802 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inHorizontalBox || !stretching); 1803 bool checkMinMaxHeight = false; 1804 1805 // The parent box is flexing us, so it has increased or decreased our height. We have to 1806 // grab our cached flexible height. 1807 // FIXME: Account for block-flow in flexible boxes. 1808 // https://bugs.webkit.org/show_bug.cgi?id=46418 1809 if (hasOverrideSize() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL 1810 && parent()->isFlexingChildren()) 1811 h = Length(overrideSize() - borderAndPaddingLogicalHeight(), Fixed); 1812 else if (treatAsReplaced) 1813 h = Length(computeReplacedLogicalHeight(), Fixed); 1814 else { 1815 h = style()->logicalHeight(); 1816 checkMinMaxHeight = true; 1817 } 1818 1819 // Block children of horizontal flexible boxes fill the height of the box. 1820 // FIXME: Account for block-flow in flexible boxes. 1821 // https://bugs.webkit.org/show_bug.cgi?id=46418 1822 if (h.isAuto() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL 1823 && parent()->isStretchingChildren()) { 1824 h = Length(parentBox()->contentLogicalHeight() - marginBefore() - marginAfter() - borderAndPaddingLogicalHeight(), Fixed); 1825 checkMinMaxHeight = false; 1826 } 1827 1828 int heightResult; 1829 if (checkMinMaxHeight) { 1830 #ifdef ANDROID_LAYOUT 1831 // in SSR mode, ignore CSS height as layout is so different 1832 if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) 1833 heightResult = -1; 1834 else 1835 #endif 1836 heightResult = computeLogicalHeightUsing(style()->logicalHeight()); 1837 if (heightResult == -1) 1838 heightResult = logicalHeight(); 1839 int minH = computeLogicalHeightUsing(style()->logicalMinHeight()); // Leave as -1 if unset. 1840 int maxH = style()->logicalMaxHeight().isUndefined() ? heightResult : computeLogicalHeightUsing(style()->logicalMaxHeight()); 1841 if (maxH == -1) 1842 maxH = heightResult; 1843 heightResult = min(maxH, heightResult); 1844 heightResult = max(minH, heightResult); 1845 } else { 1846 // The only times we don't check min/max height are when a fixed length has 1847 // been given as an override. Just use that. The value has already been adjusted 1848 // for box-sizing. 1849 heightResult = h.value() + borderAndPaddingLogicalHeight(); 1850 } 1851 1852 setLogicalHeight(heightResult); 1853 1854 if (hasPerpendicularContainingBlock) 1855 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), heightResult); 1856 } 1857 1858 // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the 1859 // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height 1860 // is specified. When we're printing, we also need this quirk if the body or root has a percentage 1861 // height since we don't set a height in RenderView when we're printing. So without this quirk, the 1862 // height has nothing to be a percentage of, and it ends up being 0. That is bad. 1863 bool paginatedContentNeedsBaseHeight = document()->printing() && h.isPercent() 1864 && (isRoot() || (isBody() && document()->documentElement()->renderer()->style()->logicalHeight().isPercent())); 1865 if (stretchesToViewport() || paginatedContentNeedsBaseHeight) { 1866 // FIXME: Finish accounting for block flow here. 1867 // https://bugs.webkit.org/show_bug.cgi?id=46603 1868 int margins = collapsedMarginBefore() + collapsedMarginAfter(); 1869 int visHeight; 1870 if (document()->printing()) 1871 visHeight = static_cast<int>(view()->pageLogicalHeight()); 1872 else { 1873 if (isHorizontalWritingMode()) 1874 visHeight = view()->viewHeight(); 1875 else 1876 visHeight = view()->viewWidth(); 1877 } 1878 if (isRoot()) 1879 setLogicalHeight(max(logicalHeight(), visHeight - margins)); 1880 else { 1881 int marginsBordersPadding = margins + parentBox()->marginBefore() + parentBox()->marginAfter() + parentBox()->borderAndPaddingLogicalHeight(); 1882 setLogicalHeight(max(logicalHeight(), visHeight - marginsBordersPadding)); 1883 } 1884 } 1885 } 1886 1887 int RenderBox::computeLogicalHeightUsing(const Length& h) 1888 { 1889 int logicalHeight = -1; 1890 if (!h.isAuto()) { 1891 if (h.isFixed()) 1892 logicalHeight = h.value(); 1893 else if (h.isPercent()) 1894 logicalHeight = computePercentageLogicalHeight(h); 1895 if (logicalHeight != -1) { 1896 logicalHeight = computeBorderBoxLogicalHeight(logicalHeight); 1897 return logicalHeight; 1898 } 1899 } 1900 return logicalHeight; 1901 } 1902 1903 int RenderBox::computePercentageLogicalHeight(const Length& height) 1904 { 1905 int result = -1; 1906 1907 // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing 1908 // block that may have a specified height and then use it. In strict mode, this violates the 1909 // specification, which states that percentage heights just revert to auto if the containing 1910 // block has an auto height. We still skip anonymous containing blocks in both modes, though, and look 1911 // only at explicit containers. 1912 bool skippedAutoHeightContainingBlock = false; 1913 RenderBlock* cb = containingBlock(); 1914 while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() && cb->style()->logicalHeight().isAuto()) { 1915 if (!document()->inQuirksMode() && !cb->isAnonymousBlock()) 1916 break; 1917 skippedAutoHeightContainingBlock = true; 1918 cb = cb->containingBlock(); 1919 cb->addPercentHeightDescendant(this); 1920 } 1921 1922 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height 1923 // explicitly specified that can be used for any percentage computations. 1924 // FIXME: We can't just check top/bottom here. 1925 // https://bugs.webkit.org/show_bug.cgi?id=46500 1926 bool isPositionedWithSpecifiedHeight = cb->isPositioned() && (!cb->style()->logicalHeight().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto())); 1927 1928 bool includeBorderPadding = isTable(); 1929 1930 // Table cells violate what the CSS spec says to do with heights. Basically we 1931 // don't care if the cell specified a height or not. We just always make ourselves 1932 // be a percentage of the cell's current content height. 1933 if (cb->isTableCell()) { 1934 if (!skippedAutoHeightContainingBlock) { 1935 result = cb->overrideSize(); 1936 if (result == -1) { 1937 // Normally we would let the cell size intrinsically, but scrolling overflow has to be 1938 // treated differently, since WinIE lets scrolled overflow regions shrink as needed. 1939 // While we can't get all cases right, we can at least detect when the cell has a specified 1940 // height or when the table has a specified height. In these cases we want to initially have 1941 // no size and allow the flexing of the table or the cell to its specified height to cause us 1942 // to grow to fill the space. This could end up being wrong in some cases, but it is 1943 // preferable to the alternative (sizing intrinsically and making the row end up too big). 1944 RenderTableCell* cell = toRenderTableCell(cb); 1945 if (scrollsOverflowY() && (!cell->style()->logicalHeight().isAuto() || !cell->table()->style()->logicalHeight().isAuto())) 1946 return 0; 1947 return -1; 1948 } 1949 includeBorderPadding = true; 1950 } 1951 } 1952 // Otherwise we only use our percentage height if our containing block had a specified 1953 // height. 1954 else if (cb->style()->logicalHeight().isFixed()) 1955 result = cb->computeContentBoxLogicalHeight(cb->style()->logicalHeight().value()); 1956 else if (cb->style()->logicalHeight().isPercent() && !isPositionedWithSpecifiedHeight) { 1957 // We need to recur and compute the percentage height for our containing block. 1958 result = cb->computePercentageLogicalHeight(cb->style()->logicalHeight()); 1959 if (result != -1) 1960 result = cb->computeContentBoxLogicalHeight(result); 1961 } else if (cb->isRenderView() || (cb->isBody() && document()->inQuirksMode()) || isPositionedWithSpecifiedHeight) { 1962 // Don't allow this to affect the block' height() member variable, since this 1963 // can get called while the block is still laying out its kids. 1964 int oldHeight = cb->logicalHeight(); 1965 cb->computeLogicalHeight(); 1966 result = cb->contentLogicalHeight(); 1967 cb->setLogicalHeight(oldHeight); 1968 } else if (cb->isRoot() && isPositioned()) 1969 // Match the positioned objects behavior, which is that positioned objects will fill their viewport 1970 // always. Note we could only hit this case by recurring into computePercentageLogicalHeight on a positioned containing block. 1971 result = cb->computeContentBoxLogicalHeight(cb->availableLogicalHeight()); 1972 1973 if (result != -1) { 1974 result = height.calcValue(result); 1975 if (includeBorderPadding) { 1976 // It is necessary to use the border-box to match WinIE's broken 1977 // box model. This is essential for sizing inside 1978 // table cells using percentage heights. 1979 result -= borderAndPaddingLogicalHeight(); 1980 result = max(0, result); 1981 } 1982 } 1983 return result; 1984 } 1985 1986 int RenderBox::computeReplacedLogicalWidth(bool includeMaxWidth) const 1987 { 1988 int logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth()); 1989 int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth()); 1990 int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth()); 1991 1992 return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth)); 1993 } 1994 1995 int RenderBox::computeReplacedLogicalWidthUsing(Length logicalWidth) const 1996 { 1997 switch (logicalWidth.type()) { 1998 case Fixed: 1999 return computeContentBoxLogicalWidth(logicalWidth.value()); 2000 case Percent: { 2001 // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced element's block-flow is perpendicular to the 2002 // containing block's block-flow. 2003 // https://bugs.webkit.org/show_bug.cgi?id=46496 2004 const int cw = isPositioned() ? containingBlockLogicalWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockLogicalWidthForContent(); 2005 if (cw > 0) 2006 return computeContentBoxLogicalWidth(logicalWidth.calcMinValue(cw)); 2007 } 2008 // fall through 2009 default: 2010 return intrinsicLogicalWidth(); 2011 } 2012 } 2013 2014 int RenderBox::computeReplacedLogicalHeight() const 2015 { 2016 int logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight()); 2017 int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight()); 2018 int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight()); 2019 2020 return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight)); 2021 } 2022 2023 int RenderBox::computeReplacedLogicalHeightUsing(Length logicalHeight) const 2024 { 2025 switch (logicalHeight.type()) { 2026 case Fixed: 2027 return computeContentBoxLogicalHeight(logicalHeight.value()); 2028 case Percent: 2029 { 2030 RenderObject* cb = isPositioned() ? container() : containingBlock(); 2031 while (cb->isAnonymous()) { 2032 cb = cb->containingBlock(); 2033 toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this)); 2034 } 2035 2036 // FIXME: This calculation is not patched for block-flow yet. 2037 // https://bugs.webkit.org/show_bug.cgi?id=46500 2038 if (cb->isPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) { 2039 ASSERT(cb->isRenderBlock()); 2040 RenderBlock* block = toRenderBlock(cb); 2041 int oldHeight = block->height(); 2042 block->computeLogicalHeight(); 2043 int newHeight = block->computeContentBoxLogicalHeight(block->contentHeight()); 2044 block->setHeight(oldHeight); 2045 return computeContentBoxLogicalHeight(logicalHeight.calcValue(newHeight)); 2046 } 2047 2048 // FIXME: availableLogicalHeight() is wrong if the replaced element's block-flow is perpendicular to the 2049 // containing block's block-flow. 2050 // https://bugs.webkit.org/show_bug.cgi?id=46496 2051 int availableHeight = isPositioned() ? containingBlockLogicalHeightForPositioned(toRenderBoxModelObject(cb)) : toRenderBox(cb)->availableLogicalHeight(); 2052 2053 // It is necessary to use the border-box to match WinIE's broken 2054 // box model. This is essential for sizing inside 2055 // table cells using percentage heights. 2056 // FIXME: This needs to be made block-flow-aware. If the cell and image are perpendicular block-flows, this isn't right. 2057 // https://bugs.webkit.org/show_bug.cgi?id=46997 2058 if (cb->isTableCell() && (cb->style()->logicalHeight().isAuto() || cb->style()->logicalHeight().isPercent())) { 2059 // Don't let table cells squeeze percent-height replaced elements 2060 // <http://bugs.webkit.org/show_bug.cgi?id=15359> 2061 availableHeight = max(availableHeight, intrinsicLogicalHeight()); 2062 return logicalHeight.calcValue(availableHeight - borderAndPaddingLogicalHeight()); 2063 } 2064 2065 return computeContentBoxLogicalHeight(logicalHeight.calcValue(availableHeight)); 2066 } 2067 default: 2068 return intrinsicLogicalHeight(); 2069 } 2070 } 2071 2072 int RenderBox::availableLogicalHeight() const 2073 { 2074 return availableLogicalHeightUsing(style()->logicalHeight()); 2075 } 2076 2077 int RenderBox::availableLogicalHeightUsing(const Length& h) const 2078 { 2079 if (h.isFixed()) 2080 return computeContentBoxLogicalHeight(h.value()); 2081 2082 if (isRenderView()) 2083 return isHorizontalWritingMode() ? toRenderView(this)->frameView()->visibleHeight() : toRenderView(this)->frameView()->visibleWidth(); 2084 2085 // We need to stop here, since we don't want to increase the height of the table 2086 // artificially. We're going to rely on this cell getting expanded to some new 2087 // height, and then when we lay out again we'll use the calculation below. 2088 if (isTableCell() && (h.isAuto() || h.isPercent())) 2089 return overrideSize() - borderAndPaddingLogicalWidth(); 2090 2091 if (h.isPercent()) 2092 return computeContentBoxLogicalHeight(h.calcValue(containingBlock()->availableLogicalHeight())); 2093 2094 // FIXME: We can't just check top/bottom here. 2095 // https://bugs.webkit.org/show_bug.cgi?id=46500 2096 if (isRenderBlock() && isPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) { 2097 RenderBlock* block = const_cast<RenderBlock*>(toRenderBlock(this)); 2098 int oldHeight = block->logicalHeight(); 2099 block->computeLogicalHeight(); 2100 int newHeight = block->computeContentBoxLogicalHeight(block->contentLogicalHeight()); 2101 block->setLogicalHeight(oldHeight); 2102 return computeContentBoxLogicalHeight(newHeight); 2103 } 2104 2105 return containingBlock()->availableLogicalHeight(); 2106 } 2107 2108 void RenderBox::computeBlockDirectionMargins(RenderBlock* containingBlock) 2109 { 2110 if (isTableCell()) { 2111 // FIXME: Not right if we allow cells to have different directionality than the table. If we do allow this, though, 2112 // we may just do it with an extra anonymous block inside the cell. 2113 setMarginBefore(0); 2114 setMarginAfter(0); 2115 return; 2116 } 2117 2118 // Margins are calculated with respect to the logical width of 2119 // the containing block (8.3) 2120 int cw = containingBlockLogicalWidthForContent(); 2121 2122 RenderStyle* containingBlockStyle = containingBlock->style(); 2123 containingBlock->setMarginBeforeForChild(this, style()->marginBeforeUsing(containingBlockStyle).calcMinValue(cw)); 2124 containingBlock->setMarginAfterForChild(this, style()->marginAfterUsing(containingBlockStyle).calcMinValue(cw)); 2125 } 2126 2127 int RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const 2128 { 2129 #if PLATFORM(ANDROID) 2130 // Fixed element's position should be decided by the visible screen size. 2131 // That is in the doc coordindate. 2132 if (style()->position() == FixedPosition && containingBlock->isRenderView()) { 2133 const RenderView* view = toRenderView(containingBlock); 2134 return PlatformBridge::screenWidthInDocCoord(view->frameView()); 2135 } 2136 #endif 2137 2138 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode()) 2139 return containingBlockLogicalHeightForPositioned(containingBlock, false); 2140 2141 if (containingBlock->isBox()) 2142 return toRenderBox(containingBlock)->clientLogicalWidth(); 2143 2144 ASSERT(containingBlock->isRenderInline() && containingBlock->isRelPositioned()); 2145 2146 const RenderInline* flow = toRenderInline(containingBlock); 2147 InlineFlowBox* first = flow->firstLineBox(); 2148 InlineFlowBox* last = flow->lastLineBox(); 2149 2150 // If the containing block is empty, return a width of 0. 2151 if (!first || !last) 2152 return 0; 2153 2154 int fromLeft; 2155 int fromRight; 2156 if (containingBlock->style()->isLeftToRightDirection()) { 2157 fromLeft = first->logicalLeft() + first->borderLogicalLeft(); 2158 fromRight = last->logicalLeft() + last->logicalWidth() - last->borderLogicalRight(); 2159 } else { 2160 fromRight = first->logicalLeft() + first->logicalWidth() - first->borderLogicalRight(); 2161 fromLeft = last->logicalLeft() + last->borderLogicalLeft(); 2162 } 2163 2164 return max(0, (fromRight - fromLeft)); 2165 } 2166 2167 int RenderBox::containingBlockLogicalHeightForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const 2168 { 2169 #if PLATFORM(ANDROID) 2170 // Fixed element's position should be decided by the visible screen size. 2171 // That is in the doc coordindate. 2172 if (style()->position() == FixedPosition && containingBlock->isRenderView()) { 2173 const RenderView* view = toRenderView(containingBlock); 2174 return PlatformBridge::screenHeightInDocCoord(view->frameView()); 2175 } 2176 #endif 2177 2178 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode()) 2179 return containingBlockLogicalWidthForPositioned(containingBlock, false); 2180 2181 if (containingBlock->isBox()) 2182 return toRenderBox(containingBlock)->clientLogicalHeight(); 2183 2184 ASSERT(containingBlock->isRenderInline() && containingBlock->isRelPositioned()); 2185 2186 const RenderInline* flow = toRenderInline(containingBlock); 2187 InlineFlowBox* first = flow->firstLineBox(); 2188 InlineFlowBox* last = flow->lastLineBox(); 2189 2190 // If the containing block is empty, return a height of 0. 2191 if (!first || !last) 2192 return 0; 2193 2194 int heightResult; 2195 IntRect boundingBox = flow->linesBoundingBox(); 2196 if (containingBlock->isHorizontalWritingMode()) 2197 heightResult = boundingBox.height(); 2198 else 2199 heightResult = boundingBox.width(); 2200 heightResult -= (containingBlock->borderBefore() + containingBlock->borderAfter()); 2201 return heightResult; 2202 } 2203 2204 static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRight, const RenderBox* child, const RenderBoxModelObject* containerBlock, int containerLogicalWidth, 2205 TextDirection containerDirection) 2206 { 2207 if (!logicalLeft.isAuto() || !logicalRight.isAuto()) 2208 return; 2209 2210 // FIXME: The static distance computation has not been patched for mixed writing modes yet. 2211 if (containerDirection == LTR) { 2212 int staticPosition = child->layer()->staticInlinePosition() - containerBlock->borderLogicalLeft(); 2213 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) { 2214 if (curr->isBox()) 2215 staticPosition += toRenderBox(curr)->logicalLeft(); 2216 } 2217 logicalLeft.setValue(Fixed, staticPosition); 2218 } else { 2219 RenderBox* enclosingBox = child->parent()->enclosingBox(); 2220 int staticPosition = child->layer()->staticInlinePosition() + containerLogicalWidth + containerBlock->borderLogicalRight(); 2221 staticPosition -= enclosingBox->logicalWidth(); 2222 for (RenderObject* curr = enclosingBox; curr && curr != containerBlock; curr = curr->container()) { 2223 if (curr->isBox()) 2224 staticPosition -= toRenderBox(curr)->logicalLeft(); 2225 } 2226 logicalRight.setValue(Fixed, staticPosition); 2227 } 2228 } 2229 2230 void RenderBox::computePositionedLogicalWidth() 2231 { 2232 if (isReplaced()) { 2233 computePositionedLogicalWidthReplaced(); 2234 return; 2235 } 2236 2237 // QUESTIONS 2238 // FIXME 1: Which RenderObject's 'direction' property should used: the 2239 // containing block (cb) as the spec seems to imply, the parent (parent()) as 2240 // was previously done in calculating the static distances, or ourself, which 2241 // was also previously done for deciding what to override when you had 2242 // over-constrained margins? Also note that the container block is used 2243 // in similar situations in other parts of the RenderBox class (see computeLogicalWidth() 2244 // and computeMarginsInContainingBlockInlineDirection()). For now we are using the parent for quirks 2245 // mode and the containing block for strict mode. 2246 2247 // FIXME 2: Should we still deal with these the cases of 'left' or 'right' having 2248 // the type 'static' in determining whether to calculate the static distance? 2249 // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1. 2250 2251 // FIXME 3: Can perhaps optimize out cases when max-width/min-width are greater 2252 // than or less than the computed width(). Be careful of box-sizing and 2253 // percentage issues. 2254 2255 // The following is based off of the W3C Working Draft from April 11, 2006 of 2256 // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements" 2257 // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width> 2258 // (block-style-comments in this function and in computePositionedLogicalWidthUsing() 2259 // correspond to text from the spec) 2260 2261 2262 // We don't use containingBlock(), since we may be positioned by an enclosing 2263 // relative positioned inline. 2264 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 2265 2266 const int containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); 2267 2268 // To match WinIE, in quirks mode use the parent's 'direction' property 2269 // instead of the the container block's. 2270 TextDirection containerDirection = (document()->inQuirksMode()) ? parent()->style()->direction() : containerBlock->style()->direction(); 2271 2272 bool isHorizontal = isHorizontalWritingMode(); 2273 const int bordersPlusPadding = borderAndPaddingLogicalWidth(); 2274 const Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop(); 2275 const Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom(); 2276 int& marginLogicalLeftAlias = isHorizontal ? m_marginLeft : m_marginTop; 2277 int& marginLogicalRightAlias = isHorizontal ? m_marginRight : m_marginBottom; 2278 2279 Length logicalLeft = style()->logicalLeft(); 2280 Length logicalRight = style()->logicalRight(); 2281 2282 /*---------------------------------------------------------------------------*\ 2283 * For the purposes of this section and the next, the term "static position" 2284 * (of an element) refers, roughly, to the position an element would have had 2285 * in the normal flow. More precisely: 2286 * 2287 * * The static position for 'left' is the distance from the left edge of the 2288 * containing block to the left margin edge of a hypothetical box that would 2289 * have been the first box of the element if its 'position' property had 2290 * been 'static' and 'float' had been 'none'. The value is negative if the 2291 * hypothetical box is to the left of the containing block. 2292 * * The static position for 'right' is the distance from the right edge of the 2293 * containing block to the right margin edge of the same hypothetical box as 2294 * above. The value is positive if the hypothetical box is to the left of the 2295 * containing block's edge. 2296 * 2297 * But rather than actually calculating the dimensions of that hypothetical box, 2298 * user agents are free to make a guess at its probable position. 2299 * 2300 * For the purposes of calculating the static position, the containing block of 2301 * fixed positioned elements is the initial containing block instead of the 2302 * viewport, and all scrollable boxes should be assumed to be scrolled to their 2303 * origin. 2304 \*---------------------------------------------------------------------------*/ 2305 2306 // see FIXME 2 2307 // Calculate the static distance if needed. 2308 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth, containerDirection); 2309 2310 // Calculate constraint equation values for 'width' case. 2311 int logicalWidthResult; 2312 int logicalLeftResult; 2313 computePositionedLogicalWidthUsing(style()->logicalWidth(), containerBlock, containerDirection, 2314 containerLogicalWidth, bordersPlusPadding, 2315 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight, 2316 logicalWidthResult, marginLogicalLeftAlias, marginLogicalRightAlias, logicalLeftResult); 2317 setLogicalWidth(logicalWidthResult); 2318 setLogicalLeft(logicalLeftResult); 2319 2320 // Calculate constraint equation values for 'max-width' case. 2321 if (!style()->logicalMaxWidth().isUndefined()) { 2322 int maxLogicalWidth; 2323 int maxMarginLogicalLeft; 2324 int maxMarginLogicalRight; 2325 int maxLogicalLeftPos; 2326 2327 computePositionedLogicalWidthUsing(style()->logicalMaxWidth(), containerBlock, containerDirection, 2328 containerLogicalWidth, bordersPlusPadding, 2329 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight, 2330 maxLogicalWidth, maxMarginLogicalLeft, maxMarginLogicalRight, maxLogicalLeftPos); 2331 2332 if (logicalWidth() > maxLogicalWidth) { 2333 setLogicalWidth(maxLogicalWidth); 2334 marginLogicalLeftAlias = maxMarginLogicalLeft; 2335 marginLogicalRightAlias = maxMarginLogicalRight; 2336 setLogicalLeft(maxLogicalLeftPos); 2337 } 2338 } 2339 2340 // Calculate constraint equation values for 'min-width' case. 2341 if (!style()->logicalMinWidth().isZero()) { 2342 int minLogicalWidth; 2343 int minMarginLogicalLeft; 2344 int minMarginLogicalRight; 2345 int minLogicalLeftPos; 2346 2347 computePositionedLogicalWidthUsing(style()->logicalMinWidth(), containerBlock, containerDirection, 2348 containerLogicalWidth, bordersPlusPadding, 2349 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight, 2350 minLogicalWidth, minMarginLogicalLeft, minMarginLogicalRight, minLogicalLeftPos); 2351 2352 if (logicalWidth() < minLogicalWidth) { 2353 setLogicalWidth(minLogicalWidth); 2354 marginLogicalLeftAlias = minMarginLogicalLeft; 2355 marginLogicalRightAlias = minMarginLogicalRight; 2356 setLogicalLeft(minLogicalLeftPos); 2357 } 2358 } 2359 2360 if (stretchesToMinIntrinsicLogicalWidth() && logicalWidth() < minPreferredLogicalWidth() - bordersPlusPadding) { 2361 computePositionedLogicalWidthUsing(Length(minPreferredLogicalWidth() - bordersPlusPadding, Fixed), containerBlock, containerDirection, 2362 containerLogicalWidth, bordersPlusPadding, 2363 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight, 2364 logicalWidthResult, marginLogicalLeftAlias, marginLogicalRightAlias, logicalLeftResult); 2365 setLogicalWidth(logicalWidthResult); 2366 setLogicalLeft(logicalLeftResult); 2367 } 2368 2369 // Put logicalWidth() into correct form. 2370 setLogicalWidth(logicalWidth() + bordersPlusPadding); 2371 } 2372 2373 static void computeLogicalLeftPositionedOffset(int& logicalLeftPos, const RenderBox* child, int logicalWidthValue, const RenderBoxModelObject* containerBlock, int containerLogicalWidth) 2374 { 2375 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped 2376 // along this axis, then we need to flip the coordinate. This can only happen if the containing block is both a flipped mode and perpendicular to us. 2377 if (containerBlock->isHorizontalWritingMode() != child->isHorizontalWritingMode() && containerBlock->style()->isFlippedBlocksWritingMode()) { 2378 logicalLeftPos = containerLogicalWidth - logicalWidthValue - logicalLeftPos; 2379 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderRight() : containerBlock->borderBottom()); 2380 } else 2381 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderLeft() : containerBlock->borderTop()); 2382 } 2383 2384 void RenderBox::computePositionedLogicalWidthUsing(Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection, 2385 int containerLogicalWidth, int bordersPlusPadding, 2386 Length logicalLeft, Length logicalRight, Length marginLogicalLeft, Length marginLogicalRight, 2387 int& logicalWidthValue, int& marginLogicalLeftValue, int& marginLogicalRightValue, int& logicalLeftPos) 2388 { 2389 // 'left' and 'right' cannot both be 'auto' because one would of been 2390 // converted to the static position already 2391 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto())); 2392 2393 int logicalLeftValue = 0; 2394 2395 bool logicalWidthIsAuto = logicalWidth.isIntrinsicOrAuto(); 2396 bool logicalLeftIsAuto = logicalLeft.isAuto(); 2397 bool logicalRightIsAuto = logicalRight.isAuto(); 2398 2399 if (!logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) { 2400 /*-----------------------------------------------------------------------*\ 2401 * If none of the three is 'auto': If both 'margin-left' and 'margin- 2402 * right' are 'auto', solve the equation under the extra constraint that 2403 * the two margins get equal values, unless this would make them negative, 2404 * in which case when direction of the containing block is 'ltr' ('rtl'), 2405 * set 'margin-left' ('margin-right') to zero and solve for 'margin-right' 2406 * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', 2407 * solve the equation for that value. If the values are over-constrained, 2408 * ignore the value for 'left' (in case the 'direction' property of the 2409 * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') 2410 * and solve for that value. 2411 \*-----------------------------------------------------------------------*/ 2412 // NOTE: It is not necessary to solve for 'right' in the over constrained 2413 // case because the value is not used for any further calculations. 2414 2415 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2416 logicalWidthValue = computeContentBoxLogicalWidth(logicalWidth.calcValue(containerLogicalWidth)); 2417 2418 const int availableSpace = containerLogicalWidth - (logicalLeftValue + logicalWidthValue + logicalRight.calcValue(containerLogicalWidth) + bordersPlusPadding); 2419 2420 // Margins are now the only unknown 2421 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) { 2422 // Both margins auto, solve for equality 2423 if (availableSpace >= 0) { 2424 marginLogicalLeftValue = availableSpace / 2; // split the difference 2425 marginLogicalRightValue = availableSpace - marginLogicalLeftValue; // account for odd valued differences 2426 } else { 2427 // see FIXME 1 2428 if (containerDirection == LTR) { 2429 marginLogicalLeftValue = 0; 2430 marginLogicalRightValue = availableSpace; // will be negative 2431 } else { 2432 marginLogicalLeftValue = availableSpace; // will be negative 2433 marginLogicalRightValue = 0; 2434 } 2435 } 2436 } else if (marginLogicalLeft.isAuto()) { 2437 // Solve for left margin 2438 marginLogicalRightValue = marginLogicalRight.calcValue(containerLogicalWidth); 2439 marginLogicalLeftValue = availableSpace - marginLogicalRightValue; 2440 } else if (marginLogicalRight.isAuto()) { 2441 // Solve for right margin 2442 marginLogicalLeftValue = marginLogicalLeft.calcValue(containerLogicalWidth); 2443 marginLogicalRightValue = availableSpace - marginLogicalLeftValue; 2444 } else { 2445 // Over-constrained, solve for left if direction is RTL 2446 marginLogicalLeftValue = marginLogicalLeft.calcValue(containerLogicalWidth); 2447 marginLogicalRightValue = marginLogicalRight.calcValue(containerLogicalWidth); 2448 2449 // see FIXME 1 -- used to be "this->style()->direction()" 2450 if (containerDirection == RTL) 2451 logicalLeftValue = (availableSpace + logicalLeftValue) - marginLogicalLeftValue - marginLogicalRightValue; 2452 } 2453 } else { 2454 /*--------------------------------------------------------------------*\ 2455 * Otherwise, set 'auto' values for 'margin-left' and 'margin-right' 2456 * to 0, and pick the one of the following six rules that applies. 2457 * 2458 * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the 2459 * width is shrink-to-fit. Then solve for 'left' 2460 * 2461 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT 2462 * ------------------------------------------------------------------ 2463 * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if 2464 * the 'direction' property of the containing block is 'ltr' set 2465 * 'left' to the static position, otherwise set 'right' to the 2466 * static position. Then solve for 'left' (if 'direction is 'rtl') 2467 * or 'right' (if 'direction' is 'ltr'). 2468 * ------------------------------------------------------------------ 2469 * 2470 * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the 2471 * width is shrink-to-fit . Then solve for 'right' 2472 * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve 2473 * for 'left' 2474 * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve 2475 * for 'width' 2476 * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve 2477 * for 'right' 2478 * 2479 * Calculation of the shrink-to-fit width is similar to calculating the 2480 * width of a table cell using the automatic table layout algorithm. 2481 * Roughly: calculate the preferred width by formatting the content 2482 * without breaking lines other than where explicit line breaks occur, 2483 * and also calculate the preferred minimum width, e.g., by trying all 2484 * possible line breaks. CSS 2.1 does not define the exact algorithm. 2485 * Thirdly, calculate the available width: this is found by solving 2486 * for 'width' after setting 'left' (in case 1) or 'right' (in case 3) 2487 * to 0. 2488 * 2489 * Then the shrink-to-fit width is: 2490 * min(max(preferred minimum width, available width), preferred width). 2491 \*--------------------------------------------------------------------*/ 2492 // NOTE: For rules 3 and 6 it is not necessary to solve for 'right' 2493 // because the value is not used for any further calculations. 2494 2495 // Calculate margins, 'auto' margins are ignored. 2496 marginLogicalLeftValue = marginLogicalLeft.calcMinValue(containerLogicalWidth); 2497 marginLogicalRightValue = marginLogicalRight.calcMinValue(containerLogicalWidth); 2498 2499 const int availableSpace = containerLogicalWidth - (marginLogicalLeftValue + marginLogicalRightValue + bordersPlusPadding); 2500 2501 // FIXME: Is there a faster way to find the correct case? 2502 // Use rule/case that applies. 2503 if (logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) { 2504 // RULE 1: (use shrink-to-fit for width, and solve of left) 2505 int logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2506 2507 // FIXME: would it be better to have shrink-to-fit in one step? 2508 int preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding; 2509 int preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding; 2510 int availableWidth = availableSpace - logicalRightValue; 2511 logicalWidthValue = min(max(preferredMinWidth, availableWidth), preferredWidth); 2512 logicalLeftValue = availableSpace - (logicalWidthValue + logicalRightValue); 2513 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && logicalRightIsAuto) { 2514 // RULE 3: (use shrink-to-fit for width, and no need solve of right) 2515 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2516 2517 // FIXME: would it be better to have shrink-to-fit in one step? 2518 int preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding; 2519 int preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding; 2520 int availableWidth = availableSpace - logicalLeftValue; 2521 logicalWidthValue = min(max(preferredMinWidth, availableWidth), preferredWidth); 2522 } else if (logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) { 2523 // RULE 4: (solve for left) 2524 logicalWidthValue = computeContentBoxLogicalWidth(logicalWidth.calcValue(containerLogicalWidth)); 2525 logicalLeftValue = availableSpace - (logicalWidthValue + logicalRight.calcValue(containerLogicalWidth)); 2526 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) { 2527 // RULE 5: (solve for width) 2528 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2529 logicalWidthValue = availableSpace - (logicalLeftValue + logicalRight.calcValue(containerLogicalWidth)); 2530 } else if (!logicalLeftIsAuto && !logicalWidthIsAuto && logicalRightIsAuto) { 2531 // RULE 6: (no need solve for right) 2532 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2533 logicalWidthValue = computeContentBoxLogicalWidth(logicalWidth.calcValue(containerLogicalWidth)); 2534 } 2535 } 2536 2537 // Use computed values to calculate the horizontal position. 2538 2539 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively 2540 // positioned, inline because right now, it is using the logical left position 2541 // of the first line box when really it should use the last line box. When 2542 // this is fixed elsewhere, this block should be removed. 2543 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) { 2544 const RenderInline* flow = toRenderInline(containerBlock); 2545 InlineFlowBox* firstLine = flow->firstLineBox(); 2546 InlineFlowBox* lastLine = flow->lastLineBox(); 2547 if (firstLine && lastLine && firstLine != lastLine) { 2548 logicalLeftPos = logicalLeftValue + marginLogicalLeftValue + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft()); 2549 return; 2550 } 2551 } 2552 2553 logicalLeftPos = logicalLeftValue + marginLogicalLeftValue; 2554 computeLogicalLeftPositionedOffset(logicalLeftPos, this, logicalWidthValue, containerBlock, containerLogicalWidth); 2555 } 2556 2557 static void computeBlockStaticDistance(Length& logicalTop, Length& logicalBottom, const RenderBox* child, const RenderBoxModelObject* containerBlock) 2558 { 2559 if (!logicalTop.isAuto() || !logicalBottom.isAuto()) 2560 return; 2561 2562 // FIXME: The static distance computation has not been patched for mixed writing modes. 2563 int staticLogicalTop = child->layer()->staticBlockPosition() - containerBlock->borderBefore(); 2564 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) { 2565 if (curr->isBox() && !curr->isTableRow()) 2566 staticLogicalTop += toRenderBox(curr)->logicalTop(); 2567 } 2568 logicalTop.setValue(Fixed, staticLogicalTop); 2569 } 2570 2571 void RenderBox::computePositionedLogicalHeight() 2572 { 2573 if (isReplaced()) { 2574 computePositionedLogicalHeightReplaced(); 2575 return; 2576 } 2577 2578 // The following is based off of the W3C Working Draft from April 11, 2006 of 2579 // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements" 2580 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height> 2581 // (block-style-comments in this function and in computePositionedLogicalHeightUsing() 2582 // correspond to text from the spec) 2583 2584 2585 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. 2586 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 2587 2588 const int containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); 2589 2590 bool isHorizontal = isHorizontalWritingMode(); 2591 bool isFlipped = style()->isFlippedBlocksWritingMode(); 2592 const int bordersPlusPadding = borderAndPaddingLogicalHeight(); 2593 const Length marginBefore = style()->marginBefore(); 2594 const Length marginAfter = style()->marginAfter(); 2595 int& marginBeforeAlias = isHorizontal ? (isFlipped ? m_marginBottom : m_marginTop) : (isFlipped ? m_marginRight: m_marginLeft); 2596 int& marginAfterAlias = isHorizontal ? (isFlipped ? m_marginTop : m_marginBottom) : (isFlipped ? m_marginLeft: m_marginRight); 2597 2598 Length logicalTop = style()->logicalTop(); 2599 Length logicalBottom = style()->logicalBottom(); 2600 2601 /*---------------------------------------------------------------------------*\ 2602 * For the purposes of this section and the next, the term "static position" 2603 * (of an element) refers, roughly, to the position an element would have had 2604 * in the normal flow. More precisely, the static position for 'top' is the 2605 * distance from the top edge of the containing block to the top margin edge 2606 * of a hypothetical box that would have been the first box of the element if 2607 * its 'position' property had been 'static' and 'float' had been 'none'. The 2608 * value is negative if the hypothetical box is above the containing block. 2609 * 2610 * But rather than actually calculating the dimensions of that hypothetical 2611 * box, user agents are free to make a guess at its probable position. 2612 * 2613 * For the purposes of calculating the static position, the containing block 2614 * of fixed positioned elements is the initial containing block instead of 2615 * the viewport. 2616 \*---------------------------------------------------------------------------*/ 2617 2618 // see FIXME 2 2619 // Calculate the static distance if needed. 2620 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock); 2621 2622 int logicalHeightResult; // Needed to compute overflow. 2623 int logicalTopPos; 2624 2625 // Calculate constraint equation values for 'height' case. 2626 computePositionedLogicalHeightUsing(style()->logicalHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, 2627 logicalTop, logicalBottom, marginBefore, marginAfter, 2628 logicalHeightResult, marginBeforeAlias, marginAfterAlias, logicalTopPos); 2629 setLogicalTop(logicalTopPos); 2630 2631 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults). 2632 // see FIXME 3 2633 2634 // Calculate constraint equation values for 'max-height' case. 2635 if (!style()->logicalMaxHeight().isUndefined()) { 2636 int maxLogicalHeight; 2637 int maxMarginBefore; 2638 int maxMarginAfter; 2639 int maxLogicalTopPos; 2640 2641 computePositionedLogicalHeightUsing(style()->logicalMaxHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, 2642 logicalTop, logicalBottom, marginBefore, marginAfter, 2643 maxLogicalHeight, maxMarginBefore, maxMarginAfter, maxLogicalTopPos); 2644 2645 if (logicalHeightResult > maxLogicalHeight) { 2646 logicalHeightResult = maxLogicalHeight; 2647 marginBeforeAlias = maxMarginBefore; 2648 marginAfterAlias = maxMarginAfter; 2649 setLogicalTop(maxLogicalTopPos); 2650 } 2651 } 2652 2653 // Calculate constraint equation values for 'min-height' case. 2654 if (!style()->logicalMinHeight().isZero()) { 2655 int minLogicalHeight; 2656 int minMarginBefore; 2657 int minMarginAfter; 2658 int minLogicalTopPos; 2659 2660 computePositionedLogicalHeightUsing(style()->logicalMinHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, 2661 logicalTop, logicalBottom, marginBefore, marginAfter, 2662 minLogicalHeight, minMarginBefore, minMarginAfter, minLogicalTopPos); 2663 2664 if (logicalHeightResult < minLogicalHeight) { 2665 logicalHeightResult = minLogicalHeight; 2666 marginBeforeAlias = minMarginBefore; 2667 marginAfterAlias = minMarginAfter; 2668 setLogicalTop(minLogicalTopPos); 2669 } 2670 } 2671 2672 // Set final height value. 2673 setLogicalHeight(logicalHeightResult + bordersPlusPadding); 2674 } 2675 2676 static void computeLogicalTopPositionedOffset(int& logicalTopPos, const RenderBox* child, int logicalHeightValue, const RenderBoxModelObject* containerBlock, int containerLogicalHeight) 2677 { 2678 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped 2679 // along this axis, then we need to flip the coordinate. This can only happen if the containing block is both a flipped mode and perpendicular to us. 2680 if ((child->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() != containerBlock->isHorizontalWritingMode()) 2681 || (child->style()->isFlippedBlocksWritingMode() != containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode())) 2682 logicalTopPos = containerLogicalHeight - logicalHeightValue - logicalTopPos; 2683 2684 // Our offset is from the logical bottom edge in a flipped environment, e.g., right for vertical-rl and bottom for horizontal-bt. 2685 if (containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()) { 2686 if (child->isHorizontalWritingMode()) 2687 logicalTopPos += containerBlock->borderBottom(); 2688 else 2689 logicalTopPos += containerBlock->borderRight(); 2690 } else { 2691 if (child->isHorizontalWritingMode()) 2692 logicalTopPos += containerBlock->borderTop(); 2693 else 2694 logicalTopPos += containerBlock->borderLeft(); 2695 } 2696 } 2697 2698 void RenderBox::computePositionedLogicalHeightUsing(Length logicalHeightLength, const RenderBoxModelObject* containerBlock, 2699 int containerLogicalHeight, int bordersPlusPadding, 2700 Length logicalTop, Length logicalBottom, Length marginBefore, Length marginAfter, 2701 int& logicalHeightValue, int& marginBeforeValue, int& marginAfterValue, int& logicalTopPos) 2702 { 2703 // 'top' and 'bottom' cannot both be 'auto' because 'top would of been 2704 // converted to the static position in computePositionedLogicalHeight() 2705 ASSERT(!(logicalTop.isAuto() && logicalBottom.isAuto())); 2706 2707 int contentLogicalHeight = logicalHeight() - bordersPlusPadding; 2708 2709 int logicalTopValue = 0; 2710 2711 bool logicalHeightIsAuto = logicalHeightLength.isAuto(); 2712 bool logicalTopIsAuto = logicalTop.isAuto(); 2713 bool logicalBottomIsAuto = logicalBottom.isAuto(); 2714 2715 // Height is never unsolved for tables. 2716 if (isTable()) { 2717 logicalHeightLength.setValue(Fixed, contentLogicalHeight); 2718 logicalHeightIsAuto = false; 2719 } 2720 2721 if (!logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) { 2722 /*-----------------------------------------------------------------------*\ 2723 * If none of the three are 'auto': If both 'margin-top' and 'margin- 2724 * bottom' are 'auto', solve the equation under the extra constraint that 2725 * the two margins get equal values. If one of 'margin-top' or 'margin- 2726 * bottom' is 'auto', solve the equation for that value. If the values 2727 * are over-constrained, ignore the value for 'bottom' and solve for that 2728 * value. 2729 \*-----------------------------------------------------------------------*/ 2730 // NOTE: It is not necessary to solve for 'bottom' in the over constrained 2731 // case because the value is not used for any further calculations. 2732 2733 logicalHeightValue = computeContentBoxLogicalHeight(logicalHeightLength.calcValue(containerLogicalHeight)); 2734 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 2735 2736 const int availableSpace = containerLogicalHeight - (logicalTopValue + logicalHeightValue + logicalBottom.calcValue(containerLogicalHeight) + bordersPlusPadding); 2737 2738 // Margins are now the only unknown 2739 if (marginBefore.isAuto() && marginAfter.isAuto()) { 2740 // Both margins auto, solve for equality 2741 // NOTE: This may result in negative values. 2742 marginBeforeValue = availableSpace / 2; // split the difference 2743 marginAfterValue = availableSpace - marginBeforeValue; // account for odd valued differences 2744 } else if (marginBefore.isAuto()) { 2745 // Solve for top margin 2746 marginAfterValue = marginAfter.calcValue(containerLogicalHeight); 2747 marginBeforeValue = availableSpace - marginAfterValue; 2748 } else if (marginAfter.isAuto()) { 2749 // Solve for bottom margin 2750 marginBeforeValue = marginBefore.calcValue(containerLogicalHeight); 2751 marginAfterValue = availableSpace - marginBeforeValue; 2752 } else { 2753 // Over-constrained, (no need solve for bottom) 2754 marginBeforeValue = marginBefore.calcValue(containerLogicalHeight); 2755 marginAfterValue = marginAfter.calcValue(containerLogicalHeight); 2756 } 2757 } else { 2758 /*--------------------------------------------------------------------*\ 2759 * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom' 2760 * to 0, and pick the one of the following six rules that applies. 2761 * 2762 * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then 2763 * the height is based on the content, and solve for 'top'. 2764 * 2765 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT 2766 * ------------------------------------------------------------------ 2767 * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then 2768 * set 'top' to the static position, and solve for 'bottom'. 2769 * ------------------------------------------------------------------ 2770 * 2771 * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then 2772 * the height is based on the content, and solve for 'bottom'. 2773 * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and 2774 * solve for 'top'. 2775 * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and 2776 * solve for 'height'. 2777 * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and 2778 * solve for 'bottom'. 2779 \*--------------------------------------------------------------------*/ 2780 // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom' 2781 // because the value is not used for any further calculations. 2782 2783 // Calculate margins, 'auto' margins are ignored. 2784 marginBeforeValue = marginBefore.calcMinValue(containerLogicalHeight); 2785 marginAfterValue = marginAfter.calcMinValue(containerLogicalHeight); 2786 2787 const int availableSpace = containerLogicalHeight - (marginBeforeValue + marginAfterValue + bordersPlusPadding); 2788 2789 // Use rule/case that applies. 2790 if (logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) { 2791 // RULE 1: (height is content based, solve of top) 2792 logicalHeightValue = contentLogicalHeight; 2793 logicalTopValue = availableSpace - (logicalHeightValue + logicalBottom.calcValue(containerLogicalHeight)); 2794 } else if (!logicalTopIsAuto && logicalHeightIsAuto && logicalBottomIsAuto) { 2795 // RULE 3: (height is content based, no need solve of bottom) 2796 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 2797 logicalHeightValue = contentLogicalHeight; 2798 } else if (logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) { 2799 // RULE 4: (solve of top) 2800 logicalHeightValue = computeContentBoxLogicalHeight(logicalHeightLength.calcValue(containerLogicalHeight)); 2801 logicalTopValue = availableSpace - (logicalHeightValue + logicalBottom.calcValue(containerLogicalHeight)); 2802 } else if (!logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) { 2803 // RULE 5: (solve of height) 2804 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 2805 logicalHeightValue = max(0, availableSpace - (logicalTopValue + logicalBottom.calcValue(containerLogicalHeight))); 2806 } else if (!logicalTopIsAuto && !logicalHeightIsAuto && logicalBottomIsAuto) { 2807 // RULE 6: (no need solve of bottom) 2808 logicalHeightValue = computeContentBoxLogicalHeight(logicalHeightLength.calcValue(containerLogicalHeight)); 2809 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 2810 } 2811 } 2812 2813 // Use computed values to calculate the vertical position. 2814 logicalTopPos = logicalTopValue + marginBeforeValue; 2815 computeLogicalTopPositionedOffset(logicalTopPos, this, logicalHeightValue, containerBlock, containerLogicalHeight); 2816 } 2817 2818 void RenderBox::computePositionedLogicalWidthReplaced() 2819 { 2820 // The following is based off of the W3C Working Draft from April 11, 2006 of 2821 // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements" 2822 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width> 2823 // (block-style-comments in this function correspond to text from the spec and 2824 // the numbers correspond to numbers in spec) 2825 2826 // We don't use containingBlock(), since we may be positioned by an enclosing 2827 // relative positioned inline. 2828 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 2829 2830 const int containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); 2831 2832 // To match WinIE, in quirks mode use the parent's 'direction' property 2833 // instead of the the container block's. 2834 TextDirection containerDirection = (document()->inQuirksMode()) ? parent()->style()->direction() : containerBlock->style()->direction(); 2835 2836 // Variables to solve. 2837 bool isHorizontal = isHorizontalWritingMode(); 2838 Length logicalLeft = style()->logicalLeft(); 2839 Length logicalRight = style()->logicalRight(); 2840 Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop(); 2841 Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom(); 2842 int& marginLogicalLeftAlias = isHorizontal ? m_marginLeft : m_marginTop; 2843 int& marginLogicalRightAlias = isHorizontal ? m_marginRight : m_marginBottom; 2844 2845 /*-----------------------------------------------------------------------*\ 2846 * 1. The used value of 'width' is determined as for inline replaced 2847 * elements. 2848 \*-----------------------------------------------------------------------*/ 2849 // NOTE: This value of width is FINAL in that the min/max width calculations 2850 // are dealt with in computeReplacedWidth(). This means that the steps to produce 2851 // correct max/min in the non-replaced version, are not necessary. 2852 setLogicalWidth(computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth()); 2853 const int availableSpace = containerLogicalWidth - logicalWidth(); 2854 2855 /*-----------------------------------------------------------------------*\ 2856 * 2. If both 'left' and 'right' have the value 'auto', then if 'direction' 2857 * of the containing block is 'ltr', set 'left' to the static position; 2858 * else if 'direction' is 'rtl', set 'right' to the static position. 2859 \*-----------------------------------------------------------------------*/ 2860 // see FIXME 2 2861 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth, containerDirection); 2862 2863 /*-----------------------------------------------------------------------*\ 2864 * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' 2865 * or 'margin-right' with '0'. 2866 \*-----------------------------------------------------------------------*/ 2867 if (logicalLeft.isAuto() || logicalRight.isAuto()) { 2868 if (marginLogicalLeft.isAuto()) 2869 marginLogicalLeft.setValue(Fixed, 0); 2870 if (marginLogicalRight.isAuto()) 2871 marginLogicalRight.setValue(Fixed, 0); 2872 } 2873 2874 /*-----------------------------------------------------------------------*\ 2875 * 4. If at this point both 'margin-left' and 'margin-right' are still 2876 * 'auto', solve the equation under the extra constraint that the two 2877 * margins must get equal values, unless this would make them negative, 2878 * in which case when the direction of the containing block is 'ltr' 2879 * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for 2880 * 'margin-right' ('margin-left'). 2881 \*-----------------------------------------------------------------------*/ 2882 int logicalLeftValue = 0; 2883 int logicalRightValue = 0; 2884 2885 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) { 2886 // 'left' and 'right' cannot be 'auto' due to step 3 2887 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto())); 2888 2889 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2890 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2891 2892 int difference = availableSpace - (logicalLeftValue + logicalRightValue); 2893 if (difference > 0) { 2894 marginLogicalLeftAlias = difference / 2; // split the difference 2895 marginLogicalRightAlias = difference - marginLogicalLeftAlias; // account for odd valued differences 2896 } else { 2897 // see FIXME 1 2898 if (containerDirection == LTR) { 2899 marginLogicalLeftAlias = 0; 2900 marginLogicalRightAlias = difference; // will be negative 2901 } else { 2902 marginLogicalLeftAlias = difference; // will be negative 2903 marginLogicalRightAlias = 0; 2904 } 2905 } 2906 2907 /*-----------------------------------------------------------------------*\ 2908 * 5. If at this point there is an 'auto' left, solve the equation for 2909 * that value. 2910 \*-----------------------------------------------------------------------*/ 2911 } else if (logicalLeft.isAuto()) { 2912 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth); 2913 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth); 2914 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2915 2916 // Solve for 'left' 2917 logicalLeftValue = availableSpace - (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias); 2918 } else if (logicalRight.isAuto()) { 2919 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth); 2920 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth); 2921 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2922 2923 // Solve for 'right' 2924 logicalRightValue = availableSpace - (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias); 2925 } else if (marginLogicalLeft.isAuto()) { 2926 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth); 2927 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2928 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2929 2930 // Solve for 'margin-left' 2931 marginLogicalLeftAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalRightAlias); 2932 } else if (marginLogicalRight.isAuto()) { 2933 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth); 2934 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2935 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2936 2937 // Solve for 'margin-right' 2938 marginLogicalRightAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias); 2939 } else { 2940 // Nothing is 'auto', just calculate the values. 2941 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth); 2942 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth); 2943 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2944 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2945 } 2946 2947 /*-----------------------------------------------------------------------*\ 2948 * 6. If at this point the values are over-constrained, ignore the value 2949 * for either 'left' (in case the 'direction' property of the 2950 * containing block is 'rtl') or 'right' (in case 'direction' is 2951 * 'ltr') and solve for that value. 2952 \*-----------------------------------------------------------------------*/ 2953 // NOTE: It is not necessary to solve for 'right' when the direction is 2954 // LTR because the value is not used. 2955 int totalLogicalWidth = logicalWidth() + logicalLeftValue + logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias; 2956 if (totalLogicalWidth > containerLogicalWidth && (containerDirection == RTL)) 2957 logicalLeftValue = containerLogicalWidth - (totalLogicalWidth - logicalLeftValue); 2958 2959 // FIXME: Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space, so that 2960 // can make the result here rather complicated to compute. 2961 2962 // Use computed values to calculate the horizontal position. 2963 2964 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively 2965 // positioned, inline containing block because right now, it is using the logical left position 2966 // of the first line box when really it should use the last line box. When 2967 // this is fixed elsewhere, this block should be removed. 2968 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) { 2969 const RenderInline* flow = toRenderInline(containerBlock); 2970 InlineFlowBox* firstLine = flow->firstLineBox(); 2971 InlineFlowBox* lastLine = flow->lastLineBox(); 2972 if (firstLine && lastLine && firstLine != lastLine) { 2973 setLogicalLeft(logicalLeftValue + marginLogicalLeftAlias + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft())); 2974 return; 2975 } 2976 } 2977 2978 int logicalLeftPos = logicalLeftValue + marginLogicalLeftAlias; 2979 computeLogicalLeftPositionedOffset(logicalLeftPos, this, logicalWidth(), containerBlock, containerLogicalWidth); 2980 setLogicalLeft(logicalLeftPos); 2981 } 2982 2983 void RenderBox::computePositionedLogicalHeightReplaced() 2984 { 2985 // The following is based off of the W3C Working Draft from April 11, 2006 of 2986 // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements" 2987 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height> 2988 // (block-style-comments in this function correspond to text from the spec and 2989 // the numbers correspond to numbers in spec) 2990 2991 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. 2992 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 2993 2994 const int containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); 2995 2996 // Variables to solve. 2997 bool isHorizontal = isHorizontalWritingMode(); 2998 bool isFlipped = style()->isFlippedBlocksWritingMode(); 2999 Length marginBefore = style()->marginBefore(); 3000 Length marginAfter = style()->marginAfter(); 3001 int& marginBeforeAlias = isHorizontal ? (isFlipped ? m_marginBottom : m_marginTop) : (isFlipped ? m_marginRight: m_marginLeft); 3002 int& marginAfterAlias = isHorizontal ? (isFlipped ? m_marginTop : m_marginBottom) : (isFlipped ? m_marginLeft: m_marginRight); 3003 3004 Length logicalTop = style()->logicalTop(); 3005 Length logicalBottom = style()->logicalBottom(); 3006 3007 /*-----------------------------------------------------------------------*\ 3008 * 1. The used value of 'height' is determined as for inline replaced 3009 * elements. 3010 \*-----------------------------------------------------------------------*/ 3011 // NOTE: This value of height is FINAL in that the min/max height calculations 3012 // are dealt with in computeReplacedHeight(). This means that the steps to produce 3013 // correct max/min in the non-replaced version, are not necessary. 3014 setLogicalHeight(computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight()); 3015 const int availableSpace = containerLogicalHeight - logicalHeight(); 3016 3017 /*-----------------------------------------------------------------------*\ 3018 * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top' 3019 * with the element's static position. 3020 \*-----------------------------------------------------------------------*/ 3021 // see FIXME 2 3022 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock); 3023 3024 /*-----------------------------------------------------------------------*\ 3025 * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or 3026 * 'margin-bottom' with '0'. 3027 \*-----------------------------------------------------------------------*/ 3028 // FIXME: The spec. says that this step should only be taken when bottom is 3029 // auto, but if only top is auto, this makes step 4 impossible. 3030 if (logicalTop.isAuto() || logicalBottom.isAuto()) { 3031 if (marginBefore.isAuto()) 3032 marginBefore.setValue(Fixed, 0); 3033 if (marginAfter.isAuto()) 3034 marginAfter.setValue(Fixed, 0); 3035 } 3036 3037 /*-----------------------------------------------------------------------*\ 3038 * 4. If at this point both 'margin-top' and 'margin-bottom' are still 3039 * 'auto', solve the equation under the extra constraint that the two 3040 * margins must get equal values. 3041 \*-----------------------------------------------------------------------*/ 3042 int logicalTopValue = 0; 3043 int logicalBottomValue = 0; 3044 3045 if (marginBefore.isAuto() && marginAfter.isAuto()) { 3046 // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined. 3047 ASSERT(!(logicalTop.isAuto() || logicalBottom.isAuto())); 3048 3049 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3050 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight); 3051 3052 int difference = availableSpace - (logicalTopValue + logicalBottomValue); 3053 // NOTE: This may result in negative values. 3054 marginBeforeAlias = difference / 2; // split the difference 3055 marginAfterAlias = difference - marginBeforeAlias; // account for odd valued differences 3056 3057 /*-----------------------------------------------------------------------*\ 3058 * 5. If at this point there is only one 'auto' left, solve the equation 3059 * for that value. 3060 \*-----------------------------------------------------------------------*/ 3061 } else if (logicalTop.isAuto()) { 3062 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight); 3063 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight); 3064 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight); 3065 3066 // Solve for 'top' 3067 logicalTopValue = availableSpace - (logicalBottomValue + marginBeforeAlias + marginAfterAlias); 3068 } else if (logicalBottom.isAuto()) { 3069 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight); 3070 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight); 3071 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3072 3073 // Solve for 'bottom' 3074 // NOTE: It is not necessary to solve for 'bottom' because we don't ever 3075 // use the value. 3076 } else if (marginBefore.isAuto()) { 3077 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight); 3078 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3079 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight); 3080 3081 // Solve for 'margin-top' 3082 marginBeforeAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginAfterAlias); 3083 } else if (marginAfter.isAuto()) { 3084 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight); 3085 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3086 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight); 3087 3088 // Solve for 'margin-bottom' 3089 marginAfterAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginBeforeAlias); 3090 } else { 3091 // Nothing is 'auto', just calculate the values. 3092 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight); 3093 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight); 3094 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3095 // NOTE: It is not necessary to solve for 'bottom' because we don't ever 3096 // use the value. 3097 } 3098 3099 /*-----------------------------------------------------------------------*\ 3100 * 6. If at this point the values are over-constrained, ignore the value 3101 * for 'bottom' and solve for that value. 3102 \*-----------------------------------------------------------------------*/ 3103 // NOTE: It is not necessary to do this step because we don't end up using 3104 // the value of 'bottom' regardless of whether the values are over-constrained 3105 // or not. 3106 3107 // Use computed values to calculate the vertical position. 3108 int logicalTopPos = logicalTopValue + marginBeforeAlias; 3109 computeLogicalTopPositionedOffset(logicalTopPos, this, logicalHeight(), containerBlock, containerLogicalHeight); 3110 setLogicalTop(logicalTopPos); 3111 } 3112 3113 IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWidthToEndOfLine) 3114 { 3115 // VisiblePositions at offsets inside containers either a) refer to the positions before/after 3116 // those containers (tables and select elements) or b) refer to the position inside an empty block. 3117 // They never refer to children. 3118 // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements. 3119 3120 // FIXME: What about border and padding? 3121 IntRect rect(x(), y(), caretWidth, height()); 3122 bool ltr = box ? box->isLeftToRightDirection() : style()->isLeftToRightDirection(); 3123 3124 if ((!caretOffset) ^ ltr) 3125 rect.move(IntSize(width() - caretWidth, 0)); 3126 3127 if (box) { 3128 RootInlineBox* rootBox = box->root(); 3129 int top = rootBox->lineTop(); 3130 rect.setY(top); 3131 rect.setHeight(rootBox->lineBottom() - top); 3132 } 3133 3134 // If height of box is smaller than font height, use the latter one, 3135 // otherwise the caret might become invisible. 3136 // 3137 // Also, if the box is not a replaced element, always use the font height. 3138 // This prevents the "big caret" bug described in: 3139 // <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point 3140 // 3141 // FIXME: ignoring :first-line, missing good reason to take care of 3142 int fontHeight = style()->fontMetrics().height(); 3143 if (fontHeight > rect.height() || (!isReplaced() && !isTable())) 3144 rect.setHeight(fontHeight); 3145 3146 if (extraWidthToEndOfLine) 3147 *extraWidthToEndOfLine = x() + width() - rect.maxX(); 3148 3149 // Move to local coords 3150 rect.move(-x(), -y()); 3151 return rect; 3152 } 3153 3154 VisiblePosition RenderBox::positionForPoint(const IntPoint& point) 3155 { 3156 // no children...return this render object's element, if there is one, and offset 0 3157 if (!firstChild()) 3158 return createVisiblePosition(node() ? firstPositionInOrBeforeNode(node()) : Position(0, 0)); 3159 3160 int xPos = point.x(); 3161 int yPos = point.y(); 3162 3163 if (isTable() && node()) { 3164 int right = contentWidth() + borderAndPaddingWidth(); 3165 int bottom = contentHeight() + borderAndPaddingHeight(); 3166 3167 if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) { 3168 if (xPos <= right / 2) 3169 return createVisiblePosition(firstPositionInOrBeforeNode(node())); 3170 return createVisiblePosition(lastPositionInOrAfterNode(node())); 3171 } 3172 } 3173 3174 // Pass off to the closest child. 3175 int minDist = INT_MAX; 3176 RenderBox* closestRenderer = 0; 3177 int newX = xPos; 3178 int newY = yPos; 3179 if (isTableRow()) { 3180 newX += x(); 3181 newY += y(); 3182 } 3183 for (RenderObject* renderObject = firstChild(); renderObject; renderObject = renderObject->nextSibling()) { 3184 if ((!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isBlockFlow() ) 3185 || renderObject->style()->visibility() != VISIBLE) 3186 continue; 3187 3188 if (!renderObject->isBox()) 3189 continue; 3190 3191 RenderBox* renderer = toRenderBox(renderObject); 3192 3193 int top = renderer->borderTop() + renderer->paddingTop() + (isTableRow() ? 0 : renderer->y()); 3194 int bottom = top + renderer->contentHeight(); 3195 int left = renderer->borderLeft() + renderer->paddingLeft() + (isTableRow() ? 0 : renderer->x()); 3196 int right = left + renderer->contentWidth(); 3197 3198 if (xPos <= right && xPos >= left && yPos <= top && yPos >= bottom) { 3199 if (renderer->isTableRow()) 3200 return renderer->positionForCoordinates(xPos + newX - renderer->x(), yPos + newY - renderer->y()); 3201 return renderer->positionForCoordinates(xPos - renderer->x(), yPos - renderer->y()); 3202 } 3203 3204 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces 3205 // and use a different compare depending on which piece (x, y) is in. 3206 IntPoint cmp; 3207 if (xPos > right) { 3208 if (yPos < top) 3209 cmp = IntPoint(right, top); 3210 else if (yPos > bottom) 3211 cmp = IntPoint(right, bottom); 3212 else 3213 cmp = IntPoint(right, yPos); 3214 } else if (xPos < left) { 3215 if (yPos < top) 3216 cmp = IntPoint(left, top); 3217 else if (yPos > bottom) 3218 cmp = IntPoint(left, bottom); 3219 else 3220 cmp = IntPoint(left, yPos); 3221 } else { 3222 if (yPos < top) 3223 cmp = IntPoint(xPos, top); 3224 else 3225 cmp = IntPoint(xPos, bottom); 3226 } 3227 3228 int x1minusx2 = cmp.x() - xPos; 3229 int y1minusy2 = cmp.y() - yPos; 3230 3231 int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2; 3232 if (dist < minDist) { 3233 closestRenderer = renderer; 3234 minDist = dist; 3235 } 3236 } 3237 3238 if (closestRenderer) 3239 return closestRenderer->positionForCoordinates(newX - closestRenderer->x(), newY - closestRenderer->y()); 3240 3241 return createVisiblePosition(firstPositionInOrBeforeNode(node())); 3242 } 3243 3244 bool RenderBox::shrinkToAvoidFloats() const 3245 { 3246 // Floating objects don't shrink. Objects that don't avoid floats don't shrink. Marquees don't shrink. 3247 if ((isInline() && !isHTMLMarquee()) || !avoidsFloats() || isFloating()) 3248 return false; 3249 3250 // All auto-width objects that avoid floats should always use lineWidth. 3251 return style()->width().isAuto(); 3252 } 3253 3254 bool RenderBox::avoidsFloats() const 3255 { 3256 return isReplaced() || hasOverflowClip() || isHR() || isLegend() || isWritingModeRoot(); 3257 } 3258 3259 void RenderBox::addShadowOverflow() 3260 { 3261 int shadowLeft; 3262 int shadowRight; 3263 int shadowTop; 3264 int shadowBottom; 3265 style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft); 3266 IntRect borderBox = borderBoxRect(); 3267 int overflowLeft = borderBox.x() + shadowLeft; 3268 int overflowRight = borderBox.maxX() + shadowRight; 3269 int overflowTop = borderBox.y() + shadowTop; 3270 int overflowBottom = borderBox.maxY() + shadowBottom; 3271 addVisualOverflow(IntRect(overflowLeft, overflowTop, overflowRight - overflowLeft, overflowBottom - overflowTop)); 3272 } 3273 3274 void RenderBox::addOverflowFromChild(RenderBox* child, const IntSize& delta) 3275 { 3276 // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then 3277 // its overflow is internal to it, and we don't care about it. layoutOverflowRectForPropagation takes care of this 3278 // and just propagates the border box rect instead. 3279 IntRect childLayoutOverflowRect = child->layoutOverflowRectForPropagation(style()); 3280 childLayoutOverflowRect.move(delta); 3281 addLayoutOverflow(childLayoutOverflowRect); 3282 3283 // Add in visual overflow from the child. Even if the child clips its overflow, it may still 3284 // have visual overflow of its own set from box shadows or reflections. It is unnecessary to propagate this 3285 // overflow if we are clipping our own overflow. 3286 if (child->hasSelfPaintingLayer() || hasOverflowClip()) 3287 return; 3288 IntRect childVisualOverflowRect = child->visualOverflowRectForPropagation(style()); 3289 childVisualOverflowRect.move(delta); 3290 addVisualOverflow(childVisualOverflowRect); 3291 } 3292 3293 void RenderBox::addLayoutOverflow(const IntRect& rect) 3294 { 3295 IntRect clientBox = clientBoxRect(); 3296 if (clientBox.contains(rect) || rect.isEmpty()) 3297 return; 3298 3299 // For overflow clip objects, we don't want to propagate overflow into unreachable areas. 3300 IntRect overflowRect(rect); 3301 if (hasOverflowClip() || isRenderView()) { 3302 // Overflow is in the block's coordinate space and thus is flipped for horizontal-bt and vertical-rl 3303 // writing modes. At this stage that is actually a simplification, since we can treat horizontal-tb/bt as the same 3304 // and vertical-lr/rl as the same. 3305 bool hasTopOverflow = !style()->isLeftToRightDirection() && !isHorizontalWritingMode(); 3306 bool hasLeftOverflow = !style()->isLeftToRightDirection() && isHorizontalWritingMode(); 3307 3308 if (!hasTopOverflow) 3309 overflowRect.shiftYEdgeTo(max(overflowRect.y(), clientBox.y())); 3310 else 3311 overflowRect.shiftMaxYEdgeTo(min(overflowRect.maxY(), clientBox.maxY())); 3312 if (!hasLeftOverflow) 3313 overflowRect.shiftXEdgeTo(max(overflowRect.x(), clientBox.x())); 3314 else 3315 overflowRect.shiftMaxXEdgeTo(min(overflowRect.maxX(), clientBox.maxX())); 3316 3317 // Now re-test with the adjusted rectangle and see if it has become unreachable or fully 3318 // contained. 3319 if (clientBox.contains(overflowRect) || overflowRect.isEmpty()) 3320 return; 3321 } 3322 3323 if (!m_overflow) 3324 m_overflow.set(new RenderOverflow(clientBox, borderBoxRect())); 3325 3326 m_overflow->addLayoutOverflow(overflowRect); 3327 } 3328 3329 void RenderBox::addVisualOverflow(const IntRect& rect) 3330 { 3331 IntRect borderBox = borderBoxRect(); 3332 if (borderBox.contains(rect) || rect.isEmpty()) 3333 return; 3334 3335 if (!m_overflow) 3336 m_overflow.set(new RenderOverflow(clientBoxRect(), borderBox)); 3337 3338 m_overflow->addVisualOverflow(rect); 3339 } 3340 3341 void RenderBox::clearLayoutOverflow() 3342 { 3343 if (!m_overflow) 3344 return; 3345 3346 if (visualOverflowRect() == borderBoxRect()) { 3347 m_overflow.clear(); 3348 return; 3349 } 3350 3351 m_overflow->resetLayoutOverflow(borderBoxRect()); 3352 } 3353 3354 int RenderBox::lineHeight(bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const 3355 { 3356 if (isReplaced()) 3357 return direction == HorizontalLine ? m_marginTop + height() + m_marginBottom : m_marginRight + width() + m_marginLeft; 3358 return 0; 3359 } 3360 3361 int RenderBox::baselinePosition(FontBaseline baselineType, bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const 3362 { 3363 if (isReplaced()) { 3364 int result = direction == HorizontalLine ? m_marginTop + height() + m_marginBottom : m_marginRight + width() + m_marginLeft; 3365 if (baselineType == AlphabeticBaseline) 3366 return result; 3367 return result - result / 2; 3368 } 3369 return 0; 3370 } 3371 3372 3373 RenderLayer* RenderBox::enclosingFloatPaintingLayer() const 3374 { 3375 const RenderObject* curr = this; 3376 while (curr) { 3377 RenderLayer* layer = curr->hasLayer() && curr->isBox() ? toRenderBoxModelObject(curr)->layer() : 0; 3378 if (layer && layer->isSelfPaintingLayer()) 3379 return layer; 3380 curr = curr->parent(); 3381 } 3382 return 0; 3383 } 3384 3385 IntRect RenderBox::logicalVisualOverflowRectForPropagation(RenderStyle* parentStyle) const 3386 { 3387 IntRect rect = visualOverflowRectForPropagation(parentStyle); 3388 if (!parentStyle->isHorizontalWritingMode()) 3389 return rect.transposedRect(); 3390 return rect; 3391 } 3392 3393 IntRect RenderBox::visualOverflowRectForPropagation(RenderStyle* parentStyle) const 3394 { 3395 // If the writing modes of the child and parent match, then we don't have to 3396 // do anything fancy. Just return the result. 3397 IntRect rect = visualOverflowRect(); 3398 if (parentStyle->writingMode() == style()->writingMode()) 3399 return rect; 3400 3401 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch 3402 // in a particular axis, then we have to flip the rect along that axis. 3403 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode) 3404 rect.setX(width() - rect.maxX()); 3405 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode) 3406 rect.setY(height() - rect.maxY()); 3407 3408 return rect; 3409 } 3410 3411 IntRect RenderBox::logicalLayoutOverflowRectForPropagation(RenderStyle* parentStyle) const 3412 { 3413 IntRect rect = layoutOverflowRectForPropagation(parentStyle); 3414 if (!parentStyle->isHorizontalWritingMode()) 3415 return rect.transposedRect(); 3416 return rect; 3417 } 3418 3419 IntRect RenderBox::layoutOverflowRectForPropagation(RenderStyle* parentStyle) const 3420 { 3421 // Only propagate interior layout overflow if we don't clip it. 3422 IntRect rect = borderBoxRect(); 3423 if (!hasOverflowClip()) 3424 rect.unite(layoutOverflowRect()); 3425 3426 bool hasTransform = hasLayer() && layer()->transform(); 3427 if (isRelPositioned() || hasTransform) { 3428 // If we are relatively positioned or if we have a transform, then we have to convert 3429 // this rectangle into physical coordinates, apply relative positioning and transforms 3430 // to it, and then convert it back. 3431 flipForWritingMode(rect); 3432 3433 if (hasTransform) 3434 rect = layer()->currentTransform().mapRect(rect); 3435 3436 if (isRelPositioned()) 3437 rect.move(relativePositionOffsetX(), relativePositionOffsetY()); 3438 3439 // Now we need to flip back. 3440 flipForWritingMode(rect); 3441 } 3442 3443 // If the writing modes of the child and parent match, then we don't have to 3444 // do anything fancy. Just return the result. 3445 if (parentStyle->writingMode() == style()->writingMode()) 3446 return rect; 3447 3448 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch 3449 // in a particular axis, then we have to flip the rect along that axis. 3450 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode) 3451 rect.setX(width() - rect.maxX()); 3452 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode) 3453 rect.setY(height() - rect.maxY()); 3454 3455 return rect; 3456 } 3457 3458 IntPoint RenderBox::flipForWritingMode(const RenderBox* child, const IntPoint& point, FlippingAdjustment adjustment) const 3459 { 3460 if (!style()->isFlippedBlocksWritingMode()) 3461 return point; 3462 3463 // The child is going to add in its x() and y(), so we have to make sure it ends up in 3464 // the right place. 3465 if (isHorizontalWritingMode()) 3466 return IntPoint(point.x(), point.y() + height() - child->height() - child->y() - (adjustment == ParentToChildFlippingAdjustment ? child->y() : 0)); 3467 return IntPoint(point.x() + width() - child->width() - child->x() - (adjustment == ParentToChildFlippingAdjustment ? child->x() : 0), point.y()); 3468 } 3469 3470 void RenderBox::flipForWritingMode(IntRect& rect) const 3471 { 3472 if (!style()->isFlippedBlocksWritingMode()) 3473 return; 3474 3475 if (isHorizontalWritingMode()) 3476 rect.setY(height() - rect.maxY()); 3477 else 3478 rect.setX(width() - rect.maxX()); 3479 } 3480 3481 int RenderBox::flipForWritingMode(int position) const 3482 { 3483 if (!style()->isFlippedBlocksWritingMode()) 3484 return position; 3485 return logicalHeight() - position; 3486 } 3487 3488 IntPoint RenderBox::flipForWritingMode(const IntPoint& position) const 3489 { 3490 if (!style()->isFlippedBlocksWritingMode()) 3491 return position; 3492 return isHorizontalWritingMode() ? IntPoint(position.x(), height() - position.y()) : IntPoint(width() - position.x(), position.y()); 3493 } 3494 3495 IntPoint RenderBox::flipForWritingModeIncludingColumns(const IntPoint& point) const 3496 { 3497 if (!hasColumns() || !style()->isFlippedBlocksWritingMode()) 3498 return flipForWritingMode(point); 3499 return toRenderBlock(this)->flipForWritingModeIncludingColumns(point); 3500 } 3501 3502 IntSize RenderBox::flipForWritingMode(const IntSize& offset) const 3503 { 3504 if (!style()->isFlippedBlocksWritingMode()) 3505 return offset; 3506 return isHorizontalWritingMode() ? IntSize(offset.width(), height() - offset.height()) : IntSize(width() - offset.width(), offset.height()); 3507 } 3508 3509 FloatPoint RenderBox::flipForWritingMode(const FloatPoint& position) const 3510 { 3511 if (!style()->isFlippedBlocksWritingMode()) 3512 return position; 3513 return isHorizontalWritingMode() ? FloatPoint(position.x(), height() - position.y()) : FloatPoint(width() - position.x(), position.y()); 3514 } 3515 3516 void RenderBox::flipForWritingMode(FloatRect& rect) const 3517 { 3518 if (!style()->isFlippedBlocksWritingMode()) 3519 return; 3520 3521 if (isHorizontalWritingMode()) 3522 rect.setY(height() - rect.maxY()); 3523 else 3524 rect.setX(width() - rect.maxX()); 3525 } 3526 3527 IntSize RenderBox::locationOffsetIncludingFlipping() const 3528 { 3529 RenderBlock* containerBlock = containingBlock(); 3530 if (!containerBlock || containerBlock == this) 3531 return locationOffset(); 3532 3533 IntRect rect(frameRect()); 3534 containerBlock->flipForWritingMode(rect); // FIXME: This is wrong if we are an absolutely positioned object enclosed by a relative-positioned inline. 3535 return IntSize(rect.x(), rect.y()); 3536 } 3537 3538 } // namespace WebCore 3539