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