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 #if PLATFORM(ANDROID) 847 // If we only want to draw the decorations, don't draw 848 // the background 849 if (paintInfo.phase != PaintPhaseBlockBackgroundDecorations) 850 #endif 851 paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), tx, ty, width, height); 852 } 853 if (style()->hasAppearance()) 854 theme()->paintDecorations(this, paintInfo, IntRect(tx, ty, width, height)); 855 } 856 paintBoxShadow(paintInfo.context, tx, ty, width, height, style(), Inset); 857 858 // The theme will tell us whether or not we should also paint the CSS border. 859 if ((!style()->hasAppearance() || (!themePainted && theme()->paintBorderOnly(this, paintInfo, IntRect(tx, ty, width, height)))) && style()->hasBorder()) 860 paintBorder(paintInfo.context, tx, ty, width, height, style()); 861 } 862 863 void RenderBox::paintMask(PaintInfo& paintInfo, int tx, int ty) 864 { 865 if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask || paintInfo.context->paintingDisabled()) 866 return; 867 868 int w = width(); 869 int h = height(); 870 871 // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat 872 // balloon layout is an example of this). 873 borderFitAdjust(tx, w); 874 875 paintMaskImages(paintInfo, tx, ty, w, h); 876 } 877 878 void RenderBox::paintMaskImages(const PaintInfo& paintInfo, int tx, int ty, int w, int h) 879 { 880 // Figure out if we need to push a transparency layer to render our mask. 881 bool pushTransparencyLayer = false; 882 bool compositedMask = hasLayer() && layer()->hasCompositedMask(); 883 CompositeOperator compositeOp = CompositeSourceOver; 884 885 bool allMaskImagesLoaded = true; 886 887 if (!compositedMask) { 888 // If the context has a rotation, scale or skew, then use a transparency layer to avoid 889 // pixel cruft around the edge of the mask. 890 const AffineTransform& currentCTM = paintInfo.context->getCTM(); 891 pushTransparencyLayer = !currentCTM.isIdentityOrTranslationOrFlipped(); 892 893 StyleImage* maskBoxImage = style()->maskBoxImage().image(); 894 const FillLayer* maskLayers = style()->maskLayers(); 895 896 // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content. 897 if (maskBoxImage) 898 allMaskImagesLoaded &= maskBoxImage->isLoaded(); 899 900 if (maskLayers) 901 allMaskImagesLoaded &= maskLayers->imagesAreLoaded(); 902 903 // Before all images have loaded, just use an empty transparency layer as the mask. 904 if (!allMaskImagesLoaded) 905 pushTransparencyLayer = true; 906 907 if (maskBoxImage && maskLayers->hasImage()) { 908 // We have a mask-box-image and mask-image, so need to composite them together before using the result as a mask. 909 pushTransparencyLayer = true; 910 } else { 911 // We have to use an extra image buffer to hold the mask. Multiple mask images need 912 // to composite together using source-over so that they can then combine into a single unified mask that 913 // can be composited with the content using destination-in. SVG images need to be able to set compositing modes 914 // as they draw images contained inside their sub-document, so we paint all our images into a separate buffer 915 // and composite that buffer as the mask. 916 // We have to check that the mask images to be rendered contain at least one image that can be actually used in rendering 917 // before pushing the transparency layer. 918 for (const FillLayer* fillLayer = maskLayers->next(); fillLayer; fillLayer = fillLayer->next()) { 919 if (fillLayer->hasImage() && fillLayer->image()->canRender(style()->effectiveZoom())) { 920 pushTransparencyLayer = true; 921 // We found one image that can be used in rendering, exit the loop 922 break; 923 } 924 } 925 } 926 927 compositeOp = CompositeDestinationIn; 928 if (pushTransparencyLayer) { 929 paintInfo.context->setCompositeOperation(CompositeDestinationIn); 930 paintInfo.context->beginTransparencyLayer(1.0f); 931 compositeOp = CompositeSourceOver; 932 } 933 } 934 935 if (allMaskImagesLoaded) { 936 paintFillLayers(paintInfo, Color(), style()->maskLayers(), tx, ty, w, h, compositeOp); 937 paintNinePieceImage(paintInfo.context, tx, ty, w, h, style(), style()->maskBoxImage(), compositeOp); 938 } 939 940 if (pushTransparencyLayer) 941 paintInfo.context->endTransparencyLayer(); 942 } 943 944 IntRect RenderBox::maskClipRect() 945 { 946 IntRect bbox = borderBoxRect(); 947 if (style()->maskBoxImage().image()) 948 return bbox; 949 950 IntRect result; 951 for (const FillLayer* maskLayer = style()->maskLayers(); maskLayer; maskLayer = maskLayer->next()) { 952 if (maskLayer->image()) { 953 IntRect maskRect; 954 IntPoint phase; 955 IntSize tileSize; 956 calculateBackgroundImageGeometry(maskLayer, bbox.x(), bbox.y(), bbox.width(), bbox.height(), maskRect, phase, tileSize); 957 result.unite(maskRect); 958 } 959 } 960 return result; 961 } 962 963 void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject) 964 { 965 if (!fillLayer) 966 return; 967 968 paintFillLayers(paintInfo, c, fillLayer->next(), tx, ty, width, height, op, backgroundObject); 969 paintFillLayer(paintInfo, c, fillLayer, tx, ty, width, height, op, backgroundObject); 970 } 971 972 void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject) 973 { 974 paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, width, height, 0, 0, 0, op, backgroundObject); 975 } 976 977 #if USE(ACCELERATED_COMPOSITING) 978 static bool layersUseImage(WrappedImagePtr image, const FillLayer* layers) 979 { 980 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) { 981 if (curLayer->image() && image == curLayer->image()->data()) 982 return true; 983 } 984 985 return false; 986 } 987 #endif 988 989 void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*) 990 { 991 if (!parent()) 992 return; 993 994 if ((style()->borderImage().image() && style()->borderImage().image()->data() == image) || 995 (style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image)) { 996 repaint(); 997 return; 998 } 999 1000 bool didFullRepaint = repaintLayerRectsForImage(image, style()->backgroundLayers(), true); 1001 if (!didFullRepaint) 1002 repaintLayerRectsForImage(image, style()->maskLayers(), false); 1003 1004 1005 #if USE(ACCELERATED_COMPOSITING) 1006 if (hasLayer() && layer()->hasCompositedMask() && layersUseImage(image, style()->maskLayers())) 1007 layer()->contentChanged(RenderLayer::MaskImageChanged); 1008 #endif 1009 } 1010 1011 bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground) 1012 { 1013 IntRect rendererRect; 1014 RenderBox* layerRenderer = 0; 1015 1016 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) { 1017 if (curLayer->image() && image == curLayer->image()->data() && curLayer->image()->canRender(style()->effectiveZoom())) { 1018 // Now that we know this image is being used, compute the renderer and the rect 1019 // if we haven't already 1020 if (!layerRenderer) { 1021 bool drawingRootBackground = drawingBackground && (isRoot() || (isBody() && !document()->documentElement()->renderer()->hasBackground())); 1022 if (drawingRootBackground) { 1023 layerRenderer = view(); 1024 1025 int rw; 1026 int rh; 1027 1028 if (FrameView* frameView = toRenderView(layerRenderer)->frameView()) { 1029 rw = frameView->contentsWidth(); 1030 rh = frameView->contentsHeight(); 1031 } else { 1032 rw = layerRenderer->width(); 1033 rh = layerRenderer->height(); 1034 } 1035 rendererRect = IntRect(-layerRenderer->marginLeft(), 1036 -layerRenderer->marginTop(), 1037 max(layerRenderer->width() + layerRenderer->marginLeft() + layerRenderer->marginRight() + layerRenderer->borderLeft() + layerRenderer->borderRight(), rw), 1038 max(layerRenderer->height() + layerRenderer->marginTop() + layerRenderer->marginBottom() + layerRenderer->borderTop() + layerRenderer->borderBottom(), rh)); 1039 } else { 1040 layerRenderer = this; 1041 rendererRect = borderBoxRect(); 1042 } 1043 } 1044 1045 IntRect repaintRect; 1046 IntPoint phase; 1047 IntSize tileSize; 1048 layerRenderer->calculateBackgroundImageGeometry(curLayer, rendererRect.x(), rendererRect.y(), rendererRect.width(), rendererRect.height(), repaintRect, phase, tileSize); 1049 layerRenderer->repaintRectangle(repaintRect); 1050 if (repaintRect == rendererRect) 1051 return true; 1052 } 1053 } 1054 return false; 1055 } 1056 1057 #if PLATFORM(MAC) 1058 1059 void RenderBox::paintCustomHighlight(int tx, int ty, const AtomicString& type, bool behindText) 1060 { 1061 Frame* frame = this->frame(); 1062 if (!frame) 1063 return; 1064 Page* page = frame->page(); 1065 if (!page) 1066 return; 1067 1068 InlineBox* boxWrap = inlineBoxWrapper(); 1069 RootInlineBox* r = boxWrap ? boxWrap->root() : 0; 1070 if (r) { 1071 FloatRect rootRect(tx + r->x(), ty + r->selectionTop(), r->logicalWidth(), r->selectionHeight()); 1072 FloatRect imageRect(tx + x(), rootRect.y(), width(), rootRect.height()); 1073 page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, rootRect, behindText, false); 1074 } else { 1075 FloatRect imageRect(tx + x(), ty + y(), width(), height()); 1076 page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, imageRect, behindText, false); 1077 } 1078 } 1079 1080 #endif 1081 1082 bool RenderBox::pushContentsClip(PaintInfo& paintInfo, int tx, int ty) 1083 { 1084 if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseSelfOutline || paintInfo.phase == PaintPhaseMask) 1085 return false; 1086 1087 bool isControlClip = hasControlClip(); 1088 bool isOverflowClip = hasOverflowClip() && !layer()->isSelfPaintingLayer(); 1089 1090 if (!isControlClip && !isOverflowClip) 1091 return false; 1092 1093 if (paintInfo.phase == PaintPhaseOutline) 1094 paintInfo.phase = PaintPhaseChildOutlines; 1095 else if (paintInfo.phase == PaintPhaseChildBlockBackground) { 1096 paintInfo.phase = PaintPhaseBlockBackground; 1097 paintObject(paintInfo, tx, ty); 1098 paintInfo.phase = PaintPhaseChildBlockBackgrounds; 1099 } 1100 IntRect clipRect(isControlClip ? controlClipRect(tx, ty) : overflowClipRect(tx, ty)); 1101 paintInfo.context->save(); 1102 if (style()->hasBorderRadius()) 1103 paintInfo.context->addRoundedRectClip(style()->getRoundedBorderFor(IntRect(tx, ty, width(), height()))); 1104 paintInfo.context->clip(clipRect); 1105 return true; 1106 } 1107 1108 void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, int tx, int ty) 1109 { 1110 ASSERT(hasControlClip() || (hasOverflowClip() && !layer()->isSelfPaintingLayer())); 1111 1112 paintInfo.context->restore(); 1113 if (originalPhase == PaintPhaseOutline) { 1114 paintInfo.phase = PaintPhaseSelfOutline; 1115 paintObject(paintInfo, tx, ty); 1116 paintInfo.phase = originalPhase; 1117 } else if (originalPhase == PaintPhaseChildBlockBackground) 1118 paintInfo.phase = originalPhase; 1119 } 1120 1121 IntRect RenderBox::overflowClipRect(int tx, int ty, OverlayScrollbarSizeRelevancy relevancy) 1122 { 1123 // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property 1124 // here. 1125 1126 int bLeft = borderLeft(); 1127 int bTop = borderTop(); 1128 1129 int clipX = tx + bLeft; 1130 int clipY = ty + bTop; 1131 int clipWidth = width() - bLeft - borderRight(); 1132 int clipHeight = height() - bTop - borderBottom(); 1133 1134 // Subtract out scrollbars if we have them. 1135 if (layer()) { 1136 clipWidth -= layer()->verticalScrollbarWidth(relevancy); 1137 clipHeight -= layer()->horizontalScrollbarHeight(relevancy); 1138 } 1139 1140 return IntRect(clipX, clipY, clipWidth, clipHeight); 1141 } 1142 1143 IntRect RenderBox::clipRect(int tx, int ty) 1144 { 1145 int clipX = tx; 1146 int clipY = ty; 1147 int clipWidth = width(); 1148 int clipHeight = height(); 1149 1150 if (!style()->clipLeft().isAuto()) { 1151 int c = style()->clipLeft().calcValue(width()); 1152 clipX += c; 1153 clipWidth -= c; 1154 } 1155 1156 if (!style()->clipRight().isAuto()) 1157 clipWidth -= width() - style()->clipRight().calcValue(width()); 1158 1159 if (!style()->clipTop().isAuto()) { 1160 int c = style()->clipTop().calcValue(height()); 1161 clipY += c; 1162 clipHeight -= c; 1163 } 1164 1165 if (!style()->clipBottom().isAuto()) 1166 clipHeight -= height() - style()->clipBottom().calcValue(height()); 1167 1168 return IntRect(clipX, clipY, clipWidth, clipHeight); 1169 } 1170 1171 int RenderBox::containingBlockLogicalWidthForContent() const 1172 { 1173 RenderBlock* cb = containingBlock(); 1174 if (shrinkToAvoidFloats()) 1175 return cb->availableLogicalWidthForLine(y(), false); 1176 return cb->availableLogicalWidth(); 1177 } 1178 1179 int RenderBox::perpendicularContainingBlockLogicalHeight() const 1180 { 1181 RenderBlock* cb = containingBlock(); 1182 RenderStyle* containingBlockStyle = cb->style(); 1183 Length logicalHeightLength = containingBlockStyle->logicalHeight(); 1184 1185 // FIXME: For now just support fixed heights. Eventually should support percentage heights as well. 1186 if (!logicalHeightLength.isFixed()) { 1187 // Rather than making the child be completely unconstrained, WinIE uses the viewport width and height 1188 // as a constraint. We do that for now as well even though it's likely being unconstrained is what the spec 1189 // will decide. 1190 return containingBlockStyle->isHorizontalWritingMode() ? view()->frameView()->visibleHeight() : view()->frameView()->visibleWidth(); 1191 } 1192 1193 // Use the content box logical height as specified by the style. 1194 return cb->computeContentBoxLogicalHeight(logicalHeightLength.value()); 1195 } 1196 1197 void RenderBox::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const 1198 { 1199 if (repaintContainer == this) 1200 return; 1201 1202 if (RenderView* v = view()) { 1203 if (v->layoutStateEnabled() && !repaintContainer) { 1204 LayoutState* layoutState = v->layoutState(); 1205 IntSize offset = layoutState->m_paintOffset; 1206 offset.expand(x(), y()); 1207 if (style()->position() == RelativePosition && layer()) 1208 offset += layer()->relativePositionOffset(); 1209 transformState.move(offset); 1210 return; 1211 } 1212 } 1213 1214 bool containerSkipped; 1215 RenderObject* o = container(repaintContainer, &containerSkipped); 1216 if (!o) 1217 return; 1218 1219 bool isFixedPos = style()->position() == FixedPosition; 1220 bool hasTransform = hasLayer() && layer()->transform(); 1221 if (hasTransform) { 1222 // If this box has a transform, it acts as a fixed position container for fixed descendants, 1223 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. 1224 fixed &= isFixedPos; 1225 } else 1226 fixed |= isFixedPos; 1227 1228 IntSize containerOffset = offsetFromContainer(o, roundedIntPoint(transformState.mappedPoint())); 1229 1230 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); 1231 if (useTransforms && shouldUseTransformFromContainer(o)) { 1232 TransformationMatrix t; 1233 getTransformFromContainer(o, containerOffset, t); 1234 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1235 } else 1236 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1237 1238 if (containerSkipped) { 1239 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe 1240 // to just subtract the delta between the repaintContainer and o. 1241 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); 1242 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1243 return; 1244 } 1245 1246 o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); 1247 } 1248 1249 void RenderBox::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const 1250 { 1251 // We don't expect absoluteToLocal() to be called during layout (yet) 1252 ASSERT(!view() || !view()->layoutStateEnabled()); 1253 1254 bool isFixedPos = style()->position() == FixedPosition; 1255 bool hasTransform = hasLayer() && layer()->transform(); 1256 if (hasTransform) { 1257 // If this box has a transform, it acts as a fixed position container for fixed descendants, 1258 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. 1259 fixed &= isFixedPos; 1260 } else 1261 fixed |= isFixedPos; 1262 1263 RenderObject* o = container(); 1264 if (!o) 1265 return; 1266 1267 o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState); 1268 1269 IntSize containerOffset = offsetFromContainer(o, IntPoint()); 1270 1271 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); 1272 if (useTransforms && shouldUseTransformFromContainer(o)) { 1273 TransformationMatrix t; 1274 getTransformFromContainer(o, containerOffset, t); 1275 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1276 } else 1277 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1278 } 1279 1280 IntSize RenderBox::offsetFromContainer(RenderObject* o, const IntPoint& point) const 1281 { 1282 ASSERT(o == container()); 1283 1284 IntSize offset; 1285 if (isRelPositioned()) 1286 offset += relativePositionOffset(); 1287 1288 if (!isInline() || isReplaced()) { 1289 if (style()->position() != AbsolutePosition && style()->position() != FixedPosition) { 1290 if (o->hasColumns()) { 1291 IntRect columnRect(frameRect()); 1292 toRenderBlock(o)->flipForWritingModeIncludingColumns(columnRect); 1293 offset += IntSize(columnRect.location().x(), columnRect.location().y()); 1294 columnRect.move(point.x(), point.y()); 1295 o->adjustForColumns(offset, columnRect.location()); 1296 } else 1297 offset += locationOffsetIncludingFlipping(); 1298 } else 1299 offset += locationOffsetIncludingFlipping(); 1300 } 1301 1302 if (o->hasOverflowClip()) 1303 offset -= toRenderBox(o)->layer()->scrolledContentOffset(); 1304 1305 if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isRenderInline()) 1306 offset += toRenderInline(o)->relativePositionedInlineOffset(this); 1307 1308 return offset; 1309 } 1310 1311 InlineBox* RenderBox::createInlineBox() 1312 { 1313 return new (renderArena()) InlineBox(this); 1314 } 1315 1316 void RenderBox::dirtyLineBoxes(bool fullLayout) 1317 { 1318 if (m_inlineBoxWrapper) { 1319 if (fullLayout) { 1320 m_inlineBoxWrapper->destroy(renderArena()); 1321 m_inlineBoxWrapper = 0; 1322 } else 1323 m_inlineBoxWrapper->dirtyLineBoxes(); 1324 } 1325 } 1326 1327 void RenderBox::positionLineBox(InlineBox* box) 1328 { 1329 if (isPositioned()) { 1330 // Cache the x position only if we were an INLINE type originally. 1331 bool wasInline = style()->isOriginalDisplayInlineType(); 1332 if (wasInline) { 1333 // The value is cached in the xPos of the box. We only need this value if 1334 // our object was inline originally, since otherwise it would have ended up underneath 1335 // the inlines. 1336 layer()->setStaticInlinePosition(lroundf(box->logicalLeft())); 1337 if (style()->hasStaticInlinePosition(box->isHorizontal())) 1338 setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. 1339 } else { 1340 // Our object was a block originally, so we make our normal flow position be 1341 // just below the line box (as though all the inlines that came before us got 1342 // wrapped in an anonymous block, which is what would have happened had we been 1343 // in flow). This value was cached in the y() of the box. 1344 layer()->setStaticBlockPosition(box->logicalTop()); 1345 if (style()->hasStaticBlockPosition(box->isHorizontal())) 1346 setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. 1347 } 1348 1349 // Nuke the box. 1350 box->remove(); 1351 box->destroy(renderArena()); 1352 } else if (isReplaced()) { 1353 setLocation(lroundf(box->x()), lroundf(box->y())); 1354 m_inlineBoxWrapper = box; 1355 } 1356 } 1357 1358 void RenderBox::deleteLineBoxWrapper() 1359 { 1360 if (m_inlineBoxWrapper) { 1361 if (!documentBeingDestroyed()) 1362 m_inlineBoxWrapper->remove(); 1363 m_inlineBoxWrapper->destroy(renderArena()); 1364 m_inlineBoxWrapper = 0; 1365 } 1366 } 1367 1368 IntRect RenderBox::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) 1369 { 1370 if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent()) 1371 return IntRect(); 1372 1373 IntRect r = visualOverflowRect(); 1374 1375 RenderView* v = view(); 1376 if (v) { 1377 // FIXME: layoutDelta needs to be applied in parts before/after transforms and 1378 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 1379 r.move(v->layoutDelta()); 1380 } 1381 1382 if (style()) { 1383 if (style()->hasAppearance()) 1384 // The theme may wish to inflate the rect used when repainting. 1385 theme()->adjustRepaintRect(this, r); 1386 1387 // We have to use maximalOutlineSize() because a child might have an outline 1388 // that projects outside of our overflowRect. 1389 if (v) { 1390 ASSERT(style()->outlineSize() <= v->maximalOutlineSize()); 1391 r.inflate(v->maximalOutlineSize()); 1392 } 1393 } 1394 1395 computeRectForRepaint(repaintContainer, r); 1396 return r; 1397 } 1398 1399 void RenderBox::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) 1400 { 1401 // The rect we compute at each step is shifted by our x/y offset in the parent container's coordinate space. 1402 // Only when we cross a writing mode boundary will we have to possibly flipForWritingMode (to convert into a more appropriate 1403 // offset corner for the enclosing container). This allows for a fully RL or BT document to repaint 1404 // properly even during layout, since the rect remains flipped all the way until the end. 1405 // 1406 // RenderView::computeRectForRepaint then converts the rect to physical coordinates. We also convert to 1407 // physical when we hit a repaintContainer boundary. Therefore the final rect returned is always in the 1408 // physical coordinate space of the repaintContainer. 1409 if (RenderView* v = view()) { 1410 // LayoutState is only valid for root-relative repainting 1411 if (v->layoutStateEnabled() && !repaintContainer) { 1412 LayoutState* layoutState = v->layoutState(); 1413 1414 if (layer() && layer()->transform()) 1415 rect = layer()->transform()->mapRect(rect); 1416 1417 if (style()->position() == RelativePosition && layer()) 1418 rect.move(layer()->relativePositionOffset()); 1419 1420 rect.move(x(), y()); 1421 rect.move(layoutState->m_paintOffset); 1422 if (layoutState->m_clipped) 1423 rect.intersect(layoutState->m_clipRect); 1424 return; 1425 } 1426 } 1427 1428 if (hasReflection()) 1429 rect.unite(reflectedRect(rect)); 1430 1431 if (repaintContainer == this) { 1432 if (repaintContainer->style()->isFlippedBlocksWritingMode()) 1433 flipForWritingMode(rect); 1434 return; 1435 } 1436 1437 bool containerSkipped; 1438 RenderObject* o = container(repaintContainer, &containerSkipped); 1439 if (!o) 1440 return; 1441 1442 if (isWritingModeRoot() && !isPositioned()) 1443 flipForWritingMode(rect); 1444 IntPoint topLeft = rect.location(); 1445 topLeft.move(x(), y()); 1446 1447 EPosition position = style()->position(); 1448 1449 // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box 1450 // in the parent's coordinate space that encloses us. 1451 if (layer() && layer()->transform()) { 1452 fixed = position == FixedPosition; 1453 rect = layer()->transform()->mapRect(rect); 1454 topLeft = rect.location(); 1455 topLeft.move(x(), y()); 1456 } else if (position == FixedPosition) 1457 fixed = true; 1458 1459 if (position == AbsolutePosition && o->isRelPositioned() && o->isRenderInline()) 1460 topLeft += toRenderInline(o)->relativePositionedInlineOffset(this); 1461 else if (position == RelativePosition && layer()) { 1462 // Apply the relative position offset when invalidating a rectangle. The layer 1463 // is translated, but the render box isn't, so we need to do this to get the 1464 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position 1465 // flag on the RenderObject has been cleared, so use the one on the style(). 1466 topLeft += layer()->relativePositionOffset(); 1467 } 1468 1469 if (o->isBlockFlow() && position != AbsolutePosition && position != FixedPosition) { 1470 RenderBlock* cb = toRenderBlock(o); 1471 if (cb->hasColumns()) { 1472 IntRect repaintRect(topLeft, rect.size()); 1473 cb->adjustRectForColumns(repaintRect); 1474 topLeft = repaintRect.location(); 1475 rect = repaintRect; 1476 } 1477 } 1478 1479 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout, 1480 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer. 1481 if (o->hasOverflowClip()) { 1482 RenderBox* containerBox = toRenderBox(o); 1483 1484 // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the 1485 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint 1486 // anyway if its size does change. 1487 topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden. 1488 1489 IntRect repaintRect(topLeft, rect.size()); 1490 IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height()); 1491 rect = intersection(repaintRect, boxRect); 1492 if (rect.isEmpty()) 1493 return; 1494 } else 1495 rect.setLocation(topLeft); 1496 1497 if (containerSkipped) { 1498 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates. 1499 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); 1500 rect.move(-containerOffset); 1501 return; 1502 } 1503 1504 o->computeRectForRepaint(repaintContainer, rect, fixed); 1505 } 1506 1507 void RenderBox::repaintDuringLayoutIfMoved(const IntRect& rect) 1508 { 1509 int newX = x(); 1510 int newY = y(); 1511 int newWidth = width(); 1512 int newHeight = height(); 1513 if (rect.x() != newX || rect.y() != newY) { 1514 // The child moved. Invalidate the object's old and new positions. We have to do this 1515 // since the object may not have gotten a layout. 1516 m_frameRect = rect; 1517 repaint(); 1518 repaintOverhangingFloats(true); 1519 m_frameRect = IntRect(newX, newY, newWidth, newHeight); 1520 repaint(); 1521 repaintOverhangingFloats(true); 1522 } 1523 } 1524 1525 #ifdef ANDROID_LAYOUT 1526 void RenderBox::setVisibleWidth(int newWidth) { 1527 const Settings* settings = document()->settings(); 1528 ASSERT(settings); 1529 if (settings->layoutAlgorithm() != Settings::kLayoutFitColumnToScreen 1530 || m_visibleWidth == newWidth) 1531 return; 1532 m_isVisibleWidthChangedBeforeLayout = true; 1533 m_visibleWidth = newWidth; 1534 } 1535 1536 bool RenderBox::checkAndSetRelayoutChildren(bool* relayoutChildren) { 1537 if (m_isVisibleWidthChangedBeforeLayout) { 1538 m_isVisibleWidthChangedBeforeLayout = false; 1539 *relayoutChildren = true; 1540 return true; 1541 } 1542 return false; 1543 } 1544 #endif 1545 1546 void RenderBox::computeLogicalWidth() 1547 { 1548 #ifdef ANDROID_LAYOUT 1549 if (view()->frameView()) 1550 setVisibleWidth(view()->frameView()->textWrapWidth()); 1551 #endif 1552 1553 if (isPositioned()) { 1554 // FIXME: This calculation is not patched for block-flow yet. 1555 // https://bugs.webkit.org/show_bug.cgi?id=46500 1556 computePositionedLogicalWidth(); 1557 return; 1558 } 1559 1560 // If layout is limited to a subtree, the subtree root's logical width does not change. 1561 if (node() && view()->frameView() && view()->frameView()->layoutRoot(true) == this) 1562 return; 1563 1564 // The parent box is flexing us, so it has increased or decreased our 1565 // width. Use the width from the style context. 1566 // FIXME: Account for block-flow in flexible boxes. 1567 // https://bugs.webkit.org/show_bug.cgi?id=46418 1568 if (hasOverrideSize() && parent()->style()->boxOrient() == HORIZONTAL 1569 && parent()->isFlexibleBox() && parent()->isFlexingChildren()) { 1570 #if PLATFORM(ANDROID) 1571 // Strangely, the slider is get overrided as width 0 on youtube.com 1572 // The wrong width will cause the touch hit test for the slider failed. 1573 // This WAR should be safe since it is only targeted to slider. 1574 // TODO: root cause this and see if any webkit update fix this. 1575 if (!(isSlider() && overrideSize() == 0)) 1576 #endif 1577 setLogicalWidth(overrideSize()); 1578 return; 1579 } 1580 1581 // FIXME: Account for block-flow in flexible boxes. 1582 // https://bugs.webkit.org/show_bug.cgi?id=46418 1583 bool inVerticalBox = parent()->isFlexibleBox() && (parent()->style()->boxOrient() == VERTICAL); 1584 bool stretching = (parent()->style()->boxAlign() == BSTRETCH); 1585 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inVerticalBox || !stretching); 1586 1587 Length logicalWidthLength = (treatAsReplaced) ? Length(computeReplacedLogicalWidth(), Fixed) : style()->logicalWidth(); 1588 1589 RenderBlock* cb = containingBlock(); 1590 int containerLogicalWidth = max(0, containingBlockLogicalWidthForContent()); 1591 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode(); 1592 int containerWidthInInlineDirection = containerLogicalWidth; 1593 if (hasPerpendicularContainingBlock) 1594 containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight(); 1595 1596 if (isInline() && !isInlineBlockOrInlineTable()) { 1597 // just calculate margins 1598 setMarginStart(style()->marginStart().calcMinValue(containerLogicalWidth)); 1599 setMarginEnd(style()->marginEnd().calcMinValue(containerLogicalWidth)); 1600 #ifdef ANDROID_LAYOUT 1601 if (treatAsReplaced) { 1602 #else 1603 if (treatAsReplaced) 1604 #endif 1605 setLogicalWidth(max(logicalWidthLength.value() + borderAndPaddingLogicalWidth(), minPreferredLogicalWidth())); 1606 1607 #ifdef ANDROID_LAYOUT 1608 // in SSR mode with replaced box, if the box width is wider than the container width, 1609 // it will be shrinked to fit to the container. 1610 if (containerLogicalWidth && (width() + m_marginLeft + m_marginRight) > containerLogicalWidth && 1611 document()->frame()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { 1612 m_marginLeft = m_marginRight = 0; 1613 setWidth(containerLogicalWidth); 1614 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = containerLogicalWidth; 1615 } 1616 } 1617 #endif 1618 return; 1619 } 1620 1621 // Width calculations 1622 if (treatAsReplaced) 1623 setLogicalWidth(logicalWidthLength.value() + borderAndPaddingLogicalWidth()); 1624 else { 1625 // Calculate LogicalWidth 1626 setLogicalWidth(computeLogicalWidthUsing(LogicalWidth, containerWidthInInlineDirection)); 1627 1628 // Calculate MaxLogicalWidth 1629 if (!style()->logicalMaxWidth().isUndefined()) { 1630 int maxLogicalWidth = computeLogicalWidthUsing(MaxLogicalWidth, containerWidthInInlineDirection); 1631 if (logicalWidth() > maxLogicalWidth) { 1632 setLogicalWidth(maxLogicalWidth); 1633 logicalWidthLength = style()->logicalMaxWidth(); 1634 } 1635 } 1636 1637 // Calculate MinLogicalWidth 1638 int minLogicalWidth = computeLogicalWidthUsing(MinLogicalWidth, containerWidthInInlineDirection); 1639 if (logicalWidth() < minLogicalWidth) { 1640 setLogicalWidth(minLogicalWidth); 1641 logicalWidthLength = style()->logicalMinWidth(); 1642 } 1643 } 1644 1645 // Fieldsets are currently the only objects that stretch to their minimum width. 1646 if (stretchesToMinIntrinsicLogicalWidth()) { 1647 setLogicalWidth(max(logicalWidth(), minPreferredLogicalWidth())); 1648 logicalWidthLength = Length(logicalWidth(), Fixed); 1649 } 1650 1651 // Margin calculations. 1652 if (logicalWidthLength.isAuto() || hasPerpendicularContainingBlock || isFloating() || isInline()) { 1653 setMarginStart(style()->marginStart().calcMinValue(containerLogicalWidth)); 1654 setMarginEnd(style()->marginEnd().calcMinValue(containerLogicalWidth)); 1655 } else 1656 computeInlineDirectionMargins(cb, containerLogicalWidth, logicalWidth()); 1657 1658 #ifdef ANDROID_LAYOUT 1659 // in SSR mode with non-replaced box, we use ANDROID_SSR_MARGIN_PADDING for left/right margin. 1660 // If the box width is wider than the container width, it will be shrinked to fit to the container. 1661 if (containerLogicalWidth && !treatAsReplaced && 1662 document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { 1663 setWidth(width() + m_marginLeft + m_marginRight); 1664 m_marginLeft = m_marginLeft > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginLeft; 1665 m_marginRight = m_marginRight > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginRight; 1666 if (width() > containerLogicalWidth) { 1667 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = containerLogicalWidth-(m_marginLeft + m_marginRight); 1668 setWidth(m_minPreferredLogicalWidth); 1669 } else 1670 setWidth(width() -(m_marginLeft + m_marginRight)); 1671 } 1672 #endif 1673 1674 if (!hasPerpendicularContainingBlock && containerLogicalWidth && containerLogicalWidth != (logicalWidth() + marginStart() + marginEnd()) 1675 && !isFloating() && !isInline() && !cb->isFlexibleBox()) 1676 cb->setMarginEndForChild(this, containerLogicalWidth - logicalWidth() - cb->marginStartForChild(this)); 1677 } 1678 1679 int RenderBox::computeLogicalWidthUsing(LogicalWidthType widthType, int availableLogicalWidth) 1680 { 1681 int logicalWidthResult = logicalWidth(); 1682 Length logicalWidth; 1683 if (widthType == LogicalWidth) 1684 logicalWidth = style()->logicalWidth(); 1685 else if (widthType == MinLogicalWidth) 1686 logicalWidth = style()->logicalMinWidth(); 1687 else 1688 logicalWidth = style()->logicalMaxWidth(); 1689 1690 if (logicalWidth.isIntrinsicOrAuto()) { 1691 int marginStart = style()->marginStart().calcMinValue(availableLogicalWidth); 1692 int marginEnd = style()->marginEnd().calcMinValue(availableLogicalWidth); 1693 if (availableLogicalWidth) 1694 logicalWidthResult = availableLogicalWidth - marginStart - marginEnd; 1695 1696 if (sizesToIntrinsicLogicalWidth(widthType)) { 1697 logicalWidthResult = max(logicalWidthResult, minPreferredLogicalWidth()); 1698 logicalWidthResult = min(logicalWidthResult, maxPreferredLogicalWidth()); 1699 } 1700 } else // FIXME: If the containing block flow is perpendicular to our direction we need to use the available logical height instead. 1701 logicalWidthResult = computeBorderBoxLogicalWidth(logicalWidth.calcValue(availableLogicalWidth)); 1702 1703 return logicalWidthResult; 1704 } 1705 1706 bool RenderBox::sizesToIntrinsicLogicalWidth(LogicalWidthType widthType) const 1707 { 1708 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks, 1709 // but they allow text to sit on the same line as the marquee. 1710 if (isFloating() || (isInlineBlockOrInlineTable() && !isHTMLMarquee())) 1711 return true; 1712 1713 // This code may look a bit strange. Basically width:intrinsic should clamp the size when testing both 1714 // min-width and width. max-width is only clamped if it is also intrinsic. 1715 Length logicalWidth = (widthType == MaxLogicalWidth) ? style()->logicalMaxWidth() : style()->logicalWidth(); 1716 if (logicalWidth.type() == Intrinsic) 1717 return true; 1718 1719 // Children of a horizontal marquee do not fill the container by default. 1720 // FIXME: Need to deal with MAUTO value properly. It could be vertical. 1721 // FIXME: Think about block-flow here. Need to find out how marquee direction relates to 1722 // block-flow (as well as how marquee overflow should relate to block flow). 1723 // https://bugs.webkit.org/show_bug.cgi?id=46472 1724 if (parent()->style()->overflowX() == OMARQUEE) { 1725 EMarqueeDirection dir = parent()->style()->marqueeDirection(); 1726 if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT) 1727 return true; 1728 } 1729 1730 // Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes 1731 // that don't stretch their kids lay out their children at their intrinsic widths. 1732 // FIXME: Think about block-flow here. 1733 // https://bugs.webkit.org/show_bug.cgi?id=46473 1734 if (parent()->isFlexibleBox() 1735 && (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH)) 1736 return true; 1737 1738 // Button, input, select, textarea, legend and datagrid treat 1739 // width value of 'auto' as 'intrinsic' unless it's in a 1740 // stretching vertical flexbox. 1741 // FIXME: Think about block-flow here. 1742 // https://bugs.webkit.org/show_bug.cgi?id=46473 1743 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))) 1744 return true; 1745 1746 return false; 1747 } 1748 1749 void RenderBox::computeInlineDirectionMargins(RenderBlock* containingBlock, int containerWidth, int childWidth) 1750 { 1751 const RenderStyle* containingBlockStyle = containingBlock->style(); 1752 Length marginStartLength = style()->marginStartUsing(containingBlockStyle); 1753 Length marginEndLength = style()->marginEndUsing(containingBlockStyle); 1754 1755 // Case One: The object is being centered in the containing block's available logical width. 1756 if ((marginStartLength.isAuto() && marginEndLength.isAuto() && childWidth < containerWidth) 1757 || (!marginStartLength.isAuto() && !marginEndLength.isAuto() && containingBlock->style()->textAlign() == WEBKIT_CENTER)) { 1758 containingBlock->setMarginStartForChild(this, max(0, (containerWidth - childWidth) / 2)); 1759 containingBlock->setMarginEndForChild(this, containerWidth - childWidth - containingBlock->marginStartForChild(this)); 1760 return; 1761 } 1762 1763 // Case Two: The object is being pushed to the start of the containing block's available logical width. 1764 if (marginEndLength.isAuto() && childWidth < containerWidth) { 1765 containingBlock->setMarginStartForChild(this, marginStartLength.calcValue(containerWidth)); 1766 containingBlock->setMarginEndForChild(this, containerWidth - childWidth - containingBlock->marginStartForChild(this)); 1767 return; 1768 } 1769 1770 // Case Three: The object is being pushed to the end of the containing block's available logical width. 1771 bool pushToEndFromTextAlign = !marginEndLength.isAuto() && ((!containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_LEFT) 1772 || (containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_RIGHT)); 1773 if ((marginStartLength.isAuto() && childWidth < containerWidth) || pushToEndFromTextAlign) { 1774 containingBlock->setMarginEndForChild(this, marginEndLength.calcValue(containerWidth)); 1775 containingBlock->setMarginStartForChild(this, containerWidth - childWidth - containingBlock->marginEndForChild(this)); 1776 return; 1777 } 1778 1779 // Case Four: Either no auto margins, or our width is >= the container width (css2.1, 10.3.3). In that case 1780 // auto margins will just turn into 0. 1781 containingBlock->setMarginStartForChild(this, marginStartLength.calcMinValue(containerWidth)); 1782 containingBlock->setMarginEndForChild(this, marginEndLength.calcMinValue(containerWidth)); 1783 } 1784 1785 void RenderBox::computeLogicalHeight() 1786 { 1787 // Cell height is managed by the table and inline non-replaced elements do not support a height property. 1788 if (isTableCell() || (isInline() && !isReplaced())) 1789 return; 1790 1791 Length h; 1792 if (isPositioned()) { 1793 // FIXME: This calculation is not patched for block-flow yet. 1794 // https://bugs.webkit.org/show_bug.cgi?id=46500 1795 computePositionedLogicalHeight(); 1796 } else { 1797 RenderBlock* cb = containingBlock(); 1798 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode(); 1799 1800 if (!hasPerpendicularContainingBlock) 1801 computeBlockDirectionMargins(cb); 1802 1803 // For tables, calculate margins only. 1804 if (isTable()) { 1805 if (hasPerpendicularContainingBlock) 1806 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), logicalHeight()); 1807 return; 1808 } 1809 1810 // FIXME: Account for block-flow in flexible boxes. 1811 // https://bugs.webkit.org/show_bug.cgi?id=46418 1812 bool inHorizontalBox = parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL; 1813 bool stretching = parent()->style()->boxAlign() == BSTRETCH; 1814 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inHorizontalBox || !stretching); 1815 bool checkMinMaxHeight = false; 1816 1817 // The parent box is flexing us, so it has increased or decreased our height. We have to 1818 // grab our cached flexible height. 1819 // FIXME: Account for block-flow in flexible boxes. 1820 // https://bugs.webkit.org/show_bug.cgi?id=46418 1821 if (hasOverrideSize() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL 1822 && parent()->isFlexingChildren()) 1823 h = Length(overrideSize() - borderAndPaddingLogicalHeight(), Fixed); 1824 else if (treatAsReplaced) 1825 h = Length(computeReplacedLogicalHeight(), Fixed); 1826 else { 1827 h = style()->logicalHeight(); 1828 checkMinMaxHeight = true; 1829 } 1830 1831 // Block children of horizontal flexible boxes fill the height of the box. 1832 // FIXME: Account for block-flow in flexible boxes. 1833 // https://bugs.webkit.org/show_bug.cgi?id=46418 1834 if (h.isAuto() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL 1835 && parent()->isStretchingChildren()) { 1836 h = Length(parentBox()->contentLogicalHeight() - marginBefore() - marginAfter() - borderAndPaddingLogicalHeight(), Fixed); 1837 checkMinMaxHeight = false; 1838 } 1839 1840 int heightResult; 1841 if (checkMinMaxHeight) { 1842 #ifdef ANDROID_LAYOUT 1843 // in SSR mode, ignore CSS height as layout is so different 1844 if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) 1845 heightResult = -1; 1846 else 1847 #endif 1848 heightResult = computeLogicalHeightUsing(style()->logicalHeight()); 1849 if (heightResult == -1) 1850 heightResult = logicalHeight(); 1851 int minH = computeLogicalHeightUsing(style()->logicalMinHeight()); // Leave as -1 if unset. 1852 int maxH = style()->logicalMaxHeight().isUndefined() ? heightResult : computeLogicalHeightUsing(style()->logicalMaxHeight()); 1853 if (maxH == -1) 1854 maxH = heightResult; 1855 heightResult = min(maxH, heightResult); 1856 heightResult = max(minH, heightResult); 1857 } else { 1858 // The only times we don't check min/max height are when a fixed length has 1859 // been given as an override. Just use that. The value has already been adjusted 1860 // for box-sizing. 1861 heightResult = h.value() + borderAndPaddingLogicalHeight(); 1862 } 1863 1864 setLogicalHeight(heightResult); 1865 1866 if (hasPerpendicularContainingBlock) 1867 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), heightResult); 1868 } 1869 1870 // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the 1871 // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height 1872 // is specified. When we're printing, we also need this quirk if the body or root has a percentage 1873 // height since we don't set a height in RenderView when we're printing. So without this quirk, the 1874 // height has nothing to be a percentage of, and it ends up being 0. That is bad. 1875 bool paginatedContentNeedsBaseHeight = document()->printing() && h.isPercent() 1876 && (isRoot() || (isBody() && document()->documentElement()->renderer()->style()->logicalHeight().isPercent())); 1877 if (stretchesToViewport() || paginatedContentNeedsBaseHeight) { 1878 // FIXME: Finish accounting for block flow here. 1879 // https://bugs.webkit.org/show_bug.cgi?id=46603 1880 int margins = collapsedMarginBefore() + collapsedMarginAfter(); 1881 int visHeight; 1882 if (document()->printing()) 1883 visHeight = static_cast<int>(view()->pageLogicalHeight()); 1884 else { 1885 if (isHorizontalWritingMode()) 1886 visHeight = view()->viewHeight(); 1887 else 1888 visHeight = view()->viewWidth(); 1889 } 1890 if (isRoot()) 1891 setLogicalHeight(max(logicalHeight(), visHeight - margins)); 1892 else { 1893 int marginsBordersPadding = margins + parentBox()->marginBefore() + parentBox()->marginAfter() + parentBox()->borderAndPaddingLogicalHeight(); 1894 setLogicalHeight(max(logicalHeight(), visHeight - marginsBordersPadding)); 1895 } 1896 } 1897 } 1898 1899 int RenderBox::computeLogicalHeightUsing(const Length& h) 1900 { 1901 int logicalHeight = -1; 1902 if (!h.isAuto()) { 1903 if (h.isFixed()) 1904 logicalHeight = h.value(); 1905 else if (h.isPercent()) 1906 logicalHeight = computePercentageLogicalHeight(h); 1907 if (logicalHeight != -1) { 1908 logicalHeight = computeBorderBoxLogicalHeight(logicalHeight); 1909 return logicalHeight; 1910 } 1911 } 1912 return logicalHeight; 1913 } 1914 1915 int RenderBox::computePercentageLogicalHeight(const Length& height) 1916 { 1917 int result = -1; 1918 1919 // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing 1920 // block that may have a specified height and then use it. In strict mode, this violates the 1921 // specification, which states that percentage heights just revert to auto if the containing 1922 // block has an auto height. We still skip anonymous containing blocks in both modes, though, and look 1923 // only at explicit containers. 1924 bool skippedAutoHeightContainingBlock = false; 1925 RenderBlock* cb = containingBlock(); 1926 while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() && cb->style()->logicalHeight().isAuto()) { 1927 if (!document()->inQuirksMode() && !cb->isAnonymousBlock()) 1928 break; 1929 skippedAutoHeightContainingBlock = true; 1930 cb = cb->containingBlock(); 1931 cb->addPercentHeightDescendant(this); 1932 } 1933 1934 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height 1935 // explicitly specified that can be used for any percentage computations. 1936 // FIXME: We can't just check top/bottom here. 1937 // https://bugs.webkit.org/show_bug.cgi?id=46500 1938 bool isPositionedWithSpecifiedHeight = cb->isPositioned() && (!cb->style()->logicalHeight().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto())); 1939 1940 bool includeBorderPadding = isTable(); 1941 1942 // Table cells violate what the CSS spec says to do with heights. Basically we 1943 // don't care if the cell specified a height or not. We just always make ourselves 1944 // be a percentage of the cell's current content height. 1945 if (cb->isTableCell()) { 1946 if (!skippedAutoHeightContainingBlock) { 1947 result = cb->overrideSize(); 1948 if (result == -1) { 1949 // Normally we would let the cell size intrinsically, but scrolling overflow has to be 1950 // treated differently, since WinIE lets scrolled overflow regions shrink as needed. 1951 // While we can't get all cases right, we can at least detect when the cell has a specified 1952 // height or when the table has a specified height. In these cases we want to initially have 1953 // no size and allow the flexing of the table or the cell to its specified height to cause us 1954 // to grow to fill the space. This could end up being wrong in some cases, but it is 1955 // preferable to the alternative (sizing intrinsically and making the row end up too big). 1956 RenderTableCell* cell = toRenderTableCell(cb); 1957 if (scrollsOverflowY() && (!cell->style()->logicalHeight().isAuto() || !cell->table()->style()->logicalHeight().isAuto())) 1958 return 0; 1959 return -1; 1960 } 1961 includeBorderPadding = true; 1962 } 1963 } 1964 // Otherwise we only use our percentage height if our containing block had a specified 1965 // height. 1966 else if (cb->style()->logicalHeight().isFixed()) 1967 result = cb->computeContentBoxLogicalHeight(cb->style()->logicalHeight().value()); 1968 else if (cb->style()->logicalHeight().isPercent() && !isPositionedWithSpecifiedHeight) { 1969 // We need to recur and compute the percentage height for our containing block. 1970 result = cb->computePercentageLogicalHeight(cb->style()->logicalHeight()); 1971 if (result != -1) 1972 result = cb->computeContentBoxLogicalHeight(result); 1973 } else if (cb->isRenderView() || (cb->isBody() && document()->inQuirksMode()) || isPositionedWithSpecifiedHeight) { 1974 // Don't allow this to affect the block' height() member variable, since this 1975 // can get called while the block is still laying out its kids. 1976 int oldHeight = cb->logicalHeight(); 1977 cb->computeLogicalHeight(); 1978 result = cb->contentLogicalHeight(); 1979 cb->setLogicalHeight(oldHeight); 1980 } else if (cb->isRoot() && isPositioned()) 1981 // Match the positioned objects behavior, which is that positioned objects will fill their viewport 1982 // always. Note we could only hit this case by recurring into computePercentageLogicalHeight on a positioned containing block. 1983 result = cb->computeContentBoxLogicalHeight(cb->availableLogicalHeight()); 1984 1985 if (result != -1) { 1986 result = height.calcValue(result); 1987 if (includeBorderPadding) { 1988 // It is necessary to use the border-box to match WinIE's broken 1989 // box model. This is essential for sizing inside 1990 // table cells using percentage heights. 1991 result -= borderAndPaddingLogicalHeight(); 1992 result = max(0, result); 1993 } 1994 } 1995 return result; 1996 } 1997 1998 int RenderBox::computeReplacedLogicalWidth(bool includeMaxWidth) const 1999 { 2000 int logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth()); 2001 int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth()); 2002 int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth()); 2003 2004 return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth)); 2005 } 2006 2007 int RenderBox::computeReplacedLogicalWidthUsing(Length logicalWidth) const 2008 { 2009 switch (logicalWidth.type()) { 2010 case Fixed: 2011 return computeContentBoxLogicalWidth(logicalWidth.value()); 2012 case Percent: { 2013 // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced element's block-flow is perpendicular to the 2014 // containing block's block-flow. 2015 // https://bugs.webkit.org/show_bug.cgi?id=46496 2016 const int cw = isPositioned() ? containingBlockLogicalWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockLogicalWidthForContent(); 2017 if (cw > 0) 2018 return computeContentBoxLogicalWidth(logicalWidth.calcMinValue(cw)); 2019 } 2020 // fall through 2021 default: 2022 return intrinsicLogicalWidth(); 2023 } 2024 } 2025 2026 int RenderBox::computeReplacedLogicalHeight() const 2027 { 2028 int logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight()); 2029 int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight()); 2030 int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight()); 2031 2032 return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight)); 2033 } 2034 2035 int RenderBox::computeReplacedLogicalHeightUsing(Length logicalHeight) const 2036 { 2037 switch (logicalHeight.type()) { 2038 case Fixed: 2039 return computeContentBoxLogicalHeight(logicalHeight.value()); 2040 case Percent: 2041 { 2042 RenderObject* cb = isPositioned() ? container() : containingBlock(); 2043 while (cb->isAnonymous()) { 2044 cb = cb->containingBlock(); 2045 toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this)); 2046 } 2047 2048 // FIXME: This calculation is not patched for block-flow yet. 2049 // https://bugs.webkit.org/show_bug.cgi?id=46500 2050 if (cb->isPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) { 2051 ASSERT(cb->isRenderBlock()); 2052 RenderBlock* block = toRenderBlock(cb); 2053 int oldHeight = block->height(); 2054 block->computeLogicalHeight(); 2055 int newHeight = block->computeContentBoxLogicalHeight(block->contentHeight()); 2056 block->setHeight(oldHeight); 2057 return computeContentBoxLogicalHeight(logicalHeight.calcValue(newHeight)); 2058 } 2059 2060 // FIXME: availableLogicalHeight() is wrong if the replaced element's block-flow is perpendicular to the 2061 // containing block's block-flow. 2062 // https://bugs.webkit.org/show_bug.cgi?id=46496 2063 int availableHeight = isPositioned() ? containingBlockLogicalHeightForPositioned(toRenderBoxModelObject(cb)) : toRenderBox(cb)->availableLogicalHeight(); 2064 2065 // It is necessary to use the border-box to match WinIE's broken 2066 // box model. This is essential for sizing inside 2067 // table cells using percentage heights. 2068 // FIXME: This needs to be made block-flow-aware. If the cell and image are perpendicular block-flows, this isn't right. 2069 // https://bugs.webkit.org/show_bug.cgi?id=46997 2070 if (cb->isTableCell() && (cb->style()->logicalHeight().isAuto() || cb->style()->logicalHeight().isPercent())) { 2071 // Don't let table cells squeeze percent-height replaced elements 2072 // <http://bugs.webkit.org/show_bug.cgi?id=15359> 2073 availableHeight = max(availableHeight, intrinsicLogicalHeight()); 2074 return logicalHeight.calcValue(availableHeight - borderAndPaddingLogicalHeight()); 2075 } 2076 2077 return computeContentBoxLogicalHeight(logicalHeight.calcValue(availableHeight)); 2078 } 2079 default: 2080 return intrinsicLogicalHeight(); 2081 } 2082 } 2083 2084 int RenderBox::availableLogicalHeight() const 2085 { 2086 return availableLogicalHeightUsing(style()->logicalHeight()); 2087 } 2088 2089 int RenderBox::availableLogicalHeightUsing(const Length& h) const 2090 { 2091 if (h.isFixed()) 2092 return computeContentBoxLogicalHeight(h.value()); 2093 2094 if (isRenderView()) 2095 return isHorizontalWritingMode() ? toRenderView(this)->frameView()->visibleHeight() : toRenderView(this)->frameView()->visibleWidth(); 2096 2097 // We need to stop here, since we don't want to increase the height of the table 2098 // artificially. We're going to rely on this cell getting expanded to some new 2099 // height, and then when we lay out again we'll use the calculation below. 2100 if (isTableCell() && (h.isAuto() || h.isPercent())) 2101 return overrideSize() - borderAndPaddingLogicalWidth(); 2102 2103 if (h.isPercent()) 2104 return computeContentBoxLogicalHeight(h.calcValue(containingBlock()->availableLogicalHeight())); 2105 2106 // FIXME: We can't just check top/bottom here. 2107 // https://bugs.webkit.org/show_bug.cgi?id=46500 2108 if (isRenderBlock() && isPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) { 2109 RenderBlock* block = const_cast<RenderBlock*>(toRenderBlock(this)); 2110 int oldHeight = block->logicalHeight(); 2111 block->computeLogicalHeight(); 2112 int newHeight = block->computeContentBoxLogicalHeight(block->contentLogicalHeight()); 2113 block->setLogicalHeight(oldHeight); 2114 return computeContentBoxLogicalHeight(newHeight); 2115 } 2116 2117 return containingBlock()->availableLogicalHeight(); 2118 } 2119 2120 void RenderBox::computeBlockDirectionMargins(RenderBlock* containingBlock) 2121 { 2122 if (isTableCell()) { 2123 // FIXME: Not right if we allow cells to have different directionality than the table. If we do allow this, though, 2124 // we may just do it with an extra anonymous block inside the cell. 2125 setMarginBefore(0); 2126 setMarginAfter(0); 2127 return; 2128 } 2129 2130 // Margins are calculated with respect to the logical width of 2131 // the containing block (8.3) 2132 int cw = containingBlockLogicalWidthForContent(); 2133 2134 RenderStyle* containingBlockStyle = containingBlock->style(); 2135 containingBlock->setMarginBeforeForChild(this, style()->marginBeforeUsing(containingBlockStyle).calcMinValue(cw)); 2136 containingBlock->setMarginAfterForChild(this, style()->marginAfterUsing(containingBlockStyle).calcMinValue(cw)); 2137 } 2138 2139 int RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const 2140 { 2141 #if PLATFORM(ANDROID) 2142 // Fixed element's position should be decided by the visible screen size. 2143 // That is in the doc coordindate. 2144 if (style()->position() == FixedPosition && containingBlock->isRenderView()) { 2145 const RenderView* view = toRenderView(containingBlock); 2146 return PlatformBridge::screenWidthInDocCoord(view->frameView()); 2147 } 2148 #endif 2149 2150 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode()) 2151 return containingBlockLogicalHeightForPositioned(containingBlock, false); 2152 2153 if (containingBlock->isBox()) 2154 return toRenderBox(containingBlock)->clientLogicalWidth(); 2155 2156 ASSERT(containingBlock->isRenderInline() && containingBlock->isRelPositioned()); 2157 2158 const RenderInline* flow = toRenderInline(containingBlock); 2159 InlineFlowBox* first = flow->firstLineBox(); 2160 InlineFlowBox* last = flow->lastLineBox(); 2161 2162 // If the containing block is empty, return a width of 0. 2163 if (!first || !last) 2164 return 0; 2165 2166 int fromLeft; 2167 int fromRight; 2168 if (containingBlock->style()->isLeftToRightDirection()) { 2169 fromLeft = first->logicalLeft() + first->borderLogicalLeft(); 2170 fromRight = last->logicalLeft() + last->logicalWidth() - last->borderLogicalRight(); 2171 } else { 2172 fromRight = first->logicalLeft() + first->logicalWidth() - first->borderLogicalRight(); 2173 fromLeft = last->logicalLeft() + last->borderLogicalLeft(); 2174 } 2175 2176 return max(0, (fromRight - fromLeft)); 2177 } 2178 2179 int RenderBox::containingBlockLogicalHeightForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const 2180 { 2181 #if PLATFORM(ANDROID) 2182 // Fixed element's position should be decided by the visible screen size. 2183 // That is in the doc coordindate. 2184 if (style()->position() == FixedPosition && containingBlock->isRenderView()) { 2185 const RenderView* view = toRenderView(containingBlock); 2186 return PlatformBridge::screenHeightInDocCoord(view->frameView()); 2187 } 2188 #endif 2189 2190 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode()) 2191 return containingBlockLogicalWidthForPositioned(containingBlock, false); 2192 2193 if (containingBlock->isBox()) 2194 return toRenderBox(containingBlock)->clientLogicalHeight(); 2195 2196 ASSERT(containingBlock->isRenderInline() && containingBlock->isRelPositioned()); 2197 2198 const RenderInline* flow = toRenderInline(containingBlock); 2199 InlineFlowBox* first = flow->firstLineBox(); 2200 InlineFlowBox* last = flow->lastLineBox(); 2201 2202 // If the containing block is empty, return a height of 0. 2203 if (!first || !last) 2204 return 0; 2205 2206 int heightResult; 2207 IntRect boundingBox = flow->linesBoundingBox(); 2208 if (containingBlock->isHorizontalWritingMode()) 2209 heightResult = boundingBox.height(); 2210 else 2211 heightResult = boundingBox.width(); 2212 heightResult -= (containingBlock->borderBefore() + containingBlock->borderAfter()); 2213 return heightResult; 2214 } 2215 2216 static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRight, const RenderBox* child, const RenderBoxModelObject* containerBlock, int containerLogicalWidth, 2217 TextDirection containerDirection) 2218 { 2219 if (!logicalLeft.isAuto() || !logicalRight.isAuto()) 2220 return; 2221 2222 // FIXME: The static distance computation has not been patched for mixed writing modes yet. 2223 if (containerDirection == LTR) { 2224 int staticPosition = child->layer()->staticInlinePosition() - containerBlock->borderLogicalLeft(); 2225 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) { 2226 if (curr->isBox()) 2227 staticPosition += toRenderBox(curr)->logicalLeft(); 2228 } 2229 logicalLeft.setValue(Fixed, staticPosition); 2230 } else { 2231 RenderBox* enclosingBox = child->parent()->enclosingBox(); 2232 int staticPosition = child->layer()->staticInlinePosition() + containerLogicalWidth + containerBlock->borderLogicalRight(); 2233 staticPosition -= enclosingBox->logicalWidth(); 2234 for (RenderObject* curr = enclosingBox; curr && curr != containerBlock; curr = curr->container()) { 2235 if (curr->isBox()) 2236 staticPosition -= toRenderBox(curr)->logicalLeft(); 2237 } 2238 logicalRight.setValue(Fixed, staticPosition); 2239 } 2240 } 2241 2242 void RenderBox::computePositionedLogicalWidth() 2243 { 2244 if (isReplaced()) { 2245 computePositionedLogicalWidthReplaced(); 2246 return; 2247 } 2248 2249 // QUESTIONS 2250 // FIXME 1: Which RenderObject's 'direction' property should used: the 2251 // containing block (cb) as the spec seems to imply, the parent (parent()) as 2252 // was previously done in calculating the static distances, or ourself, which 2253 // was also previously done for deciding what to override when you had 2254 // over-constrained margins? Also note that the container block is used 2255 // in similar situations in other parts of the RenderBox class (see computeLogicalWidth() 2256 // and computeMarginsInContainingBlockInlineDirection()). For now we are using the parent for quirks 2257 // mode and the containing block for strict mode. 2258 2259 // FIXME 2: Should we still deal with these the cases of 'left' or 'right' having 2260 // the type 'static' in determining whether to calculate the static distance? 2261 // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1. 2262 2263 // FIXME 3: Can perhaps optimize out cases when max-width/min-width are greater 2264 // than or less than the computed width(). Be careful of box-sizing and 2265 // percentage issues. 2266 2267 // The following is based off of the W3C Working Draft from April 11, 2006 of 2268 // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements" 2269 // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width> 2270 // (block-style-comments in this function and in computePositionedLogicalWidthUsing() 2271 // correspond to text from the spec) 2272 2273 2274 // We don't use containingBlock(), since we may be positioned by an enclosing 2275 // relative positioned inline. 2276 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 2277 2278 const int containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); 2279 2280 // To match WinIE, in quirks mode use the parent's 'direction' property 2281 // instead of the the container block's. 2282 TextDirection containerDirection = (document()->inQuirksMode()) ? parent()->style()->direction() : containerBlock->style()->direction(); 2283 2284 bool isHorizontal = isHorizontalWritingMode(); 2285 const int bordersPlusPadding = borderAndPaddingLogicalWidth(); 2286 const Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop(); 2287 const Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom(); 2288 int& marginLogicalLeftAlias = isHorizontal ? m_marginLeft : m_marginTop; 2289 int& marginLogicalRightAlias = isHorizontal ? m_marginRight : m_marginBottom; 2290 2291 Length logicalLeft = style()->logicalLeft(); 2292 Length logicalRight = style()->logicalRight(); 2293 2294 /*---------------------------------------------------------------------------*\ 2295 * For the purposes of this section and the next, the term "static position" 2296 * (of an element) refers, roughly, to the position an element would have had 2297 * in the normal flow. More precisely: 2298 * 2299 * * The static position for 'left' is the distance from the left edge of the 2300 * containing block to the left margin edge of a hypothetical box that would 2301 * have been the first box of the element if its 'position' property had 2302 * been 'static' and 'float' had been 'none'. The value is negative if the 2303 * hypothetical box is to the left of the containing block. 2304 * * The static position for 'right' is the distance from the right edge of the 2305 * containing block to the right margin edge of the same hypothetical box as 2306 * above. The value is positive if the hypothetical box is to the left of the 2307 * containing block's edge. 2308 * 2309 * But rather than actually calculating the dimensions of that hypothetical box, 2310 * user agents are free to make a guess at its probable position. 2311 * 2312 * For the purposes of calculating the static position, the containing block of 2313 * fixed positioned elements is the initial containing block instead of the 2314 * viewport, and all scrollable boxes should be assumed to be scrolled to their 2315 * origin. 2316 \*---------------------------------------------------------------------------*/ 2317 2318 // see FIXME 2 2319 // Calculate the static distance if needed. 2320 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth, containerDirection); 2321 2322 // Calculate constraint equation values for 'width' case. 2323 int logicalWidthResult; 2324 int logicalLeftResult; 2325 computePositionedLogicalWidthUsing(style()->logicalWidth(), containerBlock, containerDirection, 2326 containerLogicalWidth, bordersPlusPadding, 2327 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight, 2328 logicalWidthResult, marginLogicalLeftAlias, marginLogicalRightAlias, logicalLeftResult); 2329 setLogicalWidth(logicalWidthResult); 2330 setLogicalLeft(logicalLeftResult); 2331 2332 // Calculate constraint equation values for 'max-width' case. 2333 if (!style()->logicalMaxWidth().isUndefined()) { 2334 int maxLogicalWidth; 2335 int maxMarginLogicalLeft; 2336 int maxMarginLogicalRight; 2337 int maxLogicalLeftPos; 2338 2339 computePositionedLogicalWidthUsing(style()->logicalMaxWidth(), containerBlock, containerDirection, 2340 containerLogicalWidth, bordersPlusPadding, 2341 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight, 2342 maxLogicalWidth, maxMarginLogicalLeft, maxMarginLogicalRight, maxLogicalLeftPos); 2343 2344 if (logicalWidth() > maxLogicalWidth) { 2345 setLogicalWidth(maxLogicalWidth); 2346 marginLogicalLeftAlias = maxMarginLogicalLeft; 2347 marginLogicalRightAlias = maxMarginLogicalRight; 2348 setLogicalLeft(maxLogicalLeftPos); 2349 } 2350 } 2351 2352 // Calculate constraint equation values for 'min-width' case. 2353 if (!style()->logicalMinWidth().isZero()) { 2354 int minLogicalWidth; 2355 int minMarginLogicalLeft; 2356 int minMarginLogicalRight; 2357 int minLogicalLeftPos; 2358 2359 computePositionedLogicalWidthUsing(style()->logicalMinWidth(), containerBlock, containerDirection, 2360 containerLogicalWidth, bordersPlusPadding, 2361 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight, 2362 minLogicalWidth, minMarginLogicalLeft, minMarginLogicalRight, minLogicalLeftPos); 2363 2364 if (logicalWidth() < minLogicalWidth) { 2365 setLogicalWidth(minLogicalWidth); 2366 marginLogicalLeftAlias = minMarginLogicalLeft; 2367 marginLogicalRightAlias = minMarginLogicalRight; 2368 setLogicalLeft(minLogicalLeftPos); 2369 } 2370 } 2371 2372 if (stretchesToMinIntrinsicLogicalWidth() && logicalWidth() < minPreferredLogicalWidth() - bordersPlusPadding) { 2373 computePositionedLogicalWidthUsing(Length(minPreferredLogicalWidth() - bordersPlusPadding, Fixed), containerBlock, containerDirection, 2374 containerLogicalWidth, bordersPlusPadding, 2375 logicalLeft, logicalRight, marginLogicalLeft, marginLogicalRight, 2376 logicalWidthResult, marginLogicalLeftAlias, marginLogicalRightAlias, logicalLeftResult); 2377 setLogicalWidth(logicalWidthResult); 2378 setLogicalLeft(logicalLeftResult); 2379 } 2380 2381 // Put logicalWidth() into correct form. 2382 setLogicalWidth(logicalWidth() + bordersPlusPadding); 2383 } 2384 2385 static void computeLogicalLeftPositionedOffset(int& logicalLeftPos, const RenderBox* child, int logicalWidthValue, const RenderBoxModelObject* containerBlock, int containerLogicalWidth) 2386 { 2387 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped 2388 // 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. 2389 if (containerBlock->isHorizontalWritingMode() != child->isHorizontalWritingMode() && containerBlock->style()->isFlippedBlocksWritingMode()) { 2390 logicalLeftPos = containerLogicalWidth - logicalWidthValue - logicalLeftPos; 2391 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderRight() : containerBlock->borderBottom()); 2392 } else 2393 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderLeft() : containerBlock->borderTop()); 2394 } 2395 2396 void RenderBox::computePositionedLogicalWidthUsing(Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection, 2397 int containerLogicalWidth, int bordersPlusPadding, 2398 Length logicalLeft, Length logicalRight, Length marginLogicalLeft, Length marginLogicalRight, 2399 int& logicalWidthValue, int& marginLogicalLeftValue, int& marginLogicalRightValue, int& logicalLeftPos) 2400 { 2401 // 'left' and 'right' cannot both be 'auto' because one would of been 2402 // converted to the static position already 2403 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto())); 2404 2405 int logicalLeftValue = 0; 2406 2407 bool logicalWidthIsAuto = logicalWidth.isIntrinsicOrAuto(); 2408 bool logicalLeftIsAuto = logicalLeft.isAuto(); 2409 bool logicalRightIsAuto = logicalRight.isAuto(); 2410 2411 if (!logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) { 2412 /*-----------------------------------------------------------------------*\ 2413 * If none of the three is 'auto': If both 'margin-left' and 'margin- 2414 * right' are 'auto', solve the equation under the extra constraint that 2415 * the two margins get equal values, unless this would make them negative, 2416 * in which case when direction of the containing block is 'ltr' ('rtl'), 2417 * set 'margin-left' ('margin-right') to zero and solve for 'margin-right' 2418 * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', 2419 * solve the equation for that value. If the values are over-constrained, 2420 * ignore the value for 'left' (in case the 'direction' property of the 2421 * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') 2422 * and solve for that value. 2423 \*-----------------------------------------------------------------------*/ 2424 // NOTE: It is not necessary to solve for 'right' in the over constrained 2425 // case because the value is not used for any further calculations. 2426 2427 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2428 logicalWidthValue = computeContentBoxLogicalWidth(logicalWidth.calcValue(containerLogicalWidth)); 2429 2430 const int availableSpace = containerLogicalWidth - (logicalLeftValue + logicalWidthValue + logicalRight.calcValue(containerLogicalWidth) + bordersPlusPadding); 2431 2432 // Margins are now the only unknown 2433 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) { 2434 // Both margins auto, solve for equality 2435 if (availableSpace >= 0) { 2436 marginLogicalLeftValue = availableSpace / 2; // split the difference 2437 marginLogicalRightValue = availableSpace - marginLogicalLeftValue; // account for odd valued differences 2438 } else { 2439 // see FIXME 1 2440 if (containerDirection == LTR) { 2441 marginLogicalLeftValue = 0; 2442 marginLogicalRightValue = availableSpace; // will be negative 2443 } else { 2444 marginLogicalLeftValue = availableSpace; // will be negative 2445 marginLogicalRightValue = 0; 2446 } 2447 } 2448 } else if (marginLogicalLeft.isAuto()) { 2449 // Solve for left margin 2450 marginLogicalRightValue = marginLogicalRight.calcValue(containerLogicalWidth); 2451 marginLogicalLeftValue = availableSpace - marginLogicalRightValue; 2452 } else if (marginLogicalRight.isAuto()) { 2453 // Solve for right margin 2454 marginLogicalLeftValue = marginLogicalLeft.calcValue(containerLogicalWidth); 2455 marginLogicalRightValue = availableSpace - marginLogicalLeftValue; 2456 } else { 2457 // Over-constrained, solve for left if direction is RTL 2458 marginLogicalLeftValue = marginLogicalLeft.calcValue(containerLogicalWidth); 2459 marginLogicalRightValue = marginLogicalRight.calcValue(containerLogicalWidth); 2460 2461 // see FIXME 1 -- used to be "this->style()->direction()" 2462 if (containerDirection == RTL) 2463 logicalLeftValue = (availableSpace + logicalLeftValue) - marginLogicalLeftValue - marginLogicalRightValue; 2464 } 2465 } else { 2466 /*--------------------------------------------------------------------*\ 2467 * Otherwise, set 'auto' values for 'margin-left' and 'margin-right' 2468 * to 0, and pick the one of the following six rules that applies. 2469 * 2470 * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the 2471 * width is shrink-to-fit. Then solve for 'left' 2472 * 2473 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT 2474 * ------------------------------------------------------------------ 2475 * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if 2476 * the 'direction' property of the containing block is 'ltr' set 2477 * 'left' to the static position, otherwise set 'right' to the 2478 * static position. Then solve for 'left' (if 'direction is 'rtl') 2479 * or 'right' (if 'direction' is 'ltr'). 2480 * ------------------------------------------------------------------ 2481 * 2482 * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the 2483 * width is shrink-to-fit . Then solve for 'right' 2484 * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve 2485 * for 'left' 2486 * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve 2487 * for 'width' 2488 * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve 2489 * for 'right' 2490 * 2491 * Calculation of the shrink-to-fit width is similar to calculating the 2492 * width of a table cell using the automatic table layout algorithm. 2493 * Roughly: calculate the preferred width by formatting the content 2494 * without breaking lines other than where explicit line breaks occur, 2495 * and also calculate the preferred minimum width, e.g., by trying all 2496 * possible line breaks. CSS 2.1 does not define the exact algorithm. 2497 * Thirdly, calculate the available width: this is found by solving 2498 * for 'width' after setting 'left' (in case 1) or 'right' (in case 3) 2499 * to 0. 2500 * 2501 * Then the shrink-to-fit width is: 2502 * min(max(preferred minimum width, available width), preferred width). 2503 \*--------------------------------------------------------------------*/ 2504 // NOTE: For rules 3 and 6 it is not necessary to solve for 'right' 2505 // because the value is not used for any further calculations. 2506 2507 // Calculate margins, 'auto' margins are ignored. 2508 marginLogicalLeftValue = marginLogicalLeft.calcMinValue(containerLogicalWidth); 2509 marginLogicalRightValue = marginLogicalRight.calcMinValue(containerLogicalWidth); 2510 2511 const int availableSpace = containerLogicalWidth - (marginLogicalLeftValue + marginLogicalRightValue + bordersPlusPadding); 2512 2513 // FIXME: Is there a faster way to find the correct case? 2514 // Use rule/case that applies. 2515 if (logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) { 2516 // RULE 1: (use shrink-to-fit for width, and solve of left) 2517 int logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2518 2519 // FIXME: would it be better to have shrink-to-fit in one step? 2520 int preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding; 2521 int preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding; 2522 int availableWidth = availableSpace - logicalRightValue; 2523 logicalWidthValue = min(max(preferredMinWidth, availableWidth), preferredWidth); 2524 logicalLeftValue = availableSpace - (logicalWidthValue + logicalRightValue); 2525 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && logicalRightIsAuto) { 2526 // RULE 3: (use shrink-to-fit for width, and no need solve of right) 2527 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2528 2529 // FIXME: would it be better to have shrink-to-fit in one step? 2530 int preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding; 2531 int preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding; 2532 int availableWidth = availableSpace - logicalLeftValue; 2533 logicalWidthValue = min(max(preferredMinWidth, availableWidth), preferredWidth); 2534 } else if (logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) { 2535 // RULE 4: (solve for left) 2536 logicalWidthValue = computeContentBoxLogicalWidth(logicalWidth.calcValue(containerLogicalWidth)); 2537 logicalLeftValue = availableSpace - (logicalWidthValue + logicalRight.calcValue(containerLogicalWidth)); 2538 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) { 2539 // RULE 5: (solve for width) 2540 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2541 logicalWidthValue = availableSpace - (logicalLeftValue + logicalRight.calcValue(containerLogicalWidth)); 2542 } else if (!logicalLeftIsAuto && !logicalWidthIsAuto && logicalRightIsAuto) { 2543 // RULE 6: (no need solve for right) 2544 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2545 logicalWidthValue = computeContentBoxLogicalWidth(logicalWidth.calcValue(containerLogicalWidth)); 2546 } 2547 } 2548 2549 // Use computed values to calculate the horizontal position. 2550 2551 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively 2552 // positioned, inline because right now, it is using the logical left position 2553 // of the first line box when really it should use the last line box. When 2554 // this is fixed elsewhere, this block should be removed. 2555 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) { 2556 const RenderInline* flow = toRenderInline(containerBlock); 2557 InlineFlowBox* firstLine = flow->firstLineBox(); 2558 InlineFlowBox* lastLine = flow->lastLineBox(); 2559 if (firstLine && lastLine && firstLine != lastLine) { 2560 logicalLeftPos = logicalLeftValue + marginLogicalLeftValue + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft()); 2561 return; 2562 } 2563 } 2564 2565 logicalLeftPos = logicalLeftValue + marginLogicalLeftValue; 2566 computeLogicalLeftPositionedOffset(logicalLeftPos, this, logicalWidthValue, containerBlock, containerLogicalWidth); 2567 } 2568 2569 static void computeBlockStaticDistance(Length& logicalTop, Length& logicalBottom, const RenderBox* child, const RenderBoxModelObject* containerBlock) 2570 { 2571 if (!logicalTop.isAuto() || !logicalBottom.isAuto()) 2572 return; 2573 2574 // FIXME: The static distance computation has not been patched for mixed writing modes. 2575 int staticLogicalTop = child->layer()->staticBlockPosition() - containerBlock->borderBefore(); 2576 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) { 2577 if (curr->isBox() && !curr->isTableRow()) 2578 staticLogicalTop += toRenderBox(curr)->logicalTop(); 2579 } 2580 logicalTop.setValue(Fixed, staticLogicalTop); 2581 } 2582 2583 void RenderBox::computePositionedLogicalHeight() 2584 { 2585 if (isReplaced()) { 2586 computePositionedLogicalHeightReplaced(); 2587 return; 2588 } 2589 2590 // The following is based off of the W3C Working Draft from April 11, 2006 of 2591 // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements" 2592 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height> 2593 // (block-style-comments in this function and in computePositionedLogicalHeightUsing() 2594 // correspond to text from the spec) 2595 2596 2597 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. 2598 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 2599 2600 const int containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); 2601 2602 bool isHorizontal = isHorizontalWritingMode(); 2603 bool isFlipped = style()->isFlippedBlocksWritingMode(); 2604 const int bordersPlusPadding = borderAndPaddingLogicalHeight(); 2605 const Length marginBefore = style()->marginBefore(); 2606 const Length marginAfter = style()->marginAfter(); 2607 int& marginBeforeAlias = isHorizontal ? (isFlipped ? m_marginBottom : m_marginTop) : (isFlipped ? m_marginRight: m_marginLeft); 2608 int& marginAfterAlias = isHorizontal ? (isFlipped ? m_marginTop : m_marginBottom) : (isFlipped ? m_marginLeft: m_marginRight); 2609 2610 Length logicalTop = style()->logicalTop(); 2611 Length logicalBottom = style()->logicalBottom(); 2612 2613 /*---------------------------------------------------------------------------*\ 2614 * For the purposes of this section and the next, the term "static position" 2615 * (of an element) refers, roughly, to the position an element would have had 2616 * in the normal flow. More precisely, the static position for 'top' is the 2617 * distance from the top edge of the containing block to the top margin edge 2618 * of a hypothetical box that would have been the first box of the element if 2619 * its 'position' property had been 'static' and 'float' had been 'none'. The 2620 * value is negative if the hypothetical box is above the containing block. 2621 * 2622 * But rather than actually calculating the dimensions of that hypothetical 2623 * box, user agents are free to make a guess at its probable position. 2624 * 2625 * For the purposes of calculating the static position, the containing block 2626 * of fixed positioned elements is the initial containing block instead of 2627 * the viewport. 2628 \*---------------------------------------------------------------------------*/ 2629 2630 // see FIXME 2 2631 // Calculate the static distance if needed. 2632 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock); 2633 2634 int logicalHeightResult; // Needed to compute overflow. 2635 int logicalTopPos; 2636 2637 // Calculate constraint equation values for 'height' case. 2638 computePositionedLogicalHeightUsing(style()->logicalHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, 2639 logicalTop, logicalBottom, marginBefore, marginAfter, 2640 logicalHeightResult, marginBeforeAlias, marginAfterAlias, logicalTopPos); 2641 setLogicalTop(logicalTopPos); 2642 2643 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults). 2644 // see FIXME 3 2645 2646 // Calculate constraint equation values for 'max-height' case. 2647 if (!style()->logicalMaxHeight().isUndefined()) { 2648 int maxLogicalHeight; 2649 int maxMarginBefore; 2650 int maxMarginAfter; 2651 int maxLogicalTopPos; 2652 2653 computePositionedLogicalHeightUsing(style()->logicalMaxHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, 2654 logicalTop, logicalBottom, marginBefore, marginAfter, 2655 maxLogicalHeight, maxMarginBefore, maxMarginAfter, maxLogicalTopPos); 2656 2657 if (logicalHeightResult > maxLogicalHeight) { 2658 logicalHeightResult = maxLogicalHeight; 2659 marginBeforeAlias = maxMarginBefore; 2660 marginAfterAlias = maxMarginAfter; 2661 setLogicalTop(maxLogicalTopPos); 2662 } 2663 } 2664 2665 // Calculate constraint equation values for 'min-height' case. 2666 if (!style()->logicalMinHeight().isZero()) { 2667 int minLogicalHeight; 2668 int minMarginBefore; 2669 int minMarginAfter; 2670 int minLogicalTopPos; 2671 2672 computePositionedLogicalHeightUsing(style()->logicalMinHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, 2673 logicalTop, logicalBottom, marginBefore, marginAfter, 2674 minLogicalHeight, minMarginBefore, minMarginAfter, minLogicalTopPos); 2675 2676 if (logicalHeightResult < minLogicalHeight) { 2677 logicalHeightResult = minLogicalHeight; 2678 marginBeforeAlias = minMarginBefore; 2679 marginAfterAlias = minMarginAfter; 2680 setLogicalTop(minLogicalTopPos); 2681 } 2682 } 2683 2684 // Set final height value. 2685 setLogicalHeight(logicalHeightResult + bordersPlusPadding); 2686 } 2687 2688 static void computeLogicalTopPositionedOffset(int& logicalTopPos, const RenderBox* child, int logicalHeightValue, const RenderBoxModelObject* containerBlock, int containerLogicalHeight) 2689 { 2690 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped 2691 // 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. 2692 if ((child->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() != containerBlock->isHorizontalWritingMode()) 2693 || (child->style()->isFlippedBlocksWritingMode() != containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode())) 2694 logicalTopPos = containerLogicalHeight - logicalHeightValue - logicalTopPos; 2695 2696 // Our offset is from the logical bottom edge in a flipped environment, e.g., right for vertical-rl and bottom for horizontal-bt. 2697 if (containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()) { 2698 if (child->isHorizontalWritingMode()) 2699 logicalTopPos += containerBlock->borderBottom(); 2700 else 2701 logicalTopPos += containerBlock->borderRight(); 2702 } else { 2703 if (child->isHorizontalWritingMode()) 2704 logicalTopPos += containerBlock->borderTop(); 2705 else 2706 logicalTopPos += containerBlock->borderLeft(); 2707 } 2708 } 2709 2710 void RenderBox::computePositionedLogicalHeightUsing(Length logicalHeightLength, const RenderBoxModelObject* containerBlock, 2711 int containerLogicalHeight, int bordersPlusPadding, 2712 Length logicalTop, Length logicalBottom, Length marginBefore, Length marginAfter, 2713 int& logicalHeightValue, int& marginBeforeValue, int& marginAfterValue, int& logicalTopPos) 2714 { 2715 // 'top' and 'bottom' cannot both be 'auto' because 'top would of been 2716 // converted to the static position in computePositionedLogicalHeight() 2717 ASSERT(!(logicalTop.isAuto() && logicalBottom.isAuto())); 2718 2719 int contentLogicalHeight = logicalHeight() - bordersPlusPadding; 2720 2721 int logicalTopValue = 0; 2722 2723 bool logicalHeightIsAuto = logicalHeightLength.isAuto(); 2724 bool logicalTopIsAuto = logicalTop.isAuto(); 2725 bool logicalBottomIsAuto = logicalBottom.isAuto(); 2726 2727 // Height is never unsolved for tables. 2728 if (isTable()) { 2729 logicalHeightLength.setValue(Fixed, contentLogicalHeight); 2730 logicalHeightIsAuto = false; 2731 } 2732 2733 if (!logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) { 2734 /*-----------------------------------------------------------------------*\ 2735 * If none of the three are 'auto': If both 'margin-top' and 'margin- 2736 * bottom' are 'auto', solve the equation under the extra constraint that 2737 * the two margins get equal values. If one of 'margin-top' or 'margin- 2738 * bottom' is 'auto', solve the equation for that value. If the values 2739 * are over-constrained, ignore the value for 'bottom' and solve for that 2740 * value. 2741 \*-----------------------------------------------------------------------*/ 2742 // NOTE: It is not necessary to solve for 'bottom' in the over constrained 2743 // case because the value is not used for any further calculations. 2744 2745 logicalHeightValue = computeContentBoxLogicalHeight(logicalHeightLength.calcValue(containerLogicalHeight)); 2746 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 2747 2748 const int availableSpace = containerLogicalHeight - (logicalTopValue + logicalHeightValue + logicalBottom.calcValue(containerLogicalHeight) + bordersPlusPadding); 2749 2750 // Margins are now the only unknown 2751 if (marginBefore.isAuto() && marginAfter.isAuto()) { 2752 // Both margins auto, solve for equality 2753 // NOTE: This may result in negative values. 2754 marginBeforeValue = availableSpace / 2; // split the difference 2755 marginAfterValue = availableSpace - marginBeforeValue; // account for odd valued differences 2756 } else if (marginBefore.isAuto()) { 2757 // Solve for top margin 2758 marginAfterValue = marginAfter.calcValue(containerLogicalHeight); 2759 marginBeforeValue = availableSpace - marginAfterValue; 2760 } else if (marginAfter.isAuto()) { 2761 // Solve for bottom margin 2762 marginBeforeValue = marginBefore.calcValue(containerLogicalHeight); 2763 marginAfterValue = availableSpace - marginBeforeValue; 2764 } else { 2765 // Over-constrained, (no need solve for bottom) 2766 marginBeforeValue = marginBefore.calcValue(containerLogicalHeight); 2767 marginAfterValue = marginAfter.calcValue(containerLogicalHeight); 2768 } 2769 } else { 2770 /*--------------------------------------------------------------------*\ 2771 * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom' 2772 * to 0, and pick the one of the following six rules that applies. 2773 * 2774 * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then 2775 * the height is based on the content, and solve for 'top'. 2776 * 2777 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT 2778 * ------------------------------------------------------------------ 2779 * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then 2780 * set 'top' to the static position, and solve for 'bottom'. 2781 * ------------------------------------------------------------------ 2782 * 2783 * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then 2784 * the height is based on the content, and solve for 'bottom'. 2785 * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and 2786 * solve for 'top'. 2787 * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and 2788 * solve for 'height'. 2789 * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and 2790 * solve for 'bottom'. 2791 \*--------------------------------------------------------------------*/ 2792 // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom' 2793 // because the value is not used for any further calculations. 2794 2795 // Calculate margins, 'auto' margins are ignored. 2796 marginBeforeValue = marginBefore.calcMinValue(containerLogicalHeight); 2797 marginAfterValue = marginAfter.calcMinValue(containerLogicalHeight); 2798 2799 const int availableSpace = containerLogicalHeight - (marginBeforeValue + marginAfterValue + bordersPlusPadding); 2800 2801 // Use rule/case that applies. 2802 if (logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) { 2803 // RULE 1: (height is content based, solve of top) 2804 logicalHeightValue = contentLogicalHeight; 2805 logicalTopValue = availableSpace - (logicalHeightValue + logicalBottom.calcValue(containerLogicalHeight)); 2806 } else if (!logicalTopIsAuto && logicalHeightIsAuto && logicalBottomIsAuto) { 2807 // RULE 3: (height is content based, no need solve of bottom) 2808 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 2809 logicalHeightValue = contentLogicalHeight; 2810 } else if (logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) { 2811 // RULE 4: (solve of top) 2812 logicalHeightValue = computeContentBoxLogicalHeight(logicalHeightLength.calcValue(containerLogicalHeight)); 2813 logicalTopValue = availableSpace - (logicalHeightValue + logicalBottom.calcValue(containerLogicalHeight)); 2814 } else if (!logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) { 2815 // RULE 5: (solve of height) 2816 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 2817 logicalHeightValue = max(0, availableSpace - (logicalTopValue + logicalBottom.calcValue(containerLogicalHeight))); 2818 } else if (!logicalTopIsAuto && !logicalHeightIsAuto && logicalBottomIsAuto) { 2819 // RULE 6: (no need solve of bottom) 2820 logicalHeightValue = computeContentBoxLogicalHeight(logicalHeightLength.calcValue(containerLogicalHeight)); 2821 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 2822 } 2823 } 2824 2825 // Use computed values to calculate the vertical position. 2826 logicalTopPos = logicalTopValue + marginBeforeValue; 2827 computeLogicalTopPositionedOffset(logicalTopPos, this, logicalHeightValue, containerBlock, containerLogicalHeight); 2828 } 2829 2830 void RenderBox::computePositionedLogicalWidthReplaced() 2831 { 2832 // The following is based off of the W3C Working Draft from April 11, 2006 of 2833 // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements" 2834 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width> 2835 // (block-style-comments in this function correspond to text from the spec and 2836 // the numbers correspond to numbers in spec) 2837 2838 // We don't use containingBlock(), since we may be positioned by an enclosing 2839 // relative positioned inline. 2840 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 2841 2842 const int containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); 2843 2844 // To match WinIE, in quirks mode use the parent's 'direction' property 2845 // instead of the the container block's. 2846 TextDirection containerDirection = (document()->inQuirksMode()) ? parent()->style()->direction() : containerBlock->style()->direction(); 2847 2848 // Variables to solve. 2849 bool isHorizontal = isHorizontalWritingMode(); 2850 Length logicalLeft = style()->logicalLeft(); 2851 Length logicalRight = style()->logicalRight(); 2852 Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop(); 2853 Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom(); 2854 int& marginLogicalLeftAlias = isHorizontal ? m_marginLeft : m_marginTop; 2855 int& marginLogicalRightAlias = isHorizontal ? m_marginRight : m_marginBottom; 2856 2857 /*-----------------------------------------------------------------------*\ 2858 * 1. The used value of 'width' is determined as for inline replaced 2859 * elements. 2860 \*-----------------------------------------------------------------------*/ 2861 // NOTE: This value of width is FINAL in that the min/max width calculations 2862 // are dealt with in computeReplacedWidth(). This means that the steps to produce 2863 // correct max/min in the non-replaced version, are not necessary. 2864 setLogicalWidth(computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth()); 2865 const int availableSpace = containerLogicalWidth - logicalWidth(); 2866 2867 /*-----------------------------------------------------------------------*\ 2868 * 2. If both 'left' and 'right' have the value 'auto', then if 'direction' 2869 * of the containing block is 'ltr', set 'left' to the static position; 2870 * else if 'direction' is 'rtl', set 'right' to the static position. 2871 \*-----------------------------------------------------------------------*/ 2872 // see FIXME 2 2873 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth, containerDirection); 2874 2875 /*-----------------------------------------------------------------------*\ 2876 * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' 2877 * or 'margin-right' with '0'. 2878 \*-----------------------------------------------------------------------*/ 2879 if (logicalLeft.isAuto() || logicalRight.isAuto()) { 2880 if (marginLogicalLeft.isAuto()) 2881 marginLogicalLeft.setValue(Fixed, 0); 2882 if (marginLogicalRight.isAuto()) 2883 marginLogicalRight.setValue(Fixed, 0); 2884 } 2885 2886 /*-----------------------------------------------------------------------*\ 2887 * 4. If at this point both 'margin-left' and 'margin-right' are still 2888 * 'auto', solve the equation under the extra constraint that the two 2889 * margins must get equal values, unless this would make them negative, 2890 * in which case when the direction of the containing block is 'ltr' 2891 * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for 2892 * 'margin-right' ('margin-left'). 2893 \*-----------------------------------------------------------------------*/ 2894 int logicalLeftValue = 0; 2895 int logicalRightValue = 0; 2896 2897 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) { 2898 // 'left' and 'right' cannot be 'auto' due to step 3 2899 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto())); 2900 2901 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2902 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2903 2904 int difference = availableSpace - (logicalLeftValue + logicalRightValue); 2905 if (difference > 0) { 2906 marginLogicalLeftAlias = difference / 2; // split the difference 2907 marginLogicalRightAlias = difference - marginLogicalLeftAlias; // account for odd valued differences 2908 } else { 2909 // see FIXME 1 2910 if (containerDirection == LTR) { 2911 marginLogicalLeftAlias = 0; 2912 marginLogicalRightAlias = difference; // will be negative 2913 } else { 2914 marginLogicalLeftAlias = difference; // will be negative 2915 marginLogicalRightAlias = 0; 2916 } 2917 } 2918 2919 /*-----------------------------------------------------------------------*\ 2920 * 5. If at this point there is an 'auto' left, solve the equation for 2921 * that value. 2922 \*-----------------------------------------------------------------------*/ 2923 } else if (logicalLeft.isAuto()) { 2924 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth); 2925 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth); 2926 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2927 2928 // Solve for 'left' 2929 logicalLeftValue = availableSpace - (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias); 2930 } else if (logicalRight.isAuto()) { 2931 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth); 2932 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth); 2933 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2934 2935 // Solve for 'right' 2936 logicalRightValue = availableSpace - (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias); 2937 } else if (marginLogicalLeft.isAuto()) { 2938 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth); 2939 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2940 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2941 2942 // Solve for 'margin-left' 2943 marginLogicalLeftAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalRightAlias); 2944 } else if (marginLogicalRight.isAuto()) { 2945 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth); 2946 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2947 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2948 2949 // Solve for 'margin-right' 2950 marginLogicalRightAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias); 2951 } else { 2952 // Nothing is 'auto', just calculate the values. 2953 marginLogicalLeftAlias = marginLogicalLeft.calcValue(containerLogicalWidth); 2954 marginLogicalRightAlias = marginLogicalRight.calcValue(containerLogicalWidth); 2955 logicalRightValue = logicalRight.calcValue(containerLogicalWidth); 2956 logicalLeftValue = logicalLeft.calcValue(containerLogicalWidth); 2957 } 2958 2959 /*-----------------------------------------------------------------------*\ 2960 * 6. If at this point the values are over-constrained, ignore the value 2961 * for either 'left' (in case the 'direction' property of the 2962 * containing block is 'rtl') or 'right' (in case 'direction' is 2963 * 'ltr') and solve for that value. 2964 \*-----------------------------------------------------------------------*/ 2965 // NOTE: It is not necessary to solve for 'right' when the direction is 2966 // LTR because the value is not used. 2967 int totalLogicalWidth = logicalWidth() + logicalLeftValue + logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias; 2968 if (totalLogicalWidth > containerLogicalWidth && (containerDirection == RTL)) 2969 logicalLeftValue = containerLogicalWidth - (totalLogicalWidth - logicalLeftValue); 2970 2971 // FIXME: Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space, so that 2972 // can make the result here rather complicated to compute. 2973 2974 // Use computed values to calculate the horizontal position. 2975 2976 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively 2977 // positioned, inline containing block because right now, it is using the logical left position 2978 // of the first line box when really it should use the last line box. When 2979 // this is fixed elsewhere, this block should be removed. 2980 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) { 2981 const RenderInline* flow = toRenderInline(containerBlock); 2982 InlineFlowBox* firstLine = flow->firstLineBox(); 2983 InlineFlowBox* lastLine = flow->lastLineBox(); 2984 if (firstLine && lastLine && firstLine != lastLine) { 2985 setLogicalLeft(logicalLeftValue + marginLogicalLeftAlias + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft())); 2986 return; 2987 } 2988 } 2989 2990 int logicalLeftPos = logicalLeftValue + marginLogicalLeftAlias; 2991 computeLogicalLeftPositionedOffset(logicalLeftPos, this, logicalWidth(), containerBlock, containerLogicalWidth); 2992 setLogicalLeft(logicalLeftPos); 2993 } 2994 2995 void RenderBox::computePositionedLogicalHeightReplaced() 2996 { 2997 // The following is based off of the W3C Working Draft from April 11, 2006 of 2998 // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements" 2999 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height> 3000 // (block-style-comments in this function correspond to text from the spec and 3001 // the numbers correspond to numbers in spec) 3002 3003 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. 3004 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 3005 3006 const int containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); 3007 3008 // Variables to solve. 3009 bool isHorizontal = isHorizontalWritingMode(); 3010 bool isFlipped = style()->isFlippedBlocksWritingMode(); 3011 Length marginBefore = style()->marginBefore(); 3012 Length marginAfter = style()->marginAfter(); 3013 int& marginBeforeAlias = isHorizontal ? (isFlipped ? m_marginBottom : m_marginTop) : (isFlipped ? m_marginRight: m_marginLeft); 3014 int& marginAfterAlias = isHorizontal ? (isFlipped ? m_marginTop : m_marginBottom) : (isFlipped ? m_marginLeft: m_marginRight); 3015 3016 Length logicalTop = style()->logicalTop(); 3017 Length logicalBottom = style()->logicalBottom(); 3018 3019 /*-----------------------------------------------------------------------*\ 3020 * 1. The used value of 'height' is determined as for inline replaced 3021 * elements. 3022 \*-----------------------------------------------------------------------*/ 3023 // NOTE: This value of height is FINAL in that the min/max height calculations 3024 // are dealt with in computeReplacedHeight(). This means that the steps to produce 3025 // correct max/min in the non-replaced version, are not necessary. 3026 setLogicalHeight(computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight()); 3027 const int availableSpace = containerLogicalHeight - logicalHeight(); 3028 3029 /*-----------------------------------------------------------------------*\ 3030 * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top' 3031 * with the element's static position. 3032 \*-----------------------------------------------------------------------*/ 3033 // see FIXME 2 3034 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock); 3035 3036 /*-----------------------------------------------------------------------*\ 3037 * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or 3038 * 'margin-bottom' with '0'. 3039 \*-----------------------------------------------------------------------*/ 3040 // FIXME: The spec. says that this step should only be taken when bottom is 3041 // auto, but if only top is auto, this makes step 4 impossible. 3042 if (logicalTop.isAuto() || logicalBottom.isAuto()) { 3043 if (marginBefore.isAuto()) 3044 marginBefore.setValue(Fixed, 0); 3045 if (marginAfter.isAuto()) 3046 marginAfter.setValue(Fixed, 0); 3047 } 3048 3049 /*-----------------------------------------------------------------------*\ 3050 * 4. If at this point both 'margin-top' and 'margin-bottom' are still 3051 * 'auto', solve the equation under the extra constraint that the two 3052 * margins must get equal values. 3053 \*-----------------------------------------------------------------------*/ 3054 int logicalTopValue = 0; 3055 int logicalBottomValue = 0; 3056 3057 if (marginBefore.isAuto() && marginAfter.isAuto()) { 3058 // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined. 3059 ASSERT(!(logicalTop.isAuto() || logicalBottom.isAuto())); 3060 3061 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3062 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight); 3063 3064 int difference = availableSpace - (logicalTopValue + logicalBottomValue); 3065 // NOTE: This may result in negative values. 3066 marginBeforeAlias = difference / 2; // split the difference 3067 marginAfterAlias = difference - marginBeforeAlias; // account for odd valued differences 3068 3069 /*-----------------------------------------------------------------------*\ 3070 * 5. If at this point there is only one 'auto' left, solve the equation 3071 * for that value. 3072 \*-----------------------------------------------------------------------*/ 3073 } else if (logicalTop.isAuto()) { 3074 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight); 3075 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight); 3076 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight); 3077 3078 // Solve for 'top' 3079 logicalTopValue = availableSpace - (logicalBottomValue + marginBeforeAlias + marginAfterAlias); 3080 } else if (logicalBottom.isAuto()) { 3081 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight); 3082 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight); 3083 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3084 3085 // Solve for 'bottom' 3086 // NOTE: It is not necessary to solve for 'bottom' because we don't ever 3087 // use the value. 3088 } else if (marginBefore.isAuto()) { 3089 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight); 3090 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3091 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight); 3092 3093 // Solve for 'margin-top' 3094 marginBeforeAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginAfterAlias); 3095 } else if (marginAfter.isAuto()) { 3096 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight); 3097 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3098 logicalBottomValue = logicalBottom.calcValue(containerLogicalHeight); 3099 3100 // Solve for 'margin-bottom' 3101 marginAfterAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginBeforeAlias); 3102 } else { 3103 // Nothing is 'auto', just calculate the values. 3104 marginBeforeAlias = marginBefore.calcValue(containerLogicalHeight); 3105 marginAfterAlias = marginAfter.calcValue(containerLogicalHeight); 3106 logicalTopValue = logicalTop.calcValue(containerLogicalHeight); 3107 // NOTE: It is not necessary to solve for 'bottom' because we don't ever 3108 // use the value. 3109 } 3110 3111 /*-----------------------------------------------------------------------*\ 3112 * 6. If at this point the values are over-constrained, ignore the value 3113 * for 'bottom' and solve for that value. 3114 \*-----------------------------------------------------------------------*/ 3115 // NOTE: It is not necessary to do this step because we don't end up using 3116 // the value of 'bottom' regardless of whether the values are over-constrained 3117 // or not. 3118 3119 // Use computed values to calculate the vertical position. 3120 int logicalTopPos = logicalTopValue + marginBeforeAlias; 3121 computeLogicalTopPositionedOffset(logicalTopPos, this, logicalHeight(), containerBlock, containerLogicalHeight); 3122 setLogicalTop(logicalTopPos); 3123 } 3124 3125 IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWidthToEndOfLine) 3126 { 3127 // VisiblePositions at offsets inside containers either a) refer to the positions before/after 3128 // those containers (tables and select elements) or b) refer to the position inside an empty block. 3129 // They never refer to children. 3130 // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements. 3131 3132 // FIXME: What about border and padding? 3133 IntRect rect(x(), y(), caretWidth, height()); 3134 bool ltr = box ? box->isLeftToRightDirection() : style()->isLeftToRightDirection(); 3135 3136 if ((!caretOffset) ^ ltr) 3137 rect.move(IntSize(width() - caretWidth, 0)); 3138 3139 if (box) { 3140 RootInlineBox* rootBox = box->root(); 3141 int top = rootBox->lineTop(); 3142 rect.setY(top); 3143 rect.setHeight(rootBox->lineBottom() - top); 3144 } 3145 3146 // If height of box is smaller than font height, use the latter one, 3147 // otherwise the caret might become invisible. 3148 // 3149 // Also, if the box is not a replaced element, always use the font height. 3150 // This prevents the "big caret" bug described in: 3151 // <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point 3152 // 3153 // FIXME: ignoring :first-line, missing good reason to take care of 3154 int fontHeight = style()->fontMetrics().height(); 3155 if (fontHeight > rect.height() || (!isReplaced() && !isTable())) 3156 rect.setHeight(fontHeight); 3157 3158 if (extraWidthToEndOfLine) 3159 *extraWidthToEndOfLine = x() + width() - rect.maxX(); 3160 3161 // Move to local coords 3162 rect.move(-x(), -y()); 3163 return rect; 3164 } 3165 3166 VisiblePosition RenderBox::positionForPoint(const IntPoint& point) 3167 { 3168 // no children...return this render object's element, if there is one, and offset 0 3169 if (!firstChild()) 3170 return createVisiblePosition(node() ? firstPositionInOrBeforeNode(node()) : Position(0, 0)); 3171 3172 int xPos = point.x(); 3173 int yPos = point.y(); 3174 3175 if (isTable() && node()) { 3176 int right = contentWidth() + borderAndPaddingWidth(); 3177 int bottom = contentHeight() + borderAndPaddingHeight(); 3178 3179 if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) { 3180 if (xPos <= right / 2) 3181 return createVisiblePosition(firstPositionInOrBeforeNode(node())); 3182 return createVisiblePosition(lastPositionInOrAfterNode(node())); 3183 } 3184 } 3185 3186 // Pass off to the closest child. 3187 int minDist = INT_MAX; 3188 RenderBox* closestRenderer = 0; 3189 int newX = xPos; 3190 int newY = yPos; 3191 if (isTableRow()) { 3192 newX += x(); 3193 newY += y(); 3194 } 3195 for (RenderObject* renderObject = firstChild(); renderObject; renderObject = renderObject->nextSibling()) { 3196 if ((!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isBlockFlow() ) 3197 || renderObject->style()->visibility() != VISIBLE) 3198 continue; 3199 3200 if (!renderObject->isBox()) 3201 continue; 3202 3203 RenderBox* renderer = toRenderBox(renderObject); 3204 3205 int top = renderer->borderTop() + renderer->paddingTop() + (isTableRow() ? 0 : renderer->y()); 3206 int bottom = top + renderer->contentHeight(); 3207 int left = renderer->borderLeft() + renderer->paddingLeft() + (isTableRow() ? 0 : renderer->x()); 3208 int right = left + renderer->contentWidth(); 3209 3210 if (xPos <= right && xPos >= left && yPos <= top && yPos >= bottom) { 3211 if (renderer->isTableRow()) 3212 return renderer->positionForCoordinates(xPos + newX - renderer->x(), yPos + newY - renderer->y()); 3213 return renderer->positionForCoordinates(xPos - renderer->x(), yPos - renderer->y()); 3214 } 3215 3216 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces 3217 // and use a different compare depending on which piece (x, y) is in. 3218 IntPoint cmp; 3219 if (xPos > right) { 3220 if (yPos < top) 3221 cmp = IntPoint(right, top); 3222 else if (yPos > bottom) 3223 cmp = IntPoint(right, bottom); 3224 else 3225 cmp = IntPoint(right, yPos); 3226 } else if (xPos < left) { 3227 if (yPos < top) 3228 cmp = IntPoint(left, top); 3229 else if (yPos > bottom) 3230 cmp = IntPoint(left, bottom); 3231 else 3232 cmp = IntPoint(left, yPos); 3233 } else { 3234 if (yPos < top) 3235 cmp = IntPoint(xPos, top); 3236 else 3237 cmp = IntPoint(xPos, bottom); 3238 } 3239 3240 int x1minusx2 = cmp.x() - xPos; 3241 int y1minusy2 = cmp.y() - yPos; 3242 3243 int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2; 3244 if (dist < minDist) { 3245 closestRenderer = renderer; 3246 minDist = dist; 3247 } 3248 } 3249 3250 if (closestRenderer) 3251 return closestRenderer->positionForCoordinates(newX - closestRenderer->x(), newY - closestRenderer->y()); 3252 3253 return createVisiblePosition(firstPositionInOrBeforeNode(node())); 3254 } 3255 3256 bool RenderBox::shrinkToAvoidFloats() const 3257 { 3258 // Floating objects don't shrink. Objects that don't avoid floats don't shrink. Marquees don't shrink. 3259 if ((isInline() && !isHTMLMarquee()) || !avoidsFloats() || isFloating()) 3260 return false; 3261 3262 // All auto-width objects that avoid floats should always use lineWidth. 3263 return style()->width().isAuto(); 3264 } 3265 3266 bool RenderBox::avoidsFloats() const 3267 { 3268 return isReplaced() || hasOverflowClip() || isHR() || isLegend() || isWritingModeRoot() || isDeprecatedFlexItem(); 3269 } 3270 3271 void RenderBox::addShadowOverflow() 3272 { 3273 int shadowLeft; 3274 int shadowRight; 3275 int shadowTop; 3276 int shadowBottom; 3277 style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft); 3278 IntRect borderBox = borderBoxRect(); 3279 int overflowLeft = borderBox.x() + shadowLeft; 3280 int overflowRight = borderBox.maxX() + shadowRight; 3281 int overflowTop = borderBox.y() + shadowTop; 3282 int overflowBottom = borderBox.maxY() + shadowBottom; 3283 addVisualOverflow(IntRect(overflowLeft, overflowTop, overflowRight - overflowLeft, overflowBottom - overflowTop)); 3284 } 3285 3286 void RenderBox::addOverflowFromChild(RenderBox* child, const IntSize& delta) 3287 { 3288 // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then 3289 // its overflow is internal to it, and we don't care about it. layoutOverflowRectForPropagation takes care of this 3290 // and just propagates the border box rect instead. 3291 IntRect childLayoutOverflowRect = child->layoutOverflowRectForPropagation(style()); 3292 childLayoutOverflowRect.move(delta); 3293 addLayoutOverflow(childLayoutOverflowRect); 3294 3295 // Add in visual overflow from the child. Even if the child clips its overflow, it may still 3296 // have visual overflow of its own set from box shadows or reflections. It is unnecessary to propagate this 3297 // overflow if we are clipping our own overflow. 3298 if (child->hasSelfPaintingLayer() || hasOverflowClip()) 3299 return; 3300 IntRect childVisualOverflowRect = child->visualOverflowRectForPropagation(style()); 3301 childVisualOverflowRect.move(delta); 3302 addVisualOverflow(childVisualOverflowRect); 3303 } 3304 3305 void RenderBox::addLayoutOverflow(const IntRect& rect) 3306 { 3307 IntRect clientBox = clientBoxRect(); 3308 if (clientBox.contains(rect) || rect.isEmpty()) 3309 return; 3310 3311 // For overflow clip objects, we don't want to propagate overflow into unreachable areas. 3312 IntRect overflowRect(rect); 3313 if (hasOverflowClip() || isRenderView()) { 3314 // Overflow is in the block's coordinate space and thus is flipped for horizontal-bt and vertical-rl 3315 // writing modes. At this stage that is actually a simplification, since we can treat horizontal-tb/bt as the same 3316 // and vertical-lr/rl as the same. 3317 bool hasTopOverflow = !style()->isLeftToRightDirection() && !isHorizontalWritingMode(); 3318 bool hasLeftOverflow = !style()->isLeftToRightDirection() && isHorizontalWritingMode(); 3319 3320 if (!hasTopOverflow) 3321 overflowRect.shiftYEdgeTo(max(overflowRect.y(), clientBox.y())); 3322 else 3323 overflowRect.shiftMaxYEdgeTo(min(overflowRect.maxY(), clientBox.maxY())); 3324 if (!hasLeftOverflow) 3325 overflowRect.shiftXEdgeTo(max(overflowRect.x(), clientBox.x())); 3326 else 3327 overflowRect.shiftMaxXEdgeTo(min(overflowRect.maxX(), clientBox.maxX())); 3328 3329 // Now re-test with the adjusted rectangle and see if it has become unreachable or fully 3330 // contained. 3331 if (clientBox.contains(overflowRect) || overflowRect.isEmpty()) 3332 return; 3333 } 3334 3335 if (!m_overflow) 3336 m_overflow.set(new RenderOverflow(clientBox, borderBoxRect())); 3337 3338 m_overflow->addLayoutOverflow(overflowRect); 3339 } 3340 3341 void RenderBox::addVisualOverflow(const IntRect& rect) 3342 { 3343 IntRect borderBox = borderBoxRect(); 3344 if (borderBox.contains(rect) || rect.isEmpty()) 3345 return; 3346 3347 if (!m_overflow) 3348 m_overflow.set(new RenderOverflow(clientBoxRect(), borderBox)); 3349 3350 m_overflow->addVisualOverflow(rect); 3351 } 3352 3353 void RenderBox::clearLayoutOverflow() 3354 { 3355 if (!m_overflow) 3356 return; 3357 3358 if (visualOverflowRect() == borderBoxRect()) { 3359 m_overflow.clear(); 3360 return; 3361 } 3362 3363 m_overflow->resetLayoutOverflow(borderBoxRect()); 3364 } 3365 3366 int RenderBox::lineHeight(bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const 3367 { 3368 if (isReplaced()) 3369 return direction == HorizontalLine ? m_marginTop + height() + m_marginBottom : m_marginRight + width() + m_marginLeft; 3370 return 0; 3371 } 3372 3373 int RenderBox::baselinePosition(FontBaseline baselineType, bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const 3374 { 3375 if (isReplaced()) { 3376 int result = direction == HorizontalLine ? m_marginTop + height() + m_marginBottom : m_marginRight + width() + m_marginLeft; 3377 if (baselineType == AlphabeticBaseline) 3378 return result; 3379 return result - result / 2; 3380 } 3381 return 0; 3382 } 3383 3384 3385 RenderLayer* RenderBox::enclosingFloatPaintingLayer() const 3386 { 3387 const RenderObject* curr = this; 3388 while (curr) { 3389 RenderLayer* layer = curr->hasLayer() && curr->isBox() ? toRenderBoxModelObject(curr)->layer() : 0; 3390 if (layer && layer->isSelfPaintingLayer()) 3391 return layer; 3392 curr = curr->parent(); 3393 } 3394 return 0; 3395 } 3396 3397 IntRect RenderBox::logicalVisualOverflowRectForPropagation(RenderStyle* parentStyle) const 3398 { 3399 IntRect rect = visualOverflowRectForPropagation(parentStyle); 3400 if (!parentStyle->isHorizontalWritingMode()) 3401 return rect.transposedRect(); 3402 return rect; 3403 } 3404 3405 IntRect RenderBox::visualOverflowRectForPropagation(RenderStyle* parentStyle) const 3406 { 3407 // If the writing modes of the child and parent match, then we don't have to 3408 // do anything fancy. Just return the result. 3409 IntRect rect = visualOverflowRect(); 3410 if (parentStyle->writingMode() == style()->writingMode()) 3411 return rect; 3412 3413 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch 3414 // in a particular axis, then we have to flip the rect along that axis. 3415 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode) 3416 rect.setX(width() - rect.maxX()); 3417 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode) 3418 rect.setY(height() - rect.maxY()); 3419 3420 return rect; 3421 } 3422 3423 IntRect RenderBox::logicalLayoutOverflowRectForPropagation(RenderStyle* parentStyle) const 3424 { 3425 IntRect rect = layoutOverflowRectForPropagation(parentStyle); 3426 if (!parentStyle->isHorizontalWritingMode()) 3427 return rect.transposedRect(); 3428 return rect; 3429 } 3430 3431 IntRect RenderBox::layoutOverflowRectForPropagation(RenderStyle* parentStyle) const 3432 { 3433 // Only propagate interior layout overflow if we don't clip it. 3434 IntRect rect = borderBoxRect(); 3435 if (!hasOverflowClip()) 3436 rect.unite(layoutOverflowRect()); 3437 3438 bool hasTransform = hasLayer() && layer()->transform(); 3439 if (isRelPositioned() || hasTransform) { 3440 // If we are relatively positioned or if we have a transform, then we have to convert 3441 // this rectangle into physical coordinates, apply relative positioning and transforms 3442 // to it, and then convert it back. 3443 flipForWritingMode(rect); 3444 3445 if (hasTransform) 3446 rect = layer()->currentTransform().mapRect(rect); 3447 3448 if (isRelPositioned()) 3449 rect.move(relativePositionOffsetX(), relativePositionOffsetY()); 3450 3451 // Now we need to flip back. 3452 flipForWritingMode(rect); 3453 } 3454 3455 // If the writing modes of the child and parent match, then we don't have to 3456 // do anything fancy. Just return the result. 3457 if (parentStyle->writingMode() == style()->writingMode()) 3458 return rect; 3459 3460 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch 3461 // in a particular axis, then we have to flip the rect along that axis. 3462 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode) 3463 rect.setX(width() - rect.maxX()); 3464 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode) 3465 rect.setY(height() - rect.maxY()); 3466 3467 return rect; 3468 } 3469 3470 IntPoint RenderBox::flipForWritingMode(const RenderBox* child, const IntPoint& point, FlippingAdjustment adjustment) const 3471 { 3472 if (!style()->isFlippedBlocksWritingMode()) 3473 return point; 3474 3475 // The child is going to add in its x() and y(), so we have to make sure it ends up in 3476 // the right place. 3477 if (isHorizontalWritingMode()) 3478 return IntPoint(point.x(), point.y() + height() - child->height() - child->y() - (adjustment == ParentToChildFlippingAdjustment ? child->y() : 0)); 3479 return IntPoint(point.x() + width() - child->width() - child->x() - (adjustment == ParentToChildFlippingAdjustment ? child->x() : 0), point.y()); 3480 } 3481 3482 void RenderBox::flipForWritingMode(IntRect& rect) const 3483 { 3484 if (!style()->isFlippedBlocksWritingMode()) 3485 return; 3486 3487 if (isHorizontalWritingMode()) 3488 rect.setY(height() - rect.maxY()); 3489 else 3490 rect.setX(width() - rect.maxX()); 3491 } 3492 3493 int RenderBox::flipForWritingMode(int position) const 3494 { 3495 if (!style()->isFlippedBlocksWritingMode()) 3496 return position; 3497 return logicalHeight() - position; 3498 } 3499 3500 IntPoint RenderBox::flipForWritingMode(const IntPoint& position) const 3501 { 3502 if (!style()->isFlippedBlocksWritingMode()) 3503 return position; 3504 return isHorizontalWritingMode() ? IntPoint(position.x(), height() - position.y()) : IntPoint(width() - position.x(), position.y()); 3505 } 3506 3507 IntPoint RenderBox::flipForWritingModeIncludingColumns(const IntPoint& point) const 3508 { 3509 if (!hasColumns() || !style()->isFlippedBlocksWritingMode()) 3510 return flipForWritingMode(point); 3511 return toRenderBlock(this)->flipForWritingModeIncludingColumns(point); 3512 } 3513 3514 IntSize RenderBox::flipForWritingMode(const IntSize& offset) const 3515 { 3516 if (!style()->isFlippedBlocksWritingMode()) 3517 return offset; 3518 return isHorizontalWritingMode() ? IntSize(offset.width(), height() - offset.height()) : IntSize(width() - offset.width(), offset.height()); 3519 } 3520 3521 FloatPoint RenderBox::flipForWritingMode(const FloatPoint& position) const 3522 { 3523 if (!style()->isFlippedBlocksWritingMode()) 3524 return position; 3525 return isHorizontalWritingMode() ? FloatPoint(position.x(), height() - position.y()) : FloatPoint(width() - position.x(), position.y()); 3526 } 3527 3528 void RenderBox::flipForWritingMode(FloatRect& rect) const 3529 { 3530 if (!style()->isFlippedBlocksWritingMode()) 3531 return; 3532 3533 if (isHorizontalWritingMode()) 3534 rect.setY(height() - rect.maxY()); 3535 else 3536 rect.setX(width() - rect.maxX()); 3537 } 3538 3539 IntSize RenderBox::locationOffsetIncludingFlipping() const 3540 { 3541 RenderBlock* containerBlock = containingBlock(); 3542 if (!containerBlock || containerBlock == this) 3543 return locationOffset(); 3544 3545 IntRect rect(frameRect()); 3546 containerBlock->flipForWritingMode(rect); // FIXME: This is wrong if we are an absolutely positioned object enclosed by a relative-positioned inline. 3547 return IntSize(rect.x(), rect.y()); 3548 } 3549 3550 } // namespace WebCore 3551