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