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