1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) 5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@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 "Chrome.h" 29#include "ChromeClient.h" 30#include "Document.h" 31#include "FrameView.h" 32#include "GraphicsContext.h" 33#include "HitTestResult.h" 34#include "htmlediting.h" 35#include "HTMLElement.h" 36#include "HTMLFrameOwnerElement.h" 37#include "HTMLNames.h" 38#include "FloatQuad.h" 39#include "Frame.h" 40#include "Page.h" 41#include "PaintInfo.h" 42#include "RenderArena.h" 43#include "RenderBoxRegionInfo.h" 44#include "RenderFlexibleBox.h" 45#include "RenderFlowThread.h" 46#include "RenderGeometryMap.h" 47#include "RenderInline.h" 48#include "RenderLayer.h" 49#include "RenderRegion.h" 50#include "RenderTableCell.h" 51#include "RenderTheme.h" 52#include "RenderView.h" 53#include "TransformState.h" 54#include <algorithm> 55#include <math.h> 56#include <wtf/StackStats.h> 57 58#if USE(ACCELERATED_COMPOSITING) 59#include "RenderLayerCompositor.h" 60#endif 61 62using namespace std; 63 64namespace WebCore { 65 66using namespace HTMLNames; 67 68// Used by flexible boxes when flexing this element and by table cells. 69typedef WTF::HashMap<const RenderBox*, LayoutUnit> OverrideSizeMap; 70static OverrideSizeMap* gOverrideHeightMap = 0; 71static OverrideSizeMap* gOverrideWidthMap = 0; 72 73// Used by grid elements to properly size their grid items. 74static OverrideSizeMap* gOverrideContainingBlockLogicalHeightMap = 0; 75static OverrideSizeMap* gOverrideContainingBlockLogicalWidthMap = 0; 76 77 78// Size of border belt for autoscroll. When mouse pointer in border belt, 79// autoscroll is started. 80static const int autoscrollBeltSize = 20; 81static const unsigned backgroundObscurationTestMaxDepth = 4; 82 83bool RenderBox::s_hadOverflowClip = false; 84 85static bool skipBodyBackground(const RenderBox* bodyElementRenderer) 86{ 87 ASSERT(bodyElementRenderer->isBody()); 88 // The <body> only paints its background if the root element has defined a background independent of the body, 89 // or if the <body>'s parent is not the document element's renderer (e.g. inside SVG foreignObject). 90 RenderObject* documentElementRenderer = bodyElementRenderer->document()->documentElement()->renderer(); 91 return documentElementRenderer 92 && !documentElementRenderer->hasBackground() 93 && (documentElementRenderer == bodyElementRenderer->parent()); 94} 95 96RenderBox::RenderBox(ContainerNode* node) 97 : RenderBoxModelObject(node) 98 , m_minPreferredLogicalWidth(-1) 99 , m_maxPreferredLogicalWidth(-1) 100 , m_inlineBoxWrapper(0) 101{ 102 setIsBox(); 103} 104 105RenderBox::~RenderBox() 106{ 107} 108 109LayoutRect RenderBox::borderBoxRectInRegion(RenderRegion* region, RenderBoxRegionInfoFlags cacheFlag) const 110{ 111 if (!region) 112 return borderBoxRect(); 113 114 // Compute the logical width and placement in this region. 115 RenderBoxRegionInfo* boxInfo = renderBoxRegionInfo(region, cacheFlag); 116 if (!boxInfo) 117 return borderBoxRect(); 118 119 // We have cached insets. 120 LayoutUnit logicalWidth = boxInfo->logicalWidth(); 121 LayoutUnit logicalLeft = boxInfo->logicalLeft(); 122 123 // Now apply the parent inset since it is cumulative whenever anything in the containing block chain shifts. 124 // FIXME: Doesn't work right with perpendicular writing modes. 125 const RenderBlock* currentBox = containingBlock(); 126 RenderBoxRegionInfo* currentBoxInfo = currentBox->renderBoxRegionInfo(region); 127 while (currentBoxInfo && currentBoxInfo->isShifted()) { 128 if (currentBox->style()->direction() == LTR) 129 logicalLeft += currentBoxInfo->logicalLeft(); 130 else 131 logicalLeft -= (currentBox->logicalWidth() - currentBoxInfo->logicalWidth()) - currentBoxInfo->logicalLeft(); 132 currentBox = currentBox->containingBlock(); 133 region = currentBox->clampToStartAndEndRegions(region); 134 currentBoxInfo = currentBox->renderBoxRegionInfo(region); 135 } 136 137 if (cacheFlag == DoNotCacheRenderBoxRegionInfo) 138 delete boxInfo; 139 140 if (isHorizontalWritingMode()) 141 return LayoutRect(logicalLeft, 0, logicalWidth, height()); 142 return LayoutRect(0, logicalLeft, width(), logicalWidth); 143} 144 145void RenderBox::clearRenderBoxRegionInfo() 146{ 147 if (isRenderFlowThread()) 148 return; 149 150 RenderFlowThread* flowThread = flowThreadContainingBlock(); 151 if (flowThread) 152 flowThread->removeRenderBoxRegionInfo(this); 153} 154 155void RenderBox::willBeDestroyed() 156{ 157 clearOverrideSize(); 158 clearContainingBlockOverrideSize(); 159 160 RenderBlock::removePercentHeightDescendantIfNeeded(this); 161 162#if ENABLE(CSS_SHAPES) 163 ShapeOutsideInfo::removeInfo(this); 164#endif 165 166 RenderBoxModelObject::willBeDestroyed(); 167} 168 169void RenderBox::removeFloatingOrPositionedChildFromBlockLists() 170{ 171 ASSERT(isFloatingOrOutOfFlowPositioned()); 172 173 if (documentBeingDestroyed()) 174 return; 175 176 if (isFloating()) { 177 RenderBlock* parentBlock = 0; 178 for (RenderObject* curr = parent(); curr && !curr->isRenderView(); curr = curr->parent()) { 179 if (curr->isRenderBlock()) { 180 RenderBlock* currBlock = toRenderBlock(curr); 181 if (!parentBlock || currBlock->containsFloat(this)) 182 parentBlock = currBlock; 183 } 184 } 185 186 if (parentBlock) { 187 RenderObject* parent = parentBlock->parent(); 188 if (parent && parent->isFlexibleBoxIncludingDeprecated()) 189 parentBlock = toRenderBlock(parent); 190 191 parentBlock->markSiblingsWithFloatsForLayout(this); 192 parentBlock->markAllDescendantsWithFloatsForLayout(this, false); 193 } 194 } 195 196 if (isOutOfFlowPositioned()) 197 RenderBlock::removePositionedObject(this); 198} 199 200void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) 201{ 202 s_hadOverflowClip = hasOverflowClip(); 203 204 RenderStyle* oldStyle = style(); 205 if (oldStyle) { 206 // The background of the root element or the body element could propagate up to 207 // the canvas. Just dirty the entire canvas when our style changes substantially. 208 if (diff >= StyleDifferenceRepaint && node() && 209 (node()->hasTagName(htmlTag) || node()->hasTagName(bodyTag))) { 210 view()->repaint(); 211 212#if USE(ACCELERATED_COMPOSITING) 213 if (oldStyle->hasEntirelyFixedBackground() != newStyle->hasEntirelyFixedBackground()) 214 view()->compositor()->rootFixedBackgroundsChanged(); 215#endif 216 } 217 218 // When a layout hint happens and an object's position style changes, we have to do a layout 219 // to dirty the render tree using the old position value now. 220 if (diff == StyleDifferenceLayout && parent() && oldStyle->position() != newStyle->position()) { 221 markContainingBlocksForLayout(); 222 if (oldStyle->position() == StaticPosition) 223 repaint(); 224 else if (newStyle->hasOutOfFlowPosition()) 225 parent()->setChildNeedsLayout(true); 226 if (isFloating() && !isOutOfFlowPositioned() && newStyle->hasOutOfFlowPosition()) 227 removeFloatingOrPositionedChildFromBlockLists(); 228 } 229 } else if (newStyle && isBody()) 230 view()->repaint(); 231 232 RenderBoxModelObject::styleWillChange(diff, newStyle); 233} 234 235void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 236{ 237 // Horizontal writing mode definition is updated in RenderBoxModelObject::updateFromStyle, 238 // (as part of the RenderBoxModelObject::styleDidChange call below). So, we can safely cache the horizontal 239 // writing mode value before style change here. 240 bool oldHorizontalWritingMode = isHorizontalWritingMode(); 241 242 RenderBoxModelObject::styleDidChange(diff, oldStyle); 243 244 RenderStyle* newStyle = style(); 245 if (needsLayout() && oldStyle) { 246 RenderBlock::removePercentHeightDescendantIfNeeded(this); 247 248 // Normally we can do optimized positioning layout for absolute/fixed positioned objects. There is one special case, however, which is 249 // 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 250 // to determine the new static position. 251 if (isOutOfFlowPositioned() && newStyle->hasStaticBlockPosition(isHorizontalWritingMode()) && oldStyle->marginBefore() != newStyle->marginBefore() 252 && parent() && !parent()->normalChildNeedsLayout()) 253 parent()->setChildNeedsLayout(true); 254 } 255 256 if (RenderBlock::hasPercentHeightContainerMap() && firstChild() 257 && oldHorizontalWritingMode != isHorizontalWritingMode()) 258 RenderBlock::clearPercentHeightDescendantsFrom(this); 259 260 // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the 261 // new zoomed coordinate space. 262 if (hasOverflowClip() && oldStyle && newStyle && oldStyle->effectiveZoom() != newStyle->effectiveZoom()) { 263 if (int left = layer()->scrollXOffset()) { 264 left = (left / oldStyle->effectiveZoom()) * newStyle->effectiveZoom(); 265 layer()->scrollToXOffset(left); 266 } 267 if (int top = layer()->scrollYOffset()) { 268 top = (top / oldStyle->effectiveZoom()) * newStyle->effectiveZoom(); 269 layer()->scrollToYOffset(top); 270 } 271 } 272 273 // Our opaqueness might have changed without triggering layout. 274 if (diff >= StyleDifferenceRepaint && diff <= StyleDifferenceRepaintLayer) { 275 RenderObject* parentToInvalidate = parent(); 276 for (unsigned i = 0; i < backgroundObscurationTestMaxDepth && parentToInvalidate; ++i) { 277 parentToInvalidate->invalidateBackgroundObscurationStatus(); 278 parentToInvalidate = parentToInvalidate->parent(); 279 } 280 } 281 282 bool isBodyRenderer = isBody(); 283 bool isRootRenderer = isRoot(); 284 285 // Set the text color if we're the body. 286 if (isBodyRenderer) 287 document()->setTextColor(newStyle->visitedDependentColor(CSSPropertyColor)); 288 289 if (isRootRenderer || isBodyRenderer) { 290 // Propagate the new writing mode and direction up to the RenderView. 291 RenderView* viewRenderer = view(); 292 RenderStyle* viewStyle = viewRenderer->style(); 293 bool viewChangedWritingMode = false; 294 if (viewStyle->direction() != newStyle->direction() && (isRootRenderer || !document()->directionSetOnDocumentElement())) { 295 viewStyle->setDirection(newStyle->direction()); 296 if (isBodyRenderer) 297 document()->documentElement()->renderer()->style()->setDirection(newStyle->direction()); 298 setNeedsLayoutAndPrefWidthsRecalc(); 299 } 300 301 if (viewStyle->writingMode() != newStyle->writingMode() && (isRootRenderer || !document()->writingModeSetOnDocumentElement())) { 302 viewStyle->setWritingMode(newStyle->writingMode()); 303 viewChangedWritingMode = true; 304 viewRenderer->setHorizontalWritingMode(newStyle->isHorizontalWritingMode()); 305 viewRenderer->markAllDescendantsWithFloatsForLayout(); 306 if (isBodyRenderer) { 307 document()->documentElement()->renderer()->style()->setWritingMode(newStyle->writingMode()); 308 document()->documentElement()->renderer()->setHorizontalWritingMode(newStyle->isHorizontalWritingMode()); 309 } 310 setNeedsLayoutAndPrefWidthsRecalc(); 311 } 312 313 frame()->view()->recalculateScrollbarOverlayStyle(); 314 315 const Pagination& pagination = frame()->view()->pagination(); 316 if (viewChangedWritingMode && pagination.mode != Pagination::Unpaginated) { 317 viewStyle->setColumnStylesFromPaginationMode(pagination.mode); 318 if (viewRenderer->hasColumns()) 319 viewRenderer->updateColumnInfoFromStyle(viewStyle); 320 } 321 } 322 323#if ENABLE(CSS_SHAPES) 324 updateShapeOutsideInfoAfterStyleChange(style()->shapeOutside(), oldStyle ? oldStyle->shapeOutside() : 0); 325#endif 326} 327 328#if ENABLE(CSS_SHAPES) 329void RenderBox::updateShapeOutsideInfoAfterStyleChange(const ShapeValue* shapeOutside, const ShapeValue* oldShapeOutside) 330{ 331 // FIXME: A future optimization would do a deep comparison for equality. (bug 100811) 332 if (shapeOutside == oldShapeOutside) 333 return; 334 335 if (shapeOutside) { 336 ShapeOutsideInfo* shapeOutsideInfo = ShapeOutsideInfo::ensureInfo(this); 337 shapeOutsideInfo->dirtyShapeSize(); 338 } else 339 ShapeOutsideInfo::removeInfo(this); 340} 341#endif 342 343void RenderBox::updateFromStyle() 344{ 345 RenderBoxModelObject::updateFromStyle(); 346 347 RenderStyle* styleToUse = style(); 348 bool isRootObject = isRoot(); 349 bool isViewObject = isRenderView(); 350 351 // The root and the RenderView always paint their backgrounds/borders. 352 if (isRootObject || isViewObject) 353 setHasBoxDecorations(true); 354 355 setFloating(!isOutOfFlowPositioned() && styleToUse->isFloating()); 356 357 // We also handle <body> and <html>, whose overflow applies to the viewport. 358 if (styleToUse->overflowX() != OVISIBLE && !isRootObject && isRenderBlock()) { 359 bool boxHasOverflowClip = true; 360 if (isBody()) { 361 // Overflow on the body can propagate to the viewport under the following conditions. 362 // (1) The root element is <html>. 363 // (2) We are the primary <body> (can be checked by looking at document.body). 364 // (3) The root element has visible overflow. 365 if (document()->documentElement()->hasTagName(htmlTag) && 366 document()->body() == node() && 367 document()->documentElement()->renderer()->style()->overflowX() == OVISIBLE) 368 boxHasOverflowClip = false; 369 } 370 371 // Check for overflow clip. 372 // It's sufficient to just check one direction, since it's illegal to have visible on only one overflow value. 373 if (boxHasOverflowClip) { 374 if (!s_hadOverflowClip) 375 // Erase the overflow 376 repaint(); 377 setHasOverflowClip(); 378 } 379 } 380 381 setHasTransform(styleToUse->hasTransformRelatedProperty()); 382 setHasReflection(styleToUse->boxReflect()); 383} 384 385void RenderBox::layout() 386{ 387 StackStats::LayoutCheckPoint layoutCheckPoint; 388 ASSERT(needsLayout()); 389 390 RenderObject* child = firstChild(); 391 if (!child) { 392 setNeedsLayout(false); 393 return; 394 } 395 396 LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode()); 397 while (child) { 398 child->layoutIfNeeded(); 399 ASSERT(!child->needsLayout()); 400 child = child->nextSibling(); 401 } 402 statePusher.pop(); 403 invalidateBackgroundObscurationStatus(); 404 setNeedsLayout(false); 405} 406 407// More IE extensions. clientWidth and clientHeight represent the interior of an object 408// excluding border and scrollbar. 409LayoutUnit RenderBox::clientWidth() const 410{ 411 return width() - borderLeft() - borderRight() - verticalScrollbarWidth(); 412} 413 414LayoutUnit RenderBox::clientHeight() const 415{ 416 return height() - borderTop() - borderBottom() - horizontalScrollbarHeight(); 417} 418 419int RenderBox::pixelSnappedClientWidth() const 420{ 421 return snapSizeToPixel(clientWidth(), x() + clientLeft()); 422} 423 424int RenderBox::pixelSnappedClientHeight() const 425{ 426 return snapSizeToPixel(clientHeight(), y() + clientTop()); 427} 428 429int RenderBox::pixelSnappedOffsetWidth() const 430{ 431 return snapSizeToPixel(offsetWidth(), x() + clientLeft()); 432} 433 434int RenderBox::pixelSnappedOffsetHeight() const 435{ 436 return snapSizeToPixel(offsetHeight(), y() + clientTop()); 437} 438 439int RenderBox::scrollWidth() const 440{ 441 if (hasOverflowClip()) 442 return layer()->scrollWidth(); 443 // For objects with visible overflow, this matches IE. 444 // FIXME: Need to work right with writing modes. 445 if (style()->isLeftToRightDirection()) 446 return snapSizeToPixel(max(clientWidth(), layoutOverflowRect().maxX() - borderLeft()), x() + clientLeft()); 447 return clientWidth() - min<LayoutUnit>(0, layoutOverflowRect().x() - borderLeft()); 448} 449 450int RenderBox::scrollHeight() const 451{ 452 if (hasOverflowClip()) 453 return layer()->scrollHeight(); 454 // For objects with visible overflow, this matches IE. 455 // FIXME: Need to work right with writing modes. 456 return snapSizeToPixel(max(clientHeight(), layoutOverflowRect().maxY() - borderTop()), y() + clientTop()); 457} 458 459int RenderBox::scrollLeft() const 460{ 461 return hasOverflowClip() ? layer()->scrollXOffset() : 0; 462} 463 464int RenderBox::scrollTop() const 465{ 466 return hasOverflowClip() ? layer()->scrollYOffset() : 0; 467} 468 469void RenderBox::setScrollLeft(int newLeft) 470{ 471 if (hasOverflowClip()) 472 layer()->scrollToXOffset(newLeft, RenderLayer::ScrollOffsetClamped); 473} 474 475void RenderBox::setScrollTop(int newTop) 476{ 477 if (hasOverflowClip()) 478 layer()->scrollToYOffset(newTop, RenderLayer::ScrollOffsetClamped); 479} 480 481void RenderBox::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const 482{ 483 rects.append(pixelSnappedIntRect(accumulatedOffset, size())); 484} 485 486void RenderBox::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const 487{ 488 quads.append(localToAbsoluteQuad(FloatRect(0, 0, width(), height()), 0 /* mode */, wasFixed)); 489} 490 491void RenderBox::updateLayerTransform() 492{ 493 // Transform-origin depends on box size, so we need to update the layer transform after layout. 494 if (hasLayer()) 495 layer()->updateTransform(); 496} 497 498LayoutUnit RenderBox::constrainLogicalWidthInRegionByMinMax(LayoutUnit logicalWidth, LayoutUnit availableWidth, RenderBlock* cb, RenderRegion* region) const 499{ 500 RenderStyle* styleToUse = style(); 501 if (!styleToUse->logicalMaxWidth().isUndefined()) 502 logicalWidth = min(logicalWidth, computeLogicalWidthInRegionUsing(MaxSize, styleToUse->logicalMaxWidth(), availableWidth, cb, region)); 503 return max(logicalWidth, computeLogicalWidthInRegionUsing(MinSize, styleToUse->logicalMinWidth(), availableWidth, cb, region)); 504} 505 506LayoutUnit RenderBox::constrainLogicalHeightByMinMax(LayoutUnit logicalHeight) const 507{ 508 RenderStyle* styleToUse = style(); 509 if (!styleToUse->logicalMaxHeight().isUndefined()) { 510 LayoutUnit maxH = computeLogicalHeightUsing(styleToUse->logicalMaxHeight()); 511 if (maxH != -1) 512 logicalHeight = min(logicalHeight, maxH); 513 } 514 return max(logicalHeight, computeLogicalHeightUsing(styleToUse->logicalMinHeight())); 515} 516 517LayoutUnit RenderBox::constrainContentBoxLogicalHeightByMinMax(LayoutUnit logicalHeight) const 518{ 519 RenderStyle* styleToUse = style(); 520 if (!styleToUse->logicalMaxHeight().isUndefined()) { 521 LayoutUnit maxH = computeContentLogicalHeight(styleToUse->logicalMaxHeight()); 522 if (maxH != -1) 523 logicalHeight = min(logicalHeight, maxH); 524 } 525 return max(logicalHeight, computeContentLogicalHeight(styleToUse->logicalMinHeight())); 526} 527 528IntRect RenderBox::absoluteContentBox() const 529{ 530 // This is wrong with transforms and flipped writing modes. 531 IntRect rect = pixelSnappedIntRect(contentBoxRect()); 532 FloatPoint absPos = localToAbsolute(); 533 rect.move(absPos.x(), absPos.y()); 534 return rect; 535} 536 537FloatQuad RenderBox::absoluteContentQuad() const 538{ 539 LayoutRect rect = contentBoxRect(); 540 return localToAbsoluteQuad(FloatRect(rect)); 541} 542 543LayoutRect RenderBox::outlineBoundsForRepaint(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap) const 544{ 545 LayoutRect box = borderBoundingBox(); 546 adjustRectForOutlineAndShadow(box); 547 548 FloatQuad containerRelativeQuad; 549 if (geometryMap) 550 containerRelativeQuad = geometryMap->mapToContainer(box, repaintContainer); 551 else 552 containerRelativeQuad = localToContainerQuad(FloatRect(box), repaintContainer); 553 554 box = containerRelativeQuad.enclosingBoundingBox(); 555 556 // FIXME: layoutDelta needs to be applied in parts before/after transforms and 557 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 558 box.move(view()->layoutDelta()); 559 560 return box; 561} 562 563void RenderBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset) 564{ 565 if (!size().isEmpty()) 566 rects.append(pixelSnappedIntRect(additionalOffset, size())); 567} 568 569LayoutRect RenderBox::reflectionBox() const 570{ 571 LayoutRect result; 572 if (!style()->boxReflect()) 573 return result; 574 LayoutRect box = borderBoxRect(); 575 result = box; 576 switch (style()->boxReflect()->direction()) { 577 case ReflectionBelow: 578 result.move(0, box.height() + reflectionOffset()); 579 break; 580 case ReflectionAbove: 581 result.move(0, -box.height() - reflectionOffset()); 582 break; 583 case ReflectionLeft: 584 result.move(-box.width() - reflectionOffset(), 0); 585 break; 586 case ReflectionRight: 587 result.move(box.width() + reflectionOffset(), 0); 588 break; 589 } 590 return result; 591} 592 593int RenderBox::reflectionOffset() const 594{ 595 if (!style()->boxReflect()) 596 return 0; 597 RenderView* renderView = view(); 598 if (style()->boxReflect()->direction() == ReflectionLeft || style()->boxReflect()->direction() == ReflectionRight) 599 return valueForLength(style()->boxReflect()->offset(), borderBoxRect().width(), renderView); 600 return valueForLength(style()->boxReflect()->offset(), borderBoxRect().height(), renderView); 601} 602 603LayoutRect RenderBox::reflectedRect(const LayoutRect& r) const 604{ 605 if (!style()->boxReflect()) 606 return LayoutRect(); 607 608 LayoutRect box = borderBoxRect(); 609 LayoutRect result = r; 610 switch (style()->boxReflect()->direction()) { 611 case ReflectionBelow: 612 result.setY(box.maxY() + reflectionOffset() + (box.maxY() - r.maxY())); 613 break; 614 case ReflectionAbove: 615 result.setY(box.y() - reflectionOffset() - box.height() + (box.maxY() - r.maxY())); 616 break; 617 case ReflectionLeft: 618 result.setX(box.x() - reflectionOffset() - box.width() + (box.maxX() - r.maxX())); 619 break; 620 case ReflectionRight: 621 result.setX(box.maxX() + reflectionOffset() + (box.maxX() - r.maxX())); 622 break; 623 } 624 return result; 625} 626 627bool RenderBox::fixedElementLaysOutRelativeToFrame(Frame* frame, FrameView* frameView) const 628{ 629 return style() && style()->position() == FixedPosition && container()->isRenderView() && frame && frameView && frameView->fixedElementsLayoutRelativeToFrame(); 630} 631 632bool RenderBox::includeVerticalScrollbarSize() const 633{ 634 return hasOverflowClip() && !layer()->hasOverlayScrollbars() 635 && (style()->overflowY() == OSCROLL || style()->overflowY() == OAUTO); 636} 637 638bool RenderBox::includeHorizontalScrollbarSize() const 639{ 640 return hasOverflowClip() && !layer()->hasOverlayScrollbars() 641 && (style()->overflowX() == OSCROLL || style()->overflowX() == OAUTO); 642} 643 644int RenderBox::verticalScrollbarWidth() const 645{ 646 return includeVerticalScrollbarSize() ? layer()->verticalScrollbarWidth() : 0; 647} 648 649int RenderBox::horizontalScrollbarHeight() const 650{ 651 return includeHorizontalScrollbarSize() ? layer()->horizontalScrollbarHeight() : 0; 652} 653 654int RenderBox::instrinsicScrollbarLogicalWidth() const 655{ 656 if (!hasOverflowClip()) 657 return 0; 658 659 if (isHorizontalWritingMode() && style()->overflowY() == OSCROLL) { 660 ASSERT(layer()->hasVerticalScrollbar()); 661 return verticalScrollbarWidth(); 662 } 663 664 if (!isHorizontalWritingMode() && style()->overflowX() == OSCROLL) { 665 ASSERT(layer()->hasHorizontalScrollbar()); 666 return horizontalScrollbarHeight(); 667 } 668 669 return 0; 670} 671 672bool RenderBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) 673{ 674 RenderLayer* l = layer(); 675 if (l && l->scroll(direction, granularity, multiplier)) { 676 if (stopNode) 677 *stopNode = node(); 678 return true; 679 } 680 681 if (stopNode && *stopNode && *stopNode == node()) 682 return true; 683 684 RenderBlock* b = containingBlock(); 685 if (b && !b->isRenderView()) 686 return b->scroll(direction, granularity, multiplier, stopNode); 687 return false; 688} 689 690bool RenderBox::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode) 691{ 692 bool scrolled = false; 693 694 RenderLayer* l = layer(); 695 if (l) { 696#if PLATFORM(MAC) 697 // On Mac only we reset the inline direction position when doing a document scroll (e.g., hitting Home/End). 698 if (granularity == ScrollByDocument) 699 scrolled = l->scroll(logicalToPhysical(ScrollInlineDirectionBackward, isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), ScrollByDocument, multiplier); 700#endif 701 if (l->scroll(logicalToPhysical(direction, isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier)) 702 scrolled = true; 703 704 if (scrolled) { 705 if (stopNode) 706 *stopNode = node(); 707 return true; 708 } 709 } 710 711 if (stopNode && *stopNode && *stopNode == node()) 712 return true; 713 714 RenderBlock* b = containingBlock(); 715 if (b && !b->isRenderView()) 716 return b->logicalScroll(direction, granularity, multiplier, stopNode); 717 return false; 718} 719 720bool RenderBox::canBeScrolledAndHasScrollableArea() const 721{ 722 return canBeProgramaticallyScrolled() && (scrollHeight() != clientHeight() || scrollWidth() != clientWidth()); 723} 724 725bool RenderBox::canBeProgramaticallyScrolled() const 726{ 727 return (hasOverflowClip() && (scrollsOverflow() || (node() && node()->rendererIsEditable()))) || (node() && node()->isDocumentNode()); 728} 729 730bool RenderBox::usesCompositedScrolling() const 731{ 732 return hasOverflowClip() && hasLayer() && layer()->usesCompositedScrolling(); 733} 734 735void RenderBox::autoscroll(const IntPoint& position) 736{ 737 if (layer()) 738 layer()->autoscroll(position); 739} 740 741// There are two kinds of renderer that can autoscroll. 742bool RenderBox::canAutoscroll() const 743{ 744 // Check for a box that can be scrolled in its own right. 745 if (canBeScrolledAndHasScrollableArea()) 746 return true; 747 748 // Check for a box that represents the top level of a web page. 749 // This can be scrolled by calling Chrome::scrollRectIntoView. 750 // This only has an effect on the Mac platform in applications 751 // that put web views into scrolling containers, such as Mac OS X Mail. 752 // The code for this is in RenderLayer::scrollRectToVisible. 753 if (node() != document()) 754 return false; 755 Frame* frame = this->frame(); 756 if (!frame) 757 return false; 758 Page* page = frame->page(); 759 return page && page->mainFrame() == frame && frame->view()->isScrollable(); 760} 761 762// If specified point is in border belt, returned offset denotes direction of 763// scrolling. 764IntSize RenderBox::calculateAutoscrollDirection(const IntPoint& windowPoint) const 765{ 766 if (!frame()) 767 return IntSize(); 768 769 FrameView* frameView = frame()->view(); 770 if (!frameView) 771 return IntSize(); 772 773 IntSize offset; 774 IntPoint point = frameView->windowToContents(windowPoint); 775 IntRect box(absoluteBoundingBoxRect()); 776 777 if (point.x() < box.x() + autoscrollBeltSize) 778 point.move(-autoscrollBeltSize, 0); 779 else if (point.x() > box.maxX() - autoscrollBeltSize) 780 point.move(autoscrollBeltSize, 0); 781 782 if (point.y() < box.y() + autoscrollBeltSize) 783 point.move(0, -autoscrollBeltSize); 784 else if (point.y() > box.maxY() - autoscrollBeltSize) 785 point.move(0, autoscrollBeltSize); 786 return frameView->contentsToWindow(point) - windowPoint; 787} 788 789RenderBox* RenderBox::findAutoscrollable(RenderObject* renderer) 790{ 791 while (renderer && !(renderer->isBox() && toRenderBox(renderer)->canAutoscroll())) { 792 if (!renderer->parent() && renderer->node() == renderer->document() && renderer->document()->ownerElement()) 793 renderer = renderer->document()->ownerElement()->renderer(); 794 else 795 renderer = renderer->parent(); 796 } 797 798 return renderer && renderer->isBox() ? toRenderBox(renderer) : 0; 799} 800 801void RenderBox::panScroll(const IntPoint& source) 802{ 803 if (layer()) 804 layer()->panScrollFromPoint(source); 805} 806 807bool RenderBox::needsPreferredWidthsRecalculation() const 808{ 809 return style()->paddingStart().isPercent() || style()->paddingEnd().isPercent(); 810} 811 812IntSize RenderBox::scrolledContentOffset() const 813{ 814 ASSERT(hasOverflowClip()); 815 ASSERT(hasLayer()); 816 return layer()->scrolledContentOffset(); 817} 818 819LayoutSize RenderBox::cachedSizeForOverflowClip() const 820{ 821 ASSERT(hasOverflowClip()); 822 ASSERT(hasLayer()); 823 return layer()->size(); 824} 825 826void RenderBox::applyCachedClipAndScrollOffsetForRepaint(LayoutRect& paintRect) const 827{ 828 paintRect.move(-scrolledContentOffset()); // For overflow:auto/scroll/hidden. 829 830 // Do not clip scroll layer contents to reduce the number of repaints while scrolling. 831 if (usesCompositedScrolling()) 832 return; 833 834 // height() is inaccurate if we're in the middle of a layout of this RenderBox, so use the 835 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint 836 // anyway if its size does change. 837 LayoutRect clipRect(LayoutPoint(), cachedSizeForOverflowClip()); 838 paintRect = intersection(paintRect, clipRect); 839} 840 841void RenderBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const 842{ 843 minLogicalWidth = minPreferredLogicalWidth() - borderAndPaddingLogicalWidth(); 844 maxLogicalWidth = maxPreferredLogicalWidth() - borderAndPaddingLogicalWidth(); 845} 846 847LayoutUnit RenderBox::minPreferredLogicalWidth() const 848{ 849 if (preferredLogicalWidthsDirty()) { 850#ifndef NDEBUG 851 SetLayoutNeededForbiddenScope layoutForbiddenScope(const_cast<RenderBox*>(this)); 852#endif 853 const_cast<RenderBox*>(this)->computePreferredLogicalWidths(); 854 } 855 856 return m_minPreferredLogicalWidth; 857} 858 859LayoutUnit RenderBox::maxPreferredLogicalWidth() const 860{ 861 if (preferredLogicalWidthsDirty()) { 862#ifndef NDEBUG 863 SetLayoutNeededForbiddenScope layoutForbiddenScope(const_cast<RenderBox*>(this)); 864#endif 865 const_cast<RenderBox*>(this)->computePreferredLogicalWidths(); 866 } 867 868 return m_maxPreferredLogicalWidth; 869} 870 871bool RenderBox::hasOverrideHeight() const 872{ 873 return gOverrideHeightMap && gOverrideHeightMap->contains(this); 874} 875 876bool RenderBox::hasOverrideWidth() const 877{ 878 return gOverrideWidthMap && gOverrideWidthMap->contains(this); 879} 880 881void RenderBox::setOverrideLogicalContentHeight(LayoutUnit height) 882{ 883 if (!gOverrideHeightMap) 884 gOverrideHeightMap = new OverrideSizeMap(); 885 gOverrideHeightMap->set(this, height); 886} 887 888void RenderBox::setOverrideLogicalContentWidth(LayoutUnit width) 889{ 890 if (!gOverrideWidthMap) 891 gOverrideWidthMap = new OverrideSizeMap(); 892 gOverrideWidthMap->set(this, width); 893} 894 895void RenderBox::clearOverrideLogicalContentHeight() 896{ 897 if (gOverrideHeightMap) 898 gOverrideHeightMap->remove(this); 899} 900 901void RenderBox::clearOverrideLogicalContentWidth() 902{ 903 if (gOverrideWidthMap) 904 gOverrideWidthMap->remove(this); 905} 906 907void RenderBox::clearOverrideSize() 908{ 909 clearOverrideLogicalContentHeight(); 910 clearOverrideLogicalContentWidth(); 911} 912 913LayoutUnit RenderBox::overrideLogicalContentWidth() const 914{ 915 ASSERT(hasOverrideWidth()); 916 return gOverrideWidthMap->get(this); 917} 918 919LayoutUnit RenderBox::overrideLogicalContentHeight() const 920{ 921 ASSERT(hasOverrideHeight()); 922 return gOverrideHeightMap->get(this); 923} 924 925LayoutUnit RenderBox::overrideContainingBlockContentLogicalWidth() const 926{ 927 ASSERT(hasOverrideContainingBlockLogicalWidth()); 928 return gOverrideContainingBlockLogicalWidthMap->get(this); 929} 930 931LayoutUnit RenderBox::overrideContainingBlockContentLogicalHeight() const 932{ 933 ASSERT(hasOverrideContainingBlockLogicalHeight()); 934 return gOverrideContainingBlockLogicalHeightMap->get(this); 935} 936 937bool RenderBox::hasOverrideContainingBlockLogicalWidth() const 938{ 939 return gOverrideContainingBlockLogicalWidthMap && gOverrideContainingBlockLogicalWidthMap->contains(this); 940} 941 942bool RenderBox::hasOverrideContainingBlockLogicalHeight() const 943{ 944 return gOverrideContainingBlockLogicalHeightMap && gOverrideContainingBlockLogicalHeightMap->contains(this); 945} 946 947void RenderBox::setOverrideContainingBlockContentLogicalWidth(LayoutUnit logicalWidth) 948{ 949 if (!gOverrideContainingBlockLogicalWidthMap) 950 gOverrideContainingBlockLogicalWidthMap = new OverrideSizeMap; 951 gOverrideContainingBlockLogicalWidthMap->set(this, logicalWidth); 952} 953 954void RenderBox::setOverrideContainingBlockContentLogicalHeight(LayoutUnit logicalHeight) 955{ 956 if (!gOverrideContainingBlockLogicalHeightMap) 957 gOverrideContainingBlockLogicalHeightMap = new OverrideSizeMap; 958 gOverrideContainingBlockLogicalHeightMap->set(this, logicalHeight); 959} 960 961void RenderBox::clearContainingBlockOverrideSize() 962{ 963 if (gOverrideContainingBlockLogicalWidthMap) 964 gOverrideContainingBlockLogicalWidthMap->remove(this); 965 clearOverrideContainingBlockContentLogicalHeight(); 966} 967 968void RenderBox::clearOverrideContainingBlockContentLogicalHeight() 969{ 970 if (gOverrideContainingBlockLogicalHeightMap) 971 gOverrideContainingBlockLogicalHeightMap->remove(this); 972} 973 974LayoutUnit RenderBox::adjustBorderBoxLogicalWidthForBoxSizing(LayoutUnit width) const 975{ 976 LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth(); 977 if (style()->boxSizing() == CONTENT_BOX) 978 return width + bordersPlusPadding; 979 return max(width, bordersPlusPadding); 980} 981 982LayoutUnit RenderBox::adjustBorderBoxLogicalHeightForBoxSizing(LayoutUnit height) const 983{ 984 LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight(); 985 if (style()->boxSizing() == CONTENT_BOX) 986 return height + bordersPlusPadding; 987 return max(height, bordersPlusPadding); 988} 989 990LayoutUnit RenderBox::adjustContentBoxLogicalWidthForBoxSizing(LayoutUnit width) const 991{ 992 if (style()->boxSizing() == BORDER_BOX) 993 width -= borderAndPaddingLogicalWidth(); 994 return max<LayoutUnit>(0, width); 995} 996 997LayoutUnit RenderBox::adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit height) const 998{ 999 if (style()->boxSizing() == BORDER_BOX) 1000 height -= borderAndPaddingLogicalHeight(); 1001 return max<LayoutUnit>(0, height); 1002} 1003 1004// Hit Testing 1005bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action) 1006{ 1007 LayoutPoint adjustedLocation = accumulatedOffset + location(); 1008 1009 // Check kids first. 1010 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { 1011 if (!child->hasLayer() && child->nodeAtPoint(request, result, locationInContainer, adjustedLocation, action)) { 1012 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); 1013 return true; 1014 } 1015 } 1016 1017 // Check our bounds next. For this purpose always assume that we can only be hit in the 1018 // foreground phase (which is true for replaced elements like images). 1019 LayoutRect boundsRect = borderBoxRectInRegion(locationInContainer.region()); 1020 boundsRect.moveBy(adjustedLocation); 1021 if (visibleToHitTesting() && action == HitTestForeground && locationInContainer.intersects(boundsRect)) { 1022 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation)); 1023 if (!result.addNodeToRectBasedTestResult(node(), request, locationInContainer, boundsRect)) 1024 return true; 1025 } 1026 1027 return false; 1028} 1029 1030// --------------------- painting stuff ------------------------------- 1031 1032void RenderBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1033{ 1034 LayoutPoint adjustedPaintOffset = paintOffset + location(); 1035 // default implementation. Just pass paint through to the children 1036 PaintInfo childInfo(paintInfo); 1037 childInfo.updateSubtreePaintRootForChildren(this); 1038 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) 1039 child->paint(childInfo, adjustedPaintOffset); 1040} 1041 1042void RenderBox::paintRootBoxFillLayers(const PaintInfo& paintInfo) 1043{ 1044 if (paintInfo.skipRootBackground()) 1045 return; 1046 1047 RenderObject* rootBackgroundRenderer = rendererForRootBackground(); 1048 1049 const FillLayer* bgLayer = rootBackgroundRenderer->style()->backgroundLayers(); 1050 Color bgColor = rootBackgroundRenderer->style()->visitedDependentColor(CSSPropertyBackgroundColor); 1051 1052 paintFillLayers(paintInfo, bgColor, bgLayer, view()->backgroundRect(this), BackgroundBleedNone, CompositeSourceOver, rootBackgroundRenderer); 1053} 1054 1055BackgroundBleedAvoidance RenderBox::determineBackgroundBleedAvoidance(GraphicsContext* context) const 1056{ 1057 if (context->paintingDisabled()) 1058 return BackgroundBleedNone; 1059 1060 const RenderStyle* style = this->style(); 1061 1062 if (!style->hasBackground() || !style->hasBorder() || !style->hasBorderRadius() || borderImageIsLoadedAndCanBeRendered()) 1063 return BackgroundBleedNone; 1064 1065 AffineTransform ctm = context->getCTM(); 1066 FloatSize contextScaling(static_cast<float>(ctm.xScale()), static_cast<float>(ctm.yScale())); 1067 1068 // Because RoundedRect uses IntRect internally the inset applied by the 1069 // BackgroundBleedShrinkBackground strategy cannot be less than one integer 1070 // layout coordinate, even with subpixel layout enabled. To take that into 1071 // account, we clamp the contextScaling to 1.0 for the following test so 1072 // that borderObscuresBackgroundEdge can only return true if the border 1073 // widths are greater than 2 in both layout coordinates and screen 1074 // coordinates. 1075 // This precaution will become obsolete if RoundedRect is ever promoted to 1076 // a sub-pixel representation. 1077 if (contextScaling.width() > 1) 1078 contextScaling.setWidth(1); 1079 if (contextScaling.height() > 1) 1080 contextScaling.setHeight(1); 1081 1082 if (borderObscuresBackgroundEdge(contextScaling)) 1083 return BackgroundBleedShrinkBackground; 1084 if (!style->hasAppearance() && borderObscuresBackground() && backgroundHasOpaqueTopLayer()) 1085 return BackgroundBleedBackgroundOverBorder; 1086 1087 return BackgroundBleedUseTransparencyLayer; 1088} 1089 1090void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1091{ 1092 if (!paintInfo.shouldPaintWithinRoot(this)) 1093 return; 1094 1095 LayoutRect paintRect = borderBoxRectInRegion(paintInfo.renderRegion); 1096 paintRect.moveBy(paintOffset); 1097 1098 BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context); 1099 1100 // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have 1101 // custom shadows of their own. 1102 if (!boxShadowShouldBeAppliedToBackground(bleedAvoidance)) 1103 paintBoxShadow(paintInfo, paintRect, style(), Normal); 1104 1105 GraphicsContextStateSaver stateSaver(*paintInfo.context, false); 1106 if (bleedAvoidance == BackgroundBleedUseTransparencyLayer) { 1107 // To avoid the background color bleeding out behind the border, we'll render background and border 1108 // into a transparency layer, and then clip that in one go (which requires setting up the clip before 1109 // beginning the layer). 1110 RoundedRect border = style()->getRoundedBorderFor(paintRect, view()); 1111 stateSaver.save(); 1112 paintInfo.context->clipRoundedRect(border); 1113 paintInfo.context->beginTransparencyLayer(1); 1114 } 1115 1116 // If we have a native theme appearance, paint that before painting our background. 1117 // The theme will tell us whether or not we should also paint the CSS background. 1118 IntRect snappedPaintRect(pixelSnappedIntRect(paintRect)); 1119 bool themePainted = style()->hasAppearance() && !theme()->paint(this, paintInfo, snappedPaintRect); 1120 if (!themePainted) { 1121 if (bleedAvoidance == BackgroundBleedBackgroundOverBorder) 1122 paintBorder(paintInfo, paintRect, style(), bleedAvoidance); 1123 1124 paintBackground(paintInfo, paintRect, bleedAvoidance); 1125 1126 if (style()->hasAppearance()) 1127 theme()->paintDecorations(this, paintInfo, snappedPaintRect); 1128 } 1129 paintBoxShadow(paintInfo, paintRect, style(), Inset); 1130 1131 // The theme will tell us whether or not we should also paint the CSS border. 1132 if (bleedAvoidance != BackgroundBleedBackgroundOverBorder && (!style()->hasAppearance() || (!themePainted && theme()->paintBorderOnly(this, paintInfo, snappedPaintRect))) && style()->hasBorder()) 1133 paintBorder(paintInfo, paintRect, style(), bleedAvoidance); 1134 1135 if (bleedAvoidance == BackgroundBleedUseTransparencyLayer) 1136 paintInfo.context->endTransparencyLayer(); 1137} 1138 1139void RenderBox::paintBackground(const PaintInfo& paintInfo, const LayoutRect& paintRect, BackgroundBleedAvoidance bleedAvoidance) 1140{ 1141 if (isRoot()) { 1142 paintRootBoxFillLayers(paintInfo); 1143 return; 1144 } 1145 if (isBody() && skipBodyBackground(this)) 1146 return; 1147 if (backgroundIsKnownToBeObscured() && !boxShadowShouldBeAppliedToBackground(bleedAvoidance)) 1148 return; 1149 paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), paintRect, bleedAvoidance); 1150} 1151 1152bool RenderBox::getBackgroundPaintedExtent(LayoutRect& paintedExtent) const 1153{ 1154 ASSERT(hasBackground()); 1155 LayoutRect backgroundRect = pixelSnappedIntRect(borderBoxRect()); 1156 1157 Color backgroundColor = style()->visitedDependentColor(CSSPropertyBackgroundColor); 1158 if (backgroundColor.isValid() && backgroundColor.alpha()) { 1159 paintedExtent = backgroundRect; 1160 return true; 1161 } 1162 1163 if (!style()->backgroundLayers()->image() || style()->backgroundLayers()->next()) { 1164 paintedExtent = backgroundRect; 1165 return true; 1166 } 1167 1168 BackgroundImageGeometry geometry; 1169 calculateBackgroundImageGeometry(0, style()->backgroundLayers(), backgroundRect, geometry); 1170 paintedExtent = geometry.destRect(); 1171 return !geometry.hasNonLocalGeometry(); 1172} 1173 1174bool RenderBox::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) const 1175{ 1176 if (isBody() && skipBodyBackground(this)) 1177 return false; 1178 1179 Color backgroundColor = style()->visitedDependentColor(CSSPropertyBackgroundColor); 1180 if (!backgroundColor.isValid() || backgroundColor.hasAlpha()) 1181 return false; 1182 1183 // If the element has appearance, it might be painted by theme. 1184 // We cannot be sure if theme paints the background opaque. 1185 // In this case it is safe to not assume opaqueness. 1186 // FIXME: May be ask theme if it paints opaque. 1187 if (style()->hasAppearance()) 1188 return false; 1189 // FIXME: Check the opaqueness of background images. 1190 1191 // FIXME: Use rounded rect if border radius is present. 1192 if (style()->hasBorderRadius()) 1193 return false; 1194 // FIXME: The background color clip is defined by the last layer. 1195 if (style()->backgroundLayers()->next()) 1196 return false; 1197 LayoutRect backgroundRect; 1198 switch (style()->backgroundClip()) { 1199 case BorderFillBox: 1200 backgroundRect = borderBoxRect(); 1201 break; 1202 case PaddingFillBox: 1203 backgroundRect = paddingBoxRect(); 1204 break; 1205 case ContentFillBox: 1206 backgroundRect = contentBoxRect(); 1207 break; 1208 default: 1209 break; 1210 } 1211 return backgroundRect.contains(localRect); 1212} 1213 1214static bool isCandidateForOpaquenessTest(RenderBox* childBox) 1215{ 1216 RenderStyle* childStyle = childBox->style(); 1217 if (childStyle->position() != StaticPosition && childBox->containingBlock() != childBox->parent()) 1218 return false; 1219 if (childStyle->visibility() != VISIBLE || childStyle->shapeOutside()) 1220 return false; 1221 if (!childBox->width() || !childBox->height()) 1222 return false; 1223 if (RenderLayer* childLayer = childBox->layer()) { 1224#if USE(ACCELERATED_COMPOSITING) 1225 if (childLayer->isComposited()) 1226 return false; 1227#endif 1228 // FIXME: Deal with z-index. 1229 if (!childStyle->hasAutoZIndex()) 1230 return false; 1231 if (childLayer->hasTransform() || childLayer->isTransparent() || childLayer->hasFilter()) 1232 return false; 1233 } 1234 return true; 1235} 1236 1237bool RenderBox::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const 1238{ 1239 if (!maxDepthToTest) 1240 return false; 1241 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 1242 if (!child->isBox()) 1243 continue; 1244 RenderBox* childBox = toRenderBox(child); 1245 if (!isCandidateForOpaquenessTest(childBox)) 1246 continue; 1247 LayoutPoint childLocation = childBox->location(); 1248 if (childBox->isRelPositioned()) 1249 childLocation.move(childBox->relativePositionOffset()); 1250 LayoutRect childLocalRect = localRect; 1251 childLocalRect.moveBy(-childLocation); 1252 if (childLocalRect.y() < 0 || childLocalRect.x() < 0) { 1253 // If there is unobscured area above/left of a static positioned box then the rect is probably not covered. 1254 if (childBox->style()->position() == StaticPosition) 1255 return false; 1256 continue; 1257 } 1258 if (childLocalRect.maxY() > childBox->height() || childLocalRect.maxX() > childBox->width()) 1259 continue; 1260 if (childBox->backgroundIsKnownToBeOpaqueInRect(childLocalRect)) 1261 return true; 1262 if (childBox->foregroundIsKnownToBeOpaqueInRect(childLocalRect, maxDepthToTest - 1)) 1263 return true; 1264 } 1265 return false; 1266} 1267 1268bool RenderBox::computeBackgroundIsKnownToBeObscured() 1269{ 1270 // Test to see if the children trivially obscure the background. 1271 // FIXME: This test can be much more comprehensive. 1272 if (!hasBackground()) 1273 return false; 1274 // Table and root background painting is special. 1275 if (isTable() || isRoot()) 1276 return false; 1277 1278 LayoutRect backgroundRect; 1279 if (!getBackgroundPaintedExtent(backgroundRect)) 1280 return false; 1281 return foregroundIsKnownToBeOpaqueInRect(backgroundRect, backgroundObscurationTestMaxDepth); 1282} 1283 1284bool RenderBox::backgroundHasOpaqueTopLayer() const 1285{ 1286 const FillLayer* fillLayer = style()->backgroundLayers(); 1287 if (!fillLayer || fillLayer->clip() != BorderFillBox) 1288 return false; 1289 1290 // Clipped with local scrolling 1291 if (hasOverflowClip() && fillLayer->attachment() == LocalBackgroundAttachment) 1292 return false; 1293 1294 if (fillLayer->hasOpaqueImage(this) && fillLayer->hasRepeatXY() && fillLayer->image()->canRender(this, style()->effectiveZoom())) 1295 return true; 1296 1297 // If there is only one layer and no image, check whether the background color is opaque 1298 if (!fillLayer->next() && !fillLayer->hasImage()) { 1299 Color bgColor = style()->visitedDependentColor(CSSPropertyBackgroundColor); 1300 if (bgColor.isValid() && bgColor.alpha() == 255) 1301 return true; 1302 } 1303 1304 return false; 1305} 1306 1307void RenderBox::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1308{ 1309 if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask || paintInfo.context->paintingDisabled()) 1310 return; 1311 1312 LayoutRect paintRect = LayoutRect(paintOffset, size()); 1313 paintMaskImages(paintInfo, paintRect); 1314} 1315 1316void RenderBox::paintMaskImages(const PaintInfo& paintInfo, const LayoutRect& paintRect) 1317{ 1318 // Figure out if we need to push a transparency layer to render our mask. 1319 bool pushTransparencyLayer = false; 1320 bool compositedMask = hasLayer() && layer()->hasCompositedMask(); 1321 bool flattenCompositingLayers = view()->frameView() && view()->frameView()->paintBehavior() & PaintBehaviorFlattenCompositingLayers; 1322 CompositeOperator compositeOp = CompositeSourceOver; 1323 1324 bool allMaskImagesLoaded = true; 1325 1326 if (!compositedMask || flattenCompositingLayers) { 1327 pushTransparencyLayer = true; 1328 StyleImage* maskBoxImage = style()->maskBoxImage().image(); 1329 const FillLayer* maskLayers = style()->maskLayers(); 1330 1331 // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content. 1332 if (maskBoxImage) 1333 allMaskImagesLoaded &= maskBoxImage->isLoaded(); 1334 1335 if (maskLayers) 1336 allMaskImagesLoaded &= maskLayers->imagesAreLoaded(); 1337 1338 paintInfo.context->setCompositeOperation(CompositeDestinationIn); 1339 paintInfo.context->beginTransparencyLayer(1); 1340 compositeOp = CompositeSourceOver; 1341 } 1342 1343 if (allMaskImagesLoaded) { 1344 paintFillLayers(paintInfo, Color(), style()->maskLayers(), paintRect, BackgroundBleedNone, compositeOp); 1345 paintNinePieceImage(paintInfo.context, paintRect, style(), style()->maskBoxImage(), compositeOp); 1346 } 1347 1348 if (pushTransparencyLayer) 1349 paintInfo.context->endTransparencyLayer(); 1350} 1351 1352LayoutRect RenderBox::maskClipRect() 1353{ 1354 const NinePieceImage& maskBoxImage = style()->maskBoxImage(); 1355 if (maskBoxImage.image()) { 1356 LayoutRect borderImageRect = borderBoxRect(); 1357 1358 // Apply outsets to the border box. 1359 borderImageRect.expand(style()->maskBoxImageOutsets()); 1360 return borderImageRect; 1361 } 1362 1363 LayoutRect result; 1364 LayoutRect borderBox = borderBoxRect(); 1365 for (const FillLayer* maskLayer = style()->maskLayers(); maskLayer; maskLayer = maskLayer->next()) { 1366 if (maskLayer->image()) { 1367 BackgroundImageGeometry geometry; 1368 // Masks should never have fixed attachment, so it's OK for paintContainer to be null. 1369 calculateBackgroundImageGeometry(0, maskLayer, borderBox, geometry); 1370 result.unite(geometry.destRect()); 1371 } 1372 } 1373 return result; 1374} 1375 1376void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect, 1377 BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject) 1378{ 1379 Vector<const FillLayer*, 8> layers; 1380 const FillLayer* curLayer = fillLayer; 1381 bool shouldDrawBackgroundInSeparateBuffer = false; 1382 while (curLayer) { 1383 layers.append(curLayer); 1384 // Stop traversal when an opaque layer is encountered. 1385 // FIXME : It would be possible for the following occlusion culling test to be more aggressive 1386 // on layers with no repeat by testing whether the image covers the layout rect. 1387 // Testing that here would imply duplicating a lot of calculations that are currently done in 1388 // RenderBoxModelObject::paintFillLayerExtended. A more efficient solution might be to move 1389 // the layer recursion into paintFillLayerExtended, or to compute the layer geometry here 1390 // and pass it down. 1391 1392 if (!shouldDrawBackgroundInSeparateBuffer && curLayer->blendMode() != BlendModeNormal) 1393 shouldDrawBackgroundInSeparateBuffer = true; 1394 1395 // The clipOccludesNextLayers condition must be evaluated first to avoid short-circuiting. 1396 if (curLayer->clipOccludesNextLayers(curLayer == fillLayer) && curLayer->hasOpaqueImage(this) && curLayer->image()->canRender(this, style()->effectiveZoom()) && curLayer->hasRepeatXY() && curLayer->blendMode() == BlendModeNormal) 1397 break; 1398 curLayer = curLayer->next(); 1399 } 1400 1401 GraphicsContext* context = paintInfo.context; 1402 if (!context) 1403 shouldDrawBackgroundInSeparateBuffer = false; 1404 if (shouldDrawBackgroundInSeparateBuffer) 1405 context->beginTransparencyLayer(1); 1406 1407 Vector<const FillLayer*>::const_reverse_iterator topLayer = layers.rend(); 1408 for (Vector<const FillLayer*>::const_reverse_iterator it = layers.rbegin(); it != topLayer; ++it) 1409 paintFillLayer(paintInfo, c, *it, rect, bleedAvoidance, op, backgroundObject); 1410 1411 if (shouldDrawBackgroundInSeparateBuffer) 1412 context->endTransparencyLayer(); 1413} 1414 1415void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect, 1416 BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject) 1417{ 1418 paintFillLayerExtended(paintInfo, c, fillLayer, rect, bleedAvoidance, 0, LayoutSize(), op, backgroundObject); 1419} 1420 1421#if USE(ACCELERATED_COMPOSITING) 1422static bool layersUseImage(WrappedImagePtr image, const FillLayer* layers) 1423{ 1424 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) { 1425 if (curLayer->image() && image == curLayer->image()->data()) 1426 return true; 1427 } 1428 1429 return false; 1430} 1431#endif 1432 1433void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*) 1434{ 1435 if (!parent()) 1436 return; 1437 1438 if ((style()->borderImage().image() && style()->borderImage().image()->data() == image) || 1439 (style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image)) { 1440 repaint(); 1441 return; 1442 } 1443 1444 bool didFullRepaint = repaintLayerRectsForImage(image, style()->backgroundLayers(), true); 1445 if (!didFullRepaint) 1446 repaintLayerRectsForImage(image, style()->maskLayers(), false); 1447 1448 1449#if USE(ACCELERATED_COMPOSITING) 1450 if (!isComposited()) 1451 return; 1452 1453 if (layer()->hasCompositedMask() && layersUseImage(image, style()->maskLayers())) 1454 layer()->contentChanged(MaskImageChanged); 1455 if (layersUseImage(image, style()->backgroundLayers())) 1456 layer()->contentChanged(BackgroundImageChanged); 1457#endif 1458} 1459 1460bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground) 1461{ 1462 LayoutRect rendererRect; 1463 RenderBox* layerRenderer = 0; 1464 1465 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) { 1466 if (curLayer->image() && image == curLayer->image()->data() && curLayer->image()->canRender(this, style()->effectiveZoom())) { 1467 // Now that we know this image is being used, compute the renderer and the rect if we haven't already. 1468 if (!layerRenderer) { 1469 bool drawingRootBackground = drawingBackground && (isRoot() || (isBody() && !document()->documentElement()->renderer()->hasBackground())); 1470 if (drawingRootBackground) { 1471 layerRenderer = view(); 1472 1473 LayoutUnit rw; 1474 LayoutUnit rh; 1475 1476 if (FrameView* frameView = toRenderView(layerRenderer)->frameView()) { 1477 rw = frameView->contentsWidth(); 1478 rh = frameView->contentsHeight(); 1479 } else { 1480 rw = layerRenderer->width(); 1481 rh = layerRenderer->height(); 1482 } 1483 rendererRect = LayoutRect(-layerRenderer->marginLeft(), 1484 -layerRenderer->marginTop(), 1485 max(layerRenderer->width() + layerRenderer->marginWidth() + layerRenderer->borderLeft() + layerRenderer->borderRight(), rw), 1486 max(layerRenderer->height() + layerRenderer->marginHeight() + layerRenderer->borderTop() + layerRenderer->borderBottom(), rh)); 1487 } else { 1488 layerRenderer = this; 1489 rendererRect = borderBoxRect(); 1490 } 1491 } 1492 1493 BackgroundImageGeometry geometry; 1494 layerRenderer->calculateBackgroundImageGeometry(0, curLayer, rendererRect, geometry); 1495 if (geometry.hasNonLocalGeometry()) { 1496 // Rather than incur the costs of computing the paintContainer for renderers with fixed backgrounds 1497 // in order to get the right destRect, just repaint the entire renderer. 1498 layerRenderer->repaint(); 1499 return true; 1500 } 1501 1502 layerRenderer->repaintRectangle(geometry.destRect()); 1503 if (geometry.destRect() == rendererRect) 1504 return true; 1505 } 1506 } 1507 return false; 1508} 1509 1510#if PLATFORM(MAC) 1511 1512void RenderBox::paintCustomHighlight(const LayoutPoint& paintOffset, const AtomicString& type, bool behindText) 1513{ 1514 Frame* frame = this->frame(); 1515 if (!frame) 1516 return; 1517 Page* page = frame->page(); 1518 if (!page) 1519 return; 1520 1521 InlineBox* boxWrap = inlineBoxWrapper(); 1522 RootInlineBox* r = boxWrap ? boxWrap->root() : 0; 1523 if (r) { 1524 FloatRect rootRect(paintOffset.x() + r->x(), paintOffset.y() + r->selectionTop(), r->logicalWidth(), r->selectionHeight()); 1525 FloatRect imageRect(paintOffset.x() + x(), rootRect.y(), width(), rootRect.height()); 1526 page->chrome().client()->paintCustomHighlight(node(), type, imageRect, rootRect, behindText, false); 1527 } else { 1528 FloatRect imageRect(paintOffset.x() + x(), paintOffset.y() + y(), width(), height()); 1529 page->chrome().client()->paintCustomHighlight(node(), type, imageRect, imageRect, behindText, false); 1530 } 1531} 1532 1533#endif 1534 1535bool RenderBox::pushContentsClip(PaintInfo& paintInfo, const LayoutPoint& accumulatedOffset) 1536{ 1537 if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseSelfOutline || paintInfo.phase == PaintPhaseMask) 1538 return false; 1539 1540 bool isControlClip = hasControlClip(); 1541 bool isOverflowClip = hasOverflowClip() && !layer()->isSelfPaintingLayer(); 1542 1543 if (!isControlClip && !isOverflowClip) 1544 return false; 1545 1546 if (paintInfo.phase == PaintPhaseOutline) 1547 paintInfo.phase = PaintPhaseChildOutlines; 1548 else if (paintInfo.phase == PaintPhaseChildBlockBackground) { 1549 paintInfo.phase = PaintPhaseBlockBackground; 1550 paintObject(paintInfo, accumulatedOffset); 1551 paintInfo.phase = PaintPhaseChildBlockBackgrounds; 1552 } 1553 IntRect clipRect = pixelSnappedIntRect(isControlClip ? controlClipRect(accumulatedOffset) : overflowClipRect(accumulatedOffset, paintInfo.renderRegion)); 1554 paintInfo.context->save(); 1555 if (style()->hasBorderRadius()) 1556 paintInfo.context->clipRoundedRect(style()->getRoundedInnerBorderFor(LayoutRect(accumulatedOffset, size()))); 1557 paintInfo.context->clip(clipRect); 1558 return true; 1559} 1560 1561void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, const LayoutPoint& accumulatedOffset) 1562{ 1563 ASSERT(hasControlClip() || (hasOverflowClip() && !layer()->isSelfPaintingLayer())); 1564 1565 paintInfo.context->restore(); 1566 if (originalPhase == PaintPhaseOutline) { 1567 paintInfo.phase = PaintPhaseSelfOutline; 1568 paintObject(paintInfo, accumulatedOffset); 1569 paintInfo.phase = originalPhase; 1570 } else if (originalPhase == PaintPhaseChildBlockBackground) 1571 paintInfo.phase = originalPhase; 1572} 1573 1574LayoutRect RenderBox::overflowClipRect(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy) 1575{ 1576 // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property 1577 // here. 1578 LayoutRect clipRect = borderBoxRectInRegion(region); 1579 clipRect.setLocation(location + clipRect.location() + LayoutSize(borderLeft(), borderTop())); 1580 clipRect.setSize(clipRect.size() - LayoutSize(borderLeft() + borderRight(), borderTop() + borderBottom())); 1581 1582 // Subtract out scrollbars if we have them. 1583 if (layer()) { 1584 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) 1585 clipRect.move(layer()->verticalScrollbarWidth(relevancy), 0); 1586 clipRect.contract(layer()->verticalScrollbarWidth(relevancy), layer()->horizontalScrollbarHeight(relevancy)); 1587 } 1588 1589 return clipRect; 1590} 1591 1592LayoutRect RenderBox::clipRect(const LayoutPoint& location, RenderRegion* region) 1593{ 1594 LayoutRect borderBoxRect = borderBoxRectInRegion(region); 1595 LayoutRect clipRect = LayoutRect(borderBoxRect.location() + location, borderBoxRect.size()); 1596 RenderView* renderView = view(); 1597 1598 if (!style()->clipLeft().isAuto()) { 1599 LayoutUnit c = valueForLength(style()->clipLeft(), borderBoxRect.width(), renderView); 1600 clipRect.move(c, 0); 1601 clipRect.contract(c, 0); 1602 } 1603 1604 // We don't use the region-specific border box's width and height since clip offsets are (stupidly) specified 1605 // from the left and top edges. Therefore it's better to avoid constraining to smaller widths and heights. 1606 1607 if (!style()->clipRight().isAuto()) 1608 clipRect.contract(width() - valueForLength(style()->clipRight(), width(), renderView), 0); 1609 1610 if (!style()->clipTop().isAuto()) { 1611 LayoutUnit c = valueForLength(style()->clipTop(), borderBoxRect.height(), renderView); 1612 clipRect.move(0, c); 1613 clipRect.contract(0, c); 1614 } 1615 1616 if (!style()->clipBottom().isAuto()) 1617 clipRect.contract(0, height() - valueForLength(style()->clipBottom(), height(), renderView)); 1618 1619 return clipRect; 1620} 1621 1622LayoutUnit RenderBox::shrinkLogicalWidthToAvoidFloats(LayoutUnit childMarginStart, LayoutUnit childMarginEnd, const RenderBlock* cb, RenderRegion* region) const 1623{ 1624 RenderRegion* containingBlockRegion = 0; 1625 LayoutUnit logicalTopPosition = logicalTop(); 1626 if (region) { 1627 LayoutUnit offsetFromLogicalTopOfRegion = region ? region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage() : LayoutUnit(); 1628 logicalTopPosition = max(logicalTopPosition, logicalTopPosition + offsetFromLogicalTopOfRegion); 1629 containingBlockRegion = cb->clampToStartAndEndRegions(region); 1630 } 1631 1632 LayoutUnit result = cb->availableLogicalWidthForLine(logicalTopPosition, false, containingBlockRegion) - childMarginStart - childMarginEnd; 1633 1634 // We need to see if margins on either the start side or the end side can contain the floats in question. If they can, 1635 // then just using the line width is inaccurate. In the case where a float completely fits, we don't need to use the line 1636 // offset at all, but can instead push all the way to the content edge of the containing block. In the case where the float 1637 // doesn't fit, we can use the line offset, but we need to grow it by the margin to reflect the fact that the margin was 1638 // "consumed" by the float. Negative margins aren't consumed by the float, and so we ignore them. 1639 if (childMarginStart > 0) { 1640 LayoutUnit startContentSide = cb->startOffsetForContent(containingBlockRegion); 1641 LayoutUnit startContentSideWithMargin = startContentSide + childMarginStart; 1642 LayoutUnit startOffset = cb->startOffsetForLine(logicalTopPosition, false, containingBlockRegion); 1643 if (startOffset > startContentSideWithMargin) 1644 result += childMarginStart; 1645 else 1646 result += startOffset - startContentSide; 1647 } 1648 1649 if (childMarginEnd > 0) { 1650 LayoutUnit endContentSide = cb->endOffsetForContent(containingBlockRegion); 1651 LayoutUnit endContentSideWithMargin = endContentSide + childMarginEnd; 1652 LayoutUnit endOffset = cb->endOffsetForLine(logicalTopPosition, false, containingBlockRegion); 1653 if (endOffset > endContentSideWithMargin) 1654 result += childMarginEnd; 1655 else 1656 result += endOffset - endContentSide; 1657 } 1658 1659 return result; 1660} 1661 1662LayoutUnit RenderBox::containingBlockLogicalWidthForContent() const 1663{ 1664 if (hasOverrideContainingBlockLogicalWidth()) 1665 return overrideContainingBlockContentLogicalWidth(); 1666 1667 RenderBlock* cb = containingBlock(); 1668 return cb->availableLogicalWidth(); 1669} 1670 1671LayoutUnit RenderBox::containingBlockLogicalHeightForContent(AvailableLogicalHeightType heightType) const 1672{ 1673 if (hasOverrideContainingBlockLogicalHeight()) 1674 return overrideContainingBlockContentLogicalHeight(); 1675 1676 RenderBlock* cb = containingBlock(); 1677 return cb->availableLogicalHeight(heightType); 1678} 1679 1680LayoutUnit RenderBox::containingBlockLogicalWidthForContentInRegion(RenderRegion* region) const 1681{ 1682 if (!region) 1683 return containingBlockLogicalWidthForContent(); 1684 1685 RenderBlock* cb = containingBlock(); 1686 RenderRegion* containingBlockRegion = cb->clampToStartAndEndRegions(region); 1687 // FIXME: It's unclear if a region's content should use the containing block's override logical width. 1688 // If it should, the following line should call containingBlockLogicalWidthForContent. 1689 LayoutUnit result = cb->availableLogicalWidth(); 1690 RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(containingBlockRegion); 1691 if (!boxInfo) 1692 return result; 1693 return max<LayoutUnit>(0, result - (cb->logicalWidth() - boxInfo->logicalWidth())); 1694} 1695 1696LayoutUnit RenderBox::containingBlockAvailableLineWidthInRegion(RenderRegion* region) const 1697{ 1698 RenderBlock* cb = containingBlock(); 1699 RenderRegion* containingBlockRegion = 0; 1700 LayoutUnit logicalTopPosition = logicalTop(); 1701 if (region) { 1702 LayoutUnit offsetFromLogicalTopOfRegion = region ? region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage() : LayoutUnit(); 1703 logicalTopPosition = max(logicalTopPosition, logicalTopPosition + offsetFromLogicalTopOfRegion); 1704 containingBlockRegion = cb->clampToStartAndEndRegions(region); 1705 } 1706 return cb->availableLogicalWidthForLine(logicalTopPosition, false, containingBlockRegion, availableLogicalHeight(IncludeMarginBorderPadding)); 1707} 1708 1709LayoutUnit RenderBox::perpendicularContainingBlockLogicalHeight() const 1710{ 1711 if (hasOverrideContainingBlockLogicalHeight()) 1712 return overrideContainingBlockContentLogicalHeight(); 1713 1714 RenderBlock* cb = containingBlock(); 1715 if (cb->hasOverrideHeight()) 1716 return cb->overrideLogicalContentHeight(); 1717 1718 RenderStyle* containingBlockStyle = cb->style(); 1719 Length logicalHeightLength = containingBlockStyle->logicalHeight(); 1720 1721 // FIXME: For now just support fixed heights. Eventually should support percentage heights as well. 1722 if (!logicalHeightLength.isFixed()) { 1723 LayoutUnit fillFallbackExtent = containingBlockStyle->isHorizontalWritingMode() ? view()->frameView()->visibleHeight() : view()->frameView()->visibleWidth(); 1724 LayoutUnit fillAvailableExtent = containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding); 1725 return min(fillAvailableExtent, fillFallbackExtent); 1726 } 1727 1728 // Use the content box logical height as specified by the style. 1729 return cb->adjustContentBoxLogicalHeightForBoxSizing(logicalHeightLength.value()); 1730} 1731 1732void RenderBox::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const 1733{ 1734 if (repaintContainer == this) 1735 return; 1736 1737 if (RenderView* v = view()) { 1738 if (v->layoutStateEnabled() && !repaintContainer) { 1739 LayoutState* layoutState = v->layoutState(); 1740 LayoutSize offset = layoutState->m_paintOffset + locationOffset(); 1741 if (style()->hasPaintOffset() && layer()) 1742 offset += layer()->paintOffset(); 1743 transformState.move(offset); 1744 return; 1745 } 1746 } 1747 1748 bool containerSkipped; 1749 RenderObject* o = container(repaintContainer, &containerSkipped); 1750 if (!o) 1751 return; 1752 1753 bool isFixedPos = style()->position() == FixedPosition; 1754 bool hasTransform = hasLayer() && layer()->transform(); 1755 // If this box has a transform, it acts as a fixed position container for fixed descendants, 1756 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. 1757 if (hasTransform && !isFixedPos) 1758 mode &= ~IsFixed; 1759 else if (isFixedPos) 1760 mode |= IsFixed; 1761 1762 if (wasFixed) 1763 *wasFixed = mode & IsFixed; 1764 1765 LayoutSize containerOffset = offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint())); 1766 1767 bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D()); 1768 if (mode & UseTransforms && shouldUseTransformFromContainer(o)) { 1769 TransformationMatrix t; 1770 getTransformFromContainer(o, containerOffset, t); 1771 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1772 } else 1773 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1774 1775 if (containerSkipped) { 1776 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe 1777 // to just subtract the delta between the repaintContainer and o. 1778 LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); 1779 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); 1780 return; 1781 } 1782 1783 mode &= ~ApplyContainerFlip; 1784 1785 o->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed); 1786} 1787 1788const RenderObject* RenderBox::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const 1789{ 1790 ASSERT(ancestorToStopAt != this); 1791 1792 bool ancestorSkipped; 1793 RenderObject* container = this->container(ancestorToStopAt, &ancestorSkipped); 1794 if (!container) 1795 return 0; 1796 1797 bool isFixedPos = style()->position() == FixedPosition; 1798 bool hasTransform = hasLayer() && layer()->transform(); 1799 1800 LayoutSize adjustmentForSkippedAncestor; 1801 if (ancestorSkipped) { 1802 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe 1803 // to just subtract the delta between the ancestor and o. 1804 adjustmentForSkippedAncestor = -ancestorToStopAt->offsetFromAncestorContainer(container); 1805 } 1806 1807 bool offsetDependsOnPoint = false; 1808 LayoutSize containerOffset = offsetFromContainer(container, LayoutPoint(), &offsetDependsOnPoint); 1809 1810 bool preserve3D = container->style()->preserves3D() || style()->preserves3D(); 1811 if (shouldUseTransformFromContainer(container)) { 1812 TransformationMatrix t; 1813 getTransformFromContainer(container, containerOffset, t); 1814 t.translateRight(adjustmentForSkippedAncestor.width(), adjustmentForSkippedAncestor.height()); 1815 1816 geometryMap.push(this, t, preserve3D, offsetDependsOnPoint, isFixedPos, hasTransform); 1817 } else { 1818 containerOffset += adjustmentForSkippedAncestor; 1819 geometryMap.push(this, containerOffset, preserve3D, offsetDependsOnPoint, isFixedPos, hasTransform); 1820 } 1821 1822 return ancestorSkipped ? ancestorToStopAt : container; 1823} 1824 1825void RenderBox::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const 1826{ 1827 bool isFixedPos = style()->position() == FixedPosition; 1828 bool hasTransform = hasLayer() && layer()->transform(); 1829 if (hasTransform && !isFixedPos) { 1830 // If this box has a transform, it acts as a fixed position container for fixed descendants, 1831 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. 1832 mode &= ~IsFixed; 1833 } else if (isFixedPos) 1834 mode |= IsFixed; 1835 1836 RenderBoxModelObject::mapAbsoluteToLocalPoint(mode, transformState); 1837} 1838 1839LayoutSize RenderBox::offsetFromContainer(RenderObject* o, const LayoutPoint& point, bool* offsetDependsOnPoint) const 1840{ 1841 // A region "has" boxes inside it without being their container. 1842 ASSERT(o == container() || o->isRenderRegion()); 1843 1844 LayoutSize offset; 1845 if (hasPaintOffset()) 1846 offset += paintOffset(); 1847 1848 if (!isInline() || isReplaced()) { 1849 if (!style()->hasOutOfFlowPosition() && o->hasColumns()) { 1850 RenderBlock* block = toRenderBlock(o); 1851 LayoutRect columnRect(frameRect()); 1852 block->adjustStartEdgeForWritingModeIncludingColumns(columnRect); 1853 offset += toSize(columnRect.location()); 1854 LayoutPoint columnPoint = block->flipForWritingModeIncludingColumns(point + offset); 1855 offset = toLayoutSize(block->flipForWritingModeIncludingColumns(toLayoutPoint(offset))); 1856 o->adjustForColumns(offset, columnPoint); 1857 offset = block->flipForWritingMode(offset); 1858 1859 if (offsetDependsOnPoint) 1860 *offsetDependsOnPoint = true; 1861 } else 1862 offset += topLeftLocationOffset(); 1863 } 1864 1865 if (o->hasOverflowClip()) 1866 offset -= toRenderBox(o)->scrolledContentOffset(); 1867 1868 if (style()->position() == AbsolutePosition && o->isInFlowPositioned() && o->isRenderInline()) 1869 offset += toRenderInline(o)->offsetForInFlowPositionedInline(this); 1870 1871 if (offsetDependsOnPoint) 1872 *offsetDependsOnPoint |= o->isRenderFlowThread(); 1873 1874 return offset; 1875} 1876 1877InlineBox* RenderBox::createInlineBox() 1878{ 1879 return new (renderArena()) InlineBox(this); 1880} 1881 1882void RenderBox::dirtyLineBoxes(bool fullLayout) 1883{ 1884 if (m_inlineBoxWrapper) { 1885 if (fullLayout) { 1886 m_inlineBoxWrapper->destroy(renderArena()); 1887 m_inlineBoxWrapper = 0; 1888 } else 1889 m_inlineBoxWrapper->dirtyLineBoxes(); 1890 } 1891} 1892 1893void RenderBox::positionLineBox(InlineBox* box) 1894{ 1895 if (isOutOfFlowPositioned()) { 1896 // Cache the x position only if we were an INLINE type originally. 1897 bool wasInline = style()->isOriginalDisplayInlineType(); 1898 if (wasInline) { 1899 // The value is cached in the xPos of the box. We only need this value if 1900 // our object was inline originally, since otherwise it would have ended up underneath 1901 // the inlines. 1902 RootInlineBox* root = box->root(); 1903 root->block()->setStaticInlinePositionForChild(this, root->lineTopWithLeading(), roundedLayoutUnit(box->logicalLeft())); 1904 if (style()->hasStaticInlinePosition(box->isHorizontal())) 1905 setChildNeedsLayout(true, MarkOnlyThis); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. 1906 } else { 1907 // Our object was a block originally, so we make our normal flow position be 1908 // just below the line box (as though all the inlines that came before us got 1909 // wrapped in an anonymous block, which is what would have happened had we been 1910 // in flow). This value was cached in the y() of the box. 1911 layer()->setStaticBlockPosition(box->logicalTop()); 1912 if (style()->hasStaticBlockPosition(box->isHorizontal())) 1913 setChildNeedsLayout(true, MarkOnlyThis); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. 1914 } 1915 1916 // Nuke the box. 1917 box->remove(); 1918 box->destroy(renderArena()); 1919 } else if (isReplaced()) { 1920 setLocation(roundedLayoutPoint(box->topLeft())); 1921 // m_inlineBoxWrapper should already be 0. Deleting it is a safeguard against security issues. 1922 ASSERT(!m_inlineBoxWrapper); 1923 if (m_inlineBoxWrapper) 1924 deleteLineBoxWrapper(); 1925 m_inlineBoxWrapper = box; 1926 } 1927} 1928 1929void RenderBox::deleteLineBoxWrapper() 1930{ 1931 if (m_inlineBoxWrapper) { 1932 if (!documentBeingDestroyed()) 1933 m_inlineBoxWrapper->remove(); 1934 m_inlineBoxWrapper->destroy(renderArena()); 1935 m_inlineBoxWrapper = 0; 1936 } 1937} 1938 1939LayoutRect RenderBox::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const 1940{ 1941 if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent()) 1942 return LayoutRect(); 1943 1944 LayoutRect r = visualOverflowRect(); 1945 1946 RenderView* v = view(); 1947 if (v) { 1948 // FIXME: layoutDelta needs to be applied in parts before/after transforms and 1949 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 1950 r.move(v->layoutDelta()); 1951 } 1952 1953 if (style()) { 1954 // We have to use maximalOutlineSize() because a child might have an outline 1955 // that projects outside of our overflowRect. 1956 if (v) { 1957 ASSERT(style()->outlineSize() <= v->maximalOutlineSize()); 1958 r.inflate(v->maximalOutlineSize()); 1959 } 1960 } 1961 1962 computeRectForRepaint(repaintContainer, r); 1963 return r; 1964} 1965 1966void RenderBox::computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect& rect, bool fixed) const 1967{ 1968 // The rect we compute at each step is shifted by our x/y offset in the parent container's coordinate space. 1969 // Only when we cross a writing mode boundary will we have to possibly flipForWritingMode (to convert into a more appropriate 1970 // offset corner for the enclosing container). This allows for a fully RL or BT document to repaint 1971 // properly even during layout, since the rect remains flipped all the way until the end. 1972 // 1973 // RenderView::computeRectForRepaint then converts the rect to physical coordinates. We also convert to 1974 // physical when we hit a repaintContainer boundary. Therefore the final rect returned is always in the 1975 // physical coordinate space of the repaintContainer. 1976 RenderStyle* styleToUse = style(); 1977 if (RenderView* v = view()) { 1978 // LayoutState is only valid for root-relative, non-fixed position repainting 1979 if (v->layoutStateEnabled() && !repaintContainer && styleToUse->position() != FixedPosition) { 1980 LayoutState* layoutState = v->layoutState(); 1981 1982 if (layer() && layer()->transform()) 1983 rect = layer()->transform()->mapRect(pixelSnappedIntRect(rect)); 1984 1985 // We can't trust the bits on RenderObject, because this might be called while re-resolving style. 1986 if (styleToUse->hasPaintOffset() && layer()) 1987 rect.move(layer()->paintOffset()); 1988 1989 rect.moveBy(location()); 1990 rect.move(layoutState->m_paintOffset); 1991 if (layoutState->m_clipped) 1992 rect.intersect(layoutState->m_clipRect); 1993 return; 1994 } 1995 } 1996 1997 if (hasReflection()) 1998 rect.unite(reflectedRect(rect)); 1999 2000 if (repaintContainer == this) { 2001 if (repaintContainer->style()->isFlippedBlocksWritingMode()) 2002 flipForWritingMode(rect); 2003 return; 2004 } 2005 2006 bool containerSkipped; 2007 RenderObject* o = container(repaintContainer, &containerSkipped); 2008 if (!o) 2009 return; 2010 2011 if (isWritingModeRoot() && !isOutOfFlowPositioned()) 2012 flipForWritingMode(rect); 2013 2014 LayoutPoint topLeft = rect.location(); 2015 topLeft.move(locationOffset()); 2016 2017 EPosition position = styleToUse->position(); 2018 2019 // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box 2020 // in the parent's coordinate space that encloses us. 2021 if (hasLayer() && layer()->transform()) { 2022 fixed = position == FixedPosition; 2023 rect = layer()->transform()->mapRect(pixelSnappedIntRect(rect)); 2024 topLeft = rect.location(); 2025 topLeft.move(locationOffset()); 2026 } else if (position == FixedPosition) 2027 fixed = true; 2028 2029 if (position == AbsolutePosition && o->isInFlowPositioned() && o->isRenderInline()) 2030 topLeft += toRenderInline(o)->offsetForInFlowPositionedInline(this); 2031 else if (styleToUse->hasPaintOffset() && layer()) { 2032 // Apply the relative position offset when invalidating a rectangle. The layer 2033 // is translated, but the render box isn't, so we need to do this to get the 2034 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position 2035 // flag on the RenderObject has been cleared, so use the one on the style(). 2036 topLeft += layer()->paintOffset(); 2037 } 2038 2039 if (position != AbsolutePosition && position != FixedPosition && o->hasColumns() && o->isBlockFlow()) { 2040 LayoutRect repaintRect(topLeft, rect.size()); 2041 toRenderBlock(o)->adjustRectForColumns(repaintRect); 2042 topLeft = repaintRect.location(); 2043 rect = repaintRect; 2044 } 2045 2046 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout, 2047 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer. 2048 rect.setLocation(topLeft); 2049 if (o->hasOverflowClip()) { 2050 RenderBox* containerBox = toRenderBox(o); 2051 containerBox->applyCachedClipAndScrollOffsetForRepaint(rect); 2052 if (rect.isEmpty()) 2053 return; 2054 } 2055 2056 if (containerSkipped) { 2057 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates. 2058 LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); 2059 rect.move(-containerOffset); 2060 return; 2061 } 2062 2063 o->computeRectForRepaint(repaintContainer, rect, fixed); 2064} 2065 2066void RenderBox::repaintDuringLayoutIfMoved(const LayoutRect& oldRect) 2067{ 2068 if (oldRect.location() != m_frameRect.location()) { 2069 LayoutRect newRect = m_frameRect; 2070 // The child moved. Invalidate the object's old and new positions. We have to do this 2071 // since the object may not have gotten a layout. 2072 m_frameRect = oldRect; 2073 repaint(); 2074 repaintOverhangingFloats(true); 2075 m_frameRect = newRect; 2076 repaint(); 2077 repaintOverhangingFloats(true); 2078 } 2079} 2080 2081void RenderBox::repaintOverhangingFloats(bool) 2082{ 2083} 2084 2085void RenderBox::updateLogicalWidth() 2086{ 2087 LogicalExtentComputedValues computedValues; 2088 computeLogicalWidthInRegion(computedValues); 2089 2090 setLogicalWidth(computedValues.m_extent); 2091 setLogicalLeft(computedValues.m_position); 2092 setMarginStart(computedValues.m_margins.m_start); 2093 setMarginEnd(computedValues.m_margins.m_end); 2094} 2095 2096void RenderBox::computeLogicalWidthInRegion(LogicalExtentComputedValues& computedValues, RenderRegion* region) const 2097{ 2098 computedValues.m_extent = logicalWidth(); 2099 computedValues.m_position = logicalLeft(); 2100 computedValues.m_margins.m_start = marginStart(); 2101 computedValues.m_margins.m_end = marginEnd(); 2102 2103 if (isOutOfFlowPositioned()) { 2104 // FIXME: This calculation is not patched for block-flow yet. 2105 // https://bugs.webkit.org/show_bug.cgi?id=46500 2106 computePositionedLogicalWidth(computedValues, region); 2107 return; 2108 } 2109 2110 // If layout is limited to a subtree, the subtree root's logical width does not change. 2111 if (node() && view()->frameView() && view()->frameView()->layoutRoot(true) == this) 2112 return; 2113 2114 // The parent box is flexing us, so it has increased or decreased our 2115 // width. Use the width from the style context. 2116 // FIXME: Account for block-flow in flexible boxes. 2117 // https://bugs.webkit.org/show_bug.cgi?id=46418 2118 if (hasOverrideWidth() && (style()->borderFit() == BorderFitLines || parent()->isFlexibleBoxIncludingDeprecated())) { 2119 computedValues.m_extent = overrideLogicalContentWidth() + borderAndPaddingLogicalWidth(); 2120 return; 2121 } 2122 2123 // FIXME: Account for block-flow in flexible boxes. 2124 // https://bugs.webkit.org/show_bug.cgi?id=46418 2125 bool inVerticalBox = parent()->isDeprecatedFlexibleBox() && (parent()->style()->boxOrient() == VERTICAL); 2126 bool stretching = (parent()->style()->boxAlign() == BSTRETCH); 2127 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inVerticalBox || !stretching); 2128 2129 RenderStyle* styleToUse = style(); 2130 Length logicalWidthLength = treatAsReplaced ? Length(computeReplacedLogicalWidth(), Fixed) : styleToUse->logicalWidth(); 2131 2132 RenderBlock* cb = containingBlock(); 2133 LayoutUnit containerLogicalWidth = max<LayoutUnit>(0, containingBlockLogicalWidthForContentInRegion(region)); 2134 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode(); 2135 2136 if (isInline() && !isInlineBlockOrInlineTable()) { 2137 // just calculate margins 2138 RenderView* renderView = view(); 2139 computedValues.m_margins.m_start = minimumValueForLength(styleToUse->marginStart(), containerLogicalWidth, renderView); 2140 computedValues.m_margins.m_end = minimumValueForLength(styleToUse->marginEnd(), containerLogicalWidth, renderView); 2141 if (treatAsReplaced) 2142 computedValues.m_extent = max<LayoutUnit>(floatValueForLength(logicalWidthLength, 0) + borderAndPaddingLogicalWidth(), minPreferredLogicalWidth()); 2143 return; 2144 } 2145 2146 // Width calculations 2147 if (treatAsReplaced) 2148 computedValues.m_extent = logicalWidthLength.value() + borderAndPaddingLogicalWidth(); 2149 else { 2150 LayoutUnit containerWidthInInlineDirection = containerLogicalWidth; 2151 if (hasPerpendicularContainingBlock) 2152 containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight(); 2153 LayoutUnit preferredWidth = computeLogicalWidthInRegionUsing(MainOrPreferredSize, styleToUse->logicalWidth(), containerWidthInInlineDirection, cb, region); 2154 computedValues.m_extent = constrainLogicalWidthInRegionByMinMax(preferredWidth, containerWidthInInlineDirection, cb, region); 2155 } 2156 2157 // Margin calculations. 2158 if (hasPerpendicularContainingBlock || isFloating() || isInline()) { 2159 RenderView* renderView = view(); 2160 computedValues.m_margins.m_start = minimumValueForLength(styleToUse->marginStart(), containerLogicalWidth, renderView); 2161 computedValues.m_margins.m_end = minimumValueForLength(styleToUse->marginEnd(), containerLogicalWidth, renderView); 2162 } else { 2163 LayoutUnit containerLogicalWidthForAutoMargins = containerLogicalWidth; 2164 if (avoidsFloats() && cb->containsFloats()) 2165 containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidthInRegion(region); 2166 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != style()->isLeftToRightDirection(); 2167 computeInlineDirectionMargins(cb, containerLogicalWidthForAutoMargins, computedValues.m_extent, 2168 hasInvertedDirection ? computedValues.m_margins.m_end : computedValues.m_margins.m_start, 2169 hasInvertedDirection ? computedValues.m_margins.m_start : computedValues.m_margins.m_end); 2170 } 2171 2172 if (!hasPerpendicularContainingBlock && containerLogicalWidth && containerLogicalWidth != (computedValues.m_extent + computedValues.m_margins.m_start + computedValues.m_margins.m_end) 2173 && !isFloating() && !isInline() && !cb->isFlexibleBoxIncludingDeprecated() && !cb->isRenderGrid()) { 2174 LayoutUnit newMargin = containerLogicalWidth - computedValues.m_extent - cb->marginStartForChild(this); 2175 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != style()->isLeftToRightDirection(); 2176 if (hasInvertedDirection) 2177 computedValues.m_margins.m_start = newMargin; 2178 else 2179 computedValues.m_margins.m_end = newMargin; 2180 } 2181} 2182 2183LayoutUnit RenderBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth) const 2184{ 2185 LayoutUnit marginStart = 0; 2186 LayoutUnit marginEnd = 0; 2187 return fillAvailableMeasure(availableLogicalWidth, marginStart, marginEnd); 2188} 2189 2190LayoutUnit RenderBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd) const 2191{ 2192 RenderView* renderView = view(); 2193 marginStart = minimumValueForLength(style()->marginStart(), availableLogicalWidth, renderView); 2194 marginEnd = minimumValueForLength(style()->marginEnd(), availableLogicalWidth, renderView); 2195 return availableLogicalWidth - marginStart - marginEnd; 2196} 2197 2198LayoutUnit RenderBox::computeIntrinsicLogicalWidthUsing(Length logicalWidthLength, LayoutUnit availableLogicalWidth, LayoutUnit borderAndPadding) const 2199{ 2200 if (logicalWidthLength.type() == FillAvailable) 2201 return fillAvailableMeasure(availableLogicalWidth); 2202 2203 LayoutUnit minLogicalWidth = 0; 2204 LayoutUnit maxLogicalWidth = 0; 2205 computeIntrinsicLogicalWidths(minLogicalWidth, maxLogicalWidth); 2206 2207 if (logicalWidthLength.type() == MinContent) 2208 return minLogicalWidth + borderAndPadding; 2209 2210 if (logicalWidthLength.type() == MaxContent) 2211 return maxLogicalWidth + borderAndPadding; 2212 2213 if (logicalWidthLength.type() == FitContent) { 2214 minLogicalWidth += borderAndPadding; 2215 maxLogicalWidth += borderAndPadding; 2216 return max(minLogicalWidth, min(maxLogicalWidth, fillAvailableMeasure(availableLogicalWidth))); 2217 } 2218 2219 ASSERT_NOT_REACHED(); 2220 return 0; 2221} 2222 2223LayoutUnit RenderBox::computeLogicalWidthInRegionUsing(SizeType widthType, Length logicalWidth, LayoutUnit availableLogicalWidth, 2224 const RenderBlock* cb, RenderRegion* region) const 2225{ 2226 if (!logicalWidth.isIntrinsicOrAuto()) { 2227 // FIXME: If the containing block flow is perpendicular to our direction we need to use the available logical height instead. 2228 return adjustBorderBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, availableLogicalWidth, view())); 2229 } 2230 2231 if (logicalWidth.isIntrinsic()) 2232 return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth()); 2233 2234 LayoutUnit marginStart = 0; 2235 LayoutUnit marginEnd = 0; 2236 LayoutUnit logicalWidthResult = fillAvailableMeasure(availableLogicalWidth, marginStart, marginEnd); 2237 2238 if (shrinkToAvoidFloats() && cb->containsFloats()) 2239 logicalWidthResult = min(logicalWidthResult, shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, cb, region)); 2240 2241 if (widthType == MainOrPreferredSize && sizesLogicalWidthToFitContent(widthType)) 2242 return max(minPreferredLogicalWidth(), min(maxPreferredLogicalWidth(), logicalWidthResult)); 2243 return logicalWidthResult; 2244} 2245 2246static bool flexItemHasStretchAlignment(const RenderObject* flexitem) 2247{ 2248 RenderObject* parent = flexitem->parent(); 2249 return flexitem->style()->alignSelf() == AlignStretch || (flexitem->style()->alignSelf() == AlignAuto && parent->style()->alignItems() == AlignStretch); 2250} 2251 2252static bool isStretchingColumnFlexItem(const RenderObject* flexitem) 2253{ 2254 RenderObject* parent = flexitem->parent(); 2255 if (parent->isDeprecatedFlexibleBox() && parent->style()->boxOrient() == VERTICAL && parent->style()->boxAlign() == BSTRETCH) 2256 return true; 2257 2258 // We don't stretch multiline flexboxes because they need to apply line spacing (align-content) first. 2259 if (parent->isFlexibleBox() && parent->style()->flexWrap() == FlexNoWrap && parent->style()->isColumnFlexDirection() && flexItemHasStretchAlignment(flexitem)) 2260 return true; 2261 return false; 2262} 2263 2264bool RenderBox::sizesLogicalWidthToFitContent(SizeType widthType) const 2265{ 2266 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks, 2267 // but they allow text to sit on the same line as the marquee. 2268 if (isFloating() || (isInlineBlockOrInlineTable() && !isHTMLMarquee())) 2269 return true; 2270 2271 // This code may look a bit strange. Basically width:intrinsic should clamp the size when testing both 2272 // min-width and width. max-width is only clamped if it is also intrinsic. 2273 Length logicalWidth = (widthType == MaxSize) ? style()->logicalMaxWidth() : style()->logicalWidth(); 2274 if (logicalWidth.type() == Intrinsic) 2275 return true; 2276 2277 // Children of a horizontal marquee do not fill the container by default. 2278 // FIXME: Need to deal with MAUTO value properly. It could be vertical. 2279 // FIXME: Think about block-flow here. Need to find out how marquee direction relates to 2280 // block-flow (as well as how marquee overflow should relate to block flow). 2281 // https://bugs.webkit.org/show_bug.cgi?id=46472 2282 if (parent()->style()->overflowX() == OMARQUEE) { 2283 EMarqueeDirection dir = parent()->style()->marqueeDirection(); 2284 if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT) 2285 return true; 2286 } 2287 2288 // Flexible box items should shrink wrap, so we lay them out at their intrinsic widths. 2289 // In the case of columns that have a stretch alignment, we go ahead and layout at the 2290 // stretched size to avoid an extra layout when applying alignment. 2291 if (parent()->isFlexibleBox()) { 2292 // For multiline columns, we need to apply align-content first, so we can't stretch now. 2293 if (!parent()->style()->isColumnFlexDirection() || parent()->style()->flexWrap() != FlexNoWrap) 2294 return true; 2295 if (!flexItemHasStretchAlignment(this)) 2296 return true; 2297 } 2298 2299 // Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes 2300 // that don't stretch their kids lay out their children at their intrinsic widths. 2301 // FIXME: Think about block-flow here. 2302 // https://bugs.webkit.org/show_bug.cgi?id=46473 2303 if (parent()->isDeprecatedFlexibleBox() && (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH)) 2304 return true; 2305 2306 // Button, input, select, textarea, and legend treat width value of 'auto' as 'intrinsic' unless it's in a 2307 // stretching column flexbox. 2308 // FIXME: Think about block-flow here. 2309 // https://bugs.webkit.org/show_bug.cgi?id=46473 2310 if (logicalWidth.type() == Auto && !isStretchingColumnFlexItem(this) && node() && (node()->hasTagName(inputTag) || node()->hasTagName(selectTag) || node()->hasTagName(buttonTag) || node()->hasTagName(textareaTag) || node()->hasTagName(legendTag))) 2311 return true; 2312 2313 if (isHorizontalWritingMode() != containingBlock()->isHorizontalWritingMode()) 2314 return true; 2315 2316 return false; 2317} 2318 2319void RenderBox::computeInlineDirectionMargins(RenderBlock* containingBlock, LayoutUnit containerWidth, LayoutUnit childWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd) const 2320{ 2321 const RenderStyle* containingBlockStyle = containingBlock->style(); 2322 Length marginStartLength = style()->marginStartUsing(containingBlockStyle); 2323 Length marginEndLength = style()->marginEndUsing(containingBlockStyle); 2324 RenderView* renderView = view(); 2325 2326 if (isFloating() || isInline()) { 2327 // Inline blocks/tables and floats don't have their margins increased. 2328 marginStart = minimumValueForLength(marginStartLength, containerWidth, renderView); 2329 marginEnd = minimumValueForLength(marginEndLength, containerWidth, renderView); 2330 return; 2331 } 2332 2333 // Case One: The object is being centered in the containing block's available logical width. 2334 if ((marginStartLength.isAuto() && marginEndLength.isAuto() && childWidth < containerWidth) 2335 || (!marginStartLength.isAuto() && !marginEndLength.isAuto() && containingBlock->style()->textAlign() == WEBKIT_CENTER)) { 2336 // Other browsers center the margin box for align=center elements so we match them here. 2337 LayoutUnit marginStartWidth = minimumValueForLength(marginStartLength, containerWidth, renderView); 2338 LayoutUnit marginEndWidth = minimumValueForLength(marginEndLength, containerWidth, renderView); 2339 LayoutUnit centeredMarginBoxStart = max<LayoutUnit>(0, (containerWidth - childWidth - marginStartWidth - marginEndWidth) / 2); 2340 marginStart = centeredMarginBoxStart + marginStartWidth; 2341 marginEnd = containerWidth - childWidth - marginStart + marginEndWidth; 2342 return; 2343 } 2344 2345 // Case Two: The object is being pushed to the start of the containing block's available logical width. 2346 if (marginEndLength.isAuto() && childWidth < containerWidth) { 2347 marginStart = valueForLength(marginStartLength, containerWidth, renderView); 2348 marginEnd = containerWidth - childWidth - marginStart; 2349 return; 2350 } 2351 2352 // Case Three: The object is being pushed to the end of the containing block's available logical width. 2353 bool pushToEndFromTextAlign = !marginEndLength.isAuto() && ((!containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_LEFT) 2354 || (containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_RIGHT)); 2355 if ((marginStartLength.isAuto() && childWidth < containerWidth) || pushToEndFromTextAlign) { 2356 marginEnd = valueForLength(marginEndLength, containerWidth, renderView); 2357 marginStart = containerWidth - childWidth - marginEnd; 2358 return; 2359 } 2360 2361 // Case Four: Either no auto margins, or our width is >= the container width (css2.1, 10.3.3). In that case 2362 // auto margins will just turn into 0. 2363 marginStart = minimumValueForLength(marginStartLength, containerWidth, renderView); 2364 marginEnd = minimumValueForLength(marginEndLength, containerWidth, renderView); 2365} 2366 2367RenderBoxRegionInfo* RenderBox::renderBoxRegionInfo(RenderRegion* region, RenderBoxRegionInfoFlags cacheFlag) const 2368{ 2369 // Make sure nobody is trying to call this with a null region. 2370 if (!region) 2371 return 0; 2372 2373 // If we have computed our width in this region already, it will be cached, and we can 2374 // just return it. 2375 RenderBoxRegionInfo* boxInfo = region->renderBoxRegionInfo(this); 2376 if (boxInfo && cacheFlag == CacheRenderBoxRegionInfo) 2377 return boxInfo; 2378 2379 // No cached value was found, so we have to compute our insets in this region. 2380 // FIXME: For now we limit this computation to normal RenderBlocks. Future patches will expand 2381 // support to cover all boxes. 2382 RenderFlowThread* flowThread = flowThreadContainingBlock(); 2383 if (isRenderFlowThread() || !flowThread || !canHaveBoxInfoInRegion() || flowThread->style()->writingMode() != style()->writingMode()) 2384 return 0; 2385 2386 LogicalExtentComputedValues computedValues; 2387 computeLogicalWidthInRegion(computedValues, region); 2388 2389 // Now determine the insets based off where this object is supposed to be positioned. 2390 RenderBlock* cb = containingBlock(); 2391 RenderRegion* clampedContainingBlockRegion = cb->clampToStartAndEndRegions(region); 2392 RenderBoxRegionInfo* containingBlockInfo = cb->renderBoxRegionInfo(clampedContainingBlockRegion); 2393 LayoutUnit containingBlockLogicalWidth = cb->logicalWidth(); 2394 LayoutUnit containingBlockLogicalWidthInRegion = containingBlockInfo ? containingBlockInfo->logicalWidth() : containingBlockLogicalWidth; 2395 2396 LayoutUnit marginStartInRegion = computedValues.m_margins.m_start; 2397 LayoutUnit startMarginDelta = marginStartInRegion - marginStart(); 2398 LayoutUnit logicalWidthInRegion = computedValues.m_extent; 2399 LayoutUnit logicalLeftInRegion = computedValues.m_position; 2400 LayoutUnit widthDelta = logicalWidthInRegion - logicalWidth(); 2401 LayoutUnit logicalLeftDelta = isOutOfFlowPositioned() ? logicalLeftInRegion - logicalLeft() : startMarginDelta; 2402 LayoutUnit logicalRightInRegion = containingBlockLogicalWidthInRegion - (logicalLeftInRegion + logicalWidthInRegion); 2403 LayoutUnit oldLogicalRight = containingBlockLogicalWidth - (logicalLeft() + logicalWidth()); 2404 LayoutUnit logicalRightDelta = isOutOfFlowPositioned() ? logicalRightInRegion - oldLogicalRight : startMarginDelta; 2405 2406 LayoutUnit logicalLeftOffset = 0; 2407 2408 if (!isOutOfFlowPositioned() && avoidsFloats() && cb->containsFloats()) { 2409 LayoutUnit startPositionDelta = cb->computeStartPositionDeltaForChildAvoidingFloats(this, marginStartInRegion, region); 2410 if (cb->style()->isLeftToRightDirection()) 2411 logicalLeftDelta += startPositionDelta; 2412 else 2413 logicalRightDelta += startPositionDelta; 2414 } 2415 2416 if (cb->style()->isLeftToRightDirection()) 2417 logicalLeftOffset += logicalLeftDelta; 2418 else 2419 logicalLeftOffset -= (widthDelta + logicalRightDelta); 2420 2421 LayoutUnit logicalRightOffset = logicalWidth() - (logicalLeftOffset + logicalWidthInRegion); 2422 bool isShifted = (containingBlockInfo && containingBlockInfo->isShifted()) 2423 || (style()->isLeftToRightDirection() && logicalLeftOffset) 2424 || (!style()->isLeftToRightDirection() && logicalRightOffset); 2425 2426 // FIXME: Although it's unlikely, these boxes can go outside our bounds, and so we will need to incorporate them into overflow. 2427 if (cacheFlag == CacheRenderBoxRegionInfo) 2428 return region->setRenderBoxRegionInfo(this, logicalLeftOffset, logicalWidthInRegion, isShifted); 2429 return new RenderBoxRegionInfo(logicalLeftOffset, logicalWidthInRegion, isShifted); 2430} 2431 2432static bool shouldFlipBeforeAfterMargins(const RenderStyle* containingBlockStyle, const RenderStyle* childStyle) 2433{ 2434 ASSERT(containingBlockStyle->isHorizontalWritingMode() != childStyle->isHorizontalWritingMode()); 2435 WritingMode childWritingMode = childStyle->writingMode(); 2436 bool shouldFlip = false; 2437 switch (containingBlockStyle->writingMode()) { 2438 case TopToBottomWritingMode: 2439 shouldFlip = (childWritingMode == RightToLeftWritingMode); 2440 break; 2441 case BottomToTopWritingMode: 2442 shouldFlip = (childWritingMode == RightToLeftWritingMode); 2443 break; 2444 case RightToLeftWritingMode: 2445 shouldFlip = (childWritingMode == BottomToTopWritingMode); 2446 break; 2447 case LeftToRightWritingMode: 2448 shouldFlip = (childWritingMode == BottomToTopWritingMode); 2449 break; 2450 } 2451 2452 if (!containingBlockStyle->isLeftToRightDirection()) 2453 shouldFlip = !shouldFlip; 2454 2455 return shouldFlip; 2456} 2457 2458void RenderBox::updateLogicalHeight() 2459{ 2460 LogicalExtentComputedValues computedValues; 2461 computeLogicalHeight(logicalHeight(), logicalTop(), computedValues); 2462 2463 setLogicalHeight(computedValues.m_extent); 2464 setLogicalTop(computedValues.m_position); 2465 setMarginBefore(computedValues.m_margins.m_before); 2466 setMarginAfter(computedValues.m_margins.m_after); 2467} 2468 2469void RenderBox::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const 2470{ 2471 computedValues.m_extent = logicalHeight; 2472 computedValues.m_position = logicalTop; 2473 2474 // Cell height is managed by the table and inline non-replaced elements do not support a height property. 2475 if (isTableCell() || (isInline() && !isReplaced())) 2476 return; 2477 2478 Length h; 2479 if (isOutOfFlowPositioned()) 2480 computePositionedLogicalHeight(computedValues); 2481 else { 2482 RenderBlock* cb = containingBlock(); 2483 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode(); 2484 2485 if (!hasPerpendicularContainingBlock) { 2486 bool shouldFlipBeforeAfter = cb->style()->writingMode() != style()->writingMode(); 2487 computeBlockDirectionMargins(cb, 2488 shouldFlipBeforeAfter ? computedValues.m_margins.m_after : computedValues.m_margins.m_before, 2489 shouldFlipBeforeAfter ? computedValues.m_margins.m_before : computedValues.m_margins.m_after); 2490 } 2491 2492 // For tables, calculate margins only. 2493 if (isTable()) { 2494 if (hasPerpendicularContainingBlock) { 2495 bool shouldFlipBeforeAfter = shouldFlipBeforeAfterMargins(cb->style(), style()); 2496 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), computedValues.m_extent, 2497 shouldFlipBeforeAfter ? computedValues.m_margins.m_after : computedValues.m_margins.m_before, 2498 shouldFlipBeforeAfter ? computedValues.m_margins.m_before : computedValues.m_margins.m_after); 2499 } 2500 return; 2501 } 2502 2503 // FIXME: Account for block-flow in flexible boxes. 2504 // https://bugs.webkit.org/show_bug.cgi?id=46418 2505 bool inHorizontalBox = parent()->isDeprecatedFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL; 2506 bool stretching = parent()->style()->boxAlign() == BSTRETCH; 2507 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inHorizontalBox || !stretching); 2508 bool checkMinMaxHeight = false; 2509 2510 // The parent box is flexing us, so it has increased or decreased our height. We have to 2511 // grab our cached flexible height. 2512 // FIXME: Account for block-flow in flexible boxes. 2513 // https://bugs.webkit.org/show_bug.cgi?id=46418 2514 if (hasOverrideHeight() && parent()->isFlexibleBoxIncludingDeprecated()) 2515 h = Length(overrideLogicalContentHeight(), Fixed); 2516 else if (treatAsReplaced) 2517 h = Length(computeReplacedLogicalHeight(), Fixed); 2518 else { 2519 h = style()->logicalHeight(); 2520 checkMinMaxHeight = true; 2521 } 2522 2523 // Block children of horizontal flexible boxes fill the height of the box. 2524 // FIXME: Account for block-flow in flexible boxes. 2525 // https://bugs.webkit.org/show_bug.cgi?id=46418 2526 if (h.isAuto() && parent()->isDeprecatedFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL 2527 && parent()->isStretchingChildren()) { 2528 h = Length(parentBox()->contentLogicalHeight() - marginBefore() - marginAfter() - borderAndPaddingLogicalHeight(), Fixed); 2529 checkMinMaxHeight = false; 2530 } 2531 2532 LayoutUnit heightResult; 2533 if (checkMinMaxHeight) { 2534 heightResult = computeLogicalHeightUsing(style()->logicalHeight()); 2535 if (heightResult == -1) 2536 heightResult = computedValues.m_extent; 2537 heightResult = constrainLogicalHeightByMinMax(heightResult); 2538 } else { 2539 // The only times we don't check min/max height are when a fixed length has 2540 // been given as an override. Just use that. The value has already been adjusted 2541 // for box-sizing. 2542 heightResult = h.value() + borderAndPaddingLogicalHeight(); 2543 } 2544 2545 computedValues.m_extent = heightResult; 2546 2547 if (hasPerpendicularContainingBlock) { 2548 bool shouldFlipBeforeAfter = shouldFlipBeforeAfterMargins(cb->style(), style()); 2549 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), heightResult, 2550 shouldFlipBeforeAfter ? computedValues.m_margins.m_after : computedValues.m_margins.m_before, 2551 shouldFlipBeforeAfter ? computedValues.m_margins.m_before : computedValues.m_margins.m_after); 2552 } 2553 } 2554 2555 // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the 2556 // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height 2557 // is specified. When we're printing, we also need this quirk if the body or root has a percentage 2558 // height since we don't set a height in RenderView when we're printing. So without this quirk, the 2559 // height has nothing to be a percentage of, and it ends up being 0. That is bad. 2560 bool paginatedContentNeedsBaseHeight = document()->printing() && h.isPercent() 2561 && (isRoot() || (isBody() && document()->documentElement()->renderer()->style()->logicalHeight().isPercent())) && !isInline(); 2562 if (stretchesToViewport() || paginatedContentNeedsBaseHeight) { 2563 LayoutUnit margins = collapsedMarginBefore() + collapsedMarginAfter(); 2564 LayoutUnit visibleHeight = view()->pageOrViewLogicalHeight(); 2565 if (isRoot()) 2566 computedValues.m_extent = max(computedValues.m_extent, visibleHeight - margins); 2567 else { 2568 LayoutUnit marginsBordersPadding = margins + parentBox()->marginBefore() + parentBox()->marginAfter() + parentBox()->borderAndPaddingLogicalHeight(); 2569 computedValues.m_extent = max(computedValues.m_extent, visibleHeight - marginsBordersPadding); 2570 } 2571 } 2572} 2573 2574LayoutUnit RenderBox::computeLogicalHeightUsing(const Length& height) const 2575{ 2576 LayoutUnit logicalHeight = computeContentAndScrollbarLogicalHeightUsing(height); 2577 if (logicalHeight != -1) 2578 logicalHeight = adjustBorderBoxLogicalHeightForBoxSizing(logicalHeight); 2579 return logicalHeight; 2580} 2581 2582LayoutUnit RenderBox::computeContentLogicalHeight(const Length& height) const 2583{ 2584 LayoutUnit heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(height); 2585 if (heightIncludingScrollbar == -1) 2586 return -1; 2587 return std::max<LayoutUnit>(0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar) - scrollbarLogicalHeight()); 2588} 2589 2590LayoutUnit RenderBox::computeContentAndScrollbarLogicalHeightUsing(const Length& height) const 2591{ 2592 if (height.isFixed()) 2593 return height.value(); 2594 if (height.isPercent()) 2595 return computePercentageLogicalHeight(height); 2596 if (height.isViewportPercentage()) 2597 return valueForLength(height, 0, view()); 2598 return -1; 2599} 2600 2601bool RenderBox::skipContainingBlockForPercentHeightCalculation(const RenderBox* containingBlock) const 2602{ 2603 // For quirks mode and anonymous blocks, we skip auto-height containingBlocks when computing percentages. 2604 // For standards mode, we treat the percentage as auto if it has an auto-height containing block. 2605 if (!document()->inQuirksMode() && !containingBlock->isAnonymousBlock()) 2606 return false; 2607 return !containingBlock->isTableCell() && !containingBlock->isOutOfFlowPositioned() && containingBlock->style()->logicalHeight().isAuto() && isHorizontalWritingMode() == containingBlock->isHorizontalWritingMode(); 2608} 2609 2610LayoutUnit RenderBox::computePercentageLogicalHeight(const Length& height) const 2611{ 2612 LayoutUnit availableHeight = -1; 2613 2614 bool skippedAutoHeightContainingBlock = false; 2615 RenderBlock* cb = containingBlock(); 2616 const RenderBox* containingBlockChild = this; 2617 LayoutUnit rootMarginBorderPaddingHeight = 0; 2618 while (!cb->isRenderView() && skipContainingBlockForPercentHeightCalculation(cb)) { 2619 if (cb->isBody() || cb->isRoot()) 2620 rootMarginBorderPaddingHeight += cb->marginBefore() + cb->marginAfter() + cb->borderAndPaddingLogicalHeight(); 2621 skippedAutoHeightContainingBlock = true; 2622 containingBlockChild = cb; 2623 cb = cb->containingBlock(); 2624 cb->addPercentHeightDescendant(const_cast<RenderBox*>(this)); 2625 } 2626 2627 RenderStyle* cbstyle = cb->style(); 2628 2629 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height 2630 // explicitly specified that can be used for any percentage computations. 2631 bool isOutOfFlowPositionedWithSpecifiedHeight = cb->isOutOfFlowPositioned() && (!cbstyle->logicalHeight().isAuto() || (!cbstyle->logicalTop().isAuto() && !cbstyle->logicalBottom().isAuto())); 2632 2633 bool includeBorderPadding = isTable(); 2634 2635 if (isHorizontalWritingMode() != cb->isHorizontalWritingMode()) 2636 availableHeight = containingBlockChild->containingBlockLogicalWidthForContent(); 2637 else if (hasOverrideContainingBlockLogicalHeight()) 2638 availableHeight = overrideContainingBlockContentLogicalHeight(); 2639 else if (cb->isTableCell()) { 2640 if (!skippedAutoHeightContainingBlock) { 2641 // Table cells violate what the CSS spec says to do with heights. Basically we 2642 // don't care if the cell specified a height or not. We just always make ourselves 2643 // be a percentage of the cell's current content height. 2644 if (!cb->hasOverrideHeight()) { 2645 // Normally we would let the cell size intrinsically, but scrolling overflow has to be 2646 // treated differently, since WinIE lets scrolled overflow regions shrink as needed. 2647 // While we can't get all cases right, we can at least detect when the cell has a specified 2648 // height or when the table has a specified height. In these cases we want to initially have 2649 // no size and allow the flexing of the table or the cell to its specified height to cause us 2650 // to grow to fill the space. This could end up being wrong in some cases, but it is 2651 // preferable to the alternative (sizing intrinsically and making the row end up too big). 2652 RenderTableCell* cell = toRenderTableCell(cb); 2653 if (scrollsOverflowY() && (!cell->style()->logicalHeight().isAuto() || !cell->table()->style()->logicalHeight().isAuto())) 2654 return 0; 2655 return -1; 2656 } 2657 availableHeight = cb->overrideLogicalContentHeight(); 2658 includeBorderPadding = true; 2659 } 2660 } else if (cbstyle->logicalHeight().isFixed()) { 2661 LayoutUnit contentBoxHeight = cb->adjustContentBoxLogicalHeightForBoxSizing(cbstyle->logicalHeight().value()); 2662 availableHeight = max<LayoutUnit>(0, cb->constrainContentBoxLogicalHeightByMinMax(contentBoxHeight - cb->scrollbarLogicalHeight())); 2663 } else if (cbstyle->logicalHeight().isPercent() && !isOutOfFlowPositionedWithSpecifiedHeight) { 2664 // We need to recur and compute the percentage height for our containing block. 2665 LayoutUnit heightWithScrollbar = cb->computePercentageLogicalHeight(cbstyle->logicalHeight()); 2666 if (heightWithScrollbar != -1) { 2667 LayoutUnit contentBoxHeightWithScrollbar = cb->adjustContentBoxLogicalHeightForBoxSizing(heightWithScrollbar); 2668 // We need to adjust for min/max height because this method does not 2669 // handle the min/max of the current block, its caller does. So the 2670 // return value from the recursive call will not have been adjusted 2671 // yet. 2672 LayoutUnit contentBoxHeight = cb->constrainContentBoxLogicalHeightByMinMax(contentBoxHeightWithScrollbar - cb->scrollbarLogicalHeight()); 2673 availableHeight = max<LayoutUnit>(0, contentBoxHeight); 2674 } 2675 } else if (isOutOfFlowPositionedWithSpecifiedHeight) { 2676 // Don't allow this to affect the block' height() member variable, since this 2677 // can get called while the block is still laying out its kids. 2678 LogicalExtentComputedValues computedValues; 2679 cb->computeLogicalHeight(cb->logicalHeight(), 0, computedValues); 2680 availableHeight = computedValues.m_extent - cb->borderAndPaddingLogicalHeight() - cb->scrollbarLogicalHeight(); 2681 } else if (cb->isRenderView()) 2682 availableHeight = view()->pageOrViewLogicalHeight(); 2683 2684 if (availableHeight == -1) 2685 return availableHeight; 2686 2687 availableHeight -= rootMarginBorderPaddingHeight; 2688 2689 LayoutUnit result = valueForLength(height, availableHeight); 2690 if (includeBorderPadding) { 2691 // FIXME: Table cells should default to box-sizing: border-box so we can avoid this hack. 2692 // It is necessary to use the border-box to match WinIE's broken 2693 // box model. This is essential for sizing inside 2694 // table cells using percentage heights. 2695 result -= borderAndPaddingLogicalHeight(); 2696 return max<LayoutUnit>(0, result); 2697 } 2698 return result; 2699} 2700 2701LayoutUnit RenderBox::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const 2702{ 2703 return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(style()->logicalWidth()), shouldComputePreferred); 2704} 2705 2706LayoutUnit RenderBox::computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth, ShouldComputePreferred shouldComputePreferred) const 2707{ 2708 LayoutUnit minLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMinWidth().isPercent()) || style()->logicalMinWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMinWidth()); 2709 LayoutUnit maxLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMaxWidth().isPercent()) || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth()); 2710 return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth)); 2711} 2712 2713LayoutUnit RenderBox::computeReplacedLogicalWidthUsing(Length logicalWidth) const 2714{ 2715 switch (logicalWidth.type()) { 2716 case Fixed: 2717 return adjustContentBoxLogicalWidthForBoxSizing(logicalWidth.value()); 2718 case MinContent: 2719 case MaxContent: { 2720 // MinContent/MaxContent don't need the availableLogicalWidth argument. 2721 LayoutUnit availableLogicalWidth = 0; 2722 return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth(); 2723 } 2724 case ViewportPercentageWidth: 2725 case ViewportPercentageHeight: 2726 case ViewportPercentageMin: 2727 case ViewportPercentageMax: 2728 return adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, 0, view())); 2729 case FitContent: 2730 case FillAvailable: 2731 case Percent: 2732 case Calculated: { 2733 // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced element's block-flow is perpendicular to the 2734 // containing block's block-flow. 2735 // https://bugs.webkit.org/show_bug.cgi?id=46496 2736 const LayoutUnit cw = isOutOfFlowPositioned() ? containingBlockLogicalWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockLogicalWidthForContent(); 2737 Length containerLogicalWidth = containingBlock()->style()->logicalWidth(); 2738 // FIXME: Handle cases when containing block width is calculated or viewport percent. 2739 // https://bugs.webkit.org/show_bug.cgi?id=91071 2740 if (logicalWidth.isIntrinsic()) 2741 return computeIntrinsicLogicalWidthUsing(logicalWidth, cw, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth(); 2742 if (cw > 0 || (!cw && (containerLogicalWidth.isFixed() || containerLogicalWidth.isPercent()))) 2743 return adjustContentBoxLogicalWidthForBoxSizing(minimumValueForLength(logicalWidth, cw)); 2744 } 2745 // fall through 2746 case Intrinsic: 2747 case MinIntrinsic: 2748 case Auto: 2749 case Relative: 2750 case Undefined: 2751 return intrinsicLogicalWidth(); 2752 } 2753 2754 ASSERT_NOT_REACHED(); 2755 return 0; 2756} 2757 2758LayoutUnit RenderBox::computeReplacedLogicalHeight() const 2759{ 2760 return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(style()->logicalHeight())); 2761} 2762 2763LayoutUnit RenderBox::computeReplacedLogicalHeightRespectingMinMaxHeight(LayoutUnit logicalHeight) const 2764{ 2765 LayoutUnit minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight()); 2766 LayoutUnit maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight()); 2767 return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight)); 2768} 2769 2770LayoutUnit RenderBox::computeReplacedLogicalHeightUsing(Length logicalHeight) const 2771{ 2772 switch (logicalHeight.type()) { 2773 case Fixed: 2774 return adjustContentBoxLogicalHeightForBoxSizing(logicalHeight.value()); 2775 case Percent: 2776 case Calculated: 2777 { 2778 RenderObject* cb = isOutOfFlowPositioned() ? container() : containingBlock(); 2779 while (cb->isAnonymous()) { 2780 cb = cb->containingBlock(); 2781 toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this)); 2782 } 2783 2784 // FIXME: This calculation is not patched for block-flow yet. 2785 // https://bugs.webkit.org/show_bug.cgi?id=46500 2786 if (cb->isOutOfFlowPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) { 2787 ASSERT_WITH_SECURITY_IMPLICATION(cb->isRenderBlock()); 2788 RenderBlock* block = toRenderBlock(cb); 2789 LogicalExtentComputedValues computedValues; 2790 block->computeLogicalHeight(block->logicalHeight(), 0, computedValues); 2791 LayoutUnit newContentHeight = computedValues.m_extent - block->borderAndPaddingLogicalHeight() - block->scrollbarLogicalHeight(); 2792 LayoutUnit newHeight = block->adjustContentBoxLogicalHeightForBoxSizing(newContentHeight); 2793 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, newHeight)); 2794 } 2795 2796 // FIXME: availableLogicalHeight() is wrong if the replaced element's block-flow is perpendicular to the 2797 // containing block's block-flow. 2798 // https://bugs.webkit.org/show_bug.cgi?id=46496 2799 LayoutUnit availableHeight; 2800 if (isOutOfFlowPositioned()) 2801 availableHeight = containingBlockLogicalHeightForPositioned(toRenderBoxModelObject(cb)); 2802 else { 2803 availableHeight = containingBlockLogicalHeightForContent(IncludeMarginBorderPadding); 2804 // It is necessary to use the border-box to match WinIE's broken 2805 // box model. This is essential for sizing inside 2806 // table cells using percentage heights. 2807 // FIXME: This needs to be made block-flow-aware. If the cell and image are perpendicular block-flows, this isn't right. 2808 // https://bugs.webkit.org/show_bug.cgi?id=46997 2809 while (cb && !cb->isRenderView() && (cb->style()->logicalHeight().isAuto() || cb->style()->logicalHeight().isPercent())) { 2810 if (cb->isTableCell()) { 2811 // Don't let table cells squeeze percent-height replaced elements 2812 // <http://bugs.webkit.org/show_bug.cgi?id=15359> 2813 availableHeight = max(availableHeight, intrinsicLogicalHeight()); 2814 return valueForLength(logicalHeight, availableHeight - borderAndPaddingLogicalHeight()); 2815 } 2816 toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this)); 2817 cb = cb->containingBlock(); 2818 } 2819 } 2820 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, availableHeight)); 2821 } 2822 case ViewportPercentageWidth: 2823 case ViewportPercentageHeight: 2824 case ViewportPercentageMin: 2825 case ViewportPercentageMax: 2826 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, 0, view())); 2827 default: 2828 return intrinsicLogicalHeight(); 2829 } 2830} 2831 2832LayoutUnit RenderBox::availableLogicalHeight(AvailableLogicalHeightType heightType) const 2833{ 2834 return constrainLogicalHeightByMinMax(availableLogicalHeightUsing(style()->logicalHeight(), heightType)); 2835} 2836 2837LayoutUnit RenderBox::availableLogicalHeightUsing(const Length& h, AvailableLogicalHeightType heightType) const 2838{ 2839 if (isRenderView()) 2840 return isHorizontalWritingMode() ? toRenderView(this)->frameView()->visibleHeight() : toRenderView(this)->frameView()->visibleWidth(); 2841 2842 // We need to stop here, since we don't want to increase the height of the table 2843 // artificially. We're going to rely on this cell getting expanded to some new 2844 // height, and then when we lay out again we'll use the calculation below. 2845 if (isTableCell() && (h.isAuto() || h.isPercent())) { 2846 if (hasOverrideHeight()) 2847 return overrideLogicalContentHeight(); 2848 return logicalHeight() - borderAndPaddingLogicalHeight(); 2849 } 2850 2851 if (h.isPercent() && isOutOfFlowPositioned()) { 2852 // FIXME: This is wrong if the containingBlock has a perpendicular writing mode. 2853 LayoutUnit availableHeight = containingBlockLogicalHeightForPositioned(containingBlock()); 2854 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(h, availableHeight)); 2855 } 2856 2857 LayoutUnit heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(h); 2858 if (heightIncludingScrollbar != -1) 2859 return std::max<LayoutUnit>(0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar) - scrollbarLogicalHeight()); 2860 2861 // FIXME: Check logicalTop/logicalBottom here to correctly handle vertical writing-mode. 2862 // https://bugs.webkit.org/show_bug.cgi?id=46500 2863 if (isRenderBlock() && isOutOfFlowPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) { 2864 RenderBlock* block = const_cast<RenderBlock*>(toRenderBlock(this)); 2865 LogicalExtentComputedValues computedValues; 2866 block->computeLogicalHeight(block->logicalHeight(), 0, computedValues); 2867 LayoutUnit newContentHeight = computedValues.m_extent - block->borderAndPaddingLogicalHeight() - block->scrollbarLogicalHeight(); 2868 return adjustContentBoxLogicalHeightForBoxSizing(newContentHeight); 2869 } 2870 2871 // FIXME: This is wrong if the containingBlock has a perpendicular writing mode. 2872 LayoutUnit availableHeight = containingBlockLogicalHeightForContent(heightType); 2873 if (heightType == ExcludeMarginBorderPadding) { 2874 // FIXME: Margin collapsing hasn't happened yet, so this incorrectly removes collapsed margins. 2875 availableHeight -= marginBefore() + marginAfter() + borderAndPaddingLogicalHeight(); 2876 } 2877 return availableHeight; 2878} 2879 2880void RenderBox::computeBlockDirectionMargins(const RenderBlock* containingBlock, LayoutUnit& marginBefore, LayoutUnit& marginAfter) const 2881{ 2882 if (isTableCell()) { 2883 // FIXME: Not right if we allow cells to have different directionality than the table. If we do allow this, though, 2884 // we may just do it with an extra anonymous block inside the cell. 2885 marginBefore = 0; 2886 marginAfter = 0; 2887 return; 2888 } 2889 2890 // Margins are calculated with respect to the logical width of 2891 // the containing block (8.3) 2892 LayoutUnit cw = containingBlockLogicalWidthForContent(); 2893 RenderView* renderView = view(); 2894 RenderStyle* containingBlockStyle = containingBlock->style(); 2895 marginBefore = minimumValueForLength(style()->marginBeforeUsing(containingBlockStyle), cw, renderView); 2896 marginAfter = minimumValueForLength(style()->marginAfterUsing(containingBlockStyle), cw, renderView); 2897} 2898 2899void RenderBox::computeAndSetBlockDirectionMargins(const RenderBlock* containingBlock) 2900{ 2901 LayoutUnit marginBefore; 2902 LayoutUnit marginAfter; 2903 computeBlockDirectionMargins(containingBlock, marginBefore, marginAfter); 2904 containingBlock->setMarginBeforeForChild(this, marginBefore); 2905 containingBlock->setMarginAfterForChild(this, marginAfter); 2906} 2907 2908LayoutUnit RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock, RenderRegion* region, bool checkForPerpendicularWritingMode) const 2909{ 2910 // Container for position:fixed is the frame. 2911 Frame* frame = view() ? view()->frame(): 0; 2912 FrameView* frameView = view() ? view()->frameView() : 0; 2913 if (fixedElementLaysOutRelativeToFrame(frame, frameView)) 2914 return (view()->isHorizontalWritingMode() ? frameView->visibleWidth() : frameView->visibleHeight()) / frame->frameScaleFactor(); 2915 2916 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode()) 2917 return containingBlockLogicalHeightForPositioned(containingBlock, false); 2918 2919 if (containingBlock->isBox()) { 2920 RenderFlowThread* flowThread = flowThreadContainingBlock(); 2921 if (!flowThread) 2922 return toRenderBox(containingBlock)->clientLogicalWidth(); 2923 2924 const RenderBlock* cb = toRenderBlock(containingBlock); 2925 RenderBoxRegionInfo* boxInfo = 0; 2926 if (!region) { 2927 if (containingBlock->isRenderFlowThread() && !checkForPerpendicularWritingMode) 2928 return toRenderFlowThread(containingBlock)->contentLogicalWidthOfFirstRegion(); 2929 if (isWritingModeRoot()) { 2930 LayoutUnit cbPageOffset = cb->offsetFromLogicalTopOfFirstPage(); 2931 RenderRegion* cbRegion = cb->regionAtBlockOffset(cbPageOffset); 2932 if (cbRegion) { 2933 cbRegion = cb->clampToStartAndEndRegions(cbRegion); 2934 boxInfo = cb->renderBoxRegionInfo(cbRegion); 2935 } 2936 } 2937 } else if (region && flowThread->isHorizontalWritingMode() == containingBlock->isHorizontalWritingMode()) { 2938 RenderRegion* containingBlockRegion = cb->clampToStartAndEndRegions(region); 2939 boxInfo = cb->renderBoxRegionInfo(containingBlockRegion); 2940 } 2941 return (boxInfo) ? max<LayoutUnit>(0, cb->clientLogicalWidth() - (cb->logicalWidth() - boxInfo->logicalWidth())) : cb->clientLogicalWidth(); 2942 } 2943 2944 ASSERT(containingBlock->isRenderInline() && containingBlock->isInFlowPositioned()); 2945 2946 const RenderInline* flow = toRenderInline(containingBlock); 2947 InlineFlowBox* first = flow->firstLineBox(); 2948 InlineFlowBox* last = flow->lastLineBox(); 2949 2950 // If the containing block is empty, return a width of 0. 2951 if (!first || !last) 2952 return 0; 2953 2954 LayoutUnit fromLeft; 2955 LayoutUnit fromRight; 2956 if (containingBlock->style()->isLeftToRightDirection()) { 2957 fromLeft = first->logicalLeft() + first->borderLogicalLeft(); 2958 fromRight = last->logicalLeft() + last->logicalWidth() - last->borderLogicalRight(); 2959 } else { 2960 fromRight = first->logicalLeft() + first->logicalWidth() - first->borderLogicalRight(); 2961 fromLeft = last->logicalLeft() + last->borderLogicalLeft(); 2962 } 2963 2964 return max<LayoutUnit>(0, fromRight - fromLeft); 2965} 2966 2967LayoutUnit RenderBox::containingBlockLogicalHeightForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const 2968{ 2969 Frame* frame = view() ? view()->frame(): 0; 2970 FrameView* frameView = view() ? view()->frameView() : 0; 2971 if (fixedElementLaysOutRelativeToFrame(frame, frameView)) 2972 return (view()->isHorizontalWritingMode() ? frameView->visibleHeight() : frameView->visibleWidth()) / frame->frameScaleFactor(); 2973 2974 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode()) 2975 return containingBlockLogicalWidthForPositioned(containingBlock, 0, false); 2976 2977 if (containingBlock->isBox()) { 2978 const RenderBlock* cb = toRenderBlock(containingBlock); 2979 LayoutUnit result = cb->clientLogicalHeight(); 2980 RenderFlowThread* flowThread = flowThreadContainingBlock(); 2981 if (flowThread && containingBlock->isRenderFlowThread() && flowThread->isHorizontalWritingMode() == containingBlock->isHorizontalWritingMode()) 2982 return toRenderFlowThread(containingBlock)->contentLogicalHeightOfFirstRegion(); 2983 return result; 2984 } 2985 2986 ASSERT(containingBlock->isRenderInline() && containingBlock->isInFlowPositioned()); 2987 2988 const RenderInline* flow = toRenderInline(containingBlock); 2989 InlineFlowBox* first = flow->firstLineBox(); 2990 InlineFlowBox* last = flow->lastLineBox(); 2991 2992 // If the containing block is empty, return a height of 0. 2993 if (!first || !last) 2994 return 0; 2995 2996 LayoutUnit heightResult; 2997 LayoutRect boundingBox = flow->linesBoundingBox(); 2998 if (containingBlock->isHorizontalWritingMode()) 2999 heightResult = boundingBox.height(); 3000 else 3001 heightResult = boundingBox.width(); 3002 heightResult -= (containingBlock->borderBefore() + containingBlock->borderAfter()); 3003 return heightResult; 3004} 3005 3006static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRight, const RenderBox* child, const RenderBoxModelObject* containerBlock, LayoutUnit containerLogicalWidth, RenderRegion* region) 3007{ 3008 if (!logicalLeft.isAuto() || !logicalRight.isAuto()) 3009 return; 3010 3011 // FIXME: The static distance computation has not been patched for mixed writing modes yet. 3012 if (child->parent()->style()->direction() == LTR) { 3013 LayoutUnit staticPosition = child->layer()->staticInlinePosition() - containerBlock->borderLogicalLeft(); 3014 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) { 3015 if (curr->isBox()) { 3016 staticPosition += toRenderBox(curr)->logicalLeft(); 3017 if (region && curr->isRenderBlock()) { 3018 const RenderBlock* cb = toRenderBlock(curr); 3019 region = cb->clampToStartAndEndRegions(region); 3020 RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(region); 3021 if (boxInfo) 3022 staticPosition += boxInfo->logicalLeft(); 3023 } 3024 } 3025 } 3026 logicalLeft.setValue(Fixed, staticPosition); 3027 } else { 3028 RenderBox* enclosingBox = child->parent()->enclosingBox(); 3029 LayoutUnit staticPosition = child->layer()->staticInlinePosition() + containerLogicalWidth + containerBlock->borderLogicalLeft(); 3030 for (RenderObject* curr = enclosingBox; curr; curr = curr->container()) { 3031 if (curr->isBox()) { 3032 if (curr != containerBlock) 3033 staticPosition -= toRenderBox(curr)->logicalLeft(); 3034 if (curr == enclosingBox) 3035 staticPosition -= enclosingBox->logicalWidth(); 3036 if (region && curr->isRenderBlock()) { 3037 const RenderBlock* cb = toRenderBlock(curr); 3038 region = cb->clampToStartAndEndRegions(region); 3039 RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(region); 3040 if (boxInfo) { 3041 if (curr != containerBlock) 3042 staticPosition -= cb->logicalWidth() - (boxInfo->logicalLeft() + boxInfo->logicalWidth()); 3043 if (curr == enclosingBox) 3044 staticPosition += enclosingBox->logicalWidth() - boxInfo->logicalWidth(); 3045 } 3046 } 3047 } 3048 if (curr == containerBlock) 3049 break; 3050 } 3051 logicalRight.setValue(Fixed, staticPosition); 3052 } 3053} 3054 3055void RenderBox::computePositionedLogicalWidth(LogicalExtentComputedValues& computedValues, RenderRegion* region) const 3056{ 3057 if (isReplaced()) { 3058 // FIXME: Positioned replaced elements inside a flow thread are not working properly 3059 // with variable width regions (see https://bugs.webkit.org/show_bug.cgi?id=69896 ). 3060 computePositionedLogicalWidthReplaced(computedValues); 3061 return; 3062 } 3063 3064 // QUESTIONS 3065 // FIXME 1: Should we still deal with these the cases of 'left' or 'right' having 3066 // the type 'static' in determining whether to calculate the static distance? 3067 // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1. 3068 3069 // FIXME 2: Can perhaps optimize out cases when max-width/min-width are greater 3070 // than or less than the computed width(). Be careful of box-sizing and 3071 // percentage issues. 3072 3073 // The following is based off of the W3C Working Draft from April 11, 2006 of 3074 // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements" 3075 // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width> 3076 // (block-style-comments in this function and in computePositionedLogicalWidthUsing() 3077 // correspond to text from the spec) 3078 3079 3080 // We don't use containingBlock(), since we may be positioned by an enclosing 3081 // relative positioned inline. 3082 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 3083 3084 const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, region); 3085 3086 // Use the container block's direction except when calculating the static distance 3087 // This conforms with the reference results for abspos-replaced-width-margin-000.htm 3088 // of the CSS 2.1 test suite 3089 TextDirection containerDirection = containerBlock->style()->direction(); 3090 3091 bool isHorizontal = isHorizontalWritingMode(); 3092 const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth(); 3093 const Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop(); 3094 const Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom(); 3095 3096 Length logicalLeftLength = style()->logicalLeft(); 3097 Length logicalRightLength = style()->logicalRight(); 3098 3099 /*---------------------------------------------------------------------------*\ 3100 * For the purposes of this section and the next, the term "static position" 3101 * (of an element) refers, roughly, to the position an element would have had 3102 * in the normal flow. More precisely: 3103 * 3104 * * The static position for 'left' is the distance from the left edge of the 3105 * containing block to the left margin edge of a hypothetical box that would 3106 * have been the first box of the element if its 'position' property had 3107 * been 'static' and 'float' had been 'none'. The value is negative if the 3108 * hypothetical box is to the left of the containing block. 3109 * * The static position for 'right' is the distance from the right edge of the 3110 * containing block to the right margin edge of the same hypothetical box as 3111 * above. The value is positive if the hypothetical box is to the left of the 3112 * containing block's edge. 3113 * 3114 * But rather than actually calculating the dimensions of that hypothetical box, 3115 * user agents are free to make a guess at its probable position. 3116 * 3117 * For the purposes of calculating the static position, the containing block of 3118 * fixed positioned elements is the initial containing block instead of the 3119 * viewport, and all scrollable boxes should be assumed to be scrolled to their 3120 * origin. 3121 \*---------------------------------------------------------------------------*/ 3122 3123 // see FIXME 1 3124 // Calculate the static distance if needed. 3125 computeInlineStaticDistance(logicalLeftLength, logicalRightLength, this, containerBlock, containerLogicalWidth, region); 3126 3127 // Calculate constraint equation values for 'width' case. 3128 computePositionedLogicalWidthUsing(style()->logicalWidth(), containerBlock, containerDirection, 3129 containerLogicalWidth, bordersPlusPadding, 3130 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight, 3131 computedValues); 3132 3133 // Calculate constraint equation values for 'max-width' case. 3134 if (!style()->logicalMaxWidth().isUndefined()) { 3135 LogicalExtentComputedValues maxValues; 3136 3137 computePositionedLogicalWidthUsing(style()->logicalMaxWidth(), containerBlock, containerDirection, 3138 containerLogicalWidth, bordersPlusPadding, 3139 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight, 3140 maxValues); 3141 3142 if (computedValues.m_extent > maxValues.m_extent) { 3143 computedValues.m_extent = maxValues.m_extent; 3144 computedValues.m_position = maxValues.m_position; 3145 computedValues.m_margins.m_start = maxValues.m_margins.m_start; 3146 computedValues.m_margins.m_end = maxValues.m_margins.m_end; 3147 } 3148 } 3149 3150 // Calculate constraint equation values for 'min-width' case. 3151 if (!style()->logicalMinWidth().isZero() || style()->logicalMinWidth().isIntrinsic()) { 3152 LogicalExtentComputedValues minValues; 3153 3154 computePositionedLogicalWidthUsing(style()->logicalMinWidth(), containerBlock, containerDirection, 3155 containerLogicalWidth, bordersPlusPadding, 3156 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight, 3157 minValues); 3158 3159 if (computedValues.m_extent < minValues.m_extent) { 3160 computedValues.m_extent = minValues.m_extent; 3161 computedValues.m_position = minValues.m_position; 3162 computedValues.m_margins.m_start = minValues.m_margins.m_start; 3163 computedValues.m_margins.m_end = minValues.m_margins.m_end; 3164 } 3165 } 3166 3167 computedValues.m_extent += bordersPlusPadding; 3168 3169 // Adjust logicalLeft if we need to for the flipped version of our writing mode in regions. 3170 // FIXME: Add support for other types of objects as containerBlock, not only RenderBlock. 3171 RenderFlowThread* flowThread = flowThreadContainingBlock(); 3172 if (flowThread && !region && isWritingModeRoot() && isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode() && containerBlock->isRenderBlock()) { 3173 ASSERT(containerBlock->canHaveBoxInfoInRegion()); 3174 LayoutUnit logicalLeftPos = computedValues.m_position; 3175 const RenderBlock* cb = toRenderBlock(containerBlock); 3176 LayoutUnit cbPageOffset = cb->offsetFromLogicalTopOfFirstPage(); 3177 RenderRegion* cbRegion = cb->regionAtBlockOffset(cbPageOffset); 3178 if (cbRegion) { 3179 cbRegion = cb->clampToStartAndEndRegions(cbRegion); 3180 RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(cbRegion); 3181 if (boxInfo) { 3182 logicalLeftPos += boxInfo->logicalLeft(); 3183 computedValues.m_position = logicalLeftPos; 3184 } 3185 } 3186 } 3187} 3188 3189static void computeLogicalLeftPositionedOffset(LayoutUnit& logicalLeftPos, const RenderBox* child, LayoutUnit logicalWidthValue, const RenderBoxModelObject* containerBlock, LayoutUnit containerLogicalWidth) 3190{ 3191 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped 3192 // 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. 3193 if (containerBlock->isHorizontalWritingMode() != child->isHorizontalWritingMode() && containerBlock->style()->isFlippedBlocksWritingMode()) { 3194 logicalLeftPos = containerLogicalWidth - logicalWidthValue - logicalLeftPos; 3195 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderRight() : containerBlock->borderBottom()); 3196 } else 3197 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderLeft() : containerBlock->borderTop()); 3198} 3199 3200void RenderBox::computePositionedLogicalWidthUsing(Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection, 3201 LayoutUnit containerLogicalWidth, LayoutUnit bordersPlusPadding, 3202 Length logicalLeft, Length logicalRight, Length marginLogicalLeft, Length marginLogicalRight, 3203 LogicalExtentComputedValues& computedValues) const 3204{ 3205 if (logicalWidth.isIntrinsic()) 3206 logicalWidth = Length(computeIntrinsicLogicalWidthUsing(logicalWidth, containerLogicalWidth, bordersPlusPadding) - bordersPlusPadding, Fixed); 3207 3208 // 'left' and 'right' cannot both be 'auto' because one would of been 3209 // converted to the static position already 3210 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto())); 3211 3212 LayoutUnit logicalLeftValue = 0; 3213 3214 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, 0, false); 3215 3216 bool logicalWidthIsAuto = logicalWidth.isIntrinsicOrAuto(); 3217 bool logicalLeftIsAuto = logicalLeft.isAuto(); 3218 bool logicalRightIsAuto = logicalRight.isAuto(); 3219 RenderView* renderView = view(); 3220 LayoutUnit& marginLogicalLeftValue = style()->isLeftToRightDirection() ? computedValues.m_margins.m_start : computedValues.m_margins.m_end; 3221 LayoutUnit& marginLogicalRightValue = style()->isLeftToRightDirection() ? computedValues.m_margins.m_end : computedValues.m_margins.m_start; 3222 3223 if (!logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) { 3224 /*-----------------------------------------------------------------------*\ 3225 * If none of the three is 'auto': If both 'margin-left' and 'margin- 3226 * right' are 'auto', solve the equation under the extra constraint that 3227 * the two margins get equal values, unless this would make them negative, 3228 * in which case when direction of the containing block is 'ltr' ('rtl'), 3229 * set 'margin-left' ('margin-right') to zero and solve for 'margin-right' 3230 * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', 3231 * solve the equation for that value. If the values are over-constrained, 3232 * ignore the value for 'left' (in case the 'direction' property of the 3233 * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') 3234 * and solve for that value. 3235 \*-----------------------------------------------------------------------*/ 3236 // NOTE: It is not necessary to solve for 'right' in the over constrained 3237 // case because the value is not used for any further calculations. 3238 3239 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView); 3240 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth, renderView)); 3241 3242 const LayoutUnit availableSpace = containerLogicalWidth - (logicalLeftValue + computedValues.m_extent + valueForLength(logicalRight, containerLogicalWidth, renderView) + bordersPlusPadding); 3243 3244 // Margins are now the only unknown 3245 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) { 3246 // Both margins auto, solve for equality 3247 if (availableSpace >= 0) { 3248 marginLogicalLeftValue = availableSpace / 2; // split the difference 3249 marginLogicalRightValue = availableSpace - marginLogicalLeftValue; // account for odd valued differences 3250 } else { 3251 // Use the containing block's direction rather than the parent block's 3252 // per CSS 2.1 reference test abspos-non-replaced-width-margin-000. 3253 if (containerDirection == LTR) { 3254 marginLogicalLeftValue = 0; 3255 marginLogicalRightValue = availableSpace; // will be negative 3256 } else { 3257 marginLogicalLeftValue = availableSpace; // will be negative 3258 marginLogicalRightValue = 0; 3259 } 3260 } 3261 } else if (marginLogicalLeft.isAuto()) { 3262 // Solve for left margin 3263 marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); 3264 marginLogicalLeftValue = availableSpace - marginLogicalRightValue; 3265 } else if (marginLogicalRight.isAuto()) { 3266 // Solve for right margin 3267 marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); 3268 marginLogicalRightValue = availableSpace - marginLogicalLeftValue; 3269 } else { 3270 // Over-constrained, solve for left if direction is RTL 3271 marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); 3272 marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); 3273 3274 // Use the containing block's direction rather than the parent block's 3275 // per CSS 2.1 reference test abspos-non-replaced-width-margin-000. 3276 if (containerDirection == RTL) 3277 logicalLeftValue = (availableSpace + logicalLeftValue) - marginLogicalLeftValue - marginLogicalRightValue; 3278 } 3279 } else { 3280 /*--------------------------------------------------------------------*\ 3281 * Otherwise, set 'auto' values for 'margin-left' and 'margin-right' 3282 * to 0, and pick the one of the following six rules that applies. 3283 * 3284 * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the 3285 * width is shrink-to-fit. Then solve for 'left' 3286 * 3287 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT 3288 * ------------------------------------------------------------------ 3289 * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if 3290 * the 'direction' property of the containing block is 'ltr' set 3291 * 'left' to the static position, otherwise set 'right' to the 3292 * static position. Then solve for 'left' (if 'direction is 'rtl') 3293 * or 'right' (if 'direction' is 'ltr'). 3294 * ------------------------------------------------------------------ 3295 * 3296 * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the 3297 * width is shrink-to-fit . Then solve for 'right' 3298 * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve 3299 * for 'left' 3300 * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve 3301 * for 'width' 3302 * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve 3303 * for 'right' 3304 * 3305 * Calculation of the shrink-to-fit width is similar to calculating the 3306 * width of a table cell using the automatic table layout algorithm. 3307 * Roughly: calculate the preferred width by formatting the content 3308 * without breaking lines other than where explicit line breaks occur, 3309 * and also calculate the preferred minimum width, e.g., by trying all 3310 * possible line breaks. CSS 2.1 does not define the exact algorithm. 3311 * Thirdly, calculate the available width: this is found by solving 3312 * for 'width' after setting 'left' (in case 1) or 'right' (in case 3) 3313 * to 0. 3314 * 3315 * Then the shrink-to-fit width is: 3316 * min(max(preferred minimum width, available width), preferred width). 3317 \*--------------------------------------------------------------------*/ 3318 // NOTE: For rules 3 and 6 it is not necessary to solve for 'right' 3319 // because the value is not used for any further calculations. 3320 3321 // Calculate margins, 'auto' margins are ignored. 3322 marginLogicalLeftValue = minimumValueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); 3323 marginLogicalRightValue = minimumValueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); 3324 3325 const LayoutUnit availableSpace = containerLogicalWidth - (marginLogicalLeftValue + marginLogicalRightValue + bordersPlusPadding); 3326 3327 // FIXME: Is there a faster way to find the correct case? 3328 // Use rule/case that applies. 3329 if (logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) { 3330 // RULE 1: (use shrink-to-fit for width, and solve of left) 3331 LayoutUnit logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView); 3332 3333 // FIXME: would it be better to have shrink-to-fit in one step? 3334 LayoutUnit preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding; 3335 LayoutUnit preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding; 3336 LayoutUnit availableWidth = availableSpace - logicalRightValue; 3337 computedValues.m_extent = min(max(preferredMinWidth, availableWidth), preferredWidth); 3338 logicalLeftValue = availableSpace - (computedValues.m_extent + logicalRightValue); 3339 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && logicalRightIsAuto) { 3340 // RULE 3: (use shrink-to-fit for width, and no need solve of right) 3341 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView); 3342 3343 // FIXME: would it be better to have shrink-to-fit in one step? 3344 LayoutUnit preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding; 3345 LayoutUnit preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding; 3346 LayoutUnit availableWidth = availableSpace - logicalLeftValue; 3347 computedValues.m_extent = min(max(preferredMinWidth, availableWidth), preferredWidth); 3348 } else if (logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) { 3349 // RULE 4: (solve for left) 3350 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth, renderView)); 3351 logicalLeftValue = availableSpace - (computedValues.m_extent + valueForLength(logicalRight, containerLogicalWidth, renderView)); 3352 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) { 3353 // RULE 5: (solve for width) 3354 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView); 3355 computedValues.m_extent = availableSpace - (logicalLeftValue + valueForLength(logicalRight, containerLogicalWidth, renderView)); 3356 } else if (!logicalLeftIsAuto && !logicalWidthIsAuto && logicalRightIsAuto) { 3357 // RULE 6: (no need solve for right) 3358 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView); 3359 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth, renderView)); 3360 } 3361 } 3362 3363 // Use computed values to calculate the horizontal position. 3364 3365 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively 3366 // positioned, inline because right now, it is using the logical left position 3367 // of the first line box when really it should use the last line box. When 3368 // this is fixed elsewhere, this block should be removed. 3369 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) { 3370 const RenderInline* flow = toRenderInline(containerBlock); 3371 InlineFlowBox* firstLine = flow->firstLineBox(); 3372 InlineFlowBox* lastLine = flow->lastLineBox(); 3373 if (firstLine && lastLine && firstLine != lastLine) { 3374 computedValues.m_position = logicalLeftValue + marginLogicalLeftValue + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft()); 3375 return; 3376 } 3377 } 3378 3379 computedValues.m_position = logicalLeftValue + marginLogicalLeftValue; 3380 computeLogicalLeftPositionedOffset(computedValues.m_position, this, computedValues.m_extent, containerBlock, containerLogicalWidth); 3381} 3382 3383static void computeBlockStaticDistance(Length& logicalTop, Length& logicalBottom, const RenderBox* child, const RenderBoxModelObject* containerBlock) 3384{ 3385 if (!logicalTop.isAuto() || !logicalBottom.isAuto()) 3386 return; 3387 3388 // FIXME: The static distance computation has not been patched for mixed writing modes. 3389 LayoutUnit staticLogicalTop = child->layer()->staticBlockPosition() - containerBlock->borderBefore(); 3390 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) { 3391 if (curr->isBox() && !curr->isTableRow()) 3392 staticLogicalTop += toRenderBox(curr)->logicalTop(); 3393 } 3394 logicalTop.setValue(Fixed, staticLogicalTop); 3395} 3396 3397void RenderBox::computePositionedLogicalHeight(LogicalExtentComputedValues& computedValues) const 3398{ 3399 if (isReplaced()) { 3400 computePositionedLogicalHeightReplaced(computedValues); 3401 return; 3402 } 3403 3404 // The following is based off of the W3C Working Draft from April 11, 2006 of 3405 // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements" 3406 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height> 3407 // (block-style-comments in this function and in computePositionedLogicalHeightUsing() 3408 // correspond to text from the spec) 3409 3410 3411 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. 3412 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 3413 3414 const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); 3415 3416 RenderStyle* styleToUse = style(); 3417 const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight(); 3418 const Length marginBefore = styleToUse->marginBefore(); 3419 const Length marginAfter = styleToUse->marginAfter(); 3420 Length logicalTopLength = styleToUse->logicalTop(); 3421 Length logicalBottomLength = styleToUse->logicalBottom(); 3422 3423 /*---------------------------------------------------------------------------*\ 3424 * For the purposes of this section and the next, the term "static position" 3425 * (of an element) refers, roughly, to the position an element would have had 3426 * in the normal flow. More precisely, the static position for 'top' is the 3427 * distance from the top edge of the containing block to the top margin edge 3428 * of a hypothetical box that would have been the first box of the element if 3429 * its 'position' property had been 'static' and 'float' had been 'none'. The 3430 * value is negative if the hypothetical box is above the containing block. 3431 * 3432 * But rather than actually calculating the dimensions of that hypothetical 3433 * box, user agents are free to make a guess at its probable position. 3434 * 3435 * For the purposes of calculating the static position, the containing block 3436 * of fixed positioned elements is the initial containing block instead of 3437 * the viewport. 3438 \*---------------------------------------------------------------------------*/ 3439 3440 // see FIXME 1 3441 // Calculate the static distance if needed. 3442 computeBlockStaticDistance(logicalTopLength, logicalBottomLength, this, containerBlock); 3443 3444 // Calculate constraint equation values for 'height' case. 3445 LayoutUnit logicalHeight = computedValues.m_extent; 3446 computePositionedLogicalHeightUsing(styleToUse->logicalHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight, 3447 logicalTopLength, logicalBottomLength, marginBefore, marginAfter, 3448 computedValues); 3449 3450 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults). 3451 // see FIXME 2 3452 3453 // Calculate constraint equation values for 'max-height' case. 3454 if (!styleToUse->logicalMaxHeight().isUndefined()) { 3455 LogicalExtentComputedValues maxValues; 3456 3457 computePositionedLogicalHeightUsing(styleToUse->logicalMaxHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight, 3458 logicalTopLength, logicalBottomLength, marginBefore, marginAfter, 3459 maxValues); 3460 3461 if (computedValues.m_extent > maxValues.m_extent) { 3462 computedValues.m_extent = maxValues.m_extent; 3463 computedValues.m_position = maxValues.m_position; 3464 computedValues.m_margins.m_before = maxValues.m_margins.m_before; 3465 computedValues.m_margins.m_after = maxValues.m_margins.m_after; 3466 } 3467 } 3468 3469 // Calculate constraint equation values for 'min-height' case. 3470 if (!styleToUse->logicalMinHeight().isZero()) { 3471 LogicalExtentComputedValues minValues; 3472 3473 computePositionedLogicalHeightUsing(styleToUse->logicalMinHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight, 3474 logicalTopLength, logicalBottomLength, marginBefore, marginAfter, 3475 minValues); 3476 3477 if (computedValues.m_extent < minValues.m_extent) { 3478 computedValues.m_extent = minValues.m_extent; 3479 computedValues.m_position = minValues.m_position; 3480 computedValues.m_margins.m_before = minValues.m_margins.m_before; 3481 computedValues.m_margins.m_after = minValues.m_margins.m_after; 3482 } 3483 } 3484 3485 // Set final height value. 3486 computedValues.m_extent += bordersPlusPadding; 3487 3488 // Adjust logicalTop if we need to for perpendicular writing modes in regions. 3489 // FIXME: Add support for other types of objects as containerBlock, not only RenderBlock. 3490 RenderFlowThread* flowThread = flowThreadContainingBlock(); 3491 if (flowThread && isHorizontalWritingMode() != containerBlock->isHorizontalWritingMode() && containerBlock->isRenderBlock()) { 3492 ASSERT(containerBlock->canHaveBoxInfoInRegion()); 3493 LayoutUnit logicalTopPos = computedValues.m_position; 3494 const RenderBlock* cb = toRenderBlock(containerBlock); 3495 LayoutUnit cbPageOffset = cb->offsetFromLogicalTopOfFirstPage() - logicalLeft(); 3496 RenderRegion* cbRegion = cb->regionAtBlockOffset(cbPageOffset); 3497 if (cbRegion) { 3498 cbRegion = cb->clampToStartAndEndRegions(cbRegion); 3499 RenderBoxRegionInfo* boxInfo = cb->renderBoxRegionInfo(cbRegion); 3500 if (boxInfo) { 3501 logicalTopPos += boxInfo->logicalLeft(); 3502 computedValues.m_position = logicalTopPos; 3503 } 3504 } 3505 } 3506} 3507 3508static void computeLogicalTopPositionedOffset(LayoutUnit& logicalTopPos, const RenderBox* child, LayoutUnit logicalHeightValue, const RenderBoxModelObject* containerBlock, LayoutUnit containerLogicalHeight) 3509{ 3510 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped 3511 // 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. 3512 if ((child->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() != containerBlock->isHorizontalWritingMode()) 3513 || (child->style()->isFlippedBlocksWritingMode() != containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode())) 3514 logicalTopPos = containerLogicalHeight - logicalHeightValue - logicalTopPos; 3515 3516 // Our offset is from the logical bottom edge in a flipped environment, e.g., right for vertical-rl and bottom for horizontal-bt. 3517 if (containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()) { 3518 if (child->isHorizontalWritingMode()) 3519 logicalTopPos += containerBlock->borderBottom(); 3520 else 3521 logicalTopPos += containerBlock->borderRight(); 3522 } else { 3523 if (child->isHorizontalWritingMode()) 3524 logicalTopPos += containerBlock->borderTop(); 3525 else 3526 logicalTopPos += containerBlock->borderLeft(); 3527 } 3528} 3529 3530void RenderBox::computePositionedLogicalHeightUsing(Length logicalHeightLength, const RenderBoxModelObject* containerBlock, 3531 LayoutUnit containerLogicalHeight, LayoutUnit bordersPlusPadding, LayoutUnit logicalHeight, 3532 Length logicalTop, Length logicalBottom, Length marginBefore, Length marginAfter, 3533 LogicalExtentComputedValues& computedValues) const 3534{ 3535 // 'top' and 'bottom' cannot both be 'auto' because 'top would of been 3536 // converted to the static position in computePositionedLogicalHeight() 3537 ASSERT(!(logicalTop.isAuto() && logicalBottom.isAuto())); 3538 3539 LayoutUnit logicalHeightValue; 3540 LayoutUnit contentLogicalHeight = logicalHeight - bordersPlusPadding; 3541 3542 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, 0, false); 3543 3544 LayoutUnit logicalTopValue = 0; 3545 3546 bool logicalHeightIsAuto = logicalHeightLength.isAuto(); 3547 bool logicalTopIsAuto = logicalTop.isAuto(); 3548 bool logicalBottomIsAuto = logicalBottom.isAuto(); 3549 RenderView* renderView = view(); 3550 3551 // Height is never unsolved for tables. 3552 if (isTable()) { 3553 logicalHeightLength.setValue(Fixed, contentLogicalHeight); 3554 logicalHeightIsAuto = false; 3555 } 3556 3557 if (!logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) { 3558 /*-----------------------------------------------------------------------*\ 3559 * If none of the three are 'auto': If both 'margin-top' and 'margin- 3560 * bottom' are 'auto', solve the equation under the extra constraint that 3561 * the two margins get equal values. If one of 'margin-top' or 'margin- 3562 * bottom' is 'auto', solve the equation for that value. If the values 3563 * are over-constrained, ignore the value for 'bottom' and solve for that 3564 * value. 3565 \*-----------------------------------------------------------------------*/ 3566 // NOTE: It is not necessary to solve for 'bottom' in the over constrained 3567 // case because the value is not used for any further calculations. 3568 3569 logicalHeightValue = adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeightLength, containerLogicalHeight, renderView)); 3570 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView); 3571 3572 const LayoutUnit availableSpace = containerLogicalHeight - (logicalTopValue + logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight, renderView) + bordersPlusPadding); 3573 3574 // Margins are now the only unknown 3575 if (marginBefore.isAuto() && marginAfter.isAuto()) { 3576 // Both margins auto, solve for equality 3577 // NOTE: This may result in negative values. 3578 computedValues.m_margins.m_before = availableSpace / 2; // split the difference 3579 computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before; // account for odd valued differences 3580 } else if (marginBefore.isAuto()) { 3581 // Solve for top margin 3582 computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView); 3583 computedValues.m_margins.m_before = availableSpace - computedValues.m_margins.m_after; 3584 } else if (marginAfter.isAuto()) { 3585 // Solve for bottom margin 3586 computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView); 3587 computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before; 3588 } else { 3589 // Over-constrained, (no need solve for bottom) 3590 computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView); 3591 computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView); 3592 } 3593 } else { 3594 /*--------------------------------------------------------------------*\ 3595 * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom' 3596 * to 0, and pick the one of the following six rules that applies. 3597 * 3598 * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then 3599 * the height is based on the content, and solve for 'top'. 3600 * 3601 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT 3602 * ------------------------------------------------------------------ 3603 * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then 3604 * set 'top' to the static position, and solve for 'bottom'. 3605 * ------------------------------------------------------------------ 3606 * 3607 * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then 3608 * the height is based on the content, and solve for 'bottom'. 3609 * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and 3610 * solve for 'top'. 3611 * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and 3612 * solve for 'height'. 3613 * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and 3614 * solve for 'bottom'. 3615 \*--------------------------------------------------------------------*/ 3616 // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom' 3617 // because the value is not used for any further calculations. 3618 3619 // Calculate margins, 'auto' margins are ignored. 3620 computedValues.m_margins.m_before = minimumValueForLength(marginBefore, containerRelativeLogicalWidth, renderView); 3621 computedValues.m_margins.m_after = minimumValueForLength(marginAfter, containerRelativeLogicalWidth, renderView); 3622 3623 const LayoutUnit availableSpace = containerLogicalHeight - (computedValues.m_margins.m_before + computedValues.m_margins.m_after + bordersPlusPadding); 3624 3625 // Use rule/case that applies. 3626 if (logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) { 3627 // RULE 1: (height is content based, solve of top) 3628 logicalHeightValue = contentLogicalHeight; 3629 logicalTopValue = availableSpace - (logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight, renderView)); 3630 } else if (!logicalTopIsAuto && logicalHeightIsAuto && logicalBottomIsAuto) { 3631 // RULE 3: (height is content based, no need solve of bottom) 3632 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView); 3633 logicalHeightValue = contentLogicalHeight; 3634 } else if (logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) { 3635 // RULE 4: (solve of top) 3636 logicalHeightValue = adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeightLength, containerLogicalHeight, renderView)); 3637 logicalTopValue = availableSpace - (logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight, renderView)); 3638 } else if (!logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) { 3639 // RULE 5: (solve of height) 3640 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView); 3641 logicalHeightValue = max<LayoutUnit>(0, availableSpace - (logicalTopValue + valueForLength(logicalBottom, containerLogicalHeight, renderView))); 3642 } else if (!logicalTopIsAuto && !logicalHeightIsAuto && logicalBottomIsAuto) { 3643 // RULE 6: (no need solve of bottom) 3644 logicalHeightValue = adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeightLength, containerLogicalHeight, renderView)); 3645 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView); 3646 } 3647 } 3648 computedValues.m_extent = logicalHeightValue; 3649 3650 // Use computed values to calculate the vertical position. 3651 computedValues.m_position = logicalTopValue + computedValues.m_margins.m_before; 3652 computeLogicalTopPositionedOffset(computedValues.m_position, this, logicalHeightValue, containerBlock, containerLogicalHeight); 3653} 3654 3655void RenderBox::computePositionedLogicalWidthReplaced(LogicalExtentComputedValues& computedValues) const 3656{ 3657 // The following is based off of the W3C Working Draft from April 11, 2006 of 3658 // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements" 3659 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width> 3660 // (block-style-comments in this function correspond to text from the spec and 3661 // the numbers correspond to numbers in spec) 3662 3663 // We don't use containingBlock(), since we may be positioned by an enclosing 3664 // relative positioned inline. 3665 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 3666 3667 const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock); 3668 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, 0, false); 3669 3670 // To match WinIE, in quirks mode use the parent's 'direction' property 3671 // instead of the the container block's. 3672 TextDirection containerDirection = containerBlock->style()->direction(); 3673 3674 // Variables to solve. 3675 bool isHorizontal = isHorizontalWritingMode(); 3676 Length logicalLeft = style()->logicalLeft(); 3677 Length logicalRight = style()->logicalRight(); 3678 Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop(); 3679 Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom(); 3680 LayoutUnit& marginLogicalLeftAlias = style()->isLeftToRightDirection() ? computedValues.m_margins.m_start : computedValues.m_margins.m_end; 3681 LayoutUnit& marginLogicalRightAlias = style()->isLeftToRightDirection() ? computedValues.m_margins.m_end : computedValues.m_margins.m_start; 3682 3683 /*-----------------------------------------------------------------------*\ 3684 * 1. The used value of 'width' is determined as for inline replaced 3685 * elements. 3686 \*-----------------------------------------------------------------------*/ 3687 // NOTE: This value of width is FINAL in that the min/max width calculations 3688 // are dealt with in computeReplacedWidth(). This means that the steps to produce 3689 // correct max/min in the non-replaced version, are not necessary. 3690 computedValues.m_extent = computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth(); 3691 3692 const LayoutUnit availableSpace = containerLogicalWidth - computedValues.m_extent; 3693 3694 /*-----------------------------------------------------------------------*\ 3695 * 2. If both 'left' and 'right' have the value 'auto', then if 'direction' 3696 * of the containing block is 'ltr', set 'left' to the static position; 3697 * else if 'direction' is 'rtl', set 'right' to the static position. 3698 \*-----------------------------------------------------------------------*/ 3699 // see FIXME 1 3700 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth, 0); // FIXME: Pass the region. 3701 3702 /*-----------------------------------------------------------------------*\ 3703 * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' 3704 * or 'margin-right' with '0'. 3705 \*-----------------------------------------------------------------------*/ 3706 if (logicalLeft.isAuto() || logicalRight.isAuto()) { 3707 if (marginLogicalLeft.isAuto()) 3708 marginLogicalLeft.setValue(Fixed, 0); 3709 if (marginLogicalRight.isAuto()) 3710 marginLogicalRight.setValue(Fixed, 0); 3711 } 3712 3713 /*-----------------------------------------------------------------------*\ 3714 * 4. If at this point both 'margin-left' and 'margin-right' are still 3715 * 'auto', solve the equation under the extra constraint that the two 3716 * margins must get equal values, unless this would make them negative, 3717 * in which case when the direction of the containing block is 'ltr' 3718 * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for 3719 * 'margin-right' ('margin-left'). 3720 \*-----------------------------------------------------------------------*/ 3721 LayoutUnit logicalLeftValue = 0; 3722 LayoutUnit logicalRightValue = 0; 3723 RenderView* renderView = view(); 3724 3725 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) { 3726 // 'left' and 'right' cannot be 'auto' due to step 3 3727 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto())); 3728 3729 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView); 3730 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView); 3731 3732 LayoutUnit difference = availableSpace - (logicalLeftValue + logicalRightValue); 3733 if (difference > 0) { 3734 marginLogicalLeftAlias = difference / 2; // split the difference 3735 marginLogicalRightAlias = difference - marginLogicalLeftAlias; // account for odd valued differences 3736 } else { 3737 // Use the containing block's direction rather than the parent block's 3738 // per CSS 2.1 reference test abspos-replaced-width-margin-000. 3739 if (containerDirection == LTR) { 3740 marginLogicalLeftAlias = 0; 3741 marginLogicalRightAlias = difference; // will be negative 3742 } else { 3743 marginLogicalLeftAlias = difference; // will be negative 3744 marginLogicalRightAlias = 0; 3745 } 3746 } 3747 3748 /*-----------------------------------------------------------------------*\ 3749 * 5. If at this point there is an 'auto' left, solve the equation for 3750 * that value. 3751 \*-----------------------------------------------------------------------*/ 3752 } else if (logicalLeft.isAuto()) { 3753 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); 3754 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); 3755 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView); 3756 3757 // Solve for 'left' 3758 logicalLeftValue = availableSpace - (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias); 3759 } else if (logicalRight.isAuto()) { 3760 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); 3761 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); 3762 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView); 3763 3764 // Solve for 'right' 3765 logicalRightValue = availableSpace - (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias); 3766 } else if (marginLogicalLeft.isAuto()) { 3767 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); 3768 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView); 3769 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView); 3770 3771 // Solve for 'margin-left' 3772 marginLogicalLeftAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalRightAlias); 3773 } else if (marginLogicalRight.isAuto()) { 3774 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); 3775 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView); 3776 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView); 3777 3778 // Solve for 'margin-right' 3779 marginLogicalRightAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias); 3780 } else { 3781 // Nothing is 'auto', just calculate the values. 3782 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth, renderView); 3783 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth, renderView); 3784 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth, renderView); 3785 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth, renderView); 3786 // If the containing block is right-to-left, then push the left position as far to the right as possible 3787 if (containerDirection == RTL) { 3788 int totalLogicalWidth = computedValues.m_extent + logicalLeftValue + logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias; 3789 logicalLeftValue = containerLogicalWidth - (totalLogicalWidth - logicalLeftValue); 3790 } 3791 } 3792 3793 /*-----------------------------------------------------------------------*\ 3794 * 6. If at this point the values are over-constrained, ignore the value 3795 * for either 'left' (in case the 'direction' property of the 3796 * containing block is 'rtl') or 'right' (in case 'direction' is 3797 * 'ltr') and solve for that value. 3798 \*-----------------------------------------------------------------------*/ 3799 // NOTE: Constraints imposed by the width of the containing block and its content have already been accounted for above. 3800 3801 // FIXME: Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space, so that 3802 // can make the result here rather complicated to compute. 3803 3804 // Use computed values to calculate the horizontal position. 3805 3806 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively 3807 // positioned, inline containing block because right now, it is using the logical left position 3808 // of the first line box when really it should use the last line box. When 3809 // this is fixed elsewhere, this block should be removed. 3810 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) { 3811 const RenderInline* flow = toRenderInline(containerBlock); 3812 InlineFlowBox* firstLine = flow->firstLineBox(); 3813 InlineFlowBox* lastLine = flow->lastLineBox(); 3814 if (firstLine && lastLine && firstLine != lastLine) { 3815 computedValues.m_position = logicalLeftValue + marginLogicalLeftAlias + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft()); 3816 return; 3817 } 3818 } 3819 3820 LayoutUnit logicalLeftPos = logicalLeftValue + marginLogicalLeftAlias; 3821 computeLogicalLeftPositionedOffset(logicalLeftPos, this, computedValues.m_extent, containerBlock, containerLogicalWidth); 3822 computedValues.m_position = logicalLeftPos; 3823} 3824 3825void RenderBox::computePositionedLogicalHeightReplaced(LogicalExtentComputedValues& computedValues) const 3826{ 3827 // The following is based off of the W3C Working Draft from April 11, 2006 of 3828 // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements" 3829 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height> 3830 // (block-style-comments in this function correspond to text from the spec and 3831 // the numbers correspond to numbers in spec) 3832 3833 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. 3834 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container()); 3835 3836 const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock); 3837 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, 0, false); 3838 3839 // Variables to solve. 3840 Length marginBefore = style()->marginBefore(); 3841 Length marginAfter = style()->marginAfter(); 3842 LayoutUnit& marginBeforeAlias = computedValues.m_margins.m_before; 3843 LayoutUnit& marginAfterAlias = computedValues.m_margins.m_after; 3844 3845 Length logicalTop = style()->logicalTop(); 3846 Length logicalBottom = style()->logicalBottom(); 3847 RenderView* renderView = view(); 3848 3849 /*-----------------------------------------------------------------------*\ 3850 * 1. The used value of 'height' is determined as for inline replaced 3851 * elements. 3852 \*-----------------------------------------------------------------------*/ 3853 // NOTE: This value of height is FINAL in that the min/max height calculations 3854 // are dealt with in computeReplacedHeight(). This means that the steps to produce 3855 // correct max/min in the non-replaced version, are not necessary. 3856 computedValues.m_extent = computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight(); 3857 const LayoutUnit availableSpace = containerLogicalHeight - computedValues.m_extent; 3858 3859 /*-----------------------------------------------------------------------*\ 3860 * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top' 3861 * with the element's static position. 3862 \*-----------------------------------------------------------------------*/ 3863 // see FIXME 1 3864 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock); 3865 3866 /*-----------------------------------------------------------------------*\ 3867 * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or 3868 * 'margin-bottom' with '0'. 3869 \*-----------------------------------------------------------------------*/ 3870 // FIXME: The spec. says that this step should only be taken when bottom is 3871 // auto, but if only top is auto, this makes step 4 impossible. 3872 if (logicalTop.isAuto() || logicalBottom.isAuto()) { 3873 if (marginBefore.isAuto()) 3874 marginBefore.setValue(Fixed, 0); 3875 if (marginAfter.isAuto()) 3876 marginAfter.setValue(Fixed, 0); 3877 } 3878 3879 /*-----------------------------------------------------------------------*\ 3880 * 4. If at this point both 'margin-top' and 'margin-bottom' are still 3881 * 'auto', solve the equation under the extra constraint that the two 3882 * margins must get equal values. 3883 \*-----------------------------------------------------------------------*/ 3884 LayoutUnit logicalTopValue = 0; 3885 LayoutUnit logicalBottomValue = 0; 3886 3887 if (marginBefore.isAuto() && marginAfter.isAuto()) { 3888 // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined. 3889 ASSERT(!(logicalTop.isAuto() || logicalBottom.isAuto())); 3890 3891 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView); 3892 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight, renderView); 3893 3894 LayoutUnit difference = availableSpace - (logicalTopValue + logicalBottomValue); 3895 // NOTE: This may result in negative values. 3896 marginBeforeAlias = difference / 2; // split the difference 3897 marginAfterAlias = difference - marginBeforeAlias; // account for odd valued differences 3898 3899 /*-----------------------------------------------------------------------*\ 3900 * 5. If at this point there is only one 'auto' left, solve the equation 3901 * for that value. 3902 \*-----------------------------------------------------------------------*/ 3903 } else if (logicalTop.isAuto()) { 3904 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView); 3905 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView); 3906 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight, renderView); 3907 3908 // Solve for 'top' 3909 logicalTopValue = availableSpace - (logicalBottomValue + marginBeforeAlias + marginAfterAlias); 3910 } else if (logicalBottom.isAuto()) { 3911 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView); 3912 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView); 3913 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView); 3914 3915 // Solve for 'bottom' 3916 // NOTE: It is not necessary to solve for 'bottom' because we don't ever 3917 // use the value. 3918 } else if (marginBefore.isAuto()) { 3919 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView); 3920 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView); 3921 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight, renderView); 3922 3923 // Solve for 'margin-top' 3924 marginBeforeAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginAfterAlias); 3925 } else if (marginAfter.isAuto()) { 3926 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView); 3927 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView); 3928 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight, renderView); 3929 3930 // Solve for 'margin-bottom' 3931 marginAfterAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginBeforeAlias); 3932 } else { 3933 // Nothing is 'auto', just calculate the values. 3934 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth, renderView); 3935 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth, renderView); 3936 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight, renderView); 3937 // NOTE: It is not necessary to solve for 'bottom' because we don't ever 3938 // use the value. 3939 } 3940 3941 /*-----------------------------------------------------------------------*\ 3942 * 6. If at this point the values are over-constrained, ignore the value 3943 * for 'bottom' and solve for that value. 3944 \*-----------------------------------------------------------------------*/ 3945 // NOTE: It is not necessary to do this step because we don't end up using 3946 // the value of 'bottom' regardless of whether the values are over-constrained 3947 // or not. 3948 3949 // Use computed values to calculate the vertical position. 3950 LayoutUnit logicalTopPos = logicalTopValue + marginBeforeAlias; 3951 computeLogicalTopPositionedOffset(logicalTopPos, this, computedValues.m_extent, containerBlock, containerLogicalHeight); 3952 computedValues.m_position = logicalTopPos; 3953} 3954 3955LayoutRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, LayoutUnit* extraWidthToEndOfLine) 3956{ 3957 // VisiblePositions at offsets inside containers either a) refer to the positions before/after 3958 // those containers (tables and select elements) or b) refer to the position inside an empty block. 3959 // They never refer to children. 3960 // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements. 3961 3962 LayoutRect rect(location(), LayoutSize(caretWidth, height())); 3963 bool ltr = box ? box->isLeftToRightDirection() : style()->isLeftToRightDirection(); 3964 3965 if ((!caretOffset) ^ ltr) 3966 rect.move(LayoutSize(width() - caretWidth, 0)); 3967 3968 if (box) { 3969 RootInlineBox* rootBox = box->root(); 3970 LayoutUnit top = rootBox->lineTop(); 3971 rect.setY(top); 3972 rect.setHeight(rootBox->lineBottom() - top); 3973 } 3974 3975 // If height of box is smaller than font height, use the latter one, 3976 // otherwise the caret might become invisible. 3977 // 3978 // Also, if the box is not a replaced element, always use the font height. 3979 // This prevents the "big caret" bug described in: 3980 // <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point 3981 // 3982 // FIXME: ignoring :first-line, missing good reason to take care of 3983 LayoutUnit fontHeight = style()->fontMetrics().height(); 3984 if (fontHeight > rect.height() || (!isReplaced() && !isTable())) 3985 rect.setHeight(fontHeight); 3986 3987 if (extraWidthToEndOfLine) 3988 *extraWidthToEndOfLine = x() + width() - rect.maxX(); 3989 3990 // Move to local coords 3991 rect.moveBy(-location()); 3992 3993 // FIXME: Border/padding should be added for all elements but this workaround 3994 // is needed because we use offsets inside an "atomic" element to represent 3995 // positions before and after the element in deprecated editing offsets. 3996 if (node() && !(editingIgnoresContent(node()) || isTableElement(node()))) { 3997 rect.setX(rect.x() + borderLeft() + paddingLeft()); 3998 rect.setY(rect.y() + paddingTop() + borderTop()); 3999 } 4000 4001 if (!isHorizontalWritingMode()) 4002 return rect.transposedRect(); 4003 4004 return rect; 4005} 4006 4007VisiblePosition RenderBox::positionForPoint(const LayoutPoint& point) 4008{ 4009 // no children...return this render object's element, if there is one, and offset 0 4010 if (!firstChild()) 4011 return createVisiblePosition(nonPseudoNode() ? firstPositionInOrBeforeNode(nonPseudoNode()) : Position()); 4012 4013 if (isTable() && nonPseudoNode()) { 4014 LayoutUnit right = contentWidth() + borderAndPaddingWidth(); 4015 LayoutUnit bottom = contentHeight() + borderAndPaddingHeight(); 4016 4017 if (point.x() < 0 || point.x() > right || point.y() < 0 || point.y() > bottom) { 4018 if (point.x() <= right / 2) 4019 return createVisiblePosition(firstPositionInOrBeforeNode(nonPseudoNode())); 4020 return createVisiblePosition(lastPositionInOrAfterNode(nonPseudoNode())); 4021 } 4022 } 4023 4024 // Pass off to the closest child. 4025 LayoutUnit minDist = LayoutUnit::max(); 4026 RenderBox* closestRenderer = 0; 4027 LayoutPoint adjustedPoint = point; 4028 if (isTableRow()) 4029 adjustedPoint.moveBy(location()); 4030 4031 for (RenderObject* renderObject = firstChild(); renderObject; renderObject = renderObject->nextSibling()) { 4032 if ((!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isBlockFlow() ) 4033 || renderObject->style()->visibility() != VISIBLE) 4034 continue; 4035 4036 if (!renderObject->isBox()) 4037 continue; 4038 4039 RenderBox* renderer = toRenderBox(renderObject); 4040 4041 LayoutUnit top = renderer->borderTop() + renderer->paddingTop() + (isTableRow() ? LayoutUnit() : renderer->y()); 4042 LayoutUnit bottom = top + renderer->contentHeight(); 4043 LayoutUnit left = renderer->borderLeft() + renderer->paddingLeft() + (isTableRow() ? LayoutUnit() : renderer->x()); 4044 LayoutUnit right = left + renderer->contentWidth(); 4045 4046 if (point.x() <= right && point.x() >= left && point.y() <= top && point.y() >= bottom) { 4047 if (renderer->isTableRow()) 4048 return renderer->positionForPoint(point + adjustedPoint - renderer->locationOffset()); 4049 return renderer->positionForPoint(point - renderer->locationOffset()); 4050 } 4051 4052 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces 4053 // and use a different compare depending on which piece (x, y) is in. 4054 LayoutPoint cmp; 4055 if (point.x() > right) { 4056 if (point.y() < top) 4057 cmp = LayoutPoint(right, top); 4058 else if (point.y() > bottom) 4059 cmp = LayoutPoint(right, bottom); 4060 else 4061 cmp = LayoutPoint(right, point.y()); 4062 } else if (point.x() < left) { 4063 if (point.y() < top) 4064 cmp = LayoutPoint(left, top); 4065 else if (point.y() > bottom) 4066 cmp = LayoutPoint(left, bottom); 4067 else 4068 cmp = LayoutPoint(left, point.y()); 4069 } else { 4070 if (point.y() < top) 4071 cmp = LayoutPoint(point.x(), top); 4072 else 4073 cmp = LayoutPoint(point.x(), bottom); 4074 } 4075 4076 LayoutSize difference = cmp - point; 4077 4078 LayoutUnit dist = difference.width() * difference.width() + difference.height() * difference.height(); 4079 if (dist < minDist) { 4080 closestRenderer = renderer; 4081 minDist = dist; 4082 } 4083 } 4084 4085 if (closestRenderer) 4086 return closestRenderer->positionForPoint(adjustedPoint - closestRenderer->locationOffset()); 4087 4088 return createVisiblePosition(firstPositionInOrBeforeNode(nonPseudoNode())); 4089} 4090 4091bool RenderBox::shrinkToAvoidFloats() const 4092{ 4093 // Floating objects don't shrink. Objects that don't avoid floats don't shrink. Marquees don't shrink. 4094 if ((isInline() && !isHTMLMarquee()) || !avoidsFloats() || isFloating()) 4095 return false; 4096 4097 // Only auto width objects can possibly shrink to avoid floats. 4098 return style()->width().isAuto(); 4099} 4100 4101bool RenderBox::avoidsFloats() const 4102{ 4103 return isReplaced() || hasOverflowClip() || isHR() || isLegend() || isWritingModeRoot() || isFlexItemIncludingDeprecated(); 4104} 4105 4106void RenderBox::addVisualEffectOverflow() 4107{ 4108 if (!style()->boxShadow() && !style()->hasBorderImageOutsets()) 4109 return; 4110 4111 bool isFlipped = style()->isFlippedBlocksWritingMode(); 4112 bool isHorizontal = isHorizontalWritingMode(); 4113 4114 LayoutRect borderBox = borderBoxRect(); 4115 LayoutUnit overflowMinX = borderBox.x(); 4116 LayoutUnit overflowMaxX = borderBox.maxX(); 4117 LayoutUnit overflowMinY = borderBox.y(); 4118 LayoutUnit overflowMaxY = borderBox.maxY(); 4119 4120 // Compute box-shadow overflow first. 4121 if (style()->boxShadow()) { 4122 LayoutUnit shadowLeft; 4123 LayoutUnit shadowRight; 4124 LayoutUnit shadowTop; 4125 LayoutUnit shadowBottom; 4126 style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft); 4127 4128 // In flipped blocks writing modes such as vertical-rl, the physical right shadow value is actually at the lower x-coordinate. 4129 overflowMinX = borderBox.x() + ((!isFlipped || isHorizontal) ? shadowLeft : -shadowRight); 4130 overflowMaxX = borderBox.maxX() + ((!isFlipped || isHorizontal) ? shadowRight : -shadowLeft); 4131 overflowMinY = borderBox.y() + ((!isFlipped || !isHorizontal) ? shadowTop : -shadowBottom); 4132 overflowMaxY = borderBox.maxY() + ((!isFlipped || !isHorizontal) ? shadowBottom : -shadowTop); 4133 } 4134 4135 // Now compute border-image-outset overflow. 4136 if (style()->hasBorderImageOutsets()) { 4137 LayoutBoxExtent borderOutsets = style()->borderImageOutsets(); 4138 4139 // In flipped blocks writing modes, the physical sides are inverted. For example in vertical-rl, the right 4140 // border is at the lower x coordinate value. 4141 overflowMinX = min(overflowMinX, borderBox.x() - ((!isFlipped || isHorizontal) ? borderOutsets.left() : borderOutsets.right())); 4142 overflowMaxX = max(overflowMaxX, borderBox.maxX() + ((!isFlipped || isHorizontal) ? borderOutsets.right() : borderOutsets.left())); 4143 overflowMinY = min(overflowMinY, borderBox.y() - ((!isFlipped || !isHorizontal) ? borderOutsets.top() : borderOutsets.bottom())); 4144 overflowMaxY = max(overflowMaxY, borderBox.maxY() + ((!isFlipped || !isHorizontal) ? borderOutsets.bottom() : borderOutsets.top())); 4145 } 4146 4147 // Add in the final overflow with shadows and outsets combined. 4148 addVisualOverflow(LayoutRect(overflowMinX, overflowMinY, overflowMaxX - overflowMinX, overflowMaxY - overflowMinY)); 4149} 4150 4151void RenderBox::addOverflowFromChild(RenderBox* child, const LayoutSize& delta) 4152{ 4153 // Never allow flow threads to propagate overflow up to a parent. 4154 if (child->isRenderFlowThread()) 4155 return; 4156 4157 // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then 4158 // its overflow is internal to it, and we don't care about it. layoutOverflowRectForPropagation takes care of this 4159 // and just propagates the border box rect instead. 4160 LayoutRect childLayoutOverflowRect = child->layoutOverflowRectForPropagation(style()); 4161 childLayoutOverflowRect.move(delta); 4162 addLayoutOverflow(childLayoutOverflowRect); 4163 4164 // Add in visual overflow from the child. Even if the child clips its overflow, it may still 4165 // have visual overflow of its own set from box shadows or reflections. It is unnecessary to propagate this 4166 // overflow if we are clipping our own overflow. 4167 if (child->hasSelfPaintingLayer() || hasOverflowClip()) 4168 return; 4169 LayoutRect childVisualOverflowRect = child->visualOverflowRectForPropagation(style()); 4170 childVisualOverflowRect.move(delta); 4171 addVisualOverflow(childVisualOverflowRect); 4172} 4173 4174void RenderBox::addLayoutOverflow(const LayoutRect& rect) 4175{ 4176 LayoutRect clientBox = clientBoxRect(); 4177 if (clientBox.contains(rect) || rect.isEmpty()) 4178 return; 4179 4180 // For overflow clip objects, we don't want to propagate overflow into unreachable areas. 4181 LayoutRect overflowRect(rect); 4182 if (hasOverflowClip() || isRenderView()) { 4183 // Overflow is in the block's coordinate space and thus is flipped for horizontal-bt and vertical-rl 4184 // writing modes. At this stage that is actually a simplification, since we can treat horizontal-tb/bt as the same 4185 // and vertical-lr/rl as the same. 4186 bool hasTopOverflow = !style()->isLeftToRightDirection() && !isHorizontalWritingMode(); 4187 bool hasLeftOverflow = !style()->isLeftToRightDirection() && isHorizontalWritingMode(); 4188 if (isFlexibleBox() && style()->isReverseFlexDirection()) { 4189 RenderFlexibleBox* flexibleBox = toRenderFlexibleBox(this); 4190 if (flexibleBox->isHorizontalFlow()) 4191 hasLeftOverflow = true; 4192 else 4193 hasTopOverflow = true; 4194 } 4195 4196 if (hasColumns() && style()->columnProgression() == ReverseColumnProgression) { 4197 if (isHorizontalWritingMode() ^ !style()->hasInlineColumnAxis()) 4198 hasLeftOverflow = !hasLeftOverflow; 4199 else 4200 hasTopOverflow = !hasTopOverflow; 4201 } 4202 4203 if (!hasTopOverflow) 4204 overflowRect.shiftYEdgeTo(max(overflowRect.y(), clientBox.y())); 4205 else 4206 overflowRect.shiftMaxYEdgeTo(min(overflowRect.maxY(), clientBox.maxY())); 4207 if (!hasLeftOverflow) 4208 overflowRect.shiftXEdgeTo(max(overflowRect.x(), clientBox.x())); 4209 else 4210 overflowRect.shiftMaxXEdgeTo(min(overflowRect.maxX(), clientBox.maxX())); 4211 4212 // Now re-test with the adjusted rectangle and see if it has become unreachable or fully 4213 // contained. 4214 if (clientBox.contains(overflowRect) || overflowRect.isEmpty()) 4215 return; 4216 } 4217 4218 if (!m_overflow) 4219 m_overflow = adoptPtr(new RenderOverflow(clientBox, borderBoxRect())); 4220 4221 m_overflow->addLayoutOverflow(overflowRect); 4222} 4223 4224void RenderBox::addVisualOverflow(const LayoutRect& rect) 4225{ 4226 LayoutRect borderBox = borderBoxRect(); 4227 if (borderBox.contains(rect) || rect.isEmpty()) 4228 return; 4229 4230 if (!m_overflow) 4231 m_overflow = adoptPtr(new RenderOverflow(clientBoxRect(), borderBox)); 4232 4233 m_overflow->addVisualOverflow(rect); 4234} 4235 4236inline static bool percentageLogicalHeightIsResolvable(const RenderBox* box) 4237{ 4238 return RenderBox::percentageLogicalHeightIsResolvableFromBlock(box->containingBlock(), box->isOutOfFlowPositioned()); 4239} 4240 4241bool RenderBox::percentageLogicalHeightIsResolvableFromBlock(const RenderBlock* containingBlock, bool isOutOfFlowPositioned) 4242{ 4243 // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing 4244 // block that may have a specified height and then use it. In strict mode, this violates the 4245 // specification, which states that percentage heights just revert to auto if the containing 4246 // block has an auto height. We still skip anonymous containing blocks in both modes, though, and look 4247 // only at explicit containers. 4248 const RenderBlock* cb = containingBlock; 4249 bool inQuirksMode = cb->document()->inQuirksMode(); 4250 while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isOutOfFlowPositioned() && cb->style()->logicalHeight().isAuto()) { 4251 if (!inQuirksMode && !cb->isAnonymousBlock()) 4252 break; 4253 cb = cb->containingBlock(); 4254 } 4255 4256 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height 4257 // explicitly specified that can be used for any percentage computations. 4258 // FIXME: We can't just check top/bottom here. 4259 // https://bugs.webkit.org/show_bug.cgi?id=46500 4260 bool isOutOfFlowPositionedWithSpecifiedHeight = cb->isOutOfFlowPositioned() && (!cb->style()->logicalHeight().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto())); 4261 4262 // Table cells violate what the CSS spec says to do with heights. Basically we 4263 // don't care if the cell specified a height or not. We just always make ourselves 4264 // be a percentage of the cell's current content height. 4265 if (cb->isTableCell()) 4266 return true; 4267 4268 // Otherwise we only use our percentage height if our containing block had a specified 4269 // height. 4270 if (cb->style()->logicalHeight().isFixed()) 4271 return true; 4272 if (cb->style()->logicalHeight().isPercent() && !isOutOfFlowPositionedWithSpecifiedHeight) 4273 return percentageLogicalHeightIsResolvableFromBlock(cb->containingBlock(), cb->isOutOfFlowPositioned()); 4274 if (cb->isRenderView() || inQuirksMode || isOutOfFlowPositionedWithSpecifiedHeight) 4275 return true; 4276 if (cb->isRoot() && isOutOfFlowPositioned) { 4277 // Match the positioned objects behavior, which is that positioned objects will fill their viewport 4278 // always. Note we could only hit this case by recurring into computePercentageLogicalHeight on a positioned containing block. 4279 return true; 4280 } 4281 4282 return false; 4283} 4284 4285bool RenderBox::hasUnsplittableScrollingOverflow() const 4286{ 4287 // We will paginate as long as we don't scroll overflow in the pagination direction. 4288 bool isHorizontal = isHorizontalWritingMode(); 4289 if ((isHorizontal && !scrollsOverflowY()) || (!isHorizontal && !scrollsOverflowX())) 4290 return false; 4291 4292 // We do have overflow. We'll still be willing to paginate as long as the block 4293 // has auto logical height, auto or undefined max-logical-height and a zero or auto min-logical-height. 4294 // Note this is just a heuristic, and it's still possible to have overflow under these 4295 // conditions, but it should work out to be good enough for common cases. Paginating overflow 4296 // with scrollbars present is not the end of the world and is what we used to do in the old model anyway. 4297 return !style()->logicalHeight().isIntrinsicOrAuto() 4298 || (!style()->logicalMaxHeight().isIntrinsicOrAuto() && !style()->logicalMaxHeight().isUndefined() && (!style()->logicalMaxHeight().isPercent() || percentageLogicalHeightIsResolvable(this))) 4299 || (!style()->logicalMinHeight().isIntrinsicOrAuto() && style()->logicalMinHeight().isPositive() && (!style()->logicalMinHeight().isPercent() || percentageLogicalHeightIsResolvable(this))); 4300} 4301 4302bool RenderBox::isUnsplittableForPagination() const 4303{ 4304 return isReplaced() || hasUnsplittableScrollingOverflow() || (parent() && isWritingModeRoot()); 4305} 4306 4307LayoutUnit RenderBox::lineHeight(bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const 4308{ 4309 if (isReplaced()) 4310 return direction == HorizontalLine ? m_marginBox.top() + height() + m_marginBox.bottom() : m_marginBox.right() + width() + m_marginBox.left(); 4311 return 0; 4312} 4313 4314int RenderBox::baselinePosition(FontBaseline baselineType, bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const 4315{ 4316 if (isReplaced()) { 4317 int result = direction == HorizontalLine ? m_marginBox.top() + height() + m_marginBox.bottom() : m_marginBox.right() + width() + m_marginBox.left(); 4318 if (baselineType == AlphabeticBaseline) 4319 return result; 4320 return result - result / 2; 4321 } 4322 return 0; 4323} 4324 4325 4326RenderLayer* RenderBox::enclosingFloatPaintingLayer() const 4327{ 4328 const RenderObject* curr = this; 4329 while (curr) { 4330 RenderLayer* layer = curr->hasLayer() && curr->isBox() ? toRenderBox(curr)->layer() : 0; 4331 if (layer && layer->isSelfPaintingLayer()) 4332 return layer; 4333 curr = curr->parent(); 4334 } 4335 return 0; 4336} 4337 4338LayoutRect RenderBox::logicalVisualOverflowRectForPropagation(RenderStyle* parentStyle) const 4339{ 4340 LayoutRect rect = visualOverflowRectForPropagation(parentStyle); 4341 if (!parentStyle->isHorizontalWritingMode()) 4342 return rect.transposedRect(); 4343 return rect; 4344} 4345 4346LayoutRect RenderBox::visualOverflowRectForPropagation(RenderStyle* parentStyle) const 4347{ 4348 // If the writing modes of the child and parent match, then we don't have to 4349 // do anything fancy. Just return the result. 4350 LayoutRect rect = visualOverflowRect(); 4351 if (parentStyle->writingMode() == style()->writingMode()) 4352 return rect; 4353 4354 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch 4355 // in a particular axis, then we have to flip the rect along that axis. 4356 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode) 4357 rect.setX(width() - rect.maxX()); 4358 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode) 4359 rect.setY(height() - rect.maxY()); 4360 4361 return rect; 4362} 4363 4364LayoutRect RenderBox::logicalLayoutOverflowRectForPropagation(RenderStyle* parentStyle) const 4365{ 4366 LayoutRect rect = layoutOverflowRectForPropagation(parentStyle); 4367 if (!parentStyle->isHorizontalWritingMode()) 4368 return rect.transposedRect(); 4369 return rect; 4370} 4371 4372LayoutRect RenderBox::layoutOverflowRectForPropagation(RenderStyle* parentStyle) const 4373{ 4374 // Only propagate interior layout overflow if we don't clip it. 4375 LayoutRect rect = borderBoxRect(); 4376 if (!hasOverflowClip()) 4377 rect.unite(layoutOverflowRect()); 4378 4379 bool hasTransform = hasLayer() && layer()->transform(); 4380 if (hasPaintOffset() || hasTransform) { 4381 // If we are relatively positioned or if we have a transform, then we have to convert 4382 // this rectangle into physical coordinates, apply relative positioning and transforms 4383 // to it, and then convert it back. 4384 flipForWritingMode(rect); 4385 4386 if (hasTransform) 4387 rect = layer()->currentTransform().mapRect(rect); 4388 4389 if (hasPaintOffset()) 4390 rect.move(paintOffset()); 4391 4392 // Now we need to flip back. 4393 flipForWritingMode(rect); 4394 } 4395 4396 // If the writing modes of the child and parent match, then we don't have to 4397 // do anything fancy. Just return the result. 4398 if (parentStyle->writingMode() == style()->writingMode()) 4399 return rect; 4400 4401 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch 4402 // in a particular axis, then we have to flip the rect along that axis. 4403 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode) 4404 rect.setX(width() - rect.maxX()); 4405 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode) 4406 rect.setY(height() - rect.maxY()); 4407 4408 return rect; 4409} 4410 4411LayoutRect RenderBox::overflowRectForPaintRejection() const 4412{ 4413 LayoutRect overflowRect = visualOverflowRect(); 4414 if (!m_overflow || !usesCompositedScrolling()) 4415 return overflowRect; 4416 4417 overflowRect.unite(layoutOverflowRect()); 4418 overflowRect.move(-scrolledContentOffset()); 4419 return overflowRect; 4420} 4421 4422LayoutUnit RenderBox::offsetLeft() const 4423{ 4424 return adjustedPositionRelativeToOffsetParent(topLeftLocation()).x(); 4425} 4426 4427LayoutUnit RenderBox::offsetTop() const 4428{ 4429 return adjustedPositionRelativeToOffsetParent(topLeftLocation()).y(); 4430} 4431 4432LayoutPoint RenderBox::flipForWritingModeForChild(const RenderBox* child, const LayoutPoint& point) const 4433{ 4434 if (!style()->isFlippedBlocksWritingMode()) 4435 return point; 4436 4437 // The child is going to add in its x() and y(), so we have to make sure it ends up in 4438 // the right place. 4439 if (isHorizontalWritingMode()) 4440 return LayoutPoint(point.x(), point.y() + height() - child->height() - (2 * child->y())); 4441 return LayoutPoint(point.x() + width() - child->width() - (2 * child->x()), point.y()); 4442} 4443 4444void RenderBox::flipForWritingMode(LayoutRect& rect) const 4445{ 4446 if (!style()->isFlippedBlocksWritingMode()) 4447 return; 4448 4449 if (isHorizontalWritingMode()) 4450 rect.setY(height() - rect.maxY()); 4451 else 4452 rect.setX(width() - rect.maxX()); 4453} 4454 4455LayoutUnit RenderBox::flipForWritingMode(LayoutUnit position) const 4456{ 4457 if (!style()->isFlippedBlocksWritingMode()) 4458 return position; 4459 return logicalHeight() - position; 4460} 4461 4462LayoutPoint RenderBox::flipForWritingMode(const LayoutPoint& position) const 4463{ 4464 if (!style()->isFlippedBlocksWritingMode()) 4465 return position; 4466 return isHorizontalWritingMode() ? LayoutPoint(position.x(), height() - position.y()) : LayoutPoint(width() - position.x(), position.y()); 4467} 4468 4469LayoutPoint RenderBox::flipForWritingModeIncludingColumns(const LayoutPoint& point) const 4470{ 4471 if (!hasColumns() || !style()->isFlippedBlocksWritingMode()) 4472 return flipForWritingMode(point); 4473 return toRenderBlock(this)->flipForWritingModeIncludingColumns(point); 4474} 4475 4476LayoutSize RenderBox::flipForWritingMode(const LayoutSize& offset) const 4477{ 4478 if (!style()->isFlippedBlocksWritingMode()) 4479 return offset; 4480 return isHorizontalWritingMode() ? LayoutSize(offset.width(), height() - offset.height()) : LayoutSize(width() - offset.width(), offset.height()); 4481} 4482 4483FloatPoint RenderBox::flipForWritingMode(const FloatPoint& position) const 4484{ 4485 if (!style()->isFlippedBlocksWritingMode()) 4486 return position; 4487 return isHorizontalWritingMode() ? FloatPoint(position.x(), height() - position.y()) : FloatPoint(width() - position.x(), position.y()); 4488} 4489 4490void RenderBox::flipForWritingMode(FloatRect& rect) const 4491{ 4492 if (!style()->isFlippedBlocksWritingMode()) 4493 return; 4494 4495 if (isHorizontalWritingMode()) 4496 rect.setY(height() - rect.maxY()); 4497 else 4498 rect.setX(width() - rect.maxX()); 4499} 4500 4501LayoutPoint RenderBox::topLeftLocation() const 4502{ 4503 RenderBlock* containerBlock = containingBlock(); 4504 if (!containerBlock || containerBlock == this) 4505 return location(); 4506 return containerBlock->flipForWritingModeForChild(this, location()); 4507} 4508 4509LayoutSize RenderBox::topLeftLocationOffset() const 4510{ 4511 RenderBlock* containerBlock = containingBlock(); 4512 if (!containerBlock || containerBlock == this) 4513 return locationOffset(); 4514 4515 LayoutRect rect(frameRect()); 4516 containerBlock->flipForWritingMode(rect); // FIXME: This is wrong if we are an absolutely positioned object enclosed by a relative-positioned inline. 4517 return LayoutSize(rect.x(), rect.y()); 4518} 4519 4520bool RenderBox::hasRelativeDimensions() const 4521{ 4522 return style()->height().isPercent() || style()->width().isPercent() 4523 || style()->maxHeight().isPercent() || style()->maxWidth().isPercent() 4524 || style()->minHeight().isPercent() || style()->minWidth().isPercent(); 4525} 4526 4527bool RenderBox::hasRelativeLogicalHeight() const 4528{ 4529 return style()->logicalHeight().isPercent() 4530 || style()->logicalMinHeight().isPercent() 4531 || style()->logicalMaxHeight().isPercent(); 4532} 4533 4534bool RenderBox::hasViewportPercentageLogicalHeight() const 4535{ 4536 return style()->logicalHeight().isViewportPercentage() 4537 || style()->logicalMinHeight().isViewportPercentage() 4538 || style()->logicalMaxHeight().isViewportPercentage(); 4539} 4540 4541static void markBoxForRelayoutAfterSplit(RenderBox* box) 4542{ 4543 // FIXME: The table code should handle that automatically. If not, 4544 // we should fix it and remove the table part checks. 4545 if (box->isTable()) { 4546 // Because we may have added some sections with already computed column structures, we need to 4547 // sync the table structure with them now. This avoids crashes when adding new cells to the table. 4548 toRenderTable(box)->forceSectionsRecalc(); 4549 } else if (box->isTableSection()) 4550 toRenderTableSection(box)->setNeedsCellRecalc(); 4551 4552 box->setNeedsLayoutAndPrefWidthsRecalc(); 4553} 4554 4555RenderObject* RenderBox::splitAnonymousBoxesAroundChild(RenderObject* beforeChild) 4556{ 4557 bool didSplitParentAnonymousBoxes = false; 4558 4559 while (beforeChild->parent() != this) { 4560 RenderBox* boxToSplit = toRenderBox(beforeChild->parent()); 4561 if (boxToSplit->firstChild() != beforeChild && boxToSplit->isAnonymous()) { 4562 didSplitParentAnonymousBoxes = true; 4563 4564 // We have to split the parent box into two boxes and move children 4565 // from |beforeChild| to end into the new post box. 4566 RenderBox* postBox = boxToSplit->createAnonymousBoxWithSameTypeAs(this); 4567 postBox->setChildrenInline(boxToSplit->childrenInline()); 4568 RenderBox* parentBox = toRenderBox(boxToSplit->parent()); 4569 // We need to invalidate the |parentBox| before inserting the new node 4570 // so that the table repainting logic knows the structure is dirty. 4571 // See for example RenderTableCell:clippedOverflowRectForRepaint. 4572 markBoxForRelayoutAfterSplit(parentBox); 4573 parentBox->virtualChildren()->insertChildNode(parentBox, postBox, boxToSplit->nextSibling()); 4574 boxToSplit->moveChildrenTo(postBox, beforeChild, 0, true); 4575 4576 markBoxForRelayoutAfterSplit(boxToSplit); 4577 markBoxForRelayoutAfterSplit(postBox); 4578 4579 beforeChild = postBox; 4580 } else 4581 beforeChild = boxToSplit; 4582 } 4583 4584 if (didSplitParentAnonymousBoxes) 4585 markBoxForRelayoutAfterSplit(this); 4586 4587 ASSERT(beforeChild->parent() == this); 4588 return beforeChild; 4589} 4590 4591LayoutUnit RenderBox::offsetFromLogicalTopOfFirstPage() const 4592{ 4593 LayoutState* layoutState = view()->layoutState(); 4594 if ((layoutState && !layoutState->isPaginated()) || (!layoutState && !flowThreadContainingBlock())) 4595 return 0; 4596 4597 RenderBlock* containerBlock = containingBlock(); 4598 return containerBlock->offsetFromLogicalTopOfFirstPage() + logicalTop(); 4599} 4600 4601} // namespace WebCore 4602