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