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 #if PLATFORM(ANDROID) 1566 // Strangely, the slider is get overrided as width 0 on youtube.com 1567 // The wrong width will cause the touch hit test for the slider failed. 1568 // This WAR should be safe since it is only targeted to slider. 1569 // TODO: root cause this and see if any webkit update fix this. 1570 if (!(isSlider() && overrideSize() == 0)) 1571 #endif 1572 setLogicalWidth(overrideSize()); 1573 return; 1574 } 1575 1576 // FIXME: Account for block-flow in flexible boxes. 1577 // https://bugs.webkit.org/show_bug.cgi?id=46418 1578 bool inVerticalBox = parent()->isFlexibleBox() && (parent()->style()->boxOrient() == VERTICAL); 1579 bool stretching = (parent()->style()->boxAlign() == BSTRETCH); 1580 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inVerticalBox || !stretching); 1581 1582 Length logicalWidthLength = (treatAsReplaced) ? Length(computeReplacedLogicalWidth(), Fixed) : style()->logicalWidth(); 1583 1584 RenderBlock* cb = containingBlock(); 1585 int containerLogicalWidth = max(0, containingBlockLogicalWidthForContent()); 1586 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode(); 1587 int containerWidthInInlineDirection = containerLogicalWidth; 1588 if (hasPerpendicularContainingBlock) 1589 containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight(); 1590 1591 if (isInline() && !isInlineBlockOrInlineTable()) { 1592 // just calculate margins 1593 setMarginStart(style()->marginStart().calcMinValue(containerLogicalWidth)); 1594 setMarginEnd(style()->marginEnd().calcMinValue(containerLogicalWidth)); 1595 #ifdef ANDROID_LAYOUT 1596 if (treatAsReplaced) { 1597 #else 1598 if (treatAsReplaced) 1599 #endif 1600 setLogicalWidth(max(logicalWidthLength.value() + borderAndPaddingLogicalWidth(), minPreferredLogicalWidth())); 1601 1602 #ifdef ANDROID_LAYOUT 1603 // in SSR mode with replaced box, if the box width is wider than the container width, 1604 // it will be shrinked to fit to the container. 1605 if (containerLogicalWidth && (width() + m_marginLeft + m_marginRight) > containerLogicalWidth && 1606 document()->frame()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { 1607 m_marginLeft = m_marginRight = 0; 1608 setWidth(containerLogicalWidth); 1609 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = containerLogicalWidth; 1610 } 1611 } 1612 #endif 1613 return; 1614 } 1615 1616 // Width calculations 1617 if (treatAsReplaced) 1618 setLogicalWidth(logicalWidthLength.value() + borderAndPaddingLogicalWidth()); 1619 else { 1620 // Calculate LogicalWidth 1621 setLogicalWidth(computeLogicalWidthUsing(LogicalWidth, containerWidthInInlineDirection)); 1622 1623 // Calculate MaxLogicalWidth 1624 if (!style()->logicalMaxWidth().isUndefined()) { 1625 int maxLogicalWidth = computeLogicalWidthUsing(MaxLogicalWidth, containerWidthInInlineDirection); 1626 if (logicalWidth() > maxLogicalWidth) { 1627 setLogicalWidth(maxLogicalWidth); 1628 logicalWidthLength = style()->logicalMaxWidth(); 1629 } 1630 } 1631 1632 // Calculate MinLogicalWidth 1633 int minLogicalWidth = computeLogicalWidthUsing(MinLogicalWidth, containerWidthInInlineDirection); 1634 if (logicalWidth() < minLogicalWidth) { 1635 setLogicalWidth(minLogicalWidth); 1636 logicalWidthLength = style()->logicalMinWidth(); 1637 } 1638 } 1639 1640 // Fieldsets are currently the only objects that stretch to their minimum width. 1641 if (stretchesToMinIntrinsicLogicalWidth()) { 1642 setLogicalWidth(max(logicalWidth(), minPreferredLogicalWidth())); 1643 logicalWidthLength = Length(logicalWidth(), Fixed); 1644 } 1645 1646 // Margin calculations. 1647 if (logicalWidthLength.isAuto() || hasPerpendicularContainingBlock || isFloating() || isInline()) { 1648 setMarginStart(style()->marginStart().calcMinValue(containerLogicalWidth)); 1649 setMarginEnd(style()->marginEnd().calcMinValue(containerLogicalWidth)); 1650 } else 1651 computeInlineDirectionMargins(cb, containerLogicalWidth, logicalWidth()); 1652 1653 #ifdef ANDROID_LAYOUT 1654 // in SSR mode with non-replaced box, we use ANDROID_SSR_MARGIN_PADDING for left/right margin. 1655 // If the box width is wider than the container width, it will be shrinked to fit to the container. 1656 if (containerLogicalWidth && !treatAsReplaced && 1657 document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { 1658 setWidth(width() + m_marginLeft + m_marginRight); 1659 m_marginLeft = m_marginLeft > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginLeft; 1660 m_marginRight = m_marginRight > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginRight; 1661 if (width() > containerLogicalWidth) { 1662 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = containerLogicalWidth-(m_marginLeft + m_marginRight); 1663 setWidth(m_minPreferredLogicalWidth); 1664 } else 1665 setWidth(width() -(m_marginLeft + m_marginRight)); 1666 } 1667 #endif 1668 1669 if (!hasPerpendicularContainingBlock && containerLogicalWidth && containerLogicalWidth != (logicalWidth() + marginStart() + marginEnd()) 1670 && !isFloating() && !isInline() && !cb->isFlexibleBox()) 1671 cb->setMarginEndForChild(this, containerLogicalWidth - logicalWidth() - cb->marginStartForChild(this)); 1672 } 1673 1674 int RenderBox::computeLogicalWidthUsing(LogicalWidthType widthType, int availableLogicalWidth) 1675 { 1676 int logicalWidthResult = logicalWidth(); 1677 Length logicalWidth; 1678 if (widthType == LogicalWidth) 1679 logicalWidth = style()->logicalWidth(); 1680 else if (widthType == MinLogicalWidth) 1681 logicalWidth = style()->logicalMinWidth(); 1682 else 1683 logicalWidth = style()->logicalMaxWidth(); 1684 1685 if (logicalWidth.isIntrinsicOrAuto()) { 1686 int marginStart = style()->marginStart().calcMinValue(availableLogicalWidth); 1687 int marginEnd = style()->marginEnd().calcMinValue(availableLogicalWidth); 1688 if (availableLogicalWidth) 1689 logicalWidthResult = availableLogicalWidth - marginStart - marginEnd; 1690 1691 if (sizesToIntrinsicLogicalWidth(widthType)) { 1692 logicalWidthResult = max(logicalWidthResult, minPreferredLogicalWidth()); 1693 logicalWidthResult = min(logicalWidthResult, maxPreferredLogicalWidth()); 1694 } 1695 } else // FIXME: If the containing block flow is perpendicular to our direction we need to use the available logical height instead. 1696 logicalWidthResult = computeBorderBoxLogicalWidth(logicalWidth.calcValue(availableLogicalWidth)); 1697 1698 return logicalWidthResult; 1699 } 1700 1701 bool RenderBox::sizesToIntrinsicLogicalWidth(LogicalWidthType widthType) const 1702 { 1703 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks, 1704 // but they allow text to sit on the same line as the marquee. 1705 if (isFloating() || (isInlineBlockOrInlineTable() && !isHTMLMarquee())) 1706 return true; 1707 1708 // This code may look a bit strange. Basically width:intrinsic should clamp the size when testing both 1709 // min-width and width. max-width is only clamped if it is also intrinsic. 1710 Length logicalWidth = (widthType == MaxLogicalWidth) ? style()->logicalMaxWidth() : style()->logicalWidth(); 1711 if (logicalWidth.type() == Intrinsic) 1712 return true; 1713 1714 // Children of a horizontal marquee do not fill the container by default. 1715 // FIXME: Need to deal with MAUTO value properly. It could be vertical. 1716 // FIXME: Think about block-flow here. Need to find out how marquee direction relates to 1717 // block-flow (as well as how marquee overflow should relate to block flow). 1718 // https://bugs.webkit.org/show_bug.cgi?id=46472 1719 if (parent()->style()->overflowX() == OMARQUEE) { 1720 EMarqueeDirection dir = parent()->style()->marqueeDirection(); 1721 if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT) 1722 return true; 1723 } 1724 1725 // Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes 1726 // that don't stretch their kids lay out their children at their intrinsic widths. 1727 // FIXME: Think about block-flow here. 1728 // https://bugs.webkit.org/show_bug.cgi?id=46473 1729 if (parent()->isFlexibleBox() 1730 && (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH)) 1731 return true; 1732 1733 // Button, input, select, textarea, legend and datagrid treat 1734 // width value of 'auto' as 'intrinsic' unless it's in a 1735 // stretching vertical flexbox. 1736 // FIXME: Think about block-flow here. 1737 // https://bugs.webkit.org/show_bug.cgi?id=46473 1738 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))) 1739 return true; 1740 1741 return false; 1742 } 1743 1744 void RenderBox::computeInlineDirectionMargins(RenderBlock* containingBlock, int containerWidth, int childWidth) 1745 { 1746 const RenderStyle* containingBlockStyle = containingBlock->style(); 1747 Length marginStartLength = style()->marginStartUsing(containingBlockStyle); 1748 Length marginEndLength = style()->marginEndUsing(containingBlockStyle); 1749 1750 // Case One: The object is being centered in the containing block's available logical width. 1751 if ((marginStartLength.isAuto() && marginEndLength.isAuto() && childWidth < containerWidth) 1752 || (!marginStartLength.isAuto() && !marginEndLength.isAuto() && containingBlock->style()->textAlign() == WEBKIT_CENTER)) { 1753 containingBlock->setMarginStartForChild(this, max(0, (containerWidth - childWidth) / 2)); 1754 containingBlock->setMarginEndForChild(this, containerWidth - childWidth - containingBlock->marginStartForChild(this)); 1755 return; 1756 } 1757 1758 // Case Two: The object is being pushed to the start of the containing block's available logical width. 1759 if (marginEndLength.isAuto() && childWidth < containerWidth) { 1760 containingBlock->setMarginStartForChild(this, marginStartLength.calcValue(containerWidth)); 1761 containingBlock->setMarginEndForChild(this, containerWidth - childWidth - containingBlock->marginStartForChild(this)); 1762 return; 1763 } 1764 1765 // Case Three: The object is being pushed to the end of the containing block's available logical width. 1766 bool pushToEndFromTextAlign = !marginEndLength.isAuto() && ((!containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_LEFT) 1767 || (containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_RIGHT)); 1768 if ((marginStartLength.isAuto() && childWidth < containerWidth) || pushToEndFromTextAlign) { 1769 containingBlock->setMarginEndForChild(this, marginEndLength.calcValue(containerWidth)); 1770 containingBlock->setMarginStartForChild(this, containerWidth - childWidth - containingBlock->marginEndForChild(this)); 1771 return; 1772 } 1773 1774 // Case Four: Either no auto margins, or our width is >= the container width (css2.1, 10.3.3). In that case 1775 // auto margins will just turn into 0. 1776 containingBlock->setMarginStartForChild(this, marginStartLength.calcMinValue(containerWidth)); 1777 containingBlock->setMarginEndForChild(this, marginEndLength.calcMinValue(containerWidth)); 1778 } 1779 1780 void RenderBox::computeLogicalHeight() 1781 { 1782 // Cell height is managed by the table and inline non-replaced elements do not support a height property. 1783 if (isTableCell() || (isInline() && !isReplaced())) 1784 return; 1785 1786 Length h; 1787 if (isPositioned()) { 1788 // FIXME: This calculation is not patched for block-flow yet. 1789 // https://bugs.webkit.org/show_bug.cgi?id=46500 1790 computePositionedLogicalHeight(); 1791 } else { 1792 RenderBlock* cb = containingBlock(); 1793 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode(); 1794 1795 if (!hasPerpendicularContainingBlock) 1796 computeBlockDirectionMargins(cb); 1797 1798 // For tables, calculate margins only. 1799 if (isTable()) { 1800 if (hasPerpendicularContainingBlock) 1801 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), logicalHeight()); 1802 return; 1803 } 1804 1805 // FIXME: Account for block-flow in flexible boxes. 1806 // https://bugs.webkit.org/show_bug.cgi?id=46418 1807 bool inHorizontalBox = parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL; 1808 bool stretching = parent()->style()->boxAlign() == BSTRETCH; 1809 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inHorizontalBox || !stretching); 1810 bool checkMinMaxHeight = false; 1811 1812 // The parent box is flexing us, so it has increased or decreased our height. We have to 1813 // grab our cached flexible height. 1814 // FIXME: Account for block-flow in flexible boxes. 1815 // https://bugs.webkit.org/show_bug.cgi?id=46418 1816 if (hasOverrideSize() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL 1817 && parent()->isFlexingChildren()) 1818 h = Length(overrideSize() - borderAndPaddingLogicalHeight(), Fixed); 1819 else if (treatAsReplaced) 1820 h = Length(computeReplacedLogicalHeight(), Fixed); 1821 else { 1822 h = style()->logicalHeight(); 1823 checkMinMaxHeight = true; 1824 } 1825 1826 // Block children of horizontal flexible boxes fill the height of the box. 1827 // FIXME: Account for block-flow in flexible boxes. 1828 // https://bugs.webkit.org/show_bug.cgi?id=46418 1829 if (h.isAuto() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL 1830 && parent()->isStretchingChildren()) { 1831 h = Length(parentBox()->contentLogicalHeight() - marginBefore() - marginAfter() - borderAndPaddingLogicalHeight(), Fixed); 1832 checkMinMaxHeight = false; 1833 } 1834 1835 int heightResult; 1836 if (checkMinMaxHeight) { 1837 #ifdef ANDROID_LAYOUT 1838 // in SSR mode, ignore CSS height as layout is so different 1839 if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) 1840 heightResult = -1; 1841 else 1842 #endif 1843 heightResult = computeLogicalHeightUsing(style()->logicalHeight()); 1844 if (heightResult == -1) 1845 heightResult = logicalHeight(); 1846 int minH = computeLogicalHeightUsing(style()->logicalMinHeight()); // Leave as -1 if unset. 1847 int maxH = style()->logicalMaxHeight().isUndefined() ? heightResult : computeLogicalHeightUsing(style()->logicalMaxHeight()); 1848 if (maxH == -1) 1849 maxH = heightResult; 1850 heightResult = min(maxH, heightResult); 1851 heightResult = max(minH, heightResult); 1852 } else { 1853 // The only times we don't check min/max height are when a fixed length has 1854 // been given as an override. Just use that. The value has already been adjusted 1855 // for box-sizing. 1856 heightResult = h.value() + borderAndPaddingLogicalHeight(); 1857 } 1858 1859 setLogicalHeight(heightResult); 1860 1861 if (hasPerpendicularContainingBlock) 1862 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), heightResult); 1863 } 1864 1865 // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the 1866 // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height 1867 // is specified. When we're printing, we also need this quirk if the body or root has a percentage 1868 // height since we don't set a height in RenderView when we're printing. So without this quirk, the 1869 // height has nothing to be a percentage of, and it ends up being 0. That is bad. 1870 bool paginatedContentNeedsBaseHeight = document()->printing() && h.isPercent() 1871 && (isRoot() || (isBody() && document()->documentElement()->renderer()->style()->logicalHeight().isPercent())); 1872 if (stretchesToViewport() || paginatedContentNeedsBaseHeight) { 1873 // FIXME: Finish accounting for block flow here. 1874 // https://bugs.webkit.org/show_bug.cgi?id=46603 1875 int margins = collapsedMarginBefore() + collapsedMarginAfter(); 1876 int visHeight; 1877 if (document()->printing()) 1878 visHeight = static_cast<int>(view()->pageLogicalHeight()); 1879 else { 1880 if (isHorizontalWritingMode()) 1881 visHeight = view()->viewHeight(); 1882 else 1883 visHeight = view()->viewWidth(); 1884 } 1885 if (isRoot()) 1886 setLogicalHeight(max(logicalHeight(), visHeight - margins)); 1887 else { 1888 int marginsBordersPadding = margins + parentBox()->marginBefore() + parentBox()->marginAfter() + parentBox()->borderAndPaddingLogicalHeight(); 1889 setLogicalHeight(max(logicalHeight(), visHeight - marginsBordersPadding)); 1890 } 1891 } 1892 } 1893 1894 int RenderBox::computeLogicalHeightUsing(const Length& h) 1895 { 1896 int logicalHeight = -1; 1897 if (!h.isAuto()) { 1898 if (h.isFixed()) 1899 logicalHeight = h.value(); 1900 else if (h.isPercent()) 1901 logicalHeight = computePercentageLogicalHeight(h); 1902 if (logicalHeight != -1) { 1903 logicalHeight = computeBorderBoxLogicalHeight(logicalHeight); 1904 return logicalHeight; 1905 } 1906 } 1907 return logicalHeight; 1908 } 1909 1910 int RenderBox::computePercentageLogicalHeight(const Length& height) 1911 { 1912 int result = -1; 1913 1914 // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing 1915 // block that may have a specified height and then use it. In strict mode, this violates the 1916 // specification, which states that percentage heights just revert to auto if the containing 1917 // block has an auto height. We still skip anonymous containing blocks in both modes, though, and look 1918 // only at explicit containers. 1919 bool skippedAutoHeightContainingBlock = false; 1920 RenderBlock* cb = containingBlock(); 1921 while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() && cb->style()->logicalHeight().isAuto()) { 1922 if (!document()->inQuirksMode() && !cb->isAnonymousBlock()) 1923 break; 1924 skippedAutoHeightContainingBlock = true; 1925 cb = cb->containingBlock(); 1926 cb->addPercentHeightDescendant(this); 1927 } 1928 1929 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height 1930 // explicitly specified that can be used for any percentage computations. 1931 // FIXME: We can't just check top/bottom here. 1932 // https://bugs.webkit.org/show_bug.cgi?id=46500 1933 bool isPositionedWithSpecifiedHeight = cb->isPositioned() && (!cb->style()->logicalHeight().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto())); 1934 1935 bool includeBorderPadding = isTable(); 1936 1937 // Table cells violate what the CSS spec says to do with heights. Basically we 1938 // don't care if the cell specified a height or not. We just always make ourselves 1939 // be a percentage of the cell's current content height. 1940 if (cb->isTableCell()) { 1941 if (!skippedAutoHeightContainingBlock) { 1942 result = cb->overrideSize(); 1943 if (result == -1) { 1944 // Normally we would let the cell size intrinsically, but scrolling overflow has to be 1945 // treated differently, since WinIE lets scrolled overflow regions shrink as needed. 1946 // While we can't get all cases right, we can at least detect when the cell has a specified 1947 // height or when the table has a specified height. In these cases we want to initially have 1948 // no size and allow the flexing of the table or the cell to its specified height to cause us 1949 // to grow to fill the space. This could end up being wrong in some cases, but it is 1950 // preferable to the alternative (sizing intrinsically and making the row end up too big). 1951 RenderTableCell* cell = toRenderTableCell(cb); 1952 if (scrollsOverflowY() && (!cell->style()->logicalHeight().isAuto() || !cell->table()->style()->logicalHeight().isAuto())) 1953 return 0; 1954 return -1; 1955 } 1956 includeBorderPadding = true; 1957 } 1958 } 1959 // Otherwise we only use our percentage height if our containing block had a specified 1960 // height. 1961 else if (cb->style()->logicalHeight().isFixed()) 1962 result = cb->computeContentBoxLogicalHeight(cb->style()->logicalHeight().value()); 1963 else if (cb->style()->logicalHeight().isPercent() && !isPositionedWithSpecifiedHeight) { 1964 // We need to recur and compute the percentage height for our containing block. 1965 result = cb->computePercentageLogicalHeight(cb->style()->logicalHeight()); 1966 if (result != -1) 1967 result = cb->computeContentBoxLogicalHeight(result); 1968 } else if (cb->isRenderView() || (cb->isBody() && document()->inQuirksMode()) || isPositionedWithSpecifiedHeight) { 1969 // Don't allow this to affect the block' height() member variable, since this 1970 // can get called while the block is still laying out its kids. 1971 int oldHeight = cb->logicalHeight(); 1972 cb->computeLogicalHeight(); 1973 result = cb->contentLogicalHeight(); 1974 cb->setLogicalHeight(oldHeight); 1975 } else if (cb->isRoot() && isPositioned()) 1976 // Match the positioned objects behavior, which is that positioned objects will fill their viewport 1977 // always. Note we could only hit this case by recurring into computePercentageLogicalHeight on a positioned containing block. 1978 result = cb->computeContentBoxLogicalHeight(cb->availableLogicalHeight()); 1979 1980 if (result != -1) { 1981 result = height.calcValue(result); 1982 if (includeBorderPadding) { 1983 // It is necessary to use the border-box to match WinIE's broken 1984 // box model. This is essential for sizing inside 1985 // table cells using percentage heights. 1986 result -= borderAndPaddingLogicalHeight(); 1987 result = max(0, result); 1988 } 1989 } 1990 return result; 1991 } 1992 1993 int RenderBox::computeReplacedLogicalWidth(bool includeMaxWidth) const 1994 { 1995 int logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth()); 1996 int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth()); 1997 int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth()); 1998 1999 return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth)); 2000 } 2001 2002 int RenderBox::computeReplacedLogicalWidthUsing(Length logicalWidth) const 2003 { 2004 switch (logicalWidth.type()) { 2005 case Fixed: 2006 return computeContentBoxLogicalWidth(logicalWidth.value()); 2007 case Percent: { 2008 // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced element's block-flow is perpendicular to the 2009 // containing block's block-flow. 2010 // https://bugs.webkit.org/show_bug.cgi?id=46496 2011 const int cw = isPositioned() ? containingBlockLogicalWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockLogicalWidthForContent(); 2012 if (cw > 0) 2013 return computeContentBoxLogicalWidth(logicalWidth.calcMinValue(cw)); 2014 } 2015 // fall through 2016 default: 2017 return intrinsicLogicalWidth(); 2018 } 2019 } 2020 2021 int RenderBox::computeReplacedLogicalHeight() const 2022 { 2023 int logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight()); 2024 int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight()); 2025 int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight()); 2026 2027 return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight)); 2028 } 2029 2030 int RenderBox::computeReplacedLogicalHeightUsing(Length logicalHeight) const 2031 { 2032 switch (logicalHeight.type()) { 2033 case Fixed: 2034 return computeContentBoxLogicalHeight(logicalHeight.value()); 2035 case Percent: 2036 { 2037 RenderObject* cb = isPositioned() ? container() : containingBlock(); 2038 while (cb->isAnonymous()) { 2039 cb = cb->containingBlock(); 2040 toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this)); 2041 } 2042 2043 // FIXME: This calculation is not patched for block-flow yet. 2044 // https://bugs.webkit.org/show_bug.cgi?id=46500 2045 if (cb->isPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) { 2046 ASSERT(cb->isRenderBlock()); 2047 RenderBlock* block = toRenderBlock(cb); 2048 int oldHeight = block->height(); 2049 block->computeLogicalHeight(); 2050 int newHeight = block->computeContentBoxLogicalHeight(block->contentHeight()); 2051 block->setHeight(oldHeight); 2052 return computeContentBoxLogicalHeight(logicalHeight.calcValue(newHeight)); 2053 } 2054 2055 // FIXME: availableLogicalHeight() is wrong if the replaced element's block-flow is perpendicular to the 2056 // containing block's block-flow. 2057 // https://bugs.webkit.org/show_bug.cgi?id=46496 2058 int availableHeight = isPositioned() ? containingBlockLogicalHeightForPositioned(toRenderBoxModelObject(cb)) : toRenderBox(cb)->availableLogicalHeight(); 2059 2060 // It is necessary to use the border-box to match WinIE's broken 2061 // box model. This is essential for sizing inside 2062 // table cells using percentage heights. 2063 // FIXME: This needs to be made block-flow-aware. If the cell and image are perpendicular block-flows, this isn't right. 2064 // https://bugs.webkit.org/show_bug.cgi?id=46997 2065 if (cb->isTableCell() && (cb->style()->logicalHeight().isAuto() || cb->style()->logicalHeight().isPercent())) { 2066 // Don't let table cells squeeze percent-height replaced elements 2067 // <http://bugs.webkit.org/show_bug.cgi?id=15359> 2068 availableHeight = max(availableHeight, intrinsicLogicalHeight()); 2069 return logicalHeight.calcValue(availableHeight - borderAndPaddingLogicalHeight()); 2070 } 2071 2072 return computeContentBoxLogicalHeight(logicalHeight.calcValue(availableHeight)); 2073 } 2074 default: 2075 return intrinsicLogicalHeight(); 2076 } 2077 } 2078 2079 int RenderBox::availableLogicalHeight() const 2080 { 2081 return availableLogicalHeightUsing(style()->logicalHeight()); 2082 } 2083 2084 int RenderBox::availableLogicalHeightUsing(const Length& h) const 2085 { 2086 if (h.isFixed()) 2087 return computeContentBoxLogicalHeight(h.value()); 2088 2089 if (isRenderView()) 2090 return isHorizontalWritingMode() ? toRenderView(this)->frameView()->visibleHeight() : toRenderView(this)->frameView()->visibleWidth(); 2091 2092 // We need to stop here, since we don't want to increase the height of the table 2093 // artificially. We're going to rely on this cell getting expanded to some new 2094 // height, and then when we lay out again we'll use the calculation below. 2095 if (isTableCell() && (h.isAuto() || h.isPercent())) 2096 return overrideSize() - borderAndPaddingLogicalWidth(); 2097 2098 if (h.isPercent()) 2099 return computeContentBoxLogicalHeight(h.calcValue(containingBlock()->availableLogicalHeight())); 2100 2101 // FIXME: We can't just check top/bottom here. 2102 // https://bugs.webkit.org/show_bug.cgi?id=46500 2103 if (isRenderBlock() && isPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) { 2104 RenderBlock* block = const_cast<RenderBlock*>(toRenderBlock(this)); 2105 int oldHeight = block->logicalHeight(); 2106 block->computeLogicalHeight(); 2107 int newHeight = block->computeContentBoxLogicalHeight(block->contentLogicalHeight()); 2108 block->setLogicalHeight(oldHeight); 2109 return computeContentBoxLogicalHeight(newHeight); 2110 } 2111 2112 return containingBlock()->availableLogicalHeight(); 2113 } 2114 2115 void RenderBox::computeBlockDirectionMargins(RenderBlock* containingBlock) 2116 { 2117 if (isTableCell()) { 2118 // FIXME: Not right if we allow cells to have different directionality than the table. If we do allow this, though, 2119 // we may just do it with an extra anonymous block inside the cell. 2120 setMarginBefore(0); 2121 setMarginAfter(0); 2122 return; 2123 } 2124 2125 // Margins are calculated with respect to the logical width of 2126 // the containing block (8.3) 2127 int cw = containingBlockLogicalWidthForContent(); 2128 2129 RenderStyle* containingBlockStyle = containingBlock->style(); 2130 containingBlock->setMarginBeforeForChild(this, style()->marginBeforeUsing(containingBlockStyle).calcMinValue(cw)); 2131 containingBlock->setMarginAfterForChild(this, style()->marginAfterUsing(containingBlockStyle).calcMinValue(cw)); 2132 } 2133 2134 int RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const 2135 { 2136 #if PLATFORM(ANDROID) 2137 // Fixed element's position should be decided by the visible screen size. 2138 // That is in the doc coordindate. 2139 if (style()->position() == FixedPosition && containingBlock->isRenderView()) { 2140 const RenderView* view = toRenderView(containingBlock); 2141 return PlatformBridge::screenWidthInDocCoord(view->frameView()); 2142 } 2143 #endif 2144 2145 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode()) 2146 return containingBlockLogicalHeightForPositioned(containingBlock, false); 2147 2148 if (containingBlock->isBox()) 2149 return toRenderBox(containingBlock)->clientLogicalWidth(); 2150 2151 ASSERT(containingBlock->isRenderInline() && containingBlock->isRelPositioned()); 2152 2153 const RenderInline* flow = toRenderInline(containingBlock); 2154 InlineFlowBox* first = flow->firstLineBox(); 2155 InlineFlowBox* last = flow->lastLineBox(); 2156 2157 // If the containing block is empty, return a width of 0. 2158 if (!first || !last) 2159 return 0; 2160 2161 int fromLeft; 2162 int fromRight; 2163 if (containingBlock->style()->isLeftToRightDirection()) { 2164 fromLeft = first->logicalLeft() + first->borderLogicalLeft(); 2165 fromRight = last->logicalLeft() + last->logicalWidth() - last->borderLogicalRight(); 2166 } else { 2167 fromRight = first->logicalLeft() + first->logicalWidth() - first->borderLogicalRight(); 2168 fromLeft = last->logicalLeft() + last->borderLogicalLeft(); 2169 } 2170 2171 return max(0, (fromRight - fromLeft)); 2172 } 2173 2174 int RenderBox::containingBlockLogicalHeightForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const 2175 { 2176 #if PLATFORM(ANDROID) 2177 // Fixed element's position should be decided by the visible screen size. 2178 // That is in the doc coordindate. 2179 if (style()->position() == FixedPosition && containingBlock->isRenderView()) { 2180 const RenderView* view = toRenderView(containingBlock); 2181 return PlatformBridge::screenHeightInDocCoord(view->frameView()); 2182 } 2183 #endif 2184 2185 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode()) 2186 return containingBlockLogicalWidthForPositioned(containingBlock, false); 2187 2188 if (containingBlock->isBox()) 2189 return toRenderBox(containingBlock)->clientLogicalHeight(); 2190 2191 ASSERT(containingBlock->isRenderInline() && containingBlock->isRelPositioned()); 2192 2193 const RenderInline* flow = toRenderInline(containingBlock); 2194 InlineFlowBox* first = flow->firstLineBox(); 2195 InlineFlowBox* last = flow->lastLineBox(); 2196 2197 // If the containing block is empty, return a height of 0. 2198 if (!first || !last) 2199 return 0; 2200 2201 int heightResult; 2202 IntRect boundingBox = flow->linesBoundingBox(); 2203 if (containingBlock->isHorizontalWritingMode()) 2204 heightResult = boundingBox.height(); 2205 else 2206 heightResult = boundingBox.width(); 2207 heightResult -= (containingBlock->borderBefore() + containingBlock->borderAfter()); 2208 return heightResult; 2209 } 2210 2211 static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRight, const RenderBox* child, const RenderBoxModelObject* containerBlock, int containerLogicalWidth, 2212 TextDirection containerDirection) 2213 { 2214 if (!logicalLeft.isAuto() || !logicalRight.isAuto()) 2215 return; 2216 2217 // FIXME: The static distance computation has not been patched for mixed writing modes yet. 2218 if (containerDirection == LTR) { 2219 int staticPosition = child->layer()->staticInlinePosition() - containerBlock->borderLogicalLeft(); 2220 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) { 2221 if (curr->isBox()) 2222 staticPosition += toRenderBox(curr)->logicalLeft(); 2223 } 2224 logicalLeft.setValue(Fixed, staticPosition); 2225 } else { 2226 RenderBox* enclosingBox = child->parent()->enclosingBox(); 2227 int staticPosition = child->layer()->staticInlinePosition() + containerLogicalWidth + containerBlock->borderLogicalRight(); 2228 staticPosition -= enclosingBox->logicalWidth(); 2229 for (RenderObject* curr = enclosingBox; curr && curr != containerBlock; curr = curr->container()) { 2230 if (curr->isBox()) 2231 staticPosition -= toRenderBox(curr)->logicalLeft(); 2232 } 2233 logicalRight.setValue(Fixed, staticPosition); 2234 } 2235 } 2236 2237 void RenderBox::computePositionedLogicalWidth() 2238 { 2239 if (isReplaced()) { 2240 computePositionedLogicalWidthReplaced(); 2241 return; 2242 } 2243 2244 // QUESTIONS 2245 // FIXME 1: Which RenderObject's 'direction' property should used: the 2246 // containing block (cb) as the spec seems to imply, the parent (parent()) as 2247 // was previously done in calculating the static distances, or ourself, which 2248 // was also previously done for deciding what to override when you had 2249 // over-constrained margins? Also note that the container block is used 2250 // in similar situations in other parts of the RenderBox class (see computeLogicalWidth() 2251 // and computeMarginsInContainingBlockInlineDirection()). For now we are using the parent for quirks 2252 // mode and the containing block for strict mode. 2253 2254 // FIXME 2: Should we still deal with these the cases of 'left' or 'right' having 2255 // the type 'static' in determining whether to calculate the static distance? 2256 // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1. 2257 2258 // FIXME 3: Can perhaps optimize out cases when max-width/min-width are greater 2259 // than or less than the computed width(). Be careful of box-sizing and 2260 // percentage issues. 2261 2262 // The following is based off of the W3C Working Draft from April 11, 2006 of 2263 // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements" 2264 // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width> 2265 // (block-style-comments in this function and in computePositionedLogicalWidthUsing() 2266 // correspond to text from the spec) 2267 2268 2269 // We don't use containingBlock(), since we may be positioned by an enclosing 2270 // relative positioned inline. 2271 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 2272 2273 const int containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); 2274 2275 // To match WinIE, in quirks mode use the parent's 'direction' property 2276 // instead of the the container block's. 2277 TextDirection containerDirection = (document()->inQuirksMode()) ? parent()->style()->direction() : containerBlock->style()->direction(); 2278 2279 bool isHorizontal = isHorizontalWritingMode(); 2280 const int bordersPlusPadding = borderAndPaddingLogicalWidth(); 2281 const Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop(); 2282 const Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom(); 2283 int& marginLogicalLeftAlias = isHorizontal ? m_marginLeft : m_marginTop; 2284 int& marginLogicalRightAlias = isHorizontal ? m_marginRight : m_marginBottom; 2285 2286 Length logicalLeft = style()->logicalLeft(); 2287 Length logicalRight = style()->logicalRight(); 2288 2289 /*---------------------------------------------------------------------------*\ 2290 * For the purposes of this section and the next, the term "static position" 2291 * (of an element) refers, roughly, to the position an element would have had 2292 * in the normal flow. More precisely: 2293 * 2294 * * The static position for 'left' is the distance from the left edge of the 2295 * containing block to the left margin edge of a hypothetical box that would 2296 * have been the first box of the element if its 'position' property had 2297 * been 'static' and 'float' had been 'none'. The value is negative if the 2298 * hypothetical box is to the left of the containing block. 2299 * * The static position for 'right' is the distance from the right edge of the 2300 * containing block to the right margin edge of the same hypothetical box as 2301 * above. The value is positive if the hypothetical box is to the left of the 2302 * containing block's edge. 2303 * 2304 * But rather than actually calculating the dimensions of that hypothetical box, 2305 * user agents are free to make a guess at its probable position. 2306 * 2307 * For the purposes of calculating the static position, the containing block of 2308 * fixed positioned elements is the initial containing block instead of the 2309 * viewport, and all scrollable boxes should be assumed to be scrolled to their 2310 * origin. 2311 \*---------------------------------------------------------------------------*/ 2312 2313 // see FIXME 2 2314 // Calculate the static distance if needed. 2315 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth, containerDirection); 2316 2317 // Calculate constraint equation values for 'width' case. 2318 int logicalWidthResult; 2319 int logicalLeftResult; 2320 computePositionedLogicalWidthUsing(style()->logicalWidth(), containerBlock, containerDirection, 2321 containerLogicalWidth, bordersPlusPadding, 2322 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight, 2323 logicalWidthResult, marginLogicalLeftAlias, marginLogicalRightAlias, logicalLeftResult); 2324 setLogicalWidth(logicalWidthResult); 2325 setLogicalLeft(logicalLeftResult); 2326 2327 // Calculate constraint equation values for 'max-width' case. 2328 if (!style()->logicalMaxWidth().isUndefined()) { 2329 int maxLogicalWidth; 2330 int maxMarginLogicalLeft; 2331 int maxMarginLogicalRight; 2332 int maxLogicalLeftPos; 2333 2334 computePositionedLogicalWidthUsing(style()->logicalMaxWidth(), containerBlock, containerDirection, 2335 containerLogicalWidth, bordersPlusPadding, 2336 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight, 2337 maxLogicalWidth, maxMarginLogicalLeft, maxMarginLogicalRight, maxLogicalLeftPos); 2338 2339 if (logicalWidth() > maxLogicalWidth) { 2340 setLogicalWidth(maxLogicalWidth); 2341 marginLogicalLeftAlias = maxMarginLogicalLeft; 2342 marginLogicalRightAlias = maxMarginLogicalRight; 2343 setLogicalLeft(maxLogicalLeftPos); 2344 } 2345 } 2346 2347 // Calculate constraint equation values for 'min-width' case. 2348 if (!style()->logicalMinWidth().isZero()) { 2349 int minLogicalWidth; 2350 int minMarginLogicalLeft; 2351 int minMarginLogicalRight; 2352 int minLogicalLeftPos; 2353 2354 computePositionedLogicalWidthUsing(style()->logicalMinWidth(), containerBlock, containerDirection, 2355 containerLogicalWidth, bordersPlusPadding, 2356 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight, 2357 minLogicalWidth, minMarginLogicalLeft, minMarginLogicalRight, minLogicalLeftPos); 2358 2359 if (logicalWidth() < minLogicalWidth) { 2360 setLogicalWidth(minLogicalWidth); 2361 marginLogicalLeftAlias = minMarginLogicalLeft; 2362 marginLogicalRightAlias = minMarginLogicalRight; 2363 setLogicalLeft(minLogicalLeftPos); 2364 } 2365 } 2366 2367 if (stretchesToMinIntrinsicLogicalWidth() && logicalWidth() < minPreferredLogicalWidth() - bordersPlusPadding) { 2368 computePositionedLogicalWidthUsing(Length(minPreferredLogicalWidth() - bordersPlusPadding, Fixed), containerBlock, containerDirection, 2369 containerLogicalWidth, bordersPlusPadding, 2370 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight, 2371 logicalWidthResult, marginLogicalLeftAlias, marginLogicalRightAlias, logicalLeftResult); 2372 setLogicalWidth(logicalWidthResult); 2373 setLogicalLeft(logicalLeftResult); 2374 } 2375 2376 // Put logicalWidth() into correct form. 2377 setLogicalWidth(logicalWidth() + bordersPlusPadding); 2378 } 2379 2380 static void computeLogicalLeftPositionedOffset(int& logicalLeftPos, const RenderBox* child, int logicalWidthValue, const RenderBoxModelObject* containerBlock, int containerLogicalWidth) 2381 { 2382 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped 2383 // 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. 2384 if (containerBlock->isHorizontalWritingMode() != child->isHorizontalWritingMode() && containerBlock->style()->isFlippedBlocksWritingMode()) { 2385 logicalLeftPos = containerLogicalWidth - logicalWidthValue - logicalLeftPos; 2386 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderRight() : containerBlock->borderBottom()); 2387 } else 2388 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderLeft() : containerBlock->borderTop()); 2389 } 2390 2391 void RenderBox::computePositionedLogicalWidthUsing(Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection, 2392 int containerLogicalWidth, int bordersPlusPadding, 2393 Length logicalLeft, Length logicalRight, Length marginLogicalLeft, Length marginLogicalRight, 2394 int& logicalWidthValue, int& marginLogicalLeftValue, int& marginLogicalRightValue, int& logicalLeftPos) 2395 { 2396 // 'left' and 'right' cannot both be 'auto' because one would of been 2397 // converted to the static position already 2398 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto())); 2399 2400 int logicalLeftValue = 0; 2401 2402 bool logicalWidthIsAuto = logicalWidth.isIntrinsicOrAuto(); 2403 bool logicalLeftIsAuto = logicalLeft.isAuto(); 2404 bool logicalRightIsAuto = logicalRight.isAuto(); 2405 2406 if (!logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) { 2407 /*-----------------------------------------------------------------------*\ 2408 * If none of the three is 'auto': If both 'margin-left' and 'margin- 2409 * right' are 'auto', solve the equation under the extra constraint that 2410 * the two margins get equal values, unless this would make them negative, 2411 * in which case when direction of the containing block is 'ltr' ('rtl'), 2412 * set 'margin-left' ('margin-right') to zero and solve for 'margin-right' 2413 * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', 2414 * solve the equation for that value. If the values are over-constrained, 2415 * ignore the value for 'left' (in case the 'direction' property of the 2416 * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') 2417 * and solve for that value. 2418 \*-----------------------------------------------------------------------*/ 2419 // NOTE: It is not necessary to solve for 'right' in the over constrained 2420 // case because the value is not used for any further calculations. 2421 2422 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2423 logicalWidthValue = computeContentBoxLogicalWidth(logicalWidth.calcValue(containerLogicalWidth)); 2424 2425 const int availableSpace = containerLogicalWidth - (logicalLeftValue + logicalWidthValue + logicalRight.calcValue(containerLogicalWidth) + bordersPlusPadding); 2426 2427 // Margins are now the only unknown 2428 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) { 2429 // Both margins auto, solve for equality 2430 if (availableSpace >= 0) { 2431 marginLogicalLeftValue = availableSpace / 2; // split the difference 2432 marginLogicalRightValue = availableSpace - marginLogicalLeftValue; // account for odd valued differences 2433 } else { 2434 // see FIXME 1 2435 if (containerDirection == LTR) { 2436 marginLogicalLeftValue = 0; 2437 marginLogicalRightValue = availableSpace; // will be negative 2438 } else { 2439 marginLogicalLeftValue = availableSpace; // will be negative 2440 marginLogicalRightValue = 0; 2441 } 2442 } 2443 } else if (marginLogicalLeft.isAuto()) { 2444 // Solve for left margin 2445 marginLogicalRightValue = marginLogicalRight.calcValue(containerLogicalWidth); 2446 marginLogicalLeftValue = availableSpace - marginLogicalRightValue; 2447 } else if (marginLogicalRight.isAuto()) { 2448 // Solve for right margin 2449 marginLogicalLeftValue = marginLogicalLeft.calcValue(containerLogicalWidth); 2450 marginLogicalRightValue = availableSpace - marginLogicalLeftValue; 2451 } else { 2452 // Over-constrained, solve for left if direction is RTL 2453 marginLogicalLeftValue = marginLogicalLeft.calcValue(containerLogicalWidth); 2454 marginLogicalRightValue = marginLogicalRight.calcValue(containerLogicalWidth); 2455 2456 // see FIXME 1 -- used to be "this->style()->direction()" 2457 if (containerDirection == RTL) 2458 logicalLeftValue = (availableSpace + logicalLeftValue) - marginLogicalLeftValue - marginLogicalRightValue; 2459 } 2460 } else { 2461 /*--------------------------------------------------------------------*\ 2462 * Otherwise, set 'auto' values for 'margin-left' and 'margin-right' 2463 * to 0, and pick the one of the following six rules that applies. 2464 * 2465 * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the 2466 * width is shrink-to-fit. Then solve for 'left' 2467 * 2468 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT 2469 * ------------------------------------------------------------------ 2470 * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if 2471 * the 'direction' property of the containing block is 'ltr' set 2472 * 'left' to the static position, otherwise set 'right' to the 2473 * static position. Then solve for 'left' (if 'direction is 'rtl') 2474 * or 'right' (if 'direction' is 'ltr'). 2475 * ------------------------------------------------------------------ 2476 * 2477 * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the 2478 * width is shrink-to-fit . Then solve for 'right' 2479 * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve 2480 * for 'left' 2481 * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve 2482 * for 'width' 2483 * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve 2484 * for 'right' 2485 * 2486 * Calculation of the shrink-to-fit width is similar to calculating the 2487 * width of a table cell using the automatic table layout algorithm. 2488 * Roughly: calculate the preferred width by formatting the content 2489 * without breaking lines other than where explicit line breaks occur, 2490 * and also calculate the preferred minimum width, e.g., by trying all 2491 * possible line breaks. CSS 2.1 does not define the exact algorithm. 2492 * Thirdly, calculate the available width: this is found by solving 2493 * for 'width' after setting 'left' (in case 1) or 'right' (in case 3) 2494 * to 0. 2495 * 2496 * Then the shrink-to-fit width is: 2497 * min(max(preferred minimum width, available width), preferred width). 2498 \*--------------------------------------------------------------------*/ 2499 // NOTE: For rules 3 and 6 it is not necessary to solve for 'right' 2500 // because the value is not used for any further calculations. 2501 2502 // Calculate margins, 'auto' margins are ignored. 2503 marginLogicalLeftValue = marginLogicalLeft.calcMinValue(containerLogicalWidth); 2504 marginLogicalRightValue = marginLogicalRight.calcMinValue(containerLogicalWidth); 2505 2506 const int availableSpace = containerLogicalWidth - (marginLogicalLeftValue + marginLogicalRightValue + bordersPlusPadding); 2507 2508 // FIXME: Is there a faster way to find the correct case? 2509 // Use rule/case that applies. 2510 if (logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) { 2511 // RULE 1: (use shrink-to-fit for width, and solve of left) 2512 int logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2513 2514 // FIXME: would it be better to have shrink-to-fit in one step? 2515 int preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding; 2516 int preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding; 2517 int availableWidth = availableSpace - logicalRightValue; 2518 logicalWidthValue = min(max(preferredMinWidth, availableWidth), preferredWidth); 2519 logicalLeftValue = availableSpace - (logicalWidthValue + logicalRightValue); 2520 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && logicalRightIsAuto) { 2521 // RULE 3: (use shrink-to-fit for width, and no need solve of right) 2522 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2523 2524 // FIXME: would it be better to have shrink-to-fit in one step? 2525 int preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding; 2526 int preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding; 2527 int availableWidth = availableSpace - logicalLeftValue; 2528 logicalWidthValue = min(max(preferredMinWidth, availableWidth), preferredWidth); 2529 } else if (logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) { 2530 // RULE 4: (solve for left) 2531 logicalWidthValue = computeContentBoxLogicalWidth(logicalWidth.calcValue(containerLogicalWidth)); 2532 logicalLeftValue = availableSpace - (logicalWidthValue + logicalRight.calcValue(containerLogicalWidth)); 2533 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) { 2534 // RULE 5: (solve for width) 2535 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2536 logicalWidthValue = availableSpace - (logicalLeftValue + logicalRight.calcValue(containerLogicalWidth)); 2537 } else if (!logicalLeftIsAuto && !logicalWidthIsAuto && logicalRightIsAuto) { 2538 // RULE 6: (no need solve for right) 2539 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2540 logicalWidthValue = computeContentBoxLogicalWidth(logicalWidth.calcValue(containerLogicalWidth)); 2541 } 2542 } 2543 2544 // Use computed values to calculate the horizontal position. 2545 2546 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively 2547 // positioned, inline because right now, it is using the logical left position 2548 // of the first line box when really it should use the last line box. When 2549 // this is fixed elsewhere, this block should be removed. 2550 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) { 2551 const RenderInline* flow = toRenderInline(containerBlock); 2552 InlineFlowBox* firstLine = flow->firstLineBox(); 2553 InlineFlowBox* lastLine = flow->lastLineBox(); 2554 if (firstLine && lastLine && firstLine != lastLine) { 2555 logicalLeftPos = logicalLeftValue + marginLogicalLeftValue + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft()); 2556 return; 2557 } 2558 } 2559 2560 logicalLeftPos = logicalLeftValue + marginLogicalLeftValue; 2561 computeLogicalLeftPositionedOffset(logicalLeftPos, this, logicalWidthValue, containerBlock, containerLogicalWidth); 2562 } 2563 2564 static void computeBlockStaticDistance(Length& logicalTop, Length& logicalBottom, const RenderBox* child, const RenderBoxModelObject* containerBlock) 2565 { 2566 if (!logicalTop.isAuto() || !logicalBottom.isAuto()) 2567 return; 2568 2569 // FIXME: The static distance computation has not been patched for mixed writing modes. 2570 int staticLogicalTop = child->layer()->staticBlockPosition() - containerBlock->borderBefore(); 2571 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) { 2572 if (curr->isBox() && !curr->isTableRow()) 2573 staticLogicalTop += toRenderBox(curr)->logicalTop(); 2574 } 2575 logicalTop.setValue(Fixed, staticLogicalTop); 2576 } 2577 2578 void RenderBox::computePositionedLogicalHeight() 2579 { 2580 if (isReplaced()) { 2581 computePositionedLogicalHeightReplaced(); 2582 return; 2583 } 2584 2585 // The following is based off of the W3C Working Draft from April 11, 2006 of 2586 // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements" 2587 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height> 2588 // (block-style-comments in this function and in computePositionedLogicalHeightUsing() 2589 // correspond to text from the spec) 2590 2591 2592 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. 2593 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 2594 2595 const int containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); 2596 2597 bool isHorizontal = isHorizontalWritingMode(); 2598 bool isFlipped = style()->isFlippedBlocksWritingMode(); 2599 const int bordersPlusPadding = borderAndPaddingLogicalHeight(); 2600 const Length marginBefore = style()->marginBefore(); 2601 const Length marginAfter = style()->marginAfter(); 2602 int& marginBeforeAlias = isHorizontal ? (isFlipped ? m_marginBottom : m_marginTop) : (isFlipped ? m_marginRight: m_marginLeft); 2603 int& marginAfterAlias = isHorizontal ? (isFlipped ? m_marginTop : m_marginBottom) : (isFlipped ? m_marginLeft: m_marginRight); 2604 2605 Length logicalTop = style()->logicalTop(); 2606 Length logicalBottom = style()->logicalBottom(); 2607 2608 /*---------------------------------------------------------------------------*\ 2609 * For the purposes of this section and the next, the term "static position" 2610 * (of an element) refers, roughly, to the position an element would have had 2611 * in the normal flow. More precisely, the static position for 'top' is the 2612 * distance from the top edge of the containing block to the top margin edge 2613 * of a hypothetical box that would have been the first box of the element if 2614 * its 'position' property had been 'static' and 'float' had been 'none'. The 2615 * value is negative if the hypothetical box is above the containing block. 2616 * 2617 * But rather than actually calculating the dimensions of that hypothetical 2618 * box, user agents are free to make a guess at its probable position. 2619 * 2620 * For the purposes of calculating the static position, the containing block 2621 * of fixed positioned elements is the initial containing block instead of 2622 * the viewport. 2623 \*---------------------------------------------------------------------------*/ 2624 2625 // see FIXME 2 2626 // Calculate the static distance if needed. 2627 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock); 2628 2629 int logicalHeightResult; // Needed to compute overflow. 2630 int logicalTopPos; 2631 2632 // Calculate constraint equation values for 'height' case. 2633 computePositionedLogicalHeightUsing(style()->logicalHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, 2634 logicalTop, logicalBottom, marginBefore, marginAfter, 2635 logicalHeightResult, marginBeforeAlias, marginAfterAlias, logicalTopPos); 2636 setLogicalTop(logicalTopPos); 2637 2638 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults). 2639 // see FIXME 3 2640 2641 // Calculate constraint equation values for 'max-height' case. 2642 if (!style()->logicalMaxHeight().isUndefined()) { 2643 int maxLogicalHeight; 2644 int maxMarginBefore; 2645 int maxMarginAfter; 2646 int maxLogicalTopPos; 2647 2648 computePositionedLogicalHeightUsing(style()->logicalMaxHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, 2649 logicalTop, logicalBottom, marginBefore, marginAfter, 2650 maxLogicalHeight, maxMarginBefore, maxMarginAfter, maxLogicalTopPos); 2651 2652 if (logicalHeightResult > maxLogicalHeight) { 2653 logicalHeightResult = maxLogicalHeight; 2654 marginBeforeAlias = maxMarginBefore; 2655 marginAfterAlias = maxMarginAfter; 2656 setLogicalTop(maxLogicalTopPos); 2657 } 2658 } 2659 2660 // Calculate constraint equation values for 'min-height' case. 2661 if (!style()->logicalMinHeight().isZero()) { 2662 int minLogicalHeight; 2663 int minMarginBefore; 2664 int minMarginAfter; 2665 int minLogicalTopPos; 2666 2667 computePositionedLogicalHeightUsing(style()->logicalMinHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, 2668 logicalTop, logicalBottom, marginBefore, marginAfter, 2669 minLogicalHeight, minMarginBefore, minMarginAfter, minLogicalTopPos); 2670 2671 if (logicalHeightResult < minLogicalHeight) { 2672 logicalHeightResult = minLogicalHeight; 2673 marginBeforeAlias = minMarginBefore; 2674 marginAfterAlias = minMarginAfter; 2675 setLogicalTop(minLogicalTopPos); 2676 } 2677 } 2678 2679 // Set final height value. 2680 setLogicalHeight(logicalHeightResult + bordersPlusPadding); 2681 } 2682 2683 static void computeLogicalTopPositionedOffset(int& logicalTopPos, const RenderBox* child, int logicalHeightValue, const RenderBoxModelObject* containerBlock, int containerLogicalHeight) 2684 { 2685 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped 2686 // 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. 2687 if ((child->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() != containerBlock->isHorizontalWritingMode()) 2688 || (child->style()->isFlippedBlocksWritingMode() != containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode())) 2689 logicalTopPos = containerLogicalHeight - logicalHeightValue - logicalTopPos; 2690 2691 // Our offset is from the logical bottom edge in a flipped environment, e.g., right for vertical-rl and bottom for horizontal-bt. 2692 if (containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()) { 2693 if (child->isHorizontalWritingMode()) 2694 logicalTopPos += containerBlock->borderBottom(); 2695 else 2696 logicalTopPos += containerBlock->borderRight(); 2697 } else { 2698 if (child->isHorizontalWritingMode()) 2699 logicalTopPos += containerBlock->borderTop(); 2700 else 2701 logicalTopPos += containerBlock->borderLeft(); 2702 } 2703 } 2704 2705 void RenderBox::computePositionedLogicalHeightUsing(Length logicalHeightLength, const RenderBoxModelObject* containerBlock, 2706 int containerLogicalHeight, int bordersPlusPadding, 2707 Length logicalTop, Length logicalBottom, Length marginBefore, Length marginAfter, 2708 int& logicalHeightValue, int& marginBeforeValue, int& marginAfterValue, int& logicalTopPos) 2709 { 2710 // 'top' and 'bottom' cannot both be 'auto' because 'top would of been 2711 // converted to the static position in computePositionedLogicalHeight() 2712 ASSERT(!(logicalTop.isAuto() && logicalBottom.isAuto())); 2713 2714 int contentLogicalHeight = logicalHeight() - bordersPlusPadding; 2715 2716 int logicalTopValue = 0; 2717 2718 bool logicalHeightIsAuto = logicalHeightLength.isAuto(); 2719 bool logicalTopIsAuto = logicalTop.isAuto(); 2720 bool logicalBottomIsAuto = logicalBottom.isAuto(); 2721 2722 // Height is never unsolved for tables. 2723 if (isTable()) { 2724 logicalHeightLength.setValue(Fixed, contentLogicalHeight); 2725 logicalHeightIsAuto = false; 2726 } 2727 2728 if (!logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) { 2729 /*-----------------------------------------------------------------------*\ 2730 * If none of the three are 'auto': If both 'margin-top' and 'margin- 2731 * bottom' are 'auto', solve the equation under the extra constraint that 2732 * the two margins get equal values. If one of 'margin-top' or 'margin- 2733 * bottom' is 'auto', solve the equation for that value. If the values 2734 * are over-constrained, ignore the value for 'bottom' and solve for that 2735 * value. 2736 \*-----------------------------------------------------------------------*/ 2737 // NOTE: It is not necessary to solve for 'bottom' in the over constrained 2738 // case because the value is not used for any further calculations. 2739 2740 logicalHeightValue = computeContentBoxLogicalHeight(logicalHeightLength.calcValue(containerLogicalHeight)); 2741 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 2742 2743 const int availableSpace = containerLogicalHeight - (logicalTopValue + logicalHeightValue + logicalBottom.calcValue(containerLogicalHeight) + bordersPlusPadding); 2744 2745 // Margins are now the only unknown 2746 if (marginBefore.isAuto() && marginAfter.isAuto()) { 2747 // Both margins auto, solve for equality 2748 // NOTE: This may result in negative values. 2749 marginBeforeValue = availableSpace / 2; // split the difference 2750 marginAfterValue = availableSpace - marginBeforeValue; // account for odd valued differences 2751 } else if (marginBefore.isAuto()) { 2752 // Solve for top margin 2753 marginAfterValue = marginAfter.calcValue(containerLogicalHeight); 2754 marginBeforeValue = availableSpace - marginAfterValue; 2755 } else if (marginAfter.isAuto()) { 2756 // Solve for bottom margin 2757 marginBeforeValue = marginBefore.calcValue(containerLogicalHeight); 2758 marginAfterValue = availableSpace - marginBeforeValue; 2759 } else { 2760 // Over-constrained, (no need solve for bottom) 2761 marginBeforeValue = marginBefore.calcValue(containerLogicalHeight); 2762 marginAfterValue = marginAfter.calcValue(containerLogicalHeight); 2763 } 2764 } else { 2765 /*--------------------------------------------------------------------*\ 2766 * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom' 2767 * to 0, and pick the one of the following six rules that applies. 2768 * 2769 * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then 2770 * the height is based on the content, and solve for 'top'. 2771 * 2772 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT 2773 * ------------------------------------------------------------------ 2774 * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then 2775 * set 'top' to the static position, and solve for 'bottom'. 2776 * ------------------------------------------------------------------ 2777 * 2778 * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then 2779 * the height is based on the content, and solve for 'bottom'. 2780 * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and 2781 * solve for 'top'. 2782 * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and 2783 * solve for 'height'. 2784 * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and 2785 * solve for 'bottom'. 2786 \*--------------------------------------------------------------------*/ 2787 // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom' 2788 // because the value is not used for any further calculations. 2789 2790 // Calculate margins, 'auto' margins are ignored. 2791 marginBeforeValue = marginBefore.calcMinValue(containerLogicalHeight); 2792 marginAfterValue = marginAfter.calcMinValue(containerLogicalHeight); 2793 2794 const int availableSpace = containerLogicalHeight - (marginBeforeValue + marginAfterValue + bordersPlusPadding); 2795 2796 // Use rule/case that applies. 2797 if (logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) { 2798 // RULE 1: (height is content based, solve of top) 2799 logicalHeightValue = contentLogicalHeight; 2800 logicalTopValue = availableSpace - (logicalHeightValue + logicalBottom.calcValue(containerLogicalHeight)); 2801 } else if (!logicalTopIsAuto && logicalHeightIsAuto && logicalBottomIsAuto) { 2802 // RULE 3: (height is content based, no need solve of bottom) 2803 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 2804 logicalHeightValue = contentLogicalHeight; 2805 } else if (logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) { 2806 // RULE 4: (solve of top) 2807 logicalHeightValue = computeContentBoxLogicalHeight(logicalHeightLength.calcValue(containerLogicalHeight)); 2808 logicalTopValue = availableSpace - (logicalHeightValue + logicalBottom.calcValue(containerLogicalHeight)); 2809 } else if (!logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) { 2810 // RULE 5: (solve of height) 2811 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 2812 logicalHeightValue = max(0, availableSpace - (logicalTopValue + logicalBottom.calcValue(containerLogicalHeight))); 2813 } else if (!logicalTopIsAuto && !logicalHeightIsAuto && logicalBottomIsAuto) { 2814 // RULE 6: (no need solve of bottom) 2815 logicalHeightValue = computeContentBoxLogicalHeight(logicalHeightLength.calcValue(containerLogicalHeight)); 2816 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 2817 } 2818 } 2819 2820 // Use computed values to calculate the vertical position. 2821 logicalTopPos = logicalTopValue + marginBeforeValue; 2822 computeLogicalTopPositionedOffset(logicalTopPos, this, logicalHeightValue, containerBlock, containerLogicalHeight); 2823 } 2824 2825 void RenderBox::computePositionedLogicalWidthReplaced() 2826 { 2827 // The following is based off of the W3C Working Draft from April 11, 2006 of 2828 // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements" 2829 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width> 2830 // (block-style-comments in this function correspond to text from the spec and 2831 // the numbers correspond to numbers in spec) 2832 2833 // We don't use containingBlock(), since we may be positioned by an enclosing 2834 // relative positioned inline. 2835 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 2836 2837 const int containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); 2838 2839 // To match WinIE, in quirks mode use the parent's 'direction' property 2840 // instead of the the container block's. 2841 TextDirection containerDirection = (document()->inQuirksMode()) ? parent()->style()->direction() : containerBlock->style()->direction(); 2842 2843 // Variables to solve. 2844 bool isHorizontal = isHorizontalWritingMode(); 2845 Length logicalLeft = style()->logicalLeft(); 2846 Length logicalRight = style()->logicalRight(); 2847 Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop(); 2848 Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom(); 2849 int& marginLogicalLeftAlias = isHorizontal ? m_marginLeft : m_marginTop; 2850 int& marginLogicalRightAlias = isHorizontal ? m_marginRight : m_marginBottom; 2851 2852 /*-----------------------------------------------------------------------*\ 2853 * 1. The used value of 'width' is determined as for inline replaced 2854 * elements. 2855 \*-----------------------------------------------------------------------*/ 2856 // NOTE: This value of width is FINAL in that the min/max width calculations 2857 // are dealt with in computeReplacedWidth(). This means that the steps to produce 2858 // correct max/min in the non-replaced version, are not necessary. 2859 setLogicalWidth(computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth()); 2860 const int availableSpace = containerLogicalWidth - logicalWidth(); 2861 2862 /*-----------------------------------------------------------------------*\ 2863 * 2. If both 'left' and 'right' have the value 'auto', then if 'direction' 2864 * of the containing block is 'ltr', set 'left' to the static position; 2865 * else if 'direction' is 'rtl', set 'right' to the static position. 2866 \*-----------------------------------------------------------------------*/ 2867 // see FIXME 2 2868 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth, containerDirection); 2869 2870 /*-----------------------------------------------------------------------*\ 2871 * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' 2872 * or 'margin-right' with '0'. 2873 \*-----------------------------------------------------------------------*/ 2874 if (logicalLeft.isAuto() || logicalRight.isAuto()) { 2875 if (marginLogicalLeft.isAuto()) 2876 marginLogicalLeft.setValue(Fixed, 0); 2877 if (marginLogicalRight.isAuto()) 2878 marginLogicalRight.setValue(Fixed, 0); 2879 } 2880 2881 /*-----------------------------------------------------------------------*\ 2882 * 4. If at this point both 'margin-left' and 'margin-right' are still 2883 * 'auto', solve the equation under the extra constraint that the two 2884 * margins must get equal values, unless this would make them negative, 2885 * in which case when the direction of the containing block is 'ltr' 2886 * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for 2887 * 'margin-right' ('margin-left'). 2888 \*-----------------------------------------------------------------------*/ 2889 int logicalLeftValue = 0; 2890 int logicalRightValue = 0; 2891 2892 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) { 2893 // 'left' and 'right' cannot be 'auto' due to step 3 2894 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto())); 2895 2896 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2897 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2898 2899 int difference = availableSpace - (logicalLeftValue + logicalRightValue); 2900 if (difference > 0) { 2901 marginLogicalLeftAlias = difference / 2; // split the difference 2902 marginLogicalRightAlias = difference - marginLogicalLeftAlias; // account for odd valued differences 2903 } else { 2904 // see FIXME 1 2905 if (containerDirection == LTR) { 2906 marginLogicalLeftAlias = 0; 2907 marginLogicalRightAlias = difference; // will be negative 2908 } else { 2909 marginLogicalLeftAlias = difference; // will be negative 2910 marginLogicalRightAlias = 0; 2911 } 2912 } 2913 2914 /*-----------------------------------------------------------------------*\ 2915 * 5. If at this point there is an 'auto' left, solve the equation for 2916 * that value. 2917 \*-----------------------------------------------------------------------*/ 2918 } else if (logicalLeft.isAuto()) { 2919 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth); 2920 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth); 2921 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2922 2923 // Solve for 'left' 2924 logicalLeftValue = availableSpace - (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias); 2925 } else if (logicalRight.isAuto()) { 2926 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth); 2927 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth); 2928 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2929 2930 // Solve for 'right' 2931 logicalRightValue = availableSpace - (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias); 2932 } else if (marginLogicalLeft.isAuto()) { 2933 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth); 2934 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2935 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2936 2937 // Solve for 'margin-left' 2938 marginLogicalLeftAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalRightAlias); 2939 } else if (marginLogicalRight.isAuto()) { 2940 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth); 2941 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2942 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2943 2944 // Solve for 'margin-right' 2945 marginLogicalRightAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias); 2946 } else { 2947 // Nothing is 'auto', just calculate the values. 2948 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth); 2949 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth); 2950 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2951 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2952 } 2953 2954 /*-----------------------------------------------------------------------*\ 2955 * 6. If at this point the values are over-constrained, ignore the value 2956 * for either 'left' (in case the 'direction' property of the 2957 * containing block is 'rtl') or 'right' (in case 'direction' is 2958 * 'ltr') and solve for that value. 2959 \*-----------------------------------------------------------------------*/ 2960 // NOTE: It is not necessary to solve for 'right' when the direction is 2961 // LTR because the value is not used. 2962 int totalLogicalWidth = logicalWidth() + logicalLeftValue + logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias; 2963 if (totalLogicalWidth > containerLogicalWidth && (containerDirection == RTL)) 2964 logicalLeftValue = containerLogicalWidth - (totalLogicalWidth - logicalLeftValue); 2965 2966 // FIXME: Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space, so that 2967 // can make the result here rather complicated to compute. 2968 2969 // Use computed values to calculate the horizontal position. 2970 2971 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively 2972 // positioned, inline containing block because right now, it is using the logical left position 2973 // of the first line box when really it should use the last line box. When 2974 // this is fixed elsewhere, this block should be removed. 2975 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) { 2976 const RenderInline* flow = toRenderInline(containerBlock); 2977 InlineFlowBox* firstLine = flow->firstLineBox(); 2978 InlineFlowBox* lastLine = flow->lastLineBox(); 2979 if (firstLine && lastLine && firstLine != lastLine) { 2980 setLogicalLeft(logicalLeftValue + marginLogicalLeftAlias + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft())); 2981 return; 2982 } 2983 } 2984 2985 int logicalLeftPos = logicalLeftValue + marginLogicalLeftAlias; 2986 computeLogicalLeftPositionedOffset(logicalLeftPos, this, logicalWidth(), containerBlock, containerLogicalWidth); 2987 setLogicalLeft(logicalLeftPos); 2988 } 2989 2990 void RenderBox::computePositionedLogicalHeightReplaced() 2991 { 2992 // The following is based off of the W3C Working Draft from April 11, 2006 of 2993 // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements" 2994 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height> 2995 // (block-style-comments in this function correspond to text from the spec and 2996 // the numbers correspond to numbers in spec) 2997 2998 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. 2999 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 3000 3001 const int containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); 3002 3003 // Variables to solve. 3004 bool isHorizontal = isHorizontalWritingMode(); 3005 bool isFlipped = style()->isFlippedBlocksWritingMode(); 3006 Length marginBefore = style()->marginBefore(); 3007 Length marginAfter = style()->marginAfter(); 3008 int& marginBeforeAlias = isHorizontal ? (isFlipped ? m_marginBottom : m_marginTop) : (isFlipped ? m_marginRight: m_marginLeft); 3009 int& marginAfterAlias = isHorizontal ? (isFlipped ? m_marginTop : m_marginBottom) : (isFlipped ? m_marginLeft: m_marginRight); 3010 3011 Length logicalTop = style()->logicalTop(); 3012 Length logicalBottom = style()->logicalBottom(); 3013 3014 /*-----------------------------------------------------------------------*\ 3015 * 1. The used value of 'height' is determined as for inline replaced 3016 * elements. 3017 \*-----------------------------------------------------------------------*/ 3018 // NOTE: This value of height is FINAL in that the min/max height calculations 3019 // are dealt with in computeReplacedHeight(). This means that the steps to produce 3020 // correct max/min in the non-replaced version, are not necessary. 3021 setLogicalHeight(computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight()); 3022 const int availableSpace = containerLogicalHeight - logicalHeight(); 3023 3024 /*-----------------------------------------------------------------------*\ 3025 * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top' 3026 * with the element's static position. 3027 \*-----------------------------------------------------------------------*/ 3028 // see FIXME 2 3029 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock); 3030 3031 /*-----------------------------------------------------------------------*\ 3032 * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or 3033 * 'margin-bottom' with '0'. 3034 \*-----------------------------------------------------------------------*/ 3035 // FIXME: The spec. says that this step should only be taken when bottom is 3036 // auto, but if only top is auto, this makes step 4 impossible. 3037 if (logicalTop.isAuto() || logicalBottom.isAuto()) { 3038 if (marginBefore.isAuto()) 3039 marginBefore.setValue(Fixed, 0); 3040 if (marginAfter.isAuto()) 3041 marginAfter.setValue(Fixed, 0); 3042 } 3043 3044 /*-----------------------------------------------------------------------*\ 3045 * 4. If at this point both 'margin-top' and 'margin-bottom' are still 3046 * 'auto', solve the equation under the extra constraint that the two 3047 * margins must get equal values. 3048 \*-----------------------------------------------------------------------*/ 3049 int logicalTopValue = 0; 3050 int logicalBottomValue = 0; 3051 3052 if (marginBefore.isAuto() && marginAfter.isAuto()) { 3053 // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined. 3054 ASSERT(!(logicalTop.isAuto() || logicalBottom.isAuto())); 3055 3056 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3057 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight); 3058 3059 int difference = availableSpace - (logicalTopValue + logicalBottomValue); 3060 // NOTE: This may result in negative values. 3061 marginBeforeAlias = difference / 2; // split the difference 3062 marginAfterAlias = difference - marginBeforeAlias; // account for odd valued differences 3063 3064 /*-----------------------------------------------------------------------*\ 3065 * 5. If at this point there is only one 'auto' left, solve the equation 3066 * for that value. 3067 \*-----------------------------------------------------------------------*/ 3068 } else if (logicalTop.isAuto()) { 3069 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight); 3070 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight); 3071 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight); 3072 3073 // Solve for 'top' 3074 logicalTopValue = availableSpace - (logicalBottomValue + marginBeforeAlias + marginAfterAlias); 3075 } else if (logicalBottom.isAuto()) { 3076 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight); 3077 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight); 3078 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3079 3080 // Solve for 'bottom' 3081 // NOTE: It is not necessary to solve for 'bottom' because we don't ever 3082 // use the value. 3083 } else if (marginBefore.isAuto()) { 3084 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight); 3085 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3086 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight); 3087 3088 // Solve for 'margin-top' 3089 marginBeforeAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginAfterAlias); 3090 } else if (marginAfter.isAuto()) { 3091 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight); 3092 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3093 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight); 3094 3095 // Solve for 'margin-bottom' 3096 marginAfterAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginBeforeAlias); 3097 } else { 3098 // Nothing is 'auto', just calculate the values. 3099 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight); 3100 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight); 3101 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3102 // NOTE: It is not necessary to solve for 'bottom' because we don't ever 3103 // use the value. 3104 } 3105 3106 /*-----------------------------------------------------------------------*\ 3107 * 6. If at this point the values are over-constrained, ignore the value 3108 * for 'bottom' and solve for that value. 3109 \*-----------------------------------------------------------------------*/ 3110 // NOTE: It is not necessary to do this step because we don't end up using 3111 // the value of 'bottom' regardless of whether the values are over-constrained 3112 // or not. 3113 3114 // Use computed values to calculate the vertical position. 3115 int logicalTopPos = logicalTopValue + marginBeforeAlias; 3116 computeLogicalTopPositionedOffset(logicalTopPos, this, logicalHeight(), containerBlock, containerLogicalHeight); 3117 setLogicalTop(logicalTopPos); 3118 } 3119 3120 IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWidthToEndOfLine) 3121 { 3122 // VisiblePositions at offsets inside containers either a) refer to the positions before/after 3123 // those containers (tables and select elements) or b) refer to the position inside an empty block. 3124 // They never refer to children. 3125 // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements. 3126 3127 // FIXME: What about border and padding? 3128 IntRect rect(x(), y(), caretWidth, height()); 3129 bool ltr = box ? box->isLeftToRightDirection() : style()->isLeftToRightDirection(); 3130 3131 if ((!caretOffset) ^ ltr) 3132 rect.move(IntSize(width() - caretWidth, 0)); 3133 3134 if (box) { 3135 RootInlineBox* rootBox = box->root(); 3136 int top = rootBox->lineTop(); 3137 rect.setY(top); 3138 rect.setHeight(rootBox->lineBottom() - top); 3139 } 3140 3141 // If height of box is smaller than font height, use the latter one, 3142 // otherwise the caret might become invisible. 3143 // 3144 // Also, if the box is not a replaced element, always use the font height. 3145 // This prevents the "big caret" bug described in: 3146 // <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point 3147 // 3148 // FIXME: ignoring :first-line, missing good reason to take care of 3149 int fontHeight = style()->fontMetrics().height(); 3150 if (fontHeight > rect.height() || (!isReplaced() && !isTable())) 3151 rect.setHeight(fontHeight); 3152 3153 if (extraWidthToEndOfLine) 3154 *extraWidthToEndOfLine = x() + width() - rect.maxX(); 3155 3156 // Move to local coords 3157 rect.move(-x(), -y()); 3158 return rect; 3159 } 3160 3161 VisiblePosition RenderBox::positionForPoint(const IntPoint& point) 3162 { 3163 // no children...return this render object's element, if there is one, and offset 0 3164 if (!firstChild()) 3165 return createVisiblePosition(node() ? firstPositionInOrBeforeNode(node()) : Position(0, 0)); 3166 3167 int xPos = point.x(); 3168 int yPos = point.y(); 3169 3170 if (isTable() && node()) { 3171 int right = contentWidth() + borderAndPaddingWidth(); 3172 int bottom = contentHeight() + borderAndPaddingHeight(); 3173 3174 if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) { 3175 if (xPos <= right / 2) 3176 return createVisiblePosition(firstPositionInOrBeforeNode(node())); 3177 return createVisiblePosition(lastPositionInOrAfterNode(node())); 3178 } 3179 } 3180 3181 // Pass off to the closest child. 3182 int minDist = INT_MAX; 3183 RenderBox* closestRenderer = 0; 3184 int newX = xPos; 3185 int newY = yPos; 3186 if (isTableRow()) { 3187 newX += x(); 3188 newY += y(); 3189 } 3190 for (RenderObject* renderObject = firstChild(); renderObject; renderObject = renderObject->nextSibling()) { 3191 if ((!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isBlockFlow() ) 3192 || renderObject->style()->visibility() != VISIBLE) 3193 continue; 3194 3195 if (!renderObject->isBox()) 3196 continue; 3197 3198 RenderBox* renderer = toRenderBox(renderObject); 3199 3200 int top = renderer->borderTop() + renderer->paddingTop() + (isTableRow() ? 0 : renderer->y()); 3201 int bottom = top + renderer->contentHeight(); 3202 int left = renderer->borderLeft() + renderer->paddingLeft() + (isTableRow() ? 0 : renderer->x()); 3203 int right = left + renderer->contentWidth(); 3204 3205 if (xPos <= right && xPos >= left && yPos <= top && yPos >= bottom) { 3206 if (renderer->isTableRow()) 3207 return renderer->positionForCoordinates(xPos + newX - renderer->x(), yPos + newY - renderer->y()); 3208 return renderer->positionForCoordinates(xPos - renderer->x(), yPos - renderer->y()); 3209 } 3210 3211 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces 3212 // and use a different compare depending on which piece (x, y) is in. 3213 IntPoint cmp; 3214 if (xPos > right) { 3215 if (yPos < top) 3216 cmp = IntPoint(right, top); 3217 else if (yPos > bottom) 3218 cmp = IntPoint(right, bottom); 3219 else 3220 cmp = IntPoint(right, yPos); 3221 } else if (xPos < left) { 3222 if (yPos < top) 3223 cmp = IntPoint(left, top); 3224 else if (yPos > bottom) 3225 cmp = IntPoint(left, bottom); 3226 else 3227 cmp = IntPoint(left, yPos); 3228 } else { 3229 if (yPos < top) 3230 cmp = IntPoint(xPos, top); 3231 else 3232 cmp = IntPoint(xPos, bottom); 3233 } 3234 3235 int x1minusx2 = cmp.x() - xPos; 3236 int y1minusy2 = cmp.y() - yPos; 3237 3238 int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2; 3239 if (dist < minDist) { 3240 closestRenderer = renderer; 3241 minDist = dist; 3242 } 3243 } 3244 3245 if (closestRenderer) 3246 return closestRenderer->positionForCoordinates(newX - closestRenderer->x(), newY - closestRenderer->y()); 3247 3248 return createVisiblePosition(firstPositionInOrBeforeNode(node())); 3249 } 3250 3251 bool RenderBox::shrinkToAvoidFloats() const 3252 { 3253 // Floating objects don't shrink. Objects that don't avoid floats don't shrink. Marquees don't shrink. 3254 if ((isInline() && !isHTMLMarquee()) || !avoidsFloats() || isFloating()) 3255 return false; 3256 3257 // All auto-width objects that avoid floats should always use lineWidth. 3258 return style()->width().isAuto(); 3259 } 3260 3261 bool RenderBox::avoidsFloats() const 3262 { 3263 return isReplaced() || hasOverflowClip() || isHR() || isLegend() || isWritingModeRoot(); 3264 } 3265 3266 void RenderBox::addShadowOverflow() 3267 { 3268 int shadowLeft; 3269 int shadowRight; 3270 int shadowTop; 3271 int shadowBottom; 3272 style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft); 3273 IntRect borderBox = borderBoxRect(); 3274 int overflowLeft = borderBox.x() + shadowLeft; 3275 int overflowRight = borderBox.maxX() + shadowRight; 3276 int overflowTop = borderBox.y() + shadowTop; 3277 int overflowBottom = borderBox.maxY() + shadowBottom; 3278 addVisualOverflow(IntRect(overflowLeft, overflowTop, overflowRight - overflowLeft, overflowBottom - overflowTop)); 3279 } 3280 3281 void RenderBox::addOverflowFromChild(RenderBox* child, const IntSize& delta) 3282 { 3283 // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then 3284 // its overflow is internal to it, and we don't care about it. layoutOverflowRectForPropagation takes care of this 3285 // and just propagates the border box rect instead. 3286 IntRect childLayoutOverflowRect = child->layoutOverflowRectForPropagation(style()); 3287 childLayoutOverflowRect.move(delta); 3288 addLayoutOverflow(childLayoutOverflowRect); 3289 3290 // Add in visual overflow from the child. Even if the child clips its overflow, it may still 3291 // have visual overflow of its own set from box shadows or reflections. It is unnecessary to propagate this 3292 // overflow if we are clipping our own overflow. 3293 if (child->hasSelfPaintingLayer() || hasOverflowClip()) 3294 return; 3295 IntRect childVisualOverflowRect = child->visualOverflowRectForPropagation(style()); 3296 childVisualOverflowRect.move(delta); 3297 addVisualOverflow(childVisualOverflowRect); 3298 } 3299 3300 void RenderBox::addLayoutOverflow(const IntRect& rect) 3301 { 3302 IntRect clientBox = clientBoxRect(); 3303 if (clientBox.contains(rect) || rect.isEmpty()) 3304 return; 3305 3306 // For overflow clip objects, we don't want to propagate overflow into unreachable areas. 3307 IntRect overflowRect(rect); 3308 if (hasOverflowClip() || isRenderView()) { 3309 // Overflow is in the block's coordinate space and thus is flipped for horizontal-bt and vertical-rl 3310 // writing modes. At this stage that is actually a simplification, since we can treat horizontal-tb/bt as the same 3311 // and vertical-lr/rl as the same. 3312 bool hasTopOverflow = !style()->isLeftToRightDirection() && !isHorizontalWritingMode(); 3313 bool hasLeftOverflow = !style()->isLeftToRightDirection() && isHorizontalWritingMode(); 3314 3315 if (!hasTopOverflow) 3316 overflowRect.shiftYEdgeTo(max(overflowRect.y(), clientBox.y())); 3317 else 3318 overflowRect.shiftMaxYEdgeTo(min(overflowRect.maxY(), clientBox.maxY())); 3319 if (!hasLeftOverflow) 3320 overflowRect.shiftXEdgeTo(max(overflowRect.x(), clientBox.x())); 3321 else 3322 overflowRect.shiftMaxXEdgeTo(min(overflowRect.maxX(), clientBox.maxX())); 3323 3324 // Now re-test with the adjusted rectangle and see if it has become unreachable or fully 3325 // contained. 3326 if (clientBox.contains(overflowRect) || overflowRect.isEmpty()) 3327 return; 3328 } 3329 3330 if (!m_overflow) 3331 m_overflow.set(new RenderOverflow(clientBox, borderBoxRect())); 3332 3333 m_overflow->addLayoutOverflow(overflowRect); 3334 } 3335 3336 void RenderBox::addVisualOverflow(const IntRect& rect) 3337 { 3338 IntRect borderBox = borderBoxRect(); 3339 if (borderBox.contains(rect) || rect.isEmpty()) 3340 return; 3341 3342 if (!m_overflow) 3343 m_overflow.set(new RenderOverflow(clientBoxRect(), borderBox)); 3344 3345 m_overflow->addVisualOverflow(rect); 3346 } 3347 3348 void RenderBox::clearLayoutOverflow() 3349 { 3350 if (!m_overflow) 3351 return; 3352 3353 if (visualOverflowRect() == borderBoxRect()) { 3354 m_overflow.clear(); 3355 return; 3356 } 3357 3358 m_overflow->resetLayoutOverflow(borderBoxRect()); 3359 } 3360 3361 int RenderBox::lineHeight(bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const 3362 { 3363 if (isReplaced()) 3364 return direction == HorizontalLine ? m_marginTop + height() + m_marginBottom : m_marginRight + width() + m_marginLeft; 3365 return 0; 3366 } 3367 3368 int RenderBox::baselinePosition(FontBaseline baselineType, bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const 3369 { 3370 if (isReplaced()) { 3371 int result = direction == HorizontalLine ? m_marginTop + height() + m_marginBottom : m_marginRight + width() + m_marginLeft; 3372 if (baselineType == AlphabeticBaseline) 3373 return result; 3374 return result - result / 2; 3375 } 3376 return 0; 3377 } 3378 3379 3380 RenderLayer* RenderBox::enclosingFloatPaintingLayer() const 3381 { 3382 const RenderObject* curr = this; 3383 while (curr) { 3384 RenderLayer* layer = curr->hasLayer() && curr->isBox() ? toRenderBoxModelObject(curr)->layer() : 0; 3385 if (layer && layer->isSelfPaintingLayer()) 3386 return layer; 3387 curr = curr->parent(); 3388 } 3389 return 0; 3390 } 3391 3392 IntRect RenderBox::logicalVisualOverflowRectForPropagation(RenderStyle* parentStyle) const 3393 { 3394 IntRect rect = visualOverflowRectForPropagation(parentStyle); 3395 if (!parentStyle->isHorizontalWritingMode()) 3396 return rect.transposedRect(); 3397 return rect; 3398 } 3399 3400 IntRect RenderBox::visualOverflowRectForPropagation(RenderStyle* parentStyle) const 3401 { 3402 // If the writing modes of the child and parent match, then we don't have to 3403 // do anything fancy. Just return the result. 3404 IntRect rect = visualOverflowRect(); 3405 if (parentStyle->writingMode() == style()->writingMode()) 3406 return rect; 3407 3408 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch 3409 // in a particular axis, then we have to flip the rect along that axis. 3410 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode) 3411 rect.setX(width() - rect.maxX()); 3412 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode) 3413 rect.setY(height() - rect.maxY()); 3414 3415 return rect; 3416 } 3417 3418 IntRect RenderBox::logicalLayoutOverflowRectForPropagation(RenderStyle* parentStyle) const 3419 { 3420 IntRect rect = layoutOverflowRectForPropagation(parentStyle); 3421 if (!parentStyle->isHorizontalWritingMode()) 3422 return rect.transposedRect(); 3423 return rect; 3424 } 3425 3426 IntRect RenderBox::layoutOverflowRectForPropagation(RenderStyle* parentStyle) const 3427 { 3428 // Only propagate interior layout overflow if we don't clip it. 3429 IntRect rect = borderBoxRect(); 3430 if (!hasOverflowClip()) 3431 rect.unite(layoutOverflowRect()); 3432 3433 bool hasTransform = hasLayer() && layer()->transform(); 3434 if (isRelPositioned() || hasTransform) { 3435 // If we are relatively positioned or if we have a transform, then we have to convert 3436 // this rectangle into physical coordinates, apply relative positioning and transforms 3437 // to it, and then convert it back. 3438 flipForWritingMode(rect); 3439 3440 if (hasTransform) 3441 rect = layer()->currentTransform().mapRect(rect); 3442 3443 if (isRelPositioned()) 3444 rect.move(relativePositionOffsetX(), relativePositionOffsetY()); 3445 3446 // Now we need to flip back. 3447 flipForWritingMode(rect); 3448 } 3449 3450 // If the writing modes of the child and parent match, then we don't have to 3451 // do anything fancy. Just return the result. 3452 if (parentStyle->writingMode() == style()->writingMode()) 3453 return rect; 3454 3455 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch 3456 // in a particular axis, then we have to flip the rect along that axis. 3457 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode) 3458 rect.setX(width() - rect.maxX()); 3459 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode) 3460 rect.setY(height() - rect.maxY()); 3461 3462 return rect; 3463 } 3464 3465 IntPoint RenderBox::flipForWritingMode(const RenderBox* child, const IntPoint& point, FlippingAdjustment adjustment) const 3466 { 3467 if (!style()->isFlippedBlocksWritingMode()) 3468 return point; 3469 3470 // The child is going to add in its x() and y(), so we have to make sure it ends up in 3471 // the right place. 3472 if (isHorizontalWritingMode()) 3473 return IntPoint(point.x(), point.y() + height() - child->height() - child->y() - (adjustment == ParentToChildFlippingAdjustment ? child->y() : 0)); 3474 return IntPoint(point.x() + width() - child->width() - child->x() - (adjustment == ParentToChildFlippingAdjustment ? child->x() : 0), point.y()); 3475 } 3476 3477 void RenderBox::flipForWritingMode(IntRect& rect) const 3478 { 3479 if (!style()->isFlippedBlocksWritingMode()) 3480 return; 3481 3482 if (isHorizontalWritingMode()) 3483 rect.setY(height() - rect.maxY()); 3484 else 3485 rect.setX(width() - rect.maxX()); 3486 } 3487 3488 int RenderBox::flipForWritingMode(int position) const 3489 { 3490 if (!style()->isFlippedBlocksWritingMode()) 3491 return position; 3492 return logicalHeight() - position; 3493 } 3494 3495 IntPoint RenderBox::flipForWritingMode(const IntPoint& position) const 3496 { 3497 if (!style()->isFlippedBlocksWritingMode()) 3498 return position; 3499 return isHorizontalWritingMode() ? IntPoint(position.x(), height() - position.y()) : IntPoint(width() - position.x(), position.y()); 3500 } 3501 3502 IntPoint RenderBox::flipForWritingModeIncludingColumns(const IntPoint& point) const 3503 { 3504 if (!hasColumns() || !style()->isFlippedBlocksWritingMode()) 3505 return flipForWritingMode(point); 3506 return toRenderBlock(this)->flipForWritingModeIncludingColumns(point); 3507 } 3508 3509 IntSize RenderBox::flipForWritingMode(const IntSize& offset) const 3510 { 3511 if (!style()->isFlippedBlocksWritingMode()) 3512 return offset; 3513 return isHorizontalWritingMode() ? IntSize(offset.width(), height() - offset.height()) : IntSize(width() - offset.width(), offset.height()); 3514 } 3515 3516 FloatPoint RenderBox::flipForWritingMode(const FloatPoint& position) const 3517 { 3518 if (!style()->isFlippedBlocksWritingMode()) 3519 return position; 3520 return isHorizontalWritingMode() ? FloatPoint(position.x(), height() - position.y()) : FloatPoint(width() - position.x(), position.y()); 3521 } 3522 3523 void RenderBox::flipForWritingMode(FloatRect& rect) const 3524 { 3525 if (!style()->isFlippedBlocksWritingMode()) 3526 return; 3527 3528 if (isHorizontalWritingMode()) 3529 rect.setY(height() - rect.maxY()); 3530 else 3531 rect.setX(width() - rect.maxX()); 3532 } 3533 3534 IntSize RenderBox::locationOffsetIncludingFlipping() const 3535 { 3536 RenderBlock* containerBlock = containingBlock(); 3537 if (!containerBlock || containerBlock == this) 3538 return locationOffset(); 3539 3540 IntRect rect(frameRect()); 3541 containerBlock->flipForWritingMode(rect); // FIXME: This is wrong if we are an absolutely positioned object enclosed by a relative-positioned inline. 3542 return IntSize(rect.x(), rect.y()); 3543 } 3544 3545 } // namespace WebCore 3546