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 | |
79 | namespace WebCore { |
80 | |
81 | WTF_MAKE_ISO_ALLOCATED_IMPL(RenderBox); |
82 | |
83 | struct SameSizeAsRenderBox : public RenderBoxModelObject { |
84 | virtual ~SameSizeAsRenderBox() = default; |
85 | LayoutRect frameRect; |
86 | LayoutBoxExtent marginBox; |
87 | LayoutUnit preferredLogicalWidths[2]; |
88 | void* pointers[2]; |
89 | }; |
90 | |
91 | COMPILE_ASSERT(sizeof(RenderBox) == sizeof(SameSizeAsRenderBox), RenderBox_should_stay_small); |
92 | |
93 | using namespace HTMLNames; |
94 | |
95 | // Used by flexible boxes when flexing this element and by table cells. |
96 | typedef WTF::HashMap<const RenderBox*, LayoutUnit> OverrideSizeMap; |
97 | static OverrideSizeMap* gOverrideContentLogicalHeightMap = nullptr; |
98 | static 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. |
102 | typedef WTF::HashMap<const RenderBox*, Optional<LayoutUnit>> OverrideOptionalSizeMap; |
103 | static OverrideOptionalSizeMap* gOverrideContainingBlockContentLogicalHeightMap = nullptr; |
104 | static OverrideOptionalSizeMap* gOverrideContainingBlockContentLogicalWidthMap = nullptr; |
105 | |
106 | // Size of border belt for autoscroll. When mouse pointer in border belt, |
107 | // autoscroll is started. |
108 | static const int autoscrollBeltSize = 20; |
109 | static const unsigned backgroundObscurationTestMaxDepth = 4; |
110 | |
111 | using ControlStatesRendererMap = HashMap<const RenderObject*, std::unique_ptr<ControlStates>>; |
112 | static ControlStatesRendererMap& controlStatesRendererMap() |
113 | { |
114 | static NeverDestroyed<ControlStatesRendererMap> map; |
115 | return map; |
116 | } |
117 | |
118 | static ControlStates* controlStatesForRenderer(const RenderBox& renderer) |
119 | { |
120 | return controlStatesRendererMap().ensure(&renderer, [] { |
121 | return std::make_unique<ControlStates>(); |
122 | }).iterator->value.get(); |
123 | } |
124 | |
125 | static void removeControlStatesForRenderer(const RenderBox& renderer) |
126 | { |
127 | controlStatesRendererMap().remove(&renderer); |
128 | } |
129 | |
130 | bool RenderBox::s_hadOverflowClip = false; |
131 | |
132 | RenderBox::RenderBox(Element& element, RenderStyle&& style, BaseTypeFlags baseTypeFlags) |
133 | : RenderBoxModelObject(element, WTFMove(style), baseTypeFlags) |
134 | { |
135 | setIsBox(); |
136 | } |
137 | |
138 | RenderBox::RenderBox(Document& document, RenderStyle&& style, BaseTypeFlags baseTypeFlags) |
139 | : RenderBoxModelObject(document, WTFMove(style), baseTypeFlags) |
140 | { |
141 | setIsBox(); |
142 | } |
143 | |
144 | RenderBox::~RenderBox() |
145 | { |
146 | // Do not add any code here. Add it to willBeDestroyed() instead. |
147 | } |
148 | |
149 | void 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 | |
172 | RenderFragmentContainer* 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 | |
197 | bool RenderBox::hasFragmentRangeInFragmentedFlow() const |
198 | { |
199 | RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow(); |
200 | if (!fragmentedFlow || !fragmentedFlow->hasValidFragmentInfo()) |
201 | return false; |
202 | |
203 | return fragmentedFlow->hasCachedFragmentRangeForBox(*this); |
204 | } |
205 | |
206 | LayoutRect 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 | |
218 | LayoutRect RenderBox::borderBoxRectInFragment(RenderFragmentContainer*, RenderBoxFragmentInfoFlags) const |
219 | { |
220 | return borderBoxRect(); |
221 | } |
222 | |
223 | static 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 | |
236 | void 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 | |
254 | void 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 | |
295 | void 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& = 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 | |
410 | void 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 | |
434 | void 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 | |
458 | void 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 | |
503 | void 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. |
527 | LayoutUnit RenderBox::clientWidth() const |
528 | { |
529 | return paddingBoxWidth(); |
530 | } |
531 | |
532 | LayoutUnit RenderBox::clientHeight() const |
533 | { |
534 | return paddingBoxHeight(); |
535 | } |
536 | |
537 | int 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 | |
550 | int 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 | |
560 | int RenderBox::scrollLeft() const |
561 | { |
562 | return hasOverflowClip() && layer() ? layer()->scrollPosition().x() : 0; |
563 | } |
564 | |
565 | int RenderBox::scrollTop() const |
566 | { |
567 | return hasOverflowClip() && layer() ? layer()->scrollPosition().y() : 0; |
568 | } |
569 | |
570 | static 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 | |
578 | void 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 | |
586 | void 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 | |
594 | void RenderBox::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const |
595 | { |
596 | rects.append(snappedIntRect(accumulatedOffset, size())); |
597 | } |
598 | |
599 | void 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 | |
610 | void 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 | |
617 | LayoutUnit 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 | |
625 | LayoutUnit 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 | |
637 | LayoutUnit 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 | |
649 | RoundedRect::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 | |
661 | LayoutRect RenderBox::contentBoxRect() const |
662 | { |
663 | return { contentBoxLocation(), contentSize() }; |
664 | } |
665 | |
666 | LayoutPoint RenderBox::contentBoxLocation() const |
667 | { |
668 | LayoutUnit scrollbarSpace = shouldPlaceBlockDirectionScrollbarOnLeft() ? verticalScrollbarWidth() : 0; |
669 | return { borderLeft() + paddingLeft() + scrollbarSpace, borderTop() + paddingTop() }; |
670 | } |
671 | |
672 | IntRect 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 | |
681 | FloatQuad RenderBox::absoluteContentQuad() const |
682 | { |
683 | LayoutRect rect = contentBoxRect(); |
684 | return localToAbsoluteQuad(FloatRect(rect)); |
685 | } |
686 | |
687 | LayoutRect 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 | |
709 | void RenderBox::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*) |
710 | { |
711 | if (!size().isEmpty()) |
712 | rects.append(LayoutRect(additionalOffset, size())); |
713 | } |
714 | |
715 | int 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 | |
724 | LayoutRect 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 | |
748 | bool RenderBox::fixedElementLaysOutRelativeToFrame(const FrameView& frameView) const |
749 | { |
750 | return isFixedPositioned() && container()->isRenderView() && frameView.fixedElementsLayoutRelativeToFrame(); |
751 | } |
752 | |
753 | bool RenderBox::includeVerticalScrollbarSize() const |
754 | { |
755 | return hasOverflowClip() && layer() && !layer()->hasOverlayScrollbars() |
756 | && (style().overflowY() == Overflow::Scroll || style().overflowY() == Overflow::Auto); |
757 | } |
758 | |
759 | bool RenderBox::includeHorizontalScrollbarSize() const |
760 | { |
761 | return hasOverflowClip() && layer() && !layer()->hasOverlayScrollbars() |
762 | && (style().overflowX() == Overflow::Scroll || style().overflowX() == Overflow::Auto); |
763 | } |
764 | |
765 | int RenderBox::verticalScrollbarWidth() const |
766 | { |
767 | return includeVerticalScrollbarSize() ? layer()->verticalScrollbarWidth() : 0; |
768 | } |
769 | |
770 | int RenderBox::horizontalScrollbarHeight() const |
771 | { |
772 | return includeHorizontalScrollbarSize() ? layer()->horizontalScrollbarHeight() : 0; |
773 | } |
774 | |
775 | int 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 | |
793 | bool 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 | |
806 | bool 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 | |
822 | bool 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 | |
852 | bool RenderBox::canBeScrolledAndHasScrollableArea() const |
853 | { |
854 | return canBeProgramaticallyScrolled() && (hasHorizontalOverflow() || hasVerticalOverflow()); |
855 | } |
856 | |
857 | bool 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. |
863 | bool 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 | |
877 | bool RenderBox::usesCompositedScrolling() const |
878 | { |
879 | return hasOverflowClip() && hasLayer() && layer()->usesCompositedScrolling(); |
880 | } |
881 | |
882 | void 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. |
889 | bool 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. |
903 | IntSize 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 | |
924 | RenderBox* 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 | |
936 | void RenderBox::panScroll(const IntPoint& source) |
937 | { |
938 | if (layer()) |
939 | layer()->panScrollFromPoint(source); |
940 | } |
941 | |
942 | bool RenderBox::canUseOverlayScrollbars() const |
943 | { |
944 | return !style().hasPseudoStyle(PseudoId::Scrollbar) && ScrollbarTheme::theme().usesOverlayScrollbars(); |
945 | } |
946 | |
947 | bool RenderBox::hasVerticalScrollbarWithAutoBehavior() const |
948 | { |
949 | return hasOverflowClip() && (style().overflowY() == Overflow::Auto || (style().overflowY() == Overflow::Scroll && canUseOverlayScrollbars())); |
950 | } |
951 | |
952 | bool RenderBox::hasHorizontalScrollbarWithAutoBehavior() const |
953 | { |
954 | return hasOverflowClip() && (style().overflowX() == Overflow::Auto || (style().overflowX() == Overflow::Scroll && canUseOverlayScrollbars())); |
955 | } |
956 | |
957 | bool RenderBox::needsPreferredWidthsRecalculation() const |
958 | { |
959 | return style().paddingStart().isPercentOrCalculated() || style().paddingEnd().isPercentOrCalculated(); |
960 | } |
961 | |
962 | ScrollPosition RenderBox::scrollPosition() const |
963 | { |
964 | if (!hasOverflowClip()) |
965 | return { 0, 0 }; |
966 | |
967 | ASSERT(hasLayer()); |
968 | return layer()->scrollPosition(); |
969 | } |
970 | |
971 | LayoutSize RenderBox::cachedSizeForOverflowClip() const |
972 | { |
973 | ASSERT(hasOverflowClip()); |
974 | ASSERT(hasLayer()); |
975 | return layer()->size(); |
976 | } |
977 | |
978 | bool 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 | |
1006 | void RenderBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const |
1007 | { |
1008 | minLogicalWidth = minPreferredLogicalWidth() - borderAndPaddingLogicalWidth(); |
1009 | maxLogicalWidth = maxPreferredLogicalWidth() - borderAndPaddingLogicalWidth(); |
1010 | } |
1011 | |
1012 | LayoutUnit 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 | |
1024 | LayoutUnit 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 | |
1036 | bool RenderBox::hasOverrideContentLogicalHeight() const |
1037 | { |
1038 | return gOverrideContentLogicalHeightMap && gOverrideContentLogicalHeightMap->contains(this); |
1039 | } |
1040 | |
1041 | bool RenderBox::hasOverrideContentLogicalWidth() const |
1042 | { |
1043 | return gOverrideContentLogicalWidthMap && gOverrideContentLogicalWidthMap->contains(this); |
1044 | } |
1045 | |
1046 | void RenderBox::setOverrideContentLogicalHeight(LayoutUnit height) |
1047 | { |
1048 | if (!gOverrideContentLogicalHeightMap) |
1049 | gOverrideContentLogicalHeightMap = new OverrideSizeMap(); |
1050 | gOverrideContentLogicalHeightMap->set(this, height); |
1051 | } |
1052 | |
1053 | void RenderBox::setOverrideContentLogicalWidth(LayoutUnit width) |
1054 | { |
1055 | if (!gOverrideContentLogicalWidthMap) |
1056 | gOverrideContentLogicalWidthMap = new OverrideSizeMap(); |
1057 | gOverrideContentLogicalWidthMap->set(this, width); |
1058 | } |
1059 | |
1060 | void RenderBox::clearOverrideContentLogicalHeight() |
1061 | { |
1062 | if (gOverrideContentLogicalHeightMap) |
1063 | gOverrideContentLogicalHeightMap->remove(this); |
1064 | } |
1065 | |
1066 | void RenderBox::clearOverrideContentLogicalWidth() |
1067 | { |
1068 | if (gOverrideContentLogicalWidthMap) |
1069 | gOverrideContentLogicalWidthMap->remove(this); |
1070 | } |
1071 | |
1072 | void RenderBox::clearOverrideContentSize() |
1073 | { |
1074 | clearOverrideContentLogicalHeight(); |
1075 | clearOverrideContentLogicalWidth(); |
1076 | } |
1077 | |
1078 | LayoutUnit RenderBox::overrideContentLogicalWidth() const |
1079 | { |
1080 | ASSERT(hasOverrideContentLogicalWidth()); |
1081 | return gOverrideContentLogicalWidthMap->get(this); |
1082 | } |
1083 | |
1084 | LayoutUnit RenderBox::overrideContentLogicalHeight() const |
1085 | { |
1086 | ASSERT(hasOverrideContentLogicalHeight()); |
1087 | return gOverrideContentLogicalHeightMap->get(this); |
1088 | } |
1089 | |
1090 | Optional<LayoutUnit> RenderBox::overrideContainingBlockContentWidth() const |
1091 | { |
1092 | ASSERT(hasOverrideContainingBlockContentWidth()); |
1093 | return containingBlock()->style().isHorizontalWritingMode() |
1094 | ? gOverrideContainingBlockContentLogicalWidthMap->get(this) |
1095 | : gOverrideContainingBlockContentLogicalHeightMap->get(this); |
1096 | } |
1097 | |
1098 | Optional<LayoutUnit> RenderBox::overrideContainingBlockContentHeight() const |
1099 | { |
1100 | ASSERT(hasOverrideContainingBlockContentHeight()); |
1101 | return containingBlock()->style().isHorizontalWritingMode() |
1102 | ? gOverrideContainingBlockContentLogicalHeightMap->get(this) |
1103 | : gOverrideContainingBlockContentLogicalWidthMap->get(this); |
1104 | } |
1105 | |
1106 | bool 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 | |
1117 | bool 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 | |
1128 | Optional<LayoutUnit> RenderBox::overrideContainingBlockContentLogicalWidth() const |
1129 | { |
1130 | ASSERT(hasOverrideContainingBlockContentLogicalWidth()); |
1131 | return gOverrideContainingBlockContentLogicalWidthMap->get(this); |
1132 | } |
1133 | |
1134 | Optional<LayoutUnit> RenderBox::overrideContainingBlockContentLogicalHeight() const |
1135 | { |
1136 | ASSERT(hasOverrideContainingBlockContentLogicalHeight()); |
1137 | return gOverrideContainingBlockContentLogicalHeightMap->get(this); |
1138 | } |
1139 | |
1140 | bool RenderBox::hasOverrideContainingBlockContentLogicalWidth() const |
1141 | { |
1142 | return gOverrideContainingBlockContentLogicalWidthMap && gOverrideContainingBlockContentLogicalWidthMap->contains(this); |
1143 | } |
1144 | |
1145 | bool RenderBox::hasOverrideContainingBlockContentLogicalHeight() const |
1146 | { |
1147 | return gOverrideContainingBlockContentLogicalHeightMap && gOverrideContainingBlockContentLogicalHeightMap->contains(this); |
1148 | } |
1149 | |
1150 | void RenderBox::setOverrideContainingBlockContentLogicalWidth(Optional<LayoutUnit> logicalWidth) |
1151 | { |
1152 | if (!gOverrideContainingBlockContentLogicalWidthMap) |
1153 | gOverrideContainingBlockContentLogicalWidthMap = new OverrideOptionalSizeMap; |
1154 | gOverrideContainingBlockContentLogicalWidthMap->set(this, logicalWidth); |
1155 | } |
1156 | |
1157 | void RenderBox::setOverrideContainingBlockContentLogicalHeight(Optional<LayoutUnit> logicalHeight) |
1158 | { |
1159 | if (!gOverrideContainingBlockContentLogicalHeightMap) |
1160 | gOverrideContainingBlockContentLogicalHeightMap = new OverrideOptionalSizeMap; |
1161 | gOverrideContainingBlockContentLogicalHeightMap->set(this, logicalHeight); |
1162 | } |
1163 | |
1164 | void RenderBox::clearOverrideContainingBlockContentSize() |
1165 | { |
1166 | if (gOverrideContainingBlockContentLogicalWidthMap) |
1167 | gOverrideContainingBlockContentLogicalWidthMap->remove(this); |
1168 | clearOverrideContainingBlockContentLogicalHeight(); |
1169 | } |
1170 | |
1171 | void RenderBox::clearOverrideContainingBlockContentLogicalHeight() |
1172 | { |
1173 | if (gOverrideContainingBlockContentLogicalHeightMap) |
1174 | gOverrideContainingBlockContentLogicalHeightMap->remove(this); |
1175 | } |
1176 | |
1177 | LayoutUnit 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 | |
1185 | LayoutUnit 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 | |
1193 | LayoutUnit RenderBox::adjustContentBoxLogicalWidthForBoxSizing(LayoutUnit width) const |
1194 | { |
1195 | if (style().boxSizing() == BoxSizing::BorderBox) |
1196 | width -= borderAndPaddingLogicalWidth(); |
1197 | return std::max<LayoutUnit>(0, width); |
1198 | } |
1199 | |
1200 | LayoutUnit 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 |
1211 | bool 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 | |
1238 | void 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 | |
1259 | BackgroundBleedAvoidance 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 | |
1294 | void 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 | |
1358 | bool 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 | |
1372 | void 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 | |
1394 | bool 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 | |
1416 | bool 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 | |
1460 | static 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 | |
1485 | bool 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 | |
1514 | bool 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 | |
1534 | bool 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 | |
1557 | void 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 | |
1567 | void 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 | |
1576 | void 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 | |
1609 | LayoutRect 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 | |
1631 | void 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 | |
1673 | void 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 | |
1679 | static 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 | |
1688 | void 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 | |
1718 | bool 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 | |
1779 | bool 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 | |
1806 | void 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 | |
1819 | LayoutRect 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 | |
1837 | LayoutRect 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 | |
1866 | LayoutUnit 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 | |
1907 | LayoutUnit 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 | |
1919 | LayoutUnit 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 | |
1931 | LayoutUnit 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 | |
1947 | LayoutUnit 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 | |
1960 | LayoutUnit 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 | |
1987 | void 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 | |
2040 | const 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 | |
2075 | void 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 | |
2088 | LayoutSize 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 | |
2112 | std::unique_ptr<InlineElementBox> RenderBox::createInlineBox() |
2113 | { |
2114 | return std::make_unique<InlineElementBox>(*this); |
2115 | } |
2116 | |
2117 | void 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 | |
2129 | void 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 | |
2160 | void 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 | |
2171 | LayoutRect 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 | |
2182 | LayoutRect 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 | |
2201 | Optional<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 | |
2320 | void 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 | |
2335 | void RenderBox::repaintOverhangingFloats(bool) |
2336 | { |
2337 | } |
2338 | |
2339 | void 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 | |
2350 | void 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 | |
2445 | LayoutUnit RenderBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth) const |
2446 | { |
2447 | LayoutUnit marginStart; |
2448 | LayoutUnit marginEnd; |
2449 | return fillAvailableMeasure(availableLogicalWidth, marginStart, marginEnd); |
2450 | } |
2451 | |
2452 | LayoutUnit 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 | |
2459 | LayoutUnit 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 | |
2484 | LayoutUnit 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 | |
2511 | bool 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 | |
2522 | bool 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? |
2534 | bool 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 | |
2550 | bool 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 | |
2614 | void 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 | |
2672 | RenderBoxFragmentInfo* 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 | |
2687 | static 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 | |
2713 | void RenderBox::cacheIntrinsicContentLogicalHeightForFlexItem(LayoutUnit height) const |
2714 | { |
2715 | if (isFloatingOrOutOfFlowPositioned() || !parent() || !parent()->isFlexibleBox()) |
2716 | return; |
2717 | downcast<RenderFlexibleBox>(parent())->setCachedChildIntrinsicContentLogicalHeight(*this, height); |
2718 | } |
2719 | |
2720 | void 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 | |
2730 | RenderBox::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 | |
2837 | LayoutUnit 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 | |
2846 | Optional<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 | |
2853 | Optional<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 | |
2860 | Optional<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 | |
2877 | Optional<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 | |
2892 | bool 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 | |
2924 | bool RenderBox::shouldTreatChildAsReplacedInTableCells() const |
2925 | { |
2926 | if (isReplaced()) |
2927 | return true; |
2928 | return element() && (element()->isFormControlElement() || is<HTMLImageElement>(element())); |
2929 | } |
2930 | |
2931 | static 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 | |
2944 | Optional<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 | |
2998 | LayoutUnit RenderBox::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const |
2999 | { |
3000 | return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(MainOrPreferredSize, style().logicalWidth()), shouldComputePreferred); |
3001 | } |
3002 | |
3003 | LayoutUnit 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 | |
3014 | LayoutUnit 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 | |
3058 | LayoutUnit RenderBox::computeReplacedLogicalHeight(Optional<LayoutUnit>) const |
3059 | { |
3060 | return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(MainOrPreferredSize, style().logicalHeight())); |
3061 | } |
3062 | |
3063 | static 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 | |
3073 | bool 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 | |
3093 | LayoutUnit 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 | |
3104 | LayoutUnit 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 | |
3184 | LayoutUnit RenderBox::availableLogicalHeight(AvailableLogicalHeightType heightType) const |
3185 | { |
3186 | return constrainLogicalHeightByMinMax(availableLogicalHeightUsing(style().logicalHeight(), heightType), WTF::nullopt); |
3187 | } |
3188 | |
3189 | LayoutUnit 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 | |
3233 | void 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 | |
3251 | void 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 | |
3260 | LayoutUnit 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 | |
3325 | LayoutUnit 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 | |
3369 | static 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 | |
3445 | void 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 | |
3583 | static 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 | |
3594 | void 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 | |
3779 | static 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 | |
3798 | void 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 | |
3908 | static 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 | |
3930 | void 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 | |
4064 | void 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 | |
4233 | void 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 | |
4362 | LayoutRect RenderBox::localCaretRect(InlineBox* box, unsigned caretOffset, LayoutUnit* ) |
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 | |
4414 | VisiblePosition 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 | |
4499 | bool 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 | |
4509 | bool 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 | |
4516 | bool RenderBox::avoidsFloats() const |
4517 | { |
4518 | return isReplaced() || isHR() || isLegend() || isFieldset() || createsNewFormattingContext(); |
4519 | } |
4520 | |
4521 | void 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 | |
4533 | LayoutRect 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 | |
4581 | void 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 | |
4608 | void 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 | |
4644 | void 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 | |
4656 | void RenderBox::clearOverflow() |
4657 | { |
4658 | m_overflow = nullptr; |
4659 | RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow(); |
4660 | if (fragmentedFlow) |
4661 | fragmentedFlow->clearFragmentsOverflow(this); |
4662 | } |
4663 | |
4664 | bool 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 | |
4672 | bool 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 | |
4689 | bool RenderBox::() const |
4690 | { |
4691 | return isReplaced() |
4692 | || hasUnsplittableScrollingOverflow() |
4693 | || (parent() && isWritingModeRoot()) |
4694 | || (isFloating() && style().styleType() == PseudoId::FirstLetter && style().initialLetterDrop() > 0); |
4695 | } |
4696 | |
4697 | LayoutUnit 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 | |
4704 | int 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 | |
4716 | RenderLayer* 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 | |
4725 | const 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 | |
4736 | LayoutRect 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 | |
4744 | LayoutRect 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 | |
4762 | LayoutRect 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 | |
4770 | LayoutRect 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 | |
4809 | LayoutRect 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 | |
4831 | LayoutRect 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 | |
4843 | LayoutUnit RenderBox::offsetLeft() const |
4844 | { |
4845 | return adjustedPositionRelativeToOffsetParent(topLeftLocation()).x(); |
4846 | } |
4847 | |
4848 | LayoutUnit RenderBox::offsetTop() const |
4849 | { |
4850 | return adjustedPositionRelativeToOffsetParent(topLeftLocation()).y(); |
4851 | } |
4852 | |
4853 | LayoutPoint 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 | |
4865 | void 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 | |
4876 | LayoutUnit RenderBox::flipForWritingMode(LayoutUnit position) const |
4877 | { |
4878 | if (!style().isFlippedBlocksWritingMode()) |
4879 | return position; |
4880 | return logicalHeight() - position; |
4881 | } |
4882 | |
4883 | LayoutPoint 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 | |
4890 | LayoutSize 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 | |
4897 | FloatPoint 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 | |
4904 | void 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 | |
4915 | LayoutPoint 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 | |
4926 | LayoutSize 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 | |
4940 | void 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 | |
4953 | bool 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 | |
4960 | bool RenderBox::hasRelativeLogicalHeight() const |
4961 | { |
4962 | return style().logicalHeight().isPercentOrCalculated() |
4963 | || style().logicalMinHeight().isPercentOrCalculated() |
4964 | || style().logicalMaxHeight().isPercentOrCalculated(); |
4965 | } |
4966 | |
4967 | bool RenderBox::hasRelativeLogicalWidth() const |
4968 | { |
4969 | return style().logicalWidth().isPercentOrCalculated() |
4970 | || style().logicalMinWidth().isPercentOrCalculated() |
4971 | || style().logicalMaxWidth().isPercentOrCalculated(); |
4972 | } |
4973 | |
4974 | LayoutUnit 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 | |
4984 | const 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 | |