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