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, 2006, 2007, 2008, 2009, 2010 Apple Inc.
7 * All rights reserved.
8 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 *
25 */
26
27 #include "third_party/blink/renderer/core/layout/layout_box.h"
28
29 #include <math.h>
30 #include <algorithm>
31 #include <utility>
32
33 #include "cc/input/scroll_snap_data.h"
34 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h"
35 #include "third_party/blink/public/platform/web_rect.h"
36 #include "third_party/blink/public/strings/grit/blink_strings.h"
37 #include "third_party/blink/renderer/core/dom/document.h"
38 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
39 #include "third_party/blink/renderer/core/editing/editing_utilities.h"
40 #include "third_party/blink/renderer/core/editing/ime/input_method_controller.h"
41 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
42 #include "third_party/blink/renderer/core/frame/local_frame.h"
43 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
44 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
45 #include "third_party/blink/renderer/core/frame/settings.h"
46 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
47 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
48 #include "third_party/blink/renderer/core/html/forms/html_opt_group_element.h"
49 #include "third_party/blink/renderer/core/html/forms/html_select_element.h"
50 #include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
51 #include "third_party/blink/renderer/core/html/html_div_element.h"
52 #include "third_party/blink/renderer/core/html/html_element.h"
53 #include "third_party/blink/renderer/core/html/html_frame_element_base.h"
54 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
55 #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
56 #include "third_party/blink/renderer/core/html/shadow/shadow_element_utils.h"
57 #include "third_party/blink/renderer/core/input/event_handler.h"
58 #include "third_party/blink/renderer/core/input_type_names.h"
59 #include "third_party/blink/renderer/core/layout/api/line_layout_block_flow.h"
60 #include "third_party/blink/renderer/core/layout/api/line_layout_box.h"
61 #include "third_party/blink/renderer/core/layout/box_layout_extra_input.h"
62 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
63 #include "third_party/blink/renderer/core/layout/layout_analyzer.h"
64 #include "third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.h"
65 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
66 #include "third_party/blink/renderer/core/layout/layout_fieldset.h"
67 #include "third_party/blink/renderer/core/layout/layout_file_upload_control.h"
68 #include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
69 #include "third_party/blink/renderer/core/layout/layout_grid.h"
70 #include "third_party/blink/renderer/core/layout/layout_inline.h"
71 #include "third_party/blink/renderer/core/layout/layout_list_marker.h"
72 #include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h"
73 #include "third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h"
74 #include "third_party/blink/renderer/core/layout/layout_table_cell.h"
75 #include "third_party/blink/renderer/core/layout/layout_text_control.h"
76 #include "third_party/blink/renderer/core/layout/layout_view.h"
77 #include "third_party/blink/renderer/core/layout/ng/custom/custom_layout_child.h"
78 #include "third_party/blink/renderer/core/layout/ng/custom/layout_ng_custom.h"
79 #include "third_party/blink/renderer/core/layout/ng/custom/layout_worklet.h"
80 #include "third_party/blink/renderer/core/layout/ng/custom/layout_worklet_global_scope_proxy.h"
81 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h"
82 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
83 #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
84 #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h"
85 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
86 #include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h"
87 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
88 #include "third_party/blink/renderer/core/layout/ng/ng_layout_utils.h"
89 #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
90 #include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h"
91 #include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h"
92 #include "third_party/blink/renderer/core/page/autoscroll_controller.h"
93 #include "third_party/blink/renderer/core/page/page.h"
94 #include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h"
95 #include "third_party/blink/renderer/core/paint/background_image_geometry.h"
96 #include "third_party/blink/renderer/core/paint/box_paint_invalidator.h"
97 #include "third_party/blink/renderer/core/paint/box_painter.h"
98 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
99 #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
100 #include "third_party/blink/renderer/core/paint/paint_layer.h"
101 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
102 #include "third_party/blink/renderer/core/paint/rounded_border_geometry.h"
103 #include "third_party/blink/renderer/core/style/shadow_list.h"
104 #include "third_party/blink/renderer/platform/geometry/double_rect.h"
105 #include "third_party/blink/renderer/platform/geometry/float_quad.h"
106 #include "third_party/blink/renderer/platform/geometry/float_rounded_rect.h"
107 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
108 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
109 #include "third_party/blink/renderer/platform/text/platform_locale.h"
110 #include "third_party/blink/renderer/platform/wtf/size_assertions.h"
111
112 namespace blink {
113
114 // Used by flexible boxes when flexing this element and by table cells.
115 typedef WTF::HashMap<const LayoutBox*, LayoutUnit> OverrideSizeMap;
116
117 // Size of border belt for autoscroll. When mouse pointer in border belt,
118 // autoscroll is started.
119 static const int kAutoscrollBeltSize = 20;
120 static const unsigned kBackgroundObscurationTestMaxDepth = 4;
121
122 struct SameSizeAsLayoutBox : public LayoutBoxModelObject {
123 LayoutRect frame_rect;
124 LayoutSize previous_size;
125 LayoutUnit intrinsic_content_logical_height;
126 LayoutRectOutsets margin_box_outsets;
127 MinMaxSizes intrinsic_logical_widths;
128 LayoutUnit intrinsic_logical_widths_percentage_resolution_block_size;
129 void* pointers[4];
130 Vector<scoped_refptr<const NGLayoutResult>, 1> layout_results;
131 Persistent<void*> rare_data;
132 };
133
134 ASSERT_SIZE(LayoutBox, SameSizeAsLayoutBox);
135
136 namespace {
137
TextAreaIntrinsicInlineSize(const HTMLTextAreaElement & textarea,const LayoutBox & box)138 LayoutUnit TextAreaIntrinsicInlineSize(const HTMLTextAreaElement& textarea,
139 const LayoutBox& box) {
140 // <textarea>'s intrinsic inline-size always contains the scrollbar thickness
141 // regardless of actual existence of a scrollbar.
142 //
143 // See |NGBlockLayoutAlgorithm::ComputeMinMaxSizes()| and |LayoutBlock::
144 // ComputeIntrinsicLogicalWidths()|.
145 return LayoutUnit(ceilf(LayoutTextControl::GetAvgCharWidth(box.StyleRef()) *
146 textarea.cols())) +
147 LayoutTextControl::ScrollbarThickness(box);
148 }
149
TextFieldIntrinsicInlineSize(const HTMLInputElement & input,const LayoutBox & box)150 LayoutUnit TextFieldIntrinsicInlineSize(const HTMLInputElement& input,
151 const LayoutBox& box) {
152 int factor;
153 const bool includes_decoration = input.SizeShouldIncludeDecoration(factor);
154 if (factor <= 0)
155 factor = 20;
156
157 const float char_width = LayoutTextControl::GetAvgCharWidth(box.StyleRef());
158 LayoutUnit result = LayoutUnit::FromFloatCeil(char_width * factor);
159
160 float max_char_width = 0.f;
161 const Font& font = box.StyleRef().GetFont();
162 if (LayoutTextControl::HasValidAvgCharWidth(font))
163 max_char_width = roundf(font.PrimaryFont()->MaxCharWidth());
164
165 // For text inputs, IE adds some extra width.
166 if (max_char_width > 0.f)
167 result += max_char_width - char_width;
168
169 if (includes_decoration) {
170 const auto* spin_button =
171 To<HTMLElement>(input.UserAgentShadowRoot()->getElementById(
172 shadow_element_names::kIdSpinButton));
173 if (LayoutBox* spin_box =
174 spin_button ? spin_button->GetLayoutBox() : nullptr) {
175 result += spin_box->BorderAndPaddingLogicalWidth();
176 // Since the width of spin_box is not calculated yet,
177 // spin_box->LogicalWidth() returns 0. Use the computed logical
178 // width instead.
179 result += spin_box->StyleRef().LogicalWidth().Value();
180 }
181 }
182
183 return result;
184 }
185
TextAreaIntrinsicBlockSize(const HTMLTextAreaElement & textarea,const LayoutBox & box)186 LayoutUnit TextAreaIntrinsicBlockSize(const HTMLTextAreaElement& textarea,
187 const LayoutBox& box) {
188 const auto* inner_editor = textarea.InnerEditorElement();
189 if (!inner_editor || !inner_editor->GetLayoutBox()) {
190 const LayoutUnit line_height = box.LineHeight(
191 true,
192 box.StyleRef().IsHorizontalWritingMode() ? kHorizontalLine
193 : kVerticalLine,
194 kPositionOfInteriorLineBoxes);
195
196 return line_height * textarea.rows();
197 }
198 const LayoutBox& inner_box = *inner_editor->GetLayoutBox();
199 const ComputedStyle& inner_style = inner_box.StyleRef();
200 // We are able to have a horizontal scrollbar if the overflow style is
201 // scroll, or if its auto and there's no word wrap.
202 int scrollbar_thickness = 0;
203 if (box.StyleRef().OverflowInlineDirection() == EOverflow::kScroll ||
204 (box.StyleRef().OverflowInlineDirection() == EOverflow::kAuto &&
205 inner_style.OverflowWrap() == EOverflowWrap::kNormal))
206 scrollbar_thickness = LayoutTextControl::ScrollbarThickness(box);
207 return inner_box.LineHeight(true,
208 inner_style.IsHorizontalWritingMode()
209 ? kHorizontalLine
210 : kVerticalLine,
211 kPositionOfInteriorLineBoxes) *
212 textarea.rows() +
213 scrollbar_thickness;
214 }
215
TextFieldIntrinsicBlockSize(const HTMLInputElement & input,const LayoutBox & box)216 LayoutUnit TextFieldIntrinsicBlockSize(const HTMLInputElement& input,
217 const LayoutBox& box) {
218 const auto* inner_editor = input.InnerEditorElement();
219 // inner_editor's LayoutBox can be nullptr because web authors can set
220 // display:none to ::-webkit-textfield-decoration-container element.
221 const LayoutBox& target_box = (inner_editor && inner_editor->GetLayoutBox())
222 ? *inner_editor->GetLayoutBox()
223 : box;
224 return target_box.LineHeight(true,
225 target_box.StyleRef().IsHorizontalWritingMode()
226 ? kHorizontalLine
227 : kVerticalLine,
228 kPositionOfInteriorLineBoxes);
229 }
230
FileUploadControlIntrinsicInlineSize(const HTMLInputElement & input,const LayoutBox & box)231 LayoutUnit FileUploadControlIntrinsicInlineSize(const HTMLInputElement& input,
232 const LayoutBox& box) {
233 // Figure out how big the filename space needs to be for a given number of
234 // characters (using "0" as the nominal character).
235 constexpr int kDefaultWidthNumChars = 34;
236 constexpr UChar kCharacter = '0';
237 const String character_as_string = String(&kCharacter, 1);
238 const Font& font = box.StyleRef().GetFont();
239 const float min_default_label_width =
240 kDefaultWidthNumChars *
241 font.Width(ConstructTextRun(font, character_as_string, box.StyleRef(),
242 TextRun::kAllowTrailingExpansion));
243
244 const String label =
245 input.GetLocale().QueryString(IDS_FORM_FILE_NO_FILE_LABEL);
246 float default_label_width = font.Width(ConstructTextRun(
247 font, label, box.StyleRef(), TextRun::kAllowTrailingExpansion));
248 if (HTMLInputElement* button = input.UploadButton()) {
249 if (LayoutObject* button_layout_object = button->GetLayoutObject()) {
250 default_label_width +=
251 button_layout_object->PreferredLogicalWidths().max_size +
252 LayoutFileUploadControl::kAfterButtonSpacing;
253 }
254 }
255 return LayoutUnit(
256 ceilf(std::max(min_default_label_width, default_label_width)));
257 }
258
SliderIntrinsicInlineSize(const LayoutBox & box)259 LayoutUnit SliderIntrinsicInlineSize(const LayoutBox& box) {
260 constexpr int kDefaultTrackLength = 129;
261 return LayoutUnit(kDefaultTrackLength * box.StyleRef().EffectiveZoom());
262 }
263
ListBoxDefaultItemHeight(const LayoutBox & box)264 LayoutUnit ListBoxDefaultItemHeight(const LayoutBox& box) {
265 constexpr int kDefaultPaddingBottom = 1;
266
267 const SimpleFontData* font_data = box.StyleRef().GetFont().PrimaryFont();
268 if (!font_data)
269 return LayoutUnit();
270 return LayoutUnit(font_data->GetFontMetrics().Height() +
271 kDefaultPaddingBottom);
272 }
273
274 // TODO(crbug.com/1040826): This function is written in LayoutObject API
275 // so that this works in both of the legacy layout and LayoutNG. We
276 // should have LayoutNG-specific code.
ListBoxItemHeight(const HTMLSelectElement & select,const LayoutBox & box)277 LayoutUnit ListBoxItemHeight(const HTMLSelectElement& select,
278 const LayoutBox& box) {
279 const auto& items = select.GetListItems();
280 if (items.IsEmpty() || box.ShouldApplySizeContainment())
281 return ListBoxDefaultItemHeight(box);
282
283 LayoutUnit max_height;
284 for (Element* element : items) {
285 if (auto* optgroup = DynamicTo<HTMLOptGroupElement>(element))
286 element = &optgroup->OptGroupLabelElement();
287 LayoutUnit item_height;
288 if (auto* layout_box = element->GetLayoutBox())
289 item_height = layout_box->Size().Height();
290 else
291 item_height = ListBoxDefaultItemHeight(box);
292 max_height = std::max(max_height, item_height);
293 }
294 return max_height;
295 }
296
MenuListIntrinsicInlineSize(const HTMLSelectElement & select,const LayoutBox & box)297 LayoutUnit MenuListIntrinsicInlineSize(const HTMLSelectElement& select,
298 const LayoutBox& box) {
299 const ComputedStyle& style = box.StyleRef();
300 float max_option_width = 0;
301 if (!box.ShouldApplySizeContainment()) {
302 for (auto* const option : select.GetOptionList()) {
303 String text = option->TextIndentedToRespectGroupLabel();
304 const ComputedStyle* item_style =
305 option->GetComputedStyle() ? option->GetComputedStyle() : &style;
306 item_style->ApplyTextTransform(&text);
307 // We apply SELECT's style, not OPTION's style because max_option_width is
308 // used to determine intrinsic width of the menulist box.
309 TextRun text_run = ConstructTextRun(style.GetFont(), text, style);
310 max_option_width =
311 std::max(max_option_width, style.GetFont().Width(text_run));
312 }
313 }
314
315 LayoutTheme& theme = LayoutTheme::GetTheme();
316 int paddings = theme.PopupInternalPaddingStart(style) +
317 theme.PopupInternalPaddingEnd(box.GetFrame(), style);
318 return LayoutUnit(ceilf(max_option_width)) + LayoutUnit(paddings);
319 }
320
MenuListIntrinsicBlockSize(const HTMLSelectElement & select,const LayoutBox & box)321 LayoutUnit MenuListIntrinsicBlockSize(const HTMLSelectElement& select,
322 const LayoutBox& box) {
323 if (!box.StyleRef().HasEffectiveAppearance())
324 return kIndefiniteSize;
325 const SimpleFontData* font_data = box.StyleRef().GetFont().PrimaryFont();
326 DCHECK(font_data);
327 const LayoutBox* inner_box = select.InnerElement().GetLayoutBox();
328 return (font_data ? font_data->GetFontMetrics().Height() : 0) +
329 (inner_box ? inner_box->BorderAndPaddingLogicalHeight()
330 : LayoutUnit());
331 }
332
333 #if DCHECK_IS_ON()
CheckDidAddFragment(const LayoutBox & box,const NGPhysicalBoxFragment & new_fragment)334 void CheckDidAddFragment(const LayoutBox& box,
335 const NGPhysicalBoxFragment& new_fragment) {
336 // If |HasFragmentItems|, |ChildrenInline()| should be true.
337 // |HasFragmentItems| uses this condition to optimize .
338 if (new_fragment.HasItems())
339 DCHECK(box.ChildrenInline());
340
341 for (const NGPhysicalBoxFragment& fragment : box.PhysicalFragments()) {
342 if (const NGFragmentItems* fragment_items = fragment.Items())
343 fragment_items->CheckAllItemsAreValid();
344 }
345 }
346 #else
CheckDidAddFragment(const LayoutBox & box,const NGPhysicalBoxFragment & fragment)347 inline void CheckDidAddFragment(const LayoutBox& box,
348 const NGPhysicalBoxFragment& fragment) {}
349 #endif
350
351 // Applies the overflow clip to |result|. For any axis that is clipped, |result|
352 // is reset to |no_overflow_rect|. If neither axis is clipped, nothing is
353 // changed.
ApplyOverflowClip(OverflowClipAxes overflow_clip_axes,const LayoutRect & no_overflow_rect,LayoutRect & result)354 void ApplyOverflowClip(OverflowClipAxes overflow_clip_axes,
355 const LayoutRect& no_overflow_rect,
356 LayoutRect& result) {
357 if (overflow_clip_axes & kOverflowClipX) {
358 result.SetX(no_overflow_rect.X());
359 result.SetWidth(no_overflow_rect.Width());
360 }
361 if (overflow_clip_axes & kOverflowClipY) {
362 result.SetY(no_overflow_rect.Y());
363 result.SetHeight(no_overflow_rect.Height());
364 }
365 }
366
367 } // namespace
368
BoxLayoutExtraInput(LayoutBox & box)369 BoxLayoutExtraInput::BoxLayoutExtraInput(LayoutBox& box) : box(box) {
370 box.SetBoxLayoutExtraInput(this);
371 }
372
~BoxLayoutExtraInput()373 BoxLayoutExtraInput::~BoxLayoutExtraInput() {
374 box.SetBoxLayoutExtraInput(nullptr);
375 }
376
LayoutBoxRareData()377 LayoutBoxRareData::LayoutBoxRareData()
378 : spanner_placeholder_(nullptr),
379 override_logical_width_(-1),
380 override_logical_height_(-1),
381 // TODO(rego): We should store these based on physical direction.
382 has_override_containing_block_content_logical_width_(false),
383 has_override_containing_block_content_logical_height_(false),
384 has_override_percentage_resolution_block_size_(false),
385 has_previous_content_box_rect_(false),
386 percent_height_container_(nullptr),
387 snap_container_(nullptr),
388 snap_areas_(nullptr) {}
389
Trace(Visitor * visitor) const390 void LayoutBoxRareData::Trace(Visitor* visitor) const {
391 visitor->Trace(layout_child_);
392 }
393
LayoutBox(ContainerNode * node)394 LayoutBox::LayoutBox(ContainerNode* node)
395 : LayoutBoxModelObject(node),
396 intrinsic_content_logical_height_(-1),
397 intrinsic_logical_widths_percentage_resolution_block_size_(
398 LayoutUnit::Min()),
399 inline_box_wrapper_(nullptr) {
400 SetIsBox();
401 if (blink::IsA<HTMLLegendElement>(node))
402 SetIsHTMLLegendElement();
403 }
404
405 LayoutBox::~LayoutBox() = default;
406
LayerTypeRequired() const407 PaintLayerType LayoutBox::LayerTypeRequired() const {
408 NOT_DESTROYED();
409 if (IsStacked() || HasHiddenBackface() ||
410 (StyleRef().SpecifiesColumns() && !CanTraversePhysicalFragments()) ||
411 IsEffectiveRootScroller())
412 return kNormalPaintLayer;
413
414 if (HasNonVisibleOverflow())
415 return kOverflowClipPaintLayer;
416
417 if (StyleRef().IsScrollbarGutterForce())
418 return kNormalPaintLayer;
419
420 return kNoPaintLayer;
421 }
422
WillBeDestroyed()423 void LayoutBox::WillBeDestroyed() {
424 NOT_DESTROYED();
425 ClearOverrideSize();
426 ClearOverrideContainingBlockContentSize();
427 ClearOverridePercentageResolutionBlockSize();
428
429 if (IsOutOfFlowPositioned())
430 LayoutBlock::RemovePositionedObject(this);
431 RemoveFromPercentHeightContainer();
432 if (IsOrthogonalWritingModeRoot() && !DocumentBeingDestroyed())
433 UnmarkOrthogonalWritingModeRoot();
434
435 ShapeOutsideInfo::RemoveInfo(*this);
436
437 if (!DocumentBeingDestroyed()) {
438 if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
439 if (NGPaintFragment* first_inline_fragment = FirstInlineFragment())
440 first_inline_fragment->LayoutObjectWillBeDestroyed();
441 } else if (FirstInlineFragmentItemIndex()) {
442 NGFragmentItems::LayoutObjectWillBeDestroyed(*this);
443 ClearFirstInlineFragmentItemIndex();
444 }
445 if (measure_result_)
446 measure_result_->PhysicalFragment().LayoutObjectWillBeDestroyed();
447 for (auto result : layout_results_)
448 result->PhysicalFragment().LayoutObjectWillBeDestroyed();
449 GetDocument()
450 .GetFrame()
451 ->GetInputMethodController()
452 .LayoutObjectWillBeDestroyed(*this);
453 }
454
455 SetSnapContainer(nullptr);
456 LayoutBoxModelObject::WillBeDestroyed();
457 }
458
InsertedIntoTree()459 void LayoutBox::InsertedIntoTree() {
460 NOT_DESTROYED();
461 LayoutBoxModelObject::InsertedIntoTree();
462 AddScrollSnapMapping();
463 AddCustomLayoutChildIfNeeded();
464
465 if (IsOrthogonalWritingModeRoot())
466 MarkOrthogonalWritingModeRoot();
467 }
468
WillBeRemovedFromTree()469 void LayoutBox::WillBeRemovedFromTree() {
470 NOT_DESTROYED();
471 if (!DocumentBeingDestroyed() && IsOrthogonalWritingModeRoot())
472 UnmarkOrthogonalWritingModeRoot();
473
474 ClearCustomLayoutChild();
475 ClearScrollSnapMapping();
476 LayoutBoxModelObject::WillBeRemovedFromTree();
477 }
478
RemoveFloatingOrPositionedChildFromBlockLists()479 void LayoutBox::RemoveFloatingOrPositionedChildFromBlockLists() {
480 NOT_DESTROYED();
481 DCHECK(IsFloatingOrOutOfFlowPositioned());
482
483 if (DocumentBeingDestroyed())
484 return;
485
486 if (IsFloating()) {
487 LayoutBlockFlow* parent_block_flow = nullptr;
488 for (LayoutObject* curr = Parent(); curr; curr = curr->Parent()) {
489 auto* curr_block_flow = DynamicTo<LayoutBlockFlow>(curr);
490 if (curr_block_flow) {
491 if (!parent_block_flow || curr_block_flow->ContainsFloat(this))
492 parent_block_flow = curr_block_flow;
493 }
494 }
495
496 if (parent_block_flow) {
497 parent_block_flow->MarkSiblingsWithFloatsForLayout(this);
498 parent_block_flow->MarkAllDescendantsWithFloatsForLayout(this, false);
499 }
500 }
501
502 if (IsOutOfFlowPositioned())
503 LayoutBlock::RemovePositionedObject(this);
504 }
505
StyleWillChange(StyleDifference diff,const ComputedStyle & new_style)506 void LayoutBox::StyleWillChange(StyleDifference diff,
507 const ComputedStyle& new_style) {
508 NOT_DESTROYED();
509 const ComputedStyle* old_style = Style();
510 if (old_style) {
511 LayoutFlowThread* flow_thread = FlowThreadContainingBlock();
512 if (flow_thread && flow_thread != this)
513 flow_thread->FlowThreadDescendantStyleWillChange(this, diff, new_style);
514
515 // The background of the root element or the body element could propagate up
516 // to the canvas. Just dirty the entire canvas when our style changes
517 // substantially.
518 if ((diff.NeedsPaintInvalidation() || diff.NeedsLayout()) && GetNode() &&
519 (IsDocumentElement() || IsA<HTMLBodyElement>(*GetNode()))) {
520 View()->SetShouldDoFullPaintInvalidation();
521 }
522
523 // When a layout hint happens and an object's position style changes, we
524 // have to do a layout to dirty the layout tree using the old position
525 // value now.
526 if (diff.NeedsFullLayout() && Parent() &&
527 old_style->GetPosition() != new_style.GetPosition()) {
528 if (!old_style->HasOutOfFlowPosition() &&
529 new_style.HasOutOfFlowPosition()) {
530 // We're about to go out of flow. Before that takes place, we need to
531 // mark the current containing block chain for preferred widths
532 // recalculation.
533 SetNeedsLayoutAndIntrinsicWidthsRecalc(
534 layout_invalidation_reason::kStyleChange);
535
536 if (IsInLayoutNGInlineFormattingContext() &&
537 RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled() &&
538 FirstInlineFragmentItemIndex()) {
539 // Out of flow are not part of |NGFragmentItems|, and that further
540 // changes including destruction cannot be tracked. Mark it is moved
541 // out from this IFC.
542 NGFragmentItems::LayoutObjectWillBeMoved(*this);
543 ClearFirstInlineFragmentItemIndex();
544 }
545 } else {
546 MarkContainerChainForLayout();
547 }
548 if (old_style->GetPosition() == EPosition::kStatic)
549 SetShouldDoFullPaintInvalidation();
550 else if (new_style.HasOutOfFlowPosition())
551 Parent()->SetChildNeedsLayout();
552 if (IsFloating() && !IsOutOfFlowPositioned() &&
553 new_style.HasOutOfFlowPosition())
554 RemoveFloatingOrPositionedChildFromBlockLists();
555 }
556 // FIXME: This branch runs when !oldStyle, which means that layout was never
557 // called so what's the point in invalidating the whole view that we never
558 // painted?
559 } else if (IsBody()) {
560 View()->SetShouldDoFullPaintInvalidation();
561 }
562
563 LayoutBoxModelObject::StyleWillChange(diff, new_style);
564 }
565
StyleDidChange(StyleDifference diff,const ComputedStyle * old_style)566 void LayoutBox::StyleDidChange(StyleDifference diff,
567 const ComputedStyle* old_style) {
568 NOT_DESTROYED();
569 // Horizontal writing mode definition is updated in LayoutBoxModelObject::
570 // updateFromStyle, (as part of the LayoutBoxModelObject::styleDidChange call
571 // below). So, we can safely cache the horizontal writing mode value before
572 // style change here.
573 bool old_horizontal_writing_mode = IsHorizontalWritingMode();
574
575 LayoutBoxModelObject::StyleDidChange(diff, old_style);
576
577 // Reflection works through PaintLayer. Some child classes e.g. LayoutSVGBlock
578 // don't create layers and ignore reflections.
579 if (HasReflection() && !HasLayer())
580 SetHasReflection(false);
581
582 auto* parent_flow_block = DynamicTo<LayoutBlockFlow>(Parent());
583 if (IsFloatingOrOutOfFlowPositioned() && old_style &&
584 !old_style->IsFloating() && !old_style->HasOutOfFlowPosition() &&
585 parent_flow_block)
586 parent_flow_block->ChildBecameFloatingOrOutOfFlow(this);
587
588 const ComputedStyle& new_style = StyleRef();
589 if (NeedsLayout() && old_style)
590 RemoveFromPercentHeightContainer();
591
592 if (old_horizontal_writing_mode != IsHorizontalWritingMode()) {
593 if (old_style) {
594 if (IsOrthogonalWritingModeRoot())
595 MarkOrthogonalWritingModeRoot();
596 else
597 UnmarkOrthogonalWritingModeRoot();
598 }
599
600 ClearPercentHeightDescendants();
601 }
602
603 SetOverflowClipAxes(ComputeOverflowClipAxes());
604
605 // If our zoom factor changes and we have a defined scrollLeft/Top, we need to
606 // adjust that value into the new zoomed coordinate space. Note that the new
607 // scroll offset may be outside the normal min/max range of the scrollable
608 // area, which is weird but OK, because the scrollable area will update its
609 // min/max in updateAfterLayout().
610 if (IsScrollContainer() && old_style &&
611 old_style->EffectiveZoom() != new_style.EffectiveZoom()) {
612 PaintLayerScrollableArea* scrollable_area = GetScrollableArea();
613 DCHECK(scrollable_area);
614 // We use getScrollOffset() rather than scrollPosition(), because scroll
615 // offset is the distance from the beginning of flow for the box, which is
616 // the dimension we want to preserve.
617 ScrollOffset old_offset = scrollable_area->GetScrollOffset();
618 if (old_offset.Width() || old_offset.Height()) {
619 ScrollOffset new_offset = old_offset.ScaledBy(new_style.EffectiveZoom() /
620 old_style->EffectiveZoom());
621 scrollable_area->SetScrollOffsetUnconditionally(new_offset);
622 }
623 }
624
625 UpdateShapeOutsideInfoAfterStyleChange(*Style(), old_style);
626 UpdateGridPositionAfterStyleChange(old_style);
627
628 // When we're no longer a flex item because we're now absolutely positioned,
629 // we need to clear the override size so we're not affected by it anymore.
630 // This technically covers too many cases (even when out-of-flow did not
631 // change) but that should be harmless.
632 if (IsOutOfFlowPositioned() && Parent() &&
633 Parent()->StyleRef().IsDisplayFlexibleOrGridBox())
634 ClearOverrideSize();
635
636 if (LayoutMultiColumnSpannerPlaceholder* placeholder = SpannerPlaceholder())
637 placeholder->LayoutObjectInFlowThreadStyleDidChange(old_style);
638
639 UpdateBackgroundAttachmentFixedStatusAfterStyleChange();
640
641 if (old_style) {
642 LayoutFlowThread* flow_thread = FlowThreadContainingBlock();
643 if (flow_thread && flow_thread != this)
644 flow_thread->FlowThreadDescendantStyleDidChange(this, diff, *old_style);
645
646 UpdateScrollSnapMappingAfterStyleChange(*old_style);
647
648 if (ShouldClipOverflowAlongEitherAxis()) {
649 // The overflow clip paint property depends on border sizes through
650 // overflowClipRect(), and border radii, so we update properties on
651 // border size or radii change.
652 //
653 // For some controls, it depends on paddings.
654 if (!old_style->BorderSizeEquals(new_style) ||
655 !old_style->RadiiEqual(new_style) ||
656 (HasControlClip() && !old_style->PaddingEqual(new_style))) {
657 SetNeedsPaintPropertyUpdate();
658 if (Layer())
659 Layer()->SetNeedsCompositingInputsUpdate();
660 }
661 }
662
663 if (old_style->OverscrollBehaviorX() != new_style.OverscrollBehaviorX() ||
664 old_style->OverscrollBehaviorY() != new_style.OverscrollBehaviorY()) {
665 SetNeedsPaintPropertyUpdate();
666 }
667
668 if (old_style->OverflowClipMargin() != new_style.OverflowClipMargin())
669 SetNeedsPaintPropertyUpdate();
670
671 if (IsInLayoutNGInlineFormattingContext() && IsAtomicInlineLevel() &&
672 old_style->Direction() != new_style.Direction()) {
673 SetNeedsCollectInlines();
674 }
675 }
676
677 // Update the script style map, from the new computed style.
678 if (IsCustomItem())
679 GetCustomLayoutChild()->styleMap()->UpdateStyle(GetDocument(), StyleRef());
680
681 // Non-atomic inlines should be LayoutInline or LayoutText, not LayoutBox.
682 DCHECK(!IsInline() || IsAtomicInlineLevel());
683 }
684
UpdateBackgroundAttachmentFixedStatusAfterStyleChange()685 void LayoutBox::UpdateBackgroundAttachmentFixedStatusAfterStyleChange() {
686 NOT_DESTROYED();
687 if (!GetFrameView())
688 return;
689
690 SetIsBackgroundAttachmentFixedObject(
691 !BackgroundTransfersToView() &&
692 StyleRef().HasFixedAttachmentBackgroundImage());
693 }
694
UpdateShapeOutsideInfoAfterStyleChange(const ComputedStyle & style,const ComputedStyle * old_style)695 void LayoutBox::UpdateShapeOutsideInfoAfterStyleChange(
696 const ComputedStyle& style,
697 const ComputedStyle* old_style) {
698 NOT_DESTROYED();
699 const ShapeValue* shape_outside = style.ShapeOutside();
700 const ShapeValue* old_shape_outside =
701 old_style ? old_style->ShapeOutside()
702 : ComputedStyleInitialValues::InitialShapeOutside();
703
704 const Length& shape_margin = style.ShapeMargin();
705 Length old_shape_margin =
706 old_style ? old_style->ShapeMargin()
707 : ComputedStyleInitialValues::InitialShapeMargin();
708
709 float shape_image_threshold = style.ShapeImageThreshold();
710 float old_shape_image_threshold =
711 old_style ? old_style->ShapeImageThreshold()
712 : ComputedStyleInitialValues::InitialShapeImageThreshold();
713
714 // FIXME: A future optimization would do a deep comparison for equality. (bug
715 // 100811)
716 if (shape_outside == old_shape_outside && shape_margin == old_shape_margin &&
717 shape_image_threshold == old_shape_image_threshold)
718 return;
719
720 if (!shape_outside)
721 ShapeOutsideInfo::RemoveInfo(*this);
722 else
723 ShapeOutsideInfo::EnsureInfo(*this).MarkShapeAsDirty();
724
725 if (shape_outside || shape_outside != old_shape_outside)
726 MarkShapeOutsideDependentsForLayout();
727 }
728
UpdateGridPositionAfterStyleChange(const ComputedStyle * old_style)729 void LayoutBox::UpdateGridPositionAfterStyleChange(
730 const ComputedStyle* old_style) {
731 NOT_DESTROYED();
732 if (!old_style || !Parent() || !Parent()->IsLayoutGrid())
733 return;
734
735 if (old_style->GridColumnStart() == StyleRef().GridColumnStart() &&
736 old_style->GridColumnEnd() == StyleRef().GridColumnEnd() &&
737 old_style->GridRowStart() == StyleRef().GridRowStart() &&
738 old_style->GridRowEnd() == StyleRef().GridRowEnd() &&
739 old_style->Order() == StyleRef().Order() &&
740 old_style->HasOutOfFlowPosition() == StyleRef().HasOutOfFlowPosition())
741 return;
742
743 // Positioned items don't participate on the layout of the grid,
744 // so we don't need to mark the grid as dirty if they change positions.
745 if (old_style->HasOutOfFlowPosition() && StyleRef().HasOutOfFlowPosition())
746 return;
747
748 // It should be possible to not dirty the grid in some cases (like moving an
749 // explicitly placed grid item).
750 // For now, it's more simple to just always recompute the grid.
751 ToLayoutGrid(Parent())->DirtyGrid();
752 }
753
UpdateScrollSnapMappingAfterStyleChange(const ComputedStyle & old_style)754 void LayoutBox::UpdateScrollSnapMappingAfterStyleChange(
755 const ComputedStyle& old_style) {
756 NOT_DESTROYED();
757 DCHECK(Style());
758 SnapCoordinator& snap_coordinator = GetDocument().GetSnapCoordinator();
759 // scroll-snap-type and scroll-padding invalidate the snap container.
760 if (old_style.GetScrollSnapType() != StyleRef().GetScrollSnapType() ||
761 old_style.ScrollPaddingBottom() != StyleRef().ScrollPaddingBottom() ||
762 old_style.ScrollPaddingLeft() != StyleRef().ScrollPaddingLeft() ||
763 old_style.ScrollPaddingTop() != StyleRef().ScrollPaddingTop() ||
764 old_style.ScrollPaddingRight() != StyleRef().ScrollPaddingRight()) {
765 snap_coordinator.SnapContainerDidChange(*this);
766 }
767
768 // scroll-snap-align, scroll-snap-stop and scroll-margin invalidate the snap
769 // area.
770 if (old_style.GetScrollSnapAlign() != StyleRef().GetScrollSnapAlign() ||
771 old_style.ScrollSnapStop() != StyleRef().ScrollSnapStop() ||
772 old_style.ScrollMarginBottom() != StyleRef().ScrollMarginBottom() ||
773 old_style.ScrollMarginLeft() != StyleRef().ScrollMarginLeft() ||
774 old_style.ScrollMarginTop() != StyleRef().ScrollMarginTop() ||
775 old_style.ScrollMarginRight() != StyleRef().ScrollMarginRight())
776 snap_coordinator.SnapAreaDidChange(*this, StyleRef().GetScrollSnapAlign());
777
778 // Transform invalidates the snap area.
779 if (old_style.Transform() != StyleRef().Transform())
780 snap_coordinator.SnapAreaDidChange(*this, StyleRef().GetScrollSnapAlign());
781 }
782
AddScrollSnapMapping()783 void LayoutBox::AddScrollSnapMapping() {
784 NOT_DESTROYED();
785 SnapCoordinator& snap_coordinator = GetDocument().GetSnapCoordinator();
786 snap_coordinator.SnapAreaDidChange(*this, Style()->GetScrollSnapAlign());
787 }
788
ClearScrollSnapMapping()789 void LayoutBox::ClearScrollSnapMapping() {
790 NOT_DESTROYED();
791 SnapCoordinator& snap_coordinator = GetDocument().GetSnapCoordinator();
792 snap_coordinator.SnapAreaDidChange(*this, cc::ScrollSnapAlign());
793 }
794
UpdateFromStyle()795 void LayoutBox::UpdateFromStyle() {
796 NOT_DESTROYED();
797 LayoutBoxModelObject::UpdateFromStyle();
798
799 const ComputedStyle& style_to_use = StyleRef();
800 SetFloating(style_to_use.IsFloating() && !IsOutOfFlowPositioned() &&
801 !style_to_use.IsFlexOrGridItem());
802 SetHasTransformRelatedProperty(style_to_use.HasTransformRelatedProperty());
803 SetHasReflection(style_to_use.BoxReflect());
804 // LayoutTable and LayoutTableCell will overwrite this flag if needed.
805 SetHasNonCollapsedBorderDecoration(style_to_use.HasBorderDecoration());
806 }
807
LayoutSubtreeRoot()808 void LayoutBox::LayoutSubtreeRoot() {
809 NOT_DESTROYED();
810 if (RuntimeEnabledFeatures::LayoutNGEnabled() &&
811 !NGBlockNode::CanUseNewLayout(*this) && GetCachedLayoutResult()) {
812 // If this object is laid out by the legacy engine, while its containing
813 // block is laid out by NG, it means that we normally (when laying out
814 // starting at the real root, i.e. LayoutView) enter layout of this object
815 // from NG code. This takes care of setting up a BoxLayoutExtraInput
816 // structure, which makes legacy layout behave when managed by NG. Make a
817 // short detour via NG just to set things up to re-enter legacy layout
818 // correctly.
819 DCHECK_EQ(PhysicalFragmentCount(), 1u);
820 LayoutPoint old_location = Location();
821
822 // Make a copy of the cached constraint space, since we'll overwrite the
823 // layout result object as part of performing layout.
824 auto constraint_space =
825 GetCachedLayoutResult()->GetConstraintSpaceForCaching();
826
827 NGBlockNode(this).Layout(constraint_space);
828
829 // Restore the old location. While it's usually the job of the containing
830 // block to position its children, out-of-flow positioned objects set
831 // their own position, which could be wrong in this case.
832 SetLocation(old_location);
833 } else {
834 UpdateLayout();
835 }
836
837 // If this box has an associated layout-result, rebuild the spine of the
838 // fragment-tree to ensure consistency.
839 if (PhysicalFragmentCount() &&
840 RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
841 LayoutBlock* cb = ContainingBlock();
842 while (NGBlockNode::CanUseNewLayout(*cb) && !cb->NeedsLayout()) {
843 // Create and set a new identical results.
844 if (cb->measure_result_) {
845 cb->measure_result_ =
846 NGLayoutResult::CloneWithPostLayoutFragments(*cb->measure_result_);
847 }
848 for (scoped_refptr<const NGLayoutResult>& layout_result :
849 cb->layout_results_) {
850 layout_result =
851 NGLayoutResult::CloneWithPostLayoutFragments(*layout_result);
852 }
853 cb = cb->ContainingBlock();
854 }
855 }
856
857 GetDocument().GetFrame()->GetInputMethodController().DidLayoutSubtree(*this);
858 }
859
UpdateLayout()860 void LayoutBox::UpdateLayout() {
861 NOT_DESTROYED();
862 DCHECK(NeedsLayout());
863 LayoutAnalyzer::Scope analyzer(*this);
864
865 if (ChildLayoutBlockedByDisplayLock())
866 return;
867
868 LayoutObject* child = SlowFirstChild();
869 if (!child) {
870 ClearNeedsLayout();
871 return;
872 }
873
874 LayoutState state(*this);
875 while (child) {
876 child->LayoutIfNeeded();
877 DCHECK(!child->NeedsLayout());
878 child = child->NextSibling();
879 }
880 UpdateAfterLayout();
881 ClearNeedsLayout();
882 NotifyDisplayLockDidLayoutChildren();
883 }
884
885 // ClientWidth and ClientHeight represent the interior of an object excluding
886 // border and scrollbar.
887 DISABLE_CFI_PERF
ClientWidth() const888 LayoutUnit LayoutBox::ClientWidth() const {
889 NOT_DESTROYED();
890 // We need to clamp negative values. This function may be called during layout
891 // before frame_rect_ gets the final proper value. Another reason: While
892 // border side values are currently limited to 2^20px (a recent change in the
893 // code), if this limit is raised again in the future, we'd have ill effects
894 // of saturated arithmetic otherwise.
895 if (CanSkipComputeScrollbars()) {
896 return (frame_rect_.Width() - BorderLeft() - BorderRight())
897 .ClampNegativeToZero();
898 } else {
899 return (frame_rect_.Width() - BorderLeft() - BorderRight() -
900 ComputeScrollbarsInternal(kClampToContentBox).HorizontalSum())
901 .ClampNegativeToZero();
902 }
903 }
904
905 DISABLE_CFI_PERF
ClientHeight() const906 LayoutUnit LayoutBox::ClientHeight() const {
907 NOT_DESTROYED();
908 // We need to clamp negative values. This function can be called during layout
909 // before frame_rect_ gets the final proper value. The scrollbar may be wider
910 // than the padding box. Another reason: While border side values are
911 // currently limited to 2^20px (a recent change in the code), if this limit is
912 // raised again in the future, we'd have ill effects of saturated arithmetic
913 // otherwise.
914 if (CanSkipComputeScrollbars()) {
915 return (frame_rect_.Height() - BorderTop() - BorderBottom())
916 .ClampNegativeToZero();
917 } else {
918 return (frame_rect_.Height() - BorderTop() - BorderBottom() -
919 ComputeScrollbarsInternal(kClampToContentBox).VerticalSum())
920 .ClampNegativeToZero();
921 }
922 }
923
PixelSnappedClientWidth() const924 int LayoutBox::PixelSnappedClientWidth() const {
925 NOT_DESTROYED();
926 return SnapSizeToPixel(ClientWidth(), Location().X() + ClientLeft());
927 }
928
929 DISABLE_CFI_PERF
PixelSnappedClientHeight() const930 int LayoutBox::PixelSnappedClientHeight() const {
931 NOT_DESTROYED();
932 return SnapSizeToPixel(ClientHeight(), Location().Y() + ClientTop());
933 }
934
PixelSnappedClientWidthWithTableSpecialBehavior() const935 int LayoutBox::PixelSnappedClientWidthWithTableSpecialBehavior() const {
936 NOT_DESTROYED();
937 // clientWidth/Height is the visual portion of the box content, not including
938 // borders or scroll bars, but includes padding. And per
939 // https://www.w3.org/TR/CSS2/tables.html#model,
940 // table wrapper box is a principal block box that contains the table box
941 // itself and any caption boxes, and table grid box is a block-level box that
942 // contains the table's internal table boxes. When table's border is specified
943 // in CSS, the border is added to table grid box, not table wrapper box.
944 // Currently, Blink doesn't have table wrapper box, and we are supposed to
945 // retrieve clientWidth/Height from table wrapper box, not table grid box. So
946 // when we retrieve clientWidth/Height, it includes table's border size.
947 LayoutUnit client_width = ClientWidth();
948 if (IsTable())
949 client_width += BorderLeft() + BorderRight();
950 return SnapSizeToPixel(client_width, Location().X() + ClientLeft());
951 }
952
953 DISABLE_CFI_PERF
PixelSnappedClientHeightWithTableSpecialBehavior() const954 int LayoutBox::PixelSnappedClientHeightWithTableSpecialBehavior() const {
955 NOT_DESTROYED();
956 // clientWidth/Height is the visual portion of the box content, not including
957 // borders or scroll bars, but includes padding. And per
958 // https://www.w3.org/TR/CSS2/tables.html#model,
959 // table wrapper box is a principal block box that contains the table box
960 // itself and any caption boxes, and table grid box is a block-level box that
961 // contains the table's internal table boxes. When table's border is specified
962 // in CSS, the border is added to table grid box, not table wrapper box.
963 // Currently, Blink doesn't have table wrapper box, and we are supposed to
964 // retrieve clientWidth/Height from table wrapper box, not table grid box. So
965 // when we retrieve clientWidth/Height, it includes table's border size.
966 LayoutUnit client_height = ClientHeight();
967 if (IsTable())
968 client_height += BorderTop() + BorderBottom();
969 return SnapSizeToPixel(client_height, Location().Y() + ClientTop());
970 }
971
PixelSnappedOffsetWidth(const Element *) const972 int LayoutBox::PixelSnappedOffsetWidth(const Element*) const {
973 NOT_DESTROYED();
974 return SnapSizeToPixel(OffsetWidth(), Location().X() + ClientLeft());
975 }
976
PixelSnappedOffsetHeight(const Element *) const977 int LayoutBox::PixelSnappedOffsetHeight(const Element*) const {
978 NOT_DESTROYED();
979 return SnapSizeToPixel(OffsetHeight(), Location().Y() + ClientTop());
980 }
981
ScrollWidth() const982 LayoutUnit LayoutBox::ScrollWidth() const {
983 NOT_DESTROYED();
984 if (IsScrollContainer() || StyleRef().IsScrollbarGutterForce())
985 return GetScrollableArea()->ScrollWidth();
986 // For objects with visible overflow, this matches IE.
987 // FIXME: Need to work right with writing modes.
988 if (StyleRef().IsLeftToRightDirection())
989 return std::max(ClientWidth(), LayoutOverflowRect().MaxX() - BorderLeft());
990 return ClientWidth() -
991 std::min(LayoutUnit(), LayoutOverflowRect().X() - BorderLeft());
992 }
993
ScrollHeight() const994 LayoutUnit LayoutBox::ScrollHeight() const {
995 NOT_DESTROYED();
996 if (IsScrollContainer() || StyleRef().IsScrollbarGutterForce())
997 return GetScrollableArea()->ScrollHeight();
998 // For objects with visible overflow, this matches IE.
999 // FIXME: Need to work right with writing modes.
1000 return std::max(ClientHeight(), LayoutOverflowRect().MaxY() - BorderTop());
1001 }
1002
PixelSnappedScrollWidth() const1003 int LayoutBox::PixelSnappedScrollWidth() const {
1004 NOT_DESTROYED();
1005 return SnapSizeToPixel(ScrollWidth(), Location().X() + ClientLeft());
1006 }
1007
PixelSnappedScrollHeight() const1008 int LayoutBox::PixelSnappedScrollHeight() const {
1009 NOT_DESTROYED();
1010 if (IsScrollContainer())
1011 return SnapSizeToPixel(GetScrollableArea()->ScrollHeight(),
1012 Location().Y() + ClientTop());
1013 // For objects with visible overflow, this matches IE.
1014 // FIXME: Need to work right with writing modes.
1015 return SnapSizeToPixel(ScrollHeight(), Location().Y() + ClientTop());
1016 }
1017
ScrollRectToVisibleRecursive(const PhysicalRect & absolute_rect,mojom::blink::ScrollIntoViewParamsPtr params)1018 PhysicalRect LayoutBox::ScrollRectToVisibleRecursive(
1019 const PhysicalRect& absolute_rect,
1020 mojom::blink::ScrollIntoViewParamsPtr params) {
1021 NOT_DESTROYED();
1022 DCHECK(params->type == mojom::blink::ScrollType::kProgrammatic ||
1023 params->type == mojom::blink::ScrollType::kUser);
1024
1025 if (!GetFrameView())
1026 return absolute_rect;
1027
1028 // If we've reached the main frame's layout viewport (which is always set to
1029 // the global root scroller, see ViewportScrollCallback::SetScroller), abort
1030 // if the stop_at_main_frame_layout_viewport option is set. We do this so
1031 // that we can allow a smooth "scroll and zoom" animation to do the final
1032 // scroll in cases like scrolling a focused editable box into view.
1033 if (params->stop_at_main_frame_layout_viewport && IsGlobalRootScroller())
1034 return absolute_rect;
1035
1036 PhysicalRect absolute_rect_to_scroll = absolute_rect;
1037 if (absolute_rect_to_scroll.Width() <= 0)
1038 absolute_rect_to_scroll.SetWidth(LayoutUnit(1));
1039 if (absolute_rect_to_scroll.Height() <= 0)
1040 absolute_rect_to_scroll.SetHeight(LayoutUnit(1));
1041
1042 LayoutBox* parent_box = nullptr;
1043
1044 if (ContainingBlock())
1045 parent_box = ContainingBlock();
1046
1047 PhysicalRect absolute_rect_for_parent;
1048 if (!IsA<LayoutView>(this) && IsScrollContainer()) {
1049 absolute_rect_for_parent =
1050 GetScrollableArea()->ScrollIntoView(absolute_rect_to_scroll, params);
1051 } else if (!parent_box && CanBeProgramaticallyScrolled()) {
1052 ScrollableArea* area_to_scroll = params->make_visible_in_visual_viewport
1053 ? GetFrameView()->GetScrollableArea()
1054 : GetFrameView()->LayoutViewport();
1055 absolute_rect_for_parent =
1056 area_to_scroll->ScrollIntoView(absolute_rect_to_scroll, params);
1057
1058 // If the parent is a local iframe, convert to the absolute coordinate
1059 // space of its document. For remote frames, this will happen on the other
1060 // end of the IPC call.
1061 HTMLFrameOwnerElement* owner_element = GetDocument().LocalOwner();
1062 if (owner_element && owner_element->GetLayoutObject() &&
1063 AllowedToPropagateRecursiveScrollToParentFrame(params)) {
1064 parent_box = owner_element->GetLayoutObject()->EnclosingBox();
1065 LayoutView* parent_view = owner_element->GetLayoutObject()->View();
1066 absolute_rect_for_parent = View()->LocalToAncestorRect(
1067 absolute_rect_for_parent, parent_view, kTraverseDocumentBoundaries);
1068 }
1069 } else {
1070 absolute_rect_for_parent = absolute_rect_to_scroll;
1071 }
1072
1073 // If we're in a position:fixed element, scrolling the layout viewport won't
1074 // have any effect, so we avoid using the RootFrameViewport and explicitly
1075 // scroll the visual viewport if we can. If not, we're done.
1076 if (StyleRef().GetPosition() == EPosition::kFixed && Container() == View() &&
1077 params->make_visible_in_visual_viewport) {
1078 if (GetFrame()->IsMainFrame()) {
1079 // TODO(donnd): We should continue the recursion if we're in a subframe.
1080 return GetFrame()->GetPage()->GetVisualViewport().ScrollIntoView(
1081 absolute_rect_for_parent, params);
1082 } else {
1083 return absolute_rect_for_parent;
1084 }
1085 }
1086
1087 if (parent_box) {
1088 return parent_box->ScrollRectToVisibleRecursive(absolute_rect_for_parent,
1089 std::move(params));
1090 } else if (GetFrame()->IsLocalRoot() && !GetFrame()->IsMainFrame()) {
1091 if (AllowedToPropagateRecursiveScrollToParentFrame(params)) {
1092 GetFrameView()->ScrollRectToVisibleInRemoteParent(
1093 absolute_rect_for_parent, std::move(params));
1094 }
1095 }
1096
1097 return absolute_rect_for_parent;
1098 }
1099
SetMargin(const NGPhysicalBoxStrut & box)1100 void LayoutBox::SetMargin(const NGPhysicalBoxStrut& box) {
1101 NOT_DESTROYED();
1102 margin_box_outsets_.SetTop(box.top);
1103 margin_box_outsets_.SetRight(box.right);
1104 margin_box_outsets_.SetBottom(box.bottom);
1105 margin_box_outsets_.SetLeft(box.left);
1106 }
1107
AbsoluteQuads(Vector<FloatQuad> & quads,MapCoordinatesFlags mode) const1108 void LayoutBox::AbsoluteQuads(Vector<FloatQuad>& quads,
1109 MapCoordinatesFlags mode) const {
1110 NOT_DESTROYED();
1111 if (LayoutFlowThread* flow_thread = FlowThreadContainingBlock()) {
1112 flow_thread->AbsoluteQuadsForDescendant(*this, quads, mode);
1113 return;
1114 }
1115 quads.push_back(LocalRectToAbsoluteQuad(PhysicalBorderBoxRect(), mode));
1116 }
1117
LocalBoundingBoxRectForAccessibility() const1118 FloatRect LayoutBox::LocalBoundingBoxRectForAccessibility() const {
1119 NOT_DESTROYED();
1120 return FloatRect(0, 0, frame_rect_.Width().ToFloat(),
1121 frame_rect_.Height().ToFloat());
1122 }
1123
UpdateAfterLayout()1124 void LayoutBox::UpdateAfterLayout() {
1125 NOT_DESTROYED();
1126 // Transform-origin depends on box size, so we need to update the layer
1127 // transform after layout.
1128 if (HasLayer()) {
1129 Layer()->UpdateTransformationMatrix();
1130 Layer()->UpdateSizeAndScrollingAfterLayout();
1131 }
1132
1133 // When we've finished layout, if we aren't a LayoutNG object, we need to
1134 // reset our cached layout result. LayoutNG inside of
1135 // |NGBlockNode::RunOldLayout| will call |LayoutBox::SetCachedLayoutResult|
1136 // with a new synthesized layout result.
1137 //
1138 // We also want to make sure that if our entrance point into layout changes,
1139 // e.g. an OOF-positioned object is laid out by an NG containing block, then
1140 // Legacy, then NG again, NG won't use a stale layout result.
1141 if (IsOutOfFlowPositioned() && !IsLayoutNGObject())
1142 ClearLayoutResults();
1143
1144 Document& document = GetDocument();
1145 document.IncLayoutCallsCounter();
1146 document.GetFrame()->GetInputMethodController().DidUpdateLayout(*this);
1147 if (IsLayoutNGObject())
1148 document.IncLayoutCallsCounterNG();
1149 }
1150
HasOverrideIntrinsicContentWidth() const1151 bool LayoutBox::HasOverrideIntrinsicContentWidth() const {
1152 NOT_DESTROYED();
1153 if (!ShouldApplySizeContainment())
1154 return false;
1155
1156 const Length& intrinsic_length = StyleRef().ContainIntrinsicSize().Width();
1157 return !intrinsic_length.IsAuto();
1158 }
1159
HasOverrideIntrinsicContentHeight() const1160 bool LayoutBox::HasOverrideIntrinsicContentHeight() const {
1161 NOT_DESTROYED();
1162 if (!ShouldApplySizeContainment())
1163 return false;
1164
1165 const Length& intrinsic_length = StyleRef().ContainIntrinsicSize().Height();
1166 return !intrinsic_length.IsAuto();
1167 }
1168
OverrideIntrinsicContentWidth() const1169 LayoutUnit LayoutBox::OverrideIntrinsicContentWidth() const {
1170 NOT_DESTROYED();
1171 DCHECK(HasOverrideIntrinsicContentWidth());
1172 const auto& style = StyleRef();
1173 const Length& intrinsic_length = style.ContainIntrinsicSize().Width();
1174 DCHECK(!intrinsic_length.IsAuto());
1175 DCHECK(intrinsic_length.IsFixed());
1176 DCHECK_GE(intrinsic_length.Value(), 0.f);
1177 return LayoutUnit(intrinsic_length.Value());
1178 }
1179
OverrideIntrinsicContentHeight() const1180 LayoutUnit LayoutBox::OverrideIntrinsicContentHeight() const {
1181 NOT_DESTROYED();
1182 DCHECK(HasOverrideIntrinsicContentHeight());
1183 const auto& style = StyleRef();
1184 const Length& intrinsic_length = style.ContainIntrinsicSize().Height();
1185 DCHECK(!intrinsic_length.IsAuto());
1186 DCHECK(intrinsic_length.IsFixed());
1187 DCHECK_GE(intrinsic_length.Value(), 0.f);
1188 return LayoutUnit(intrinsic_length.Value());
1189 }
1190
DefaultIntrinsicContentInlineSize() const1191 LayoutUnit LayoutBox::DefaultIntrinsicContentInlineSize() const {
1192 NOT_DESTROYED();
1193 // If the intrinsic-inline-size is specified, then we shouldn't ever need to
1194 // get here.
1195 DCHECK(!HasOverrideIntrinsicContentLogicalWidth());
1196
1197 if (!IsA<Element>(GetNode()))
1198 return kIndefiniteSize;
1199 const Element& element = *To<Element>(GetNode());
1200
1201 auto* select = DynamicTo<HTMLSelectElement>(element);
1202 if (UNLIKELY(select && select->UsesMenuList())) {
1203 return MenuListIntrinsicInlineSize(*select, *this);
1204 }
1205 auto* input = DynamicTo<HTMLInputElement>(element);
1206 if (UNLIKELY(input)) {
1207 if (input->IsTextField())
1208 return TextFieldIntrinsicInlineSize(*input, *this);
1209 const AtomicString& type = input->type();
1210 if (type == input_type_names::kFile)
1211 return FileUploadControlIntrinsicInlineSize(*input, *this);
1212 else if (type == input_type_names::kRange)
1213 return SliderIntrinsicInlineSize(*this);
1214 return kIndefiniteSize;
1215 }
1216 auto* textarea = DynamicTo<HTMLTextAreaElement>(element);
1217 if (UNLIKELY(textarea))
1218 return TextAreaIntrinsicInlineSize(*textarea, *this);
1219
1220 if (IsSliderContainer(element))
1221 return SliderIntrinsicInlineSize(*this);
1222 return kIndefiniteSize;
1223 }
1224
DefaultIntrinsicContentBlockSize() const1225 LayoutUnit LayoutBox::DefaultIntrinsicContentBlockSize() const {
1226 NOT_DESTROYED();
1227 // If the intrinsic-block-size is specified, then we shouldn't ever need to
1228 // get here.
1229 DCHECK(!HasOverrideIntrinsicContentLogicalHeight());
1230
1231 if (const auto* select = DynamicTo<HTMLSelectElement>(GetNode())) {
1232 if (select->UsesMenuList()) {
1233 return MenuListIntrinsicBlockSize(*select, *this);
1234 } else {
1235 return ListBoxItemHeight(*select, *this) * select->ListBoxSize() -
1236 ComputeLogicalScrollbars().BlockSum();
1237 }
1238 } else if (IsTextFieldIncludingNG()) {
1239 return TextFieldIntrinsicBlockSize(*To<HTMLInputElement>(GetNode()), *this);
1240 } else if (IsTextAreaIncludingNG()) {
1241 return TextAreaIntrinsicBlockSize(*To<HTMLTextAreaElement>(GetNode()),
1242 *this);
1243 }
1244 return kIndefiniteSize;
1245 }
1246
LogicalHeightWithVisibleOverflow() const1247 LayoutUnit LayoutBox::LogicalHeightWithVisibleOverflow() const {
1248 NOT_DESTROYED();
1249 if (!LayoutOverflowIsSet() || IsScrollContainer() ||
1250 StyleRef().OverflowY() == EOverflow::kClip)
1251 return LogicalHeight();
1252 LayoutRect overflow = LayoutOverflowRect();
1253 if (StyleRef().IsHorizontalWritingMode())
1254 return overflow.MaxY();
1255 return overflow.MaxX();
1256 }
1257
ConstrainLogicalWidthByMinMax(LayoutUnit logical_width,LayoutUnit available_width,const LayoutBlock * cb,bool allow_intrinsic) const1258 LayoutUnit LayoutBox::ConstrainLogicalWidthByMinMax(
1259 LayoutUnit logical_width,
1260 LayoutUnit available_width,
1261 const LayoutBlock* cb,
1262 bool allow_intrinsic) const {
1263 NOT_DESTROYED();
1264 const ComputedStyle& style_to_use = StyleRef();
1265
1266 // This implements the transferred min/max sizes per
1267 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio
1268 if (ShouldComputeLogicalHeightFromAspectRatio()) {
1269 MinMaxSizes transferred_min_max =
1270 ComputeMinMaxLogicalWidthFromAspectRatio();
1271 logical_width = transferred_min_max.ClampSizeToMinAndMax(logical_width);
1272 }
1273
1274 if (!style_to_use.LogicalMaxWidth().IsNone() &&
1275 (allow_intrinsic ||
1276 !style_to_use.LogicalMaxWidth().IsContentOrIntrinsic())) {
1277 logical_width = std::min(
1278 logical_width,
1279 ComputeLogicalWidthUsing(kMaxSize, style_to_use.LogicalMaxWidth(),
1280 available_width, cb));
1281 }
1282
1283 // If we have an aspect-ratio, check if we need to apply min-width: auto.
1284 Length min_length = style_to_use.LogicalMinWidth();
1285 if (!style_to_use.AspectRatio().IsAuto() &&
1286 style_to_use.LogicalWidth().IsAuto() && min_length.IsAuto() &&
1287 style_to_use.OverflowInlineDirection() == EOverflow::kVisible) {
1288 // Make sure we actually used the aspect ratio.
1289 if (ShouldComputeLogicalWidthFromAspectRatio())
1290 min_length = Length::MinIntrinsic();
1291 }
1292 if (!allow_intrinsic && style_to_use.LogicalMinWidth().IsContentOrIntrinsic())
1293 return logical_width;
1294 return std::max(logical_width, ComputeLogicalWidthUsing(kMinSize, min_length,
1295 available_width, cb));
1296 }
1297
ConstrainLogicalHeightByMinMax(LayoutUnit logical_height,LayoutUnit intrinsic_content_height) const1298 LayoutUnit LayoutBox::ConstrainLogicalHeightByMinMax(
1299 LayoutUnit logical_height,
1300 LayoutUnit intrinsic_content_height) const {
1301 NOT_DESTROYED();
1302 // Note that the values 'min-content', 'max-content' and 'fit-content' should
1303 // behave as the initial value if specified in the block direction.
1304 const Length& logical_max_height = StyleRef().LogicalMaxHeight();
1305 if (!logical_max_height.IsNone() && !logical_max_height.IsMinContent() &&
1306 !logical_max_height.IsMaxContent() &&
1307 !logical_max_height.IsMinIntrinsic() &&
1308 !logical_max_height.IsFitContent()) {
1309 LayoutUnit max_h = ComputeLogicalHeightUsing(kMaxSize, logical_max_height,
1310 intrinsic_content_height);
1311 if (max_h != -1)
1312 logical_height = std::min(logical_height, max_h);
1313 }
1314 Length logical_min_height = StyleRef().LogicalMinHeight();
1315 if (logical_min_height.IsAuto() &&
1316 ShouldComputeLogicalHeightFromAspectRatio() &&
1317 intrinsic_content_height != kIndefiniteSize &&
1318 intrinsic_content_height != LayoutUnit::Max() &&
1319 StyleRef().OverflowBlockDirection() == EOverflow::kVisible) {
1320 logical_min_height = Length::Fixed(intrinsic_content_height);
1321 }
1322 if (logical_min_height.IsMinContent() || logical_min_height.IsMaxContent() ||
1323 logical_min_height.IsMinIntrinsic() || logical_min_height.IsFitContent())
1324 logical_min_height = Length::Auto();
1325 return std::max(logical_height,
1326 ComputeLogicalHeightUsing(kMinSize, logical_min_height,
1327 intrinsic_content_height));
1328 }
1329
ConstrainContentBoxLogicalHeightByMinMax(LayoutUnit logical_height,LayoutUnit intrinsic_content_height) const1330 LayoutUnit LayoutBox::ConstrainContentBoxLogicalHeightByMinMax(
1331 LayoutUnit logical_height,
1332 LayoutUnit intrinsic_content_height) const {
1333 NOT_DESTROYED();
1334 // If the min/max height and logical height are both percentages we take
1335 // advantage of already knowing the current resolved percentage height
1336 // to avoid recursing up through our containing blocks again to determine it.
1337 const ComputedStyle& style_to_use = StyleRef();
1338 if (!style_to_use.LogicalMaxHeight().IsNone()) {
1339 if (style_to_use.LogicalMaxHeight().IsPercent() &&
1340 style_to_use.LogicalHeight().IsPercent()) {
1341 LayoutUnit available_logical_height(
1342 logical_height / style_to_use.LogicalHeight().Value() * 100);
1343 logical_height = std::min(logical_height,
1344 ValueForLength(style_to_use.LogicalMaxHeight(),
1345 available_logical_height));
1346 } else {
1347 LayoutUnit max_height(ComputeContentLogicalHeight(
1348 kMaxSize, style_to_use.LogicalMaxHeight(), intrinsic_content_height));
1349 if (max_height != -1)
1350 logical_height = std::min(logical_height, max_height);
1351 }
1352 }
1353
1354 if (style_to_use.LogicalMinHeight().IsPercent() &&
1355 style_to_use.LogicalHeight().IsPercent()) {
1356 LayoutUnit available_logical_height(
1357 logical_height / style_to_use.LogicalHeight().Value() * 100);
1358 logical_height =
1359 std::max(logical_height, ValueForLength(style_to_use.LogicalMinHeight(),
1360 available_logical_height));
1361 } else {
1362 logical_height = std::max(
1363 logical_height,
1364 ComputeContentLogicalHeight(kMinSize, style_to_use.LogicalMinHeight(),
1365 intrinsic_content_height));
1366 }
1367
1368 return logical_height;
1369 }
1370
SetLocationAndUpdateOverflowControlsIfNeeded(const LayoutPoint & location)1371 void LayoutBox::SetLocationAndUpdateOverflowControlsIfNeeded(
1372 const LayoutPoint& location) {
1373 NOT_DESTROYED();
1374 if (!HasLayer()) {
1375 SetLocation(location);
1376 return;
1377 }
1378 // The Layer does not yet have the up to date subpixel accumulation
1379 // so we base the size strictly on the frame rect's location.
1380 IntSize old_pixel_snapped_border_rect_size =
1381 PixelSnappedBorderBoxRect().Size();
1382 SetLocation(location);
1383 // TODO(crbug.com/1020913): This is problematic because this function may be
1384 // called after layout of this LayoutBox. Changing scroll container size here
1385 // will cause inconsistent layout. Also we should be careful not to set
1386 // this LayoutBox NeedsLayout. This will be unnecessary when we support
1387 // subpixel layout of scrollable area and overflow controls.
1388 if (PixelSnappedBorderBoxRect().Size() !=
1389 old_pixel_snapped_border_rect_size) {
1390 bool needed_layout = NeedsLayout();
1391 PaintLayerScrollableArea::FreezeScrollbarsScope freeze_scrollbar;
1392 Layer()->UpdateSizeAndScrollingAfterLayout();
1393 // The above call should not schedule new NeedsLayout.
1394 DCHECK(needed_layout || !NeedsLayout());
1395 }
1396 }
1397
AbsoluteContentQuad(MapCoordinatesFlags flags) const1398 FloatQuad LayoutBox::AbsoluteContentQuad(MapCoordinatesFlags flags) const {
1399 NOT_DESTROYED();
1400 PhysicalRect rect = PhysicalContentBoxRect();
1401 return LocalRectToAbsoluteQuad(rect, flags);
1402 }
1403
PhysicalBackgroundRect(BackgroundRectType rect_type) const1404 PhysicalRect LayoutBox::PhysicalBackgroundRect(
1405 BackgroundRectType rect_type) const {
1406 NOT_DESTROYED();
1407 // If the background transfers to view, the used background of this object
1408 // is transparent.
1409 if (rect_type == kBackgroundKnownOpaqueRect && BackgroundTransfersToView())
1410 return PhysicalRect();
1411
1412 EFillBox background_box = EFillBox::kText;
1413 // Find the largest background rect of the given opaqueness.
1414 if (const FillLayer* current = &(StyleRef().BackgroundLayers())) {
1415 do {
1416 const FillLayer* cur = current;
1417 current = current->Next();
1418 if (rect_type == kBackgroundKnownOpaqueRect) {
1419 if (cur->GetBlendMode() != BlendMode::kNormal ||
1420 cur->Composite() != kCompositeSourceOver)
1421 continue;
1422
1423 bool layer_known_opaque = false;
1424 // Check if the image is opaque and fills the clip.
1425 if (const StyleImage* image = cur->GetImage()) {
1426 if ((cur->RepeatX() == EFillRepeat::kRepeatFill ||
1427 cur->RepeatX() == EFillRepeat::kRoundFill) &&
1428 (cur->RepeatY() == EFillRepeat::kRepeatFill ||
1429 cur->RepeatY() == EFillRepeat::kRoundFill) &&
1430 image->KnownToBeOpaque(GetDocument(), StyleRef())) {
1431 layer_known_opaque = true;
1432 }
1433 }
1434
1435 // The background color is painted into the last layer.
1436 if (!cur->Next()) {
1437 Color background_color =
1438 ResolveColor(GetCSSPropertyBackgroundColor());
1439 if (!background_color.HasAlpha())
1440 layer_known_opaque = true;
1441 }
1442
1443 // If neither the image nor the color are opaque then skip this layer.
1444 if (!layer_known_opaque)
1445 continue;
1446 }
1447 EFillBox current_clip = cur->Clip();
1448 // Restrict clip if attachment is local.
1449 if (current_clip == EFillBox::kBorder &&
1450 cur->Attachment() == EFillAttachment::kLocal)
1451 current_clip = EFillBox::kPadding;
1452
1453 // If we're asking for the clip rect, a content-box clipped fill layer can
1454 // be scrolled into the padding box of the overflow container.
1455 if (rect_type == kBackgroundClipRect &&
1456 current_clip == EFillBox::kContent &&
1457 cur->Attachment() == EFillAttachment::kLocal) {
1458 current_clip = EFillBox::kPadding;
1459 }
1460
1461 background_box = EnclosingFillBox(background_box, current_clip);
1462 } while (current);
1463 }
1464 switch (background_box) {
1465 case EFillBox::kBorder:
1466 return PhysicalBorderBoxRect();
1467 case EFillBox::kPadding:
1468 return PhysicalPaddingBoxRect();
1469 case EFillBox::kContent:
1470 return PhysicalContentBoxRect();
1471 default:
1472 break;
1473 }
1474 return PhysicalRect();
1475 }
1476
AddOutlineRects(Vector<PhysicalRect> & rects,const PhysicalOffset & additional_offset,NGOutlineType) const1477 void LayoutBox::AddOutlineRects(Vector<PhysicalRect>& rects,
1478 const PhysicalOffset& additional_offset,
1479 NGOutlineType) const {
1480 NOT_DESTROYED();
1481 rects.emplace_back(additional_offset, Size());
1482 }
1483
CanResize() const1484 bool LayoutBox::CanResize() const {
1485 NOT_DESTROYED();
1486 // We need a special case for <iframe> because they never have
1487 // hasOverflowClip(). However, they do "implicitly" clip their contents, so
1488 // we want to allow resizing them also.
1489 return (IsScrollContainer() || IsLayoutIFrame()) && StyleRef().HasResize();
1490 }
1491
ComputeMinMaxLogicalWidthFromAspectRatio() const1492 MinMaxSizes LayoutBox::ComputeMinMaxLogicalWidthFromAspectRatio() const {
1493 NOT_DESTROYED();
1494 DCHECK_NE(StyleRef().AspectRatio().GetType(), EAspectRatioType::kAuto);
1495
1496 // The spec requires us to clamp these by the specified size (it calls it the
1497 // preferred size). However, we actually don't need to worry about that,
1498 // because we only use this if the width is indefinite.
1499
1500 // We do not need to compute the min/max inline sizes; as long as we always
1501 // apply the transferred min/max size before the explicit min/max size, the
1502 // result will be identical.
1503
1504 LogicalSize ratio = StyleRef().LogicalAspectRatio();
1505 MinMaxSizes block_min_max{
1506 ConstrainLogicalHeightByMinMax(LayoutUnit(), kIndefiniteSize),
1507 ConstrainLogicalHeightByMinMax(LayoutUnit::Max(), kIndefiniteSize)};
1508 if (block_min_max.max_size == kIndefiniteSize)
1509 block_min_max.max_size = LayoutUnit::Max();
1510
1511 NGBoxStrut border_padding(BorderStart() + ComputedCSSPaddingStart(),
1512 BorderEnd() + ComputedCSSPaddingEnd(),
1513 BorderBefore() + ComputedCSSPaddingBefore(),
1514 BorderAfter() + ComputedCSSPaddingAfter());
1515
1516 MinMaxSizes transferred_min_max = {LayoutUnit(), LayoutUnit::Max()};
1517 if (block_min_max.min_size > LayoutUnit()) {
1518 transferred_min_max.min_size = InlineSizeFromAspectRatio(
1519 border_padding, ratio, StyleRef().BoxSizing(), block_min_max.min_size);
1520 }
1521 if (block_min_max.max_size != LayoutUnit::Max()) {
1522 transferred_min_max.max_size = InlineSizeFromAspectRatio(
1523 border_padding, ratio, StyleRef().BoxSizing(), block_min_max.max_size);
1524 }
1525 // Minimum size wins over maximum size.
1526 transferred_min_max.max_size =
1527 std::max(transferred_min_max.max_size, transferred_min_max.min_size);
1528 return transferred_min_max;
1529 }
1530
HasScrollbarGutters(ScrollbarOrientation orientation) const1531 bool LayoutBox::HasScrollbarGutters(ScrollbarOrientation orientation) const {
1532 NOT_DESTROYED();
1533 if (StyleRef().IsScrollbarGutterAuto())
1534 return false;
1535
1536 bool is_stable = StyleRef().IsScrollbarGutterStable();
1537 bool is_always = StyleRef().IsScrollbarGutterAlways();
1538
1539 if (!is_stable && !is_always)
1540 return false;
1541
1542 if (orientation == kVerticalScrollbar) {
1543 EOverflow overflow = StyleRef().OverflowY();
1544 return (StyleRef().IsScrollbarGutterForce() ||
1545 overflow == EOverflow::kAuto || overflow == EOverflow::kScroll) &&
1546 StyleRef().IsHorizontalWritingMode() &&
1547 !(is_stable && UsesOverlayScrollbars());
1548 } else {
1549 EOverflow overflow = StyleRef().OverflowX();
1550 return (StyleRef().IsScrollbarGutterForce() ||
1551 overflow == EOverflow::kAuto || overflow == EOverflow::kScroll) &&
1552 !StyleRef().IsHorizontalWritingMode() &&
1553 !(is_stable && UsesOverlayScrollbars());
1554 }
1555 }
1556
ComputeScrollbarsInternal(ShouldClampToContentBox clamp_to_content_box,OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const1557 NGPhysicalBoxStrut LayoutBox::ComputeScrollbarsInternal(
1558 ShouldClampToContentBox clamp_to_content_box,
1559 OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
1560 NOT_DESTROYED();
1561 NGPhysicalBoxStrut scrollbars;
1562 PaintLayerScrollableArea* scrollable_area = GetScrollableArea();
1563 if (!scrollable_area)
1564 return scrollbars;
1565
1566 if (HasScrollbarGutters(kVerticalScrollbar)) {
1567 LayoutUnit gutter_size =
1568 LayoutUnit(scrollable_area->HypotheticalScrollbarThickness(
1569 kVerticalScrollbar, /* should_include_overlay_thickness */ true));
1570 if (ShouldPlaceVerticalScrollbarOnLeft()) {
1571 scrollbars.left = gutter_size;
1572 if (StyleRef().IsScrollbarGutterBoth())
1573 scrollbars.right = gutter_size;
1574 } else {
1575 scrollbars.right = gutter_size;
1576 if (StyleRef().IsScrollbarGutterBoth())
1577 scrollbars.left = gutter_size;
1578 }
1579 } else if (ShouldPlaceVerticalScrollbarOnLeft()) {
1580 scrollbars.left = LayoutUnit(scrollable_area->VerticalScrollbarWidth(
1581 overlay_scrollbar_clip_behavior));
1582 } else {
1583 scrollbars.right = LayoutUnit(scrollable_area->VerticalScrollbarWidth(
1584 overlay_scrollbar_clip_behavior));
1585 }
1586
1587 if (HasScrollbarGutters(kHorizontalScrollbar)) {
1588 LayoutUnit gutter_size =
1589 LayoutUnit(scrollable_area->HypotheticalScrollbarThickness(
1590 kHorizontalScrollbar, /* should_include_overlay_thickness */ true));
1591 scrollbars.bottom = gutter_size;
1592 if (StyleRef().IsScrollbarGutterBoth())
1593 scrollbars.top = gutter_size;
1594 } else {
1595 scrollbars.bottom = LayoutUnit(scrollable_area->HorizontalScrollbarHeight(
1596 overlay_scrollbar_clip_behavior));
1597 }
1598
1599 // Use the width of the vertical scrollbar, unless it's larger than the
1600 // logical width of the content box, in which case we'll use that instead.
1601 // Scrollbar handling is quite bad in such situations, and this code here
1602 // is just to make sure that left-hand scrollbars don't mess up
1603 // scrollWidth. For the full story, visit http://crbug.com/724255.
1604 if (scrollbars.left > 0 && clamp_to_content_box == kClampToContentBox) {
1605 LayoutUnit max_width = frame_rect_.Width() - BorderAndPaddingWidth();
1606 scrollbars.left =
1607 std::min(scrollbars.left, max_width.ClampNegativeToZero());
1608 }
1609
1610 return scrollbars;
1611 }
1612
CanBeScrolledAndHasScrollableArea() const1613 bool LayoutBox::CanBeScrolledAndHasScrollableArea() const {
1614 NOT_DESTROYED();
1615 return CanBeProgramaticallyScrolled() &&
1616 (PixelSnappedScrollHeight() != PixelSnappedClientHeight() ||
1617 PixelSnappedScrollWidth() != PixelSnappedClientWidth());
1618 }
1619
CanBeProgramaticallyScrolled() const1620 bool LayoutBox::CanBeProgramaticallyScrolled() const {
1621 NOT_DESTROYED();
1622 Node* node = GetNode();
1623 if (node && node->IsDocumentNode())
1624 return true;
1625
1626 if (!IsScrollContainer())
1627 return false;
1628
1629 bool has_scrollable_overflow =
1630 HasScrollableOverflowX() || HasScrollableOverflowY();
1631 if (ScrollsOverflow() && has_scrollable_overflow)
1632 return true;
1633
1634 return node && HasEditableStyle(*node);
1635 }
1636
Autoscroll(const PhysicalOffset & position_in_root_frame)1637 void LayoutBox::Autoscroll(const PhysicalOffset& position_in_root_frame) {
1638 NOT_DESTROYED();
1639 LocalFrame* frame = GetFrame();
1640 if (!frame)
1641 return;
1642
1643 LocalFrameView* frame_view = frame->View();
1644 if (!frame_view)
1645 return;
1646
1647 PhysicalOffset absolute_position =
1648 frame_view->ConvertFromRootFrame(position_in_root_frame);
1649 ScrollRectToVisibleRecursive(
1650 PhysicalRect(absolute_position,
1651 PhysicalSize(LayoutUnit(1), LayoutUnit(1))),
1652 ScrollAlignment::CreateScrollIntoViewParams(
1653 ScrollAlignment::ToEdgeIfNeeded(), ScrollAlignment::ToEdgeIfNeeded(),
1654 mojom::blink::ScrollType::kUser));
1655 }
1656
CanAutoscroll() const1657 bool LayoutBox::CanAutoscroll() const {
1658 NOT_DESTROYED();
1659 // TODO(skobes): Remove one of these methods.
1660 return CanBeScrolledAndHasScrollableArea();
1661 }
1662
1663 // If specified point is outside the border-belt-excluded box (the border box
1664 // inset by the autoscroll activation threshold), returned offset denotes
1665 // direction of scrolling.
CalculateAutoscrollDirection(const FloatPoint & point_in_root_frame) const1666 PhysicalOffset LayoutBox::CalculateAutoscrollDirection(
1667 const FloatPoint& point_in_root_frame) const {
1668 NOT_DESTROYED();
1669 if (!GetFrame())
1670 return PhysicalOffset();
1671
1672 LocalFrameView* frame_view = GetFrame()->View();
1673 if (!frame_view)
1674 return PhysicalOffset();
1675
1676 PhysicalRect absolute_scrolling_box(AbsoluteBoundingBoxRect());
1677
1678 // Exclude scrollbars so the border belt (activation area) starts from the
1679 // scrollbar-content edge rather than the window edge.
1680 ExcludeScrollbars(absolute_scrolling_box,
1681 kExcludeOverlayScrollbarSizeForHitTesting);
1682
1683 PhysicalRect belt_box =
1684 View()->GetFrameView()->ConvertToRootFrame(absolute_scrolling_box);
1685 belt_box.Inflate(LayoutUnit(-kAutoscrollBeltSize));
1686 FloatPoint point = point_in_root_frame;
1687
1688 if (point.X() < belt_box.X())
1689 point.Move(-kAutoscrollBeltSize, 0);
1690 else if (point.X() > belt_box.Right())
1691 point.Move(kAutoscrollBeltSize, 0);
1692
1693 if (point.Y() < belt_box.Y())
1694 point.Move(0, -kAutoscrollBeltSize);
1695 else if (point.Y() > belt_box.Bottom())
1696 point.Move(0, kAutoscrollBeltSize);
1697
1698 return PhysicalOffset::FromFloatSizeRound(point - point_in_root_frame);
1699 }
1700
FindAutoscrollable(LayoutObject * layout_object,bool is_middle_click_autoscroll)1701 LayoutBox* LayoutBox::FindAutoscrollable(LayoutObject* layout_object,
1702 bool is_middle_click_autoscroll) {
1703 while (layout_object && !(layout_object->IsBox() &&
1704 To<LayoutBox>(layout_object)->CanAutoscroll())) {
1705 // Do not start selection-based autoscroll when the node is inside a
1706 // fixed-position element.
1707 if (!is_middle_click_autoscroll && layout_object->IsBox() &&
1708 To<LayoutBox>(layout_object)->HasLayer() &&
1709 To<LayoutBox>(layout_object)->Layer()->FixedToViewport()) {
1710 return nullptr;
1711 }
1712
1713 if (!layout_object->Parent() &&
1714 layout_object->GetNode() == layout_object->GetDocument() &&
1715 layout_object->GetDocument().LocalOwner()) {
1716 layout_object =
1717 layout_object->GetDocument().LocalOwner()->GetLayoutObject();
1718 } else {
1719 layout_object = layout_object->Parent();
1720 }
1721 }
1722
1723 return DynamicTo<LayoutBox>(layout_object);
1724 }
1725
HasHorizontallyScrollableAncestor(LayoutObject * layout_object)1726 bool LayoutBox::HasHorizontallyScrollableAncestor(LayoutObject* layout_object) {
1727 while (layout_object) {
1728 if (layout_object->IsBox() &&
1729 To<LayoutBox>(layout_object)->HasScrollableOverflowX())
1730 return true;
1731
1732 // Scroll is not propagating.
1733 if (layout_object->StyleRef().OverscrollBehaviorX() !=
1734 EOverscrollBehavior::kAuto)
1735 break;
1736
1737 if (!layout_object->Parent() &&
1738 layout_object->GetNode() == layout_object->GetDocument() &&
1739 layout_object->GetDocument().LocalOwner()) {
1740 layout_object =
1741 layout_object->GetDocument().LocalOwner()->GetLayoutObject();
1742 } else {
1743 layout_object = layout_object->Parent();
1744 }
1745 }
1746
1747 return false;
1748 }
1749
ScrollByRecursively(const ScrollOffset & delta)1750 void LayoutBox::ScrollByRecursively(const ScrollOffset& delta) {
1751 NOT_DESTROYED();
1752 if (delta.IsZero() || !IsScrollContainer())
1753 return;
1754
1755 PaintLayerScrollableArea* scrollable_area = GetScrollableArea();
1756 DCHECK(scrollable_area);
1757 ScrollOffset new_scroll_offset = scrollable_area->GetScrollOffset() + delta;
1758 scrollable_area->SetScrollOffset(new_scroll_offset,
1759 mojom::blink::ScrollType::kProgrammatic);
1760
1761 // If this layer can't do the scroll we ask the next layer up that can
1762 // scroll to try.
1763 ScrollOffset remaining_scroll_offset =
1764 new_scroll_offset - scrollable_area->GetScrollOffset();
1765 if (!remaining_scroll_offset.IsZero() && Parent()) {
1766 if (LayoutBox* scrollable_box = EnclosingScrollableBox())
1767 scrollable_box->ScrollByRecursively(remaining_scroll_offset);
1768
1769 LocalFrame* frame = GetFrame();
1770 if (frame && frame->GetPage()) {
1771 frame->GetPage()
1772 ->GetAutoscrollController()
1773 .UpdateAutoscrollLayoutObject();
1774 }
1775 }
1776 // FIXME: If we didn't scroll the whole way, do we want to try looking at
1777 // the frames ownerElement?
1778 // https://bugs.webkit.org/show_bug.cgi?id=28237
1779 }
1780
NeedsPreferredWidthsRecalculation() const1781 bool LayoutBox::NeedsPreferredWidthsRecalculation() const {
1782 NOT_DESTROYED();
1783 return StyleRef().PaddingStart().IsPercentOrCalc() ||
1784 StyleRef().PaddingEnd().IsPercentOrCalc();
1785 }
1786
OriginAdjustmentForScrollbars() const1787 IntSize LayoutBox::OriginAdjustmentForScrollbars() const {
1788 NOT_DESTROYED();
1789 if (CanSkipComputeScrollbars()) {
1790 return IntSize();
1791 } else {
1792 NGPhysicalBoxStrut scrollbars =
1793 ComputeScrollbarsInternal(kClampToContentBox);
1794 return IntSize(scrollbars.left.ToInt(), scrollbars.top.ToInt());
1795 }
1796 }
1797
ScrollOrigin() const1798 IntPoint LayoutBox::ScrollOrigin() const {
1799 NOT_DESTROYED();
1800 return GetScrollableArea() ? GetScrollableArea()->ScrollOrigin() : IntPoint();
1801 }
1802
ScrolledContentOffset() const1803 LayoutSize LayoutBox::ScrolledContentOffset() const {
1804 NOT_DESTROYED();
1805 DCHECK(IsScrollContainer());
1806 DCHECK(GetScrollableArea());
1807 return LayoutSize(GetScrollableArea()->GetScrollOffset());
1808 }
1809
PixelSnappedScrolledContentOffset() const1810 LayoutSize LayoutBox::PixelSnappedScrolledContentOffset() const {
1811 NOT_DESTROYED();
1812 DCHECK(IsScrollContainer());
1813 DCHECK(GetScrollableArea());
1814 return LayoutSize(GetScrollableArea()->ScrollOffsetInt());
1815 }
1816
ClippingRect(const PhysicalOffset & location) const1817 PhysicalRect LayoutBox::ClippingRect(const PhysicalOffset& location) const {
1818 NOT_DESTROYED();
1819 PhysicalRect result(PhysicalRect::InfiniteIntRect());
1820 if (ShouldClipOverflowAlongEitherAxis())
1821 result = OverflowClipRect(location);
1822
1823 if (HasClip())
1824 result.Intersect(ClipRect(location));
1825
1826 return result;
1827 }
1828
PerspectiveOrigin(const PhysicalSize * size) const1829 FloatPoint LayoutBox::PerspectiveOrigin(const PhysicalSize* size) const {
1830 if (!HasTransformRelatedProperty())
1831 return FloatPoint();
1832
1833 // Use the |size| parameter instead of |Size()| if present.
1834 FloatSize float_size = size ? FloatSize(*size) : FloatSize(Size());
1835
1836 return FloatPointForLengthPoint(StyleRef().PerspectiveOrigin(), float_size);
1837 }
1838
MapVisualRectToContainer(const LayoutObject * container_object,const PhysicalOffset & container_offset,const LayoutObject * ancestor,VisualRectFlags visual_rect_flags,TransformState & transform_state) const1839 bool LayoutBox::MapVisualRectToContainer(
1840 const LayoutObject* container_object,
1841 const PhysicalOffset& container_offset,
1842 const LayoutObject* ancestor,
1843 VisualRectFlags visual_rect_flags,
1844 TransformState& transform_state) const {
1845 NOT_DESTROYED();
1846 bool container_preserve_3d = container_object->StyleRef().Preserves3D();
1847
1848 TransformState::TransformAccumulation accumulation =
1849 container_preserve_3d ? TransformState::kAccumulateTransform
1850 : TransformState::kFlattenTransform;
1851
1852 // If there is no transform on this box, adjust for container offset and
1853 // container scrolling, then apply container clip.
1854 if (!ShouldUseTransformFromContainer(container_object)) {
1855 transform_state.Move(container_offset, accumulation);
1856 if (container_object->IsBox() && container_object != ancestor &&
1857 !To<LayoutBox>(container_object)
1858 ->MapContentsRectToBoxSpace(transform_state, accumulation, *this,
1859 visual_rect_flags)) {
1860 return false;
1861 }
1862 return true;
1863 }
1864
1865 // Otherwise, do the following:
1866 // 1. Expand for pixel snapping.
1867 // 2. Generate transformation matrix combining, in this order
1868 // a) transform,
1869 // b) container offset,
1870 // c) container scroll offset,
1871 // d) perspective applied by container.
1872 // 3. Apply transform Transform+flattening.
1873 // 4. Apply container clip.
1874
1875 // 1. Expand for pixel snapping.
1876 // Use EnclosingBoundingBox because we cannot properly compute pixel
1877 // snapping for painted elements within the transform since we don't know
1878 // the desired subpixel accumulation at this point, and the transform may
1879 // include a scale. This only makes sense for non-preserve3D.
1880 if (!StyleRef().Preserves3D()) {
1881 transform_state.Flatten();
1882 transform_state.SetQuad(
1883 FloatQuad(transform_state.LastPlanarQuad().EnclosingBoundingBox()));
1884 }
1885
1886 // 2. Generate transformation matrix.
1887 // a) Transform.
1888 TransformationMatrix transform;
1889 if (Layer() && Layer()->Transform())
1890 transform.Multiply(Layer()->CurrentTransform());
1891
1892 // b) Container offset.
1893 transform.PostTranslate(container_offset.left.ToFloat(),
1894 container_offset.top.ToFloat());
1895
1896 // c) Container scroll offset.
1897 if (container_object->IsBox() && container_object != ancestor &&
1898 To<LayoutBox>(container_object)->ContainedContentsScroll(*this)) {
1899 LayoutSize offset(
1900 -To<LayoutBox>(container_object)->ScrolledContentOffset());
1901 transform.PostTranslate(offset.Width(), offset.Height());
1902 }
1903
1904 bool has_perspective = container_object && container_object->HasLayer() &&
1905 container_object->StyleRef().HasPerspective();
1906 if (has_perspective && RuntimeEnabledFeatures::TransformInteropEnabled() &&
1907 container_object != NonAnonymousAncestor())
1908 has_perspective = false;
1909
1910 // d) Perspective applied by container.
1911 if (has_perspective) {
1912 // Perspective on the container affects us, so we have to factor it in here.
1913 DCHECK(container_object->HasLayer());
1914 FloatPoint perspective_origin;
1915 if (const auto* container_box = DynamicTo<LayoutBox>(container_object))
1916 perspective_origin = container_box->PerspectiveOrigin();
1917
1918 TransformationMatrix perspective_matrix;
1919 perspective_matrix.ApplyPerspective(
1920 container_object->StyleRef().Perspective());
1921 perspective_matrix.ApplyTransformOrigin(perspective_origin.X(),
1922 perspective_origin.Y(), 0);
1923
1924 transform = perspective_matrix * transform;
1925 }
1926
1927 // 3. Apply transform and flatten.
1928 transform_state.ApplyTransform(transform, accumulation);
1929 if (!container_preserve_3d)
1930 transform_state.Flatten();
1931
1932 // 4. Apply container clip.
1933 if (container_object->IsBox() && container_object != ancestor &&
1934 container_object->HasClipRelatedProperty()) {
1935 return To<LayoutBox>(container_object)
1936 ->ApplyBoxClips(transform_state, accumulation, visual_rect_flags);
1937 }
1938
1939 return true;
1940 }
1941
MapContentsRectToBoxSpace(TransformState & transform_state,TransformState::TransformAccumulation accumulation,const LayoutObject & contents,VisualRectFlags visual_rect_flags) const1942 bool LayoutBox::MapContentsRectToBoxSpace(
1943 TransformState& transform_state,
1944 TransformState::TransformAccumulation accumulation,
1945 const LayoutObject& contents,
1946 VisualRectFlags visual_rect_flags) const {
1947 NOT_DESTROYED();
1948 if (!HasClipRelatedProperty())
1949 return true;
1950
1951 if (ContainedContentsScroll(contents))
1952 transform_state.Move(PhysicalOffset(-ScrolledContentOffset()));
1953
1954 return ApplyBoxClips(transform_state, accumulation, visual_rect_flags);
1955 }
1956
ContainedContentsScroll(const LayoutObject & contents) const1957 bool LayoutBox::ContainedContentsScroll(const LayoutObject& contents) const {
1958 NOT_DESTROYED();
1959 if (IsA<LayoutView>(this) &&
1960 contents.StyleRef().GetPosition() == EPosition::kFixed) {
1961 return false;
1962 }
1963 return IsScrollContainer();
1964 }
1965
ApplyBoxClips(TransformState & transform_state,TransformState::TransformAccumulation accumulation,VisualRectFlags visual_rect_flags) const1966 bool LayoutBox::ApplyBoxClips(
1967 TransformState& transform_state,
1968 TransformState::TransformAccumulation accumulation,
1969 VisualRectFlags visual_rect_flags) const {
1970 NOT_DESTROYED();
1971 // This won't work fully correctly for fixed-position elements, who should
1972 // receive CSS clip but for whom the current object is not in the containing
1973 // block chain.
1974 PhysicalRect clip_rect = ClippingRect(PhysicalOffset());
1975
1976 transform_state.Flatten();
1977 PhysicalRect rect(transform_state.LastPlanarQuad().EnclosingBoundingBox());
1978 bool does_intersect;
1979 if (visual_rect_flags & kEdgeInclusive) {
1980 does_intersect = rect.InclusiveIntersect(clip_rect);
1981 } else {
1982 rect.Intersect(clip_rect);
1983 does_intersect = !rect.IsEmpty();
1984 }
1985 transform_state.SetQuad(FloatQuad(FloatRect(rect)));
1986
1987 return does_intersect;
1988 }
1989
PreferredLogicalWidths() const1990 MinMaxSizes LayoutBox::PreferredLogicalWidths() const {
1991 NOT_DESTROYED();
1992 NOTREACHED();
1993 return MinMaxSizes();
1994 }
1995
IntrinsicLogicalWidths(MinMaxSizesType type) const1996 MinMaxSizes LayoutBox::IntrinsicLogicalWidths(MinMaxSizesType type) const {
1997 NOT_DESTROYED();
1998 if (!ShouldComputeSizeAsReplaced() && type == MinMaxSizesType::kContent &&
1999 !StyleRef().AspectRatio().IsAuto()) {
2000 MinMaxSizes sizes;
2001 if (ComputeLogicalWidthFromAspectRatio(&sizes.min_size)) {
2002 sizes.max_size = sizes.min_size;
2003 return sizes;
2004 }
2005 }
2006 const_cast<LayoutBox*>(this)->UpdateCachedIntrinsicLogicalWidthsIfNeeded();
2007 return intrinsic_logical_widths_;
2008 }
2009
UpdateCachedIntrinsicLogicalWidthsIfNeeded()2010 void LayoutBox::UpdateCachedIntrinsicLogicalWidthsIfNeeded() {
2011 NOT_DESTROYED();
2012 if (!IntrinsicLogicalWidthsDirty())
2013 return;
2014
2015 #if DCHECK_IS_ON()
2016 SetLayoutNeededForbiddenScope layout_forbidden_scope(*this);
2017 #endif
2018
2019 intrinsic_logical_widths_ = ComputeIntrinsicLogicalWidths();
2020 intrinsic_logical_widths_percentage_resolution_block_size_ =
2021 LayoutUnit::Min();
2022 ClearIntrinsicLogicalWidthsDirty();
2023 }
2024
OverrideLogicalWidth() const2025 LayoutUnit LayoutBox::OverrideLogicalWidth() const {
2026 NOT_DESTROYED();
2027 DCHECK(HasOverrideLogicalWidth());
2028 if (extra_input_ && extra_input_->override_inline_size)
2029 return *extra_input_->override_inline_size;
2030 return rare_data_->override_logical_width_;
2031 }
2032
OverrideLogicalHeight() const2033 LayoutUnit LayoutBox::OverrideLogicalHeight() const {
2034 NOT_DESTROYED();
2035 DCHECK(HasOverrideLogicalHeight());
2036 if (extra_input_ && extra_input_->override_block_size)
2037 return *extra_input_->override_block_size;
2038 return rare_data_->override_logical_height_;
2039 }
2040
IsOverrideLogicalHeightDefinite() const2041 bool LayoutBox::IsOverrideLogicalHeightDefinite() const {
2042 NOT_DESTROYED();
2043 return extra_input_ && extra_input_->is_override_block_size_definite;
2044 }
2045
HasOverrideLogicalHeight() const2046 bool LayoutBox::HasOverrideLogicalHeight() const {
2047 NOT_DESTROYED();
2048 if (extra_input_ && extra_input_->override_block_size)
2049 return true;
2050 return rare_data_ && rare_data_->override_logical_height_ != -1;
2051 }
2052
HasOverrideLogicalWidth() const2053 bool LayoutBox::HasOverrideLogicalWidth() const {
2054 NOT_DESTROYED();
2055 if (extra_input_ && extra_input_->override_inline_size)
2056 return true;
2057 return rare_data_ && rare_data_->override_logical_width_ != -1;
2058 }
2059
SetOverrideLogicalHeight(LayoutUnit height)2060 void LayoutBox::SetOverrideLogicalHeight(LayoutUnit height) {
2061 NOT_DESTROYED();
2062 DCHECK(!extra_input_);
2063 DCHECK_GE(height, 0);
2064 EnsureRareData().override_logical_height_ = height;
2065 }
2066
SetOverrideLogicalWidth(LayoutUnit width)2067 void LayoutBox::SetOverrideLogicalWidth(LayoutUnit width) {
2068 NOT_DESTROYED();
2069 DCHECK(!extra_input_);
2070 DCHECK_GE(width, 0);
2071 EnsureRareData().override_logical_width_ = width;
2072 }
2073
ClearOverrideLogicalHeight()2074 void LayoutBox::ClearOverrideLogicalHeight() {
2075 NOT_DESTROYED();
2076 DCHECK(!extra_input_);
2077 if (rare_data_)
2078 rare_data_->override_logical_height_ = LayoutUnit(-1);
2079 }
2080
ClearOverrideLogicalWidth()2081 void LayoutBox::ClearOverrideLogicalWidth() {
2082 NOT_DESTROYED();
2083 DCHECK(!extra_input_);
2084 if (rare_data_)
2085 rare_data_->override_logical_width_ = LayoutUnit(-1);
2086 }
2087
ClearOverrideSize()2088 void LayoutBox::ClearOverrideSize() {
2089 NOT_DESTROYED();
2090 ClearOverrideLogicalHeight();
2091 ClearOverrideLogicalWidth();
2092 }
2093
OverrideContentLogicalWidth() const2094 LayoutUnit LayoutBox::OverrideContentLogicalWidth() const {
2095 NOT_DESTROYED();
2096 return (OverrideLogicalWidth() - BorderAndPaddingLogicalWidth() -
2097 ComputeLogicalScrollbars().InlineSum())
2098 .ClampNegativeToZero();
2099 }
2100
OverrideContentLogicalHeight() const2101 LayoutUnit LayoutBox::OverrideContentLogicalHeight() const {
2102 NOT_DESTROYED();
2103 return (OverrideLogicalHeight() - BorderAndPaddingLogicalHeight() -
2104 ComputeLogicalScrollbars().BlockSum())
2105 .ClampNegativeToZero();
2106 }
2107
OverrideContainingBlockContentWidth() const2108 LayoutUnit LayoutBox::OverrideContainingBlockContentWidth() const {
2109 NOT_DESTROYED();
2110 DCHECK(HasOverrideContainingBlockContentWidth());
2111 return ContainingBlock()->StyleRef().IsHorizontalWritingMode()
2112 ? OverrideContainingBlockContentLogicalWidth()
2113 : OverrideContainingBlockContentLogicalHeight();
2114 }
2115
OverrideContainingBlockContentHeight() const2116 LayoutUnit LayoutBox::OverrideContainingBlockContentHeight() const {
2117 NOT_DESTROYED();
2118 DCHECK(HasOverrideContainingBlockContentHeight());
2119 return ContainingBlock()->StyleRef().IsHorizontalWritingMode()
2120 ? OverrideContainingBlockContentLogicalHeight()
2121 : OverrideContainingBlockContentLogicalWidth();
2122 }
2123
HasOverrideContainingBlockContentWidth() const2124 bool LayoutBox::HasOverrideContainingBlockContentWidth() const {
2125 NOT_DESTROYED();
2126 if (!ContainingBlock())
2127 return false;
2128
2129 return ContainingBlock()->StyleRef().IsHorizontalWritingMode()
2130 ? HasOverrideContainingBlockContentLogicalWidth()
2131 : HasOverrideContainingBlockContentLogicalHeight();
2132 }
2133
HasOverrideContainingBlockContentHeight() const2134 bool LayoutBox::HasOverrideContainingBlockContentHeight() const {
2135 NOT_DESTROYED();
2136 if (!ContainingBlock())
2137 return false;
2138
2139 return ContainingBlock()->StyleRef().IsHorizontalWritingMode()
2140 ? HasOverrideContainingBlockContentLogicalHeight()
2141 : HasOverrideContainingBlockContentLogicalWidth();
2142 }
2143
2144 // TODO (lajava) Shouldn't we implement these functions based on physical
2145 // direction ?.
OverrideContainingBlockContentLogicalWidth() const2146 LayoutUnit LayoutBox::OverrideContainingBlockContentLogicalWidth() const {
2147 NOT_DESTROYED();
2148 DCHECK(HasOverrideContainingBlockContentLogicalWidth());
2149 if (extra_input_)
2150 return extra_input_->containing_block_content_inline_size;
2151 return rare_data_->override_containing_block_content_logical_width_;
2152 }
2153
2154 // TODO (lajava) Shouldn't we implement these functions based on physical
2155 // direction ?.
OverrideContainingBlockContentLogicalHeight() const2156 LayoutUnit LayoutBox::OverrideContainingBlockContentLogicalHeight() const {
2157 NOT_DESTROYED();
2158 DCHECK(HasOverrideContainingBlockContentLogicalHeight());
2159 if (extra_input_)
2160 return extra_input_->containing_block_content_block_size;
2161 return rare_data_->override_containing_block_content_logical_height_;
2162 }
2163
2164 // TODO (lajava) Shouldn't we implement these functions based on physical
2165 // direction ?.
HasOverrideContainingBlockContentLogicalWidth() const2166 bool LayoutBox::HasOverrideContainingBlockContentLogicalWidth() const {
2167 NOT_DESTROYED();
2168 if (extra_input_)
2169 return true;
2170 return rare_data_ &&
2171 rare_data_->has_override_containing_block_content_logical_width_;
2172 }
2173
2174 // TODO (lajava) Shouldn't we implement these functions based on physical
2175 // direction ?.
HasOverrideContainingBlockContentLogicalHeight() const2176 bool LayoutBox::HasOverrideContainingBlockContentLogicalHeight() const {
2177 NOT_DESTROYED();
2178 if (extra_input_)
2179 return true;
2180 return rare_data_ &&
2181 rare_data_->has_override_containing_block_content_logical_height_;
2182 }
2183
2184 // TODO (lajava) Shouldn't we implement these functions based on physical
2185 // direction ?.
SetOverrideContainingBlockContentLogicalWidth(LayoutUnit logical_width)2186 void LayoutBox::SetOverrideContainingBlockContentLogicalWidth(
2187 LayoutUnit logical_width) {
2188 NOT_DESTROYED();
2189 DCHECK(!extra_input_);
2190 DCHECK_GE(logical_width, LayoutUnit(-1));
2191 EnsureRareData().override_containing_block_content_logical_width_ =
2192 logical_width;
2193 EnsureRareData().has_override_containing_block_content_logical_width_ = true;
2194 }
2195
2196 // TODO (lajava) Shouldn't we implement these functions based on physical
2197 // direction ?.
SetOverrideContainingBlockContentLogicalHeight(LayoutUnit logical_height)2198 void LayoutBox::SetOverrideContainingBlockContentLogicalHeight(
2199 LayoutUnit logical_height) {
2200 NOT_DESTROYED();
2201 DCHECK(!extra_input_);
2202 DCHECK_GE(logical_height, LayoutUnit(-1));
2203 EnsureRareData().override_containing_block_content_logical_height_ =
2204 logical_height;
2205 EnsureRareData().has_override_containing_block_content_logical_height_ = true;
2206 }
2207
2208 // TODO (lajava) Shouldn't we implement these functions based on physical
2209 // direction ?.
ClearOverrideContainingBlockContentSize()2210 void LayoutBox::ClearOverrideContainingBlockContentSize() {
2211 NOT_DESTROYED();
2212 DCHECK(!extra_input_);
2213 if (!rare_data_)
2214 return;
2215 EnsureRareData().has_override_containing_block_content_logical_width_ = false;
2216 EnsureRareData().has_override_containing_block_content_logical_height_ =
2217 false;
2218 }
2219
OverridePercentageResolutionBlockSize() const2220 LayoutUnit LayoutBox::OverridePercentageResolutionBlockSize() const {
2221 NOT_DESTROYED();
2222 DCHECK(HasOverridePercentageResolutionBlockSize());
2223 return rare_data_->override_percentage_resolution_block_size_;
2224 }
2225
HasOverridePercentageResolutionBlockSize() const2226 bool LayoutBox::HasOverridePercentageResolutionBlockSize() const {
2227 NOT_DESTROYED();
2228 return rare_data_ &&
2229 rare_data_->has_override_percentage_resolution_block_size_;
2230 }
2231
SetOverridePercentageResolutionBlockSize(LayoutUnit logical_height)2232 void LayoutBox::SetOverridePercentageResolutionBlockSize(
2233 LayoutUnit logical_height) {
2234 NOT_DESTROYED();
2235 DCHECK_GE(logical_height, LayoutUnit(-1));
2236 auto& rare_data = EnsureRareData();
2237 rare_data.override_percentage_resolution_block_size_ = logical_height;
2238 rare_data.has_override_percentage_resolution_block_size_ = true;
2239 }
2240
ClearOverridePercentageResolutionBlockSize()2241 void LayoutBox::ClearOverridePercentageResolutionBlockSize() {
2242 NOT_DESTROYED();
2243 if (!rare_data_)
2244 return;
2245 EnsureRareData().has_override_percentage_resolution_block_size_ = false;
2246 }
2247
OverrideAvailableInlineSize() const2248 LayoutUnit LayoutBox::OverrideAvailableInlineSize() const {
2249 NOT_DESTROYED();
2250 DCHECK(HasOverrideAvailableInlineSize());
2251 if (extra_input_)
2252 return extra_input_->available_inline_size;
2253 return LayoutUnit();
2254 }
2255
AdjustBorderBoxLogicalWidthForBoxSizing(float width) const2256 LayoutUnit LayoutBox::AdjustBorderBoxLogicalWidthForBoxSizing(
2257 float width) const {
2258 NOT_DESTROYED();
2259 LayoutUnit borders_plus_padding = CollapsedBorderAndCSSPaddingLogicalWidth();
2260 LayoutUnit result(width);
2261 if (StyleRef().BoxSizing() == EBoxSizing::kContentBox)
2262 return result + borders_plus_padding;
2263 return std::max(result, borders_plus_padding);
2264 }
2265
AdjustBorderBoxLogicalHeightForBoxSizing(float height) const2266 LayoutUnit LayoutBox::AdjustBorderBoxLogicalHeightForBoxSizing(
2267 float height) const {
2268 NOT_DESTROYED();
2269 LayoutUnit borders_plus_padding = CollapsedBorderAndCSSPaddingLogicalHeight();
2270 LayoutUnit result(height);
2271 if (StyleRef().BoxSizing() == EBoxSizing::kContentBox)
2272 return result + borders_plus_padding;
2273 return std::max(result, borders_plus_padding);
2274 }
2275
AdjustContentBoxLogicalWidthForBoxSizing(float width) const2276 LayoutUnit LayoutBox::AdjustContentBoxLogicalWidthForBoxSizing(
2277 float width) const {
2278 NOT_DESTROYED();
2279 LayoutUnit result(width);
2280 if (StyleRef().BoxSizing() == EBoxSizing::kBorderBox)
2281 result -= CollapsedBorderAndCSSPaddingLogicalWidth();
2282 return std::max(LayoutUnit(), result);
2283 }
2284
AdjustContentBoxLogicalHeightForBoxSizing(float height) const2285 LayoutUnit LayoutBox::AdjustContentBoxLogicalHeightForBoxSizing(
2286 float height) const {
2287 NOT_DESTROYED();
2288 LayoutUnit result(height);
2289 if (StyleRef().BoxSizing() == EBoxSizing::kBorderBox)
2290 result -= CollapsedBorderAndCSSPaddingLogicalHeight();
2291 return std::max(LayoutUnit(), result);
2292 }
2293
2294 // Hit Testing
MayIntersect(const HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset) const2295 bool LayoutBox::MayIntersect(const HitTestResult& result,
2296 const HitTestLocation& hit_test_location,
2297 const PhysicalOffset& accumulated_offset) const {
2298 NOT_DESTROYED();
2299 // Check if we need to do anything at all.
2300 // If we have clipping, then we can't have any spillout.
2301 // TODO(pdr): Why is this optimization not valid for the effective root?
2302 if (UNLIKELY(IsEffectiveRootScroller()))
2303 return true;
2304
2305 PhysicalRect overflow_box;
2306 if (result.GetHitTestRequest().GetType() &
2307 HitTestRequest::kHitTestVisualOverflow) {
2308 overflow_box = PhysicalVisualOverflowRectIncludingFilters();
2309 } else {
2310 // Unite because overflow may not include borders.
2311 overflow_box = PhysicalBorderBoxRect();
2312 if (!ShouldClipOverflowAlongBothAxis() && HasVisualOverflow())
2313 overflow_box.Unite(PhysicalVisualOverflowRect());
2314 }
2315
2316 overflow_box.Move(accumulated_offset);
2317 return hit_test_location.Intersects(overflow_box);
2318 }
2319
HitTestAllPhases(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,HitTestFilter hit_test_filter)2320 bool LayoutBox::HitTestAllPhases(HitTestResult& result,
2321 const HitTestLocation& hit_test_location,
2322 const PhysicalOffset& accumulated_offset,
2323 HitTestFilter hit_test_filter) {
2324 NOT_DESTROYED();
2325 if (!MayIntersect(result, hit_test_location, accumulated_offset))
2326 return false;
2327 return LayoutObject::HitTestAllPhases(result, hit_test_location,
2328 accumulated_offset, hit_test_filter);
2329 }
2330
NodeAtPoint(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,HitTestAction action)2331 bool LayoutBox::NodeAtPoint(HitTestResult& result,
2332 const HitTestLocation& hit_test_location,
2333 const PhysicalOffset& accumulated_offset,
2334 HitTestAction action) {
2335 NOT_DESTROYED();
2336 if (!MayIntersect(result, hit_test_location, accumulated_offset))
2337 return false;
2338
2339 bool should_hit_test_self = IsInSelfHitTestingPhase(action);
2340
2341 if (should_hit_test_self && IsScrollContainer() &&
2342 HitTestOverflowControl(result, hit_test_location, accumulated_offset))
2343 return true;
2344
2345 bool skip_children = (result.GetHitTestRequest().GetStopNode() == this) ||
2346 ChildPaintBlockedByDisplayLock();
2347 if (!skip_children && ShouldClipOverflowAlongEitherAxis()) {
2348 // PaintLayer::HitTestContentsForFragments checked the fragments'
2349 // foreground rect for intersection if a layer is self painting,
2350 // so only do the overflow clip check here for non-self-painting layers.
2351 if (!HasSelfPaintingLayer() &&
2352 !hit_test_location.Intersects(OverflowClipRect(
2353 accumulated_offset, kExcludeOverlayScrollbarSizeForHitTesting))) {
2354 skip_children = true;
2355 }
2356 if (!skip_children && StyleRef().HasBorderRadius()) {
2357 PhysicalRect bounds_rect(accumulated_offset, Size());
2358 skip_children = !hit_test_location.Intersects(
2359 RoundedBorderGeometry::PixelSnappedRoundedInnerBorder(StyleRef(),
2360 bounds_rect));
2361 }
2362 }
2363
2364 if (!skip_children &&
2365 HitTestChildren(result, hit_test_location, accumulated_offset, action)) {
2366 return true;
2367 }
2368
2369 if (StyleRef().HasBorderRadius() &&
2370 HitTestClippedOutByBorder(hit_test_location, accumulated_offset))
2371 return false;
2372
2373 // Now hit test ourselves.
2374 if (should_hit_test_self &&
2375 VisibleToHitTestRequest(result.GetHitTestRequest())) {
2376 PhysicalRect bounds_rect;
2377 if (result.GetHitTestRequest().GetType() &
2378 HitTestRequest::kHitTestVisualOverflow) {
2379 bounds_rect = PhysicalVisualOverflowRectIncludingFilters();
2380 } else {
2381 bounds_rect = PhysicalBorderBoxRect();
2382 }
2383 bounds_rect.Move(accumulated_offset);
2384 if (hit_test_location.Intersects(bounds_rect)) {
2385 UpdateHitTestResult(result,
2386 hit_test_location.Point() - accumulated_offset);
2387 if (result.AddNodeToListBasedTestResult(NodeForHitTest(),
2388 hit_test_location,
2389 bounds_rect) == kStopHitTesting)
2390 return true;
2391 }
2392 }
2393
2394 return false;
2395 }
2396
HitTestChildren(HitTestResult & result,const HitTestLocation & hit_test_location,const PhysicalOffset & accumulated_offset,HitTestAction action)2397 bool LayoutBox::HitTestChildren(HitTestResult& result,
2398 const HitTestLocation& hit_test_location,
2399 const PhysicalOffset& accumulated_offset,
2400 HitTestAction action) {
2401 NOT_DESTROYED();
2402 for (LayoutObject* child = SlowLastChild(); child;
2403 child = child->PreviousSibling()) {
2404 if (child->HasLayer() &&
2405 To<LayoutBoxModelObject>(child)->Layer()->IsSelfPaintingLayer())
2406 continue;
2407
2408 PhysicalOffset child_accumulated_offset = accumulated_offset;
2409 if (auto* box = DynamicTo<LayoutBox>(child))
2410 child_accumulated_offset += box->PhysicalLocation(this);
2411
2412 if (child->NodeAtPoint(result, hit_test_location, child_accumulated_offset,
2413 action))
2414 return true;
2415 }
2416
2417 return false;
2418 }
2419
HitTestClippedOutByBorder(const HitTestLocation & hit_test_location,const PhysicalOffset & border_box_location) const2420 bool LayoutBox::HitTestClippedOutByBorder(
2421 const HitTestLocation& hit_test_location,
2422 const PhysicalOffset& border_box_location) const {
2423 NOT_DESTROYED();
2424 PhysicalRect border_rect = PhysicalBorderBoxRect();
2425 border_rect.Move(border_box_location);
2426 return !hit_test_location.Intersects(
2427 RoundedBorderGeometry::PixelSnappedRoundedBorder(StyleRef(),
2428 border_rect));
2429 }
2430
Paint(const PaintInfo & paint_info) const2431 void LayoutBox::Paint(const PaintInfo& paint_info) const {
2432 NOT_DESTROYED();
2433 BoxPainter(*this).Paint(paint_info);
2434 }
2435
PaintBoxDecorationBackground(const PaintInfo & paint_info,const PhysicalOffset & paint_offset) const2436 void LayoutBox::PaintBoxDecorationBackground(
2437 const PaintInfo& paint_info,
2438 const PhysicalOffset& paint_offset) const {
2439 NOT_DESTROYED();
2440 BoxPainter(*this).PaintBoxDecorationBackground(paint_info, paint_offset);
2441 }
2442
GetBackgroundPaintedExtent(PhysicalRect & painted_extent) const2443 bool LayoutBox::GetBackgroundPaintedExtent(PhysicalRect& painted_extent) const {
2444 NOT_DESTROYED();
2445 DCHECK(StyleRef().HasBackground());
2446
2447 // LayoutView is special in the sense that it expands to the whole canvas,
2448 // thus can't be handled by this function.
2449 DCHECK(!IsA<LayoutView>(this));
2450
2451 PhysicalRect background_rect(PhysicalBorderBoxRect());
2452
2453 Color background_color = ResolveColor(GetCSSPropertyBackgroundColor());
2454 if (background_color.Alpha()) {
2455 painted_extent = background_rect;
2456 return true;
2457 }
2458
2459 if (!StyleRef().BackgroundLayers().GetImage() ||
2460 StyleRef().BackgroundLayers().Next()) {
2461 painted_extent = background_rect;
2462 return true;
2463 }
2464
2465 BackgroundImageGeometry geometry(*this);
2466 // TODO(schenney): This function should be rethought as it's called during
2467 // and outside of the paint phase. Potentially returning different results at
2468 // different phases. crbug.com/732934
2469 geometry.Calculate(nullptr, PaintPhase::kBlockBackground,
2470 kGlobalPaintNormalPhase, StyleRef().BackgroundLayers(),
2471 background_rect);
2472 if (geometry.HasNonLocalGeometry())
2473 return false;
2474 painted_extent = PhysicalRect(geometry.SnappedDestRect());
2475 return true;
2476 }
2477
BackgroundIsKnownToBeOpaqueInRect(const PhysicalRect & local_rect) const2478 bool LayoutBox::BackgroundIsKnownToBeOpaqueInRect(
2479 const PhysicalRect& local_rect) const {
2480 NOT_DESTROYED();
2481 // If the element has appearance, it might be painted by theme.
2482 // We cannot be sure if theme paints the background opaque.
2483 // In this case it is safe to not assume opaqueness.
2484 // FIXME: May be ask theme if it paints opaque.
2485 if (StyleRef().HasEffectiveAppearance())
2486 return false;
2487 // FIXME: Check the opaqueness of background images.
2488
2489 // FIXME: Use rounded rect if border radius is present.
2490 if (StyleRef().HasBorderRadius())
2491 return false;
2492 if (HasClipPath())
2493 return false;
2494 if (StyleRef().HasBlendMode())
2495 return false;
2496 return PhysicalBackgroundRect(kBackgroundKnownOpaqueRect)
2497 .Contains(local_rect);
2498 }
2499
2500 // TODO(wangxianzhu): The current rules are very basic. May use more complex
2501 // rules if they can improve LCD text.
TextIsKnownToBeOnOpaqueBackground() const2502 bool LayoutBox::TextIsKnownToBeOnOpaqueBackground() const {
2503 NOT_DESTROYED();
2504 // Text may overflow the background area.
2505 if (!ShouldClipOverflowAlongEitherAxis())
2506 return false;
2507 // Same as BackgroundIsKnownToBeOpaqueInRect() about appearance.
2508 if (StyleRef().HasEffectiveAppearance())
2509 return false;
2510
2511 PhysicalRect rect = OverflowClipRect(PhysicalOffset());
2512 return PhysicalBackgroundRect(kBackgroundKnownOpaqueRect).Contains(rect);
2513 }
2514
IsCandidateForOpaquenessTest(const LayoutBox & child_box)2515 static bool IsCandidateForOpaquenessTest(const LayoutBox& child_box) {
2516 // Skip all layers to simplify ForegroundIsKnownToBeOpaqueInRect(). This
2517 // covers cases of clipped, transformed, translucent, composited, etc.
2518 if (child_box.HasLayer())
2519 return false;
2520 const ComputedStyle& child_style = child_box.StyleRef();
2521 if (child_style.Visibility() != EVisibility::kVisible ||
2522 child_style.ShapeOutside())
2523 return false;
2524 if (child_box.Size().IsZero())
2525 return false;
2526 return true;
2527 }
2528
ForegroundIsKnownToBeOpaqueInRect(const PhysicalRect & local_rect,unsigned max_depth_to_test) const2529 bool LayoutBox::ForegroundIsKnownToBeOpaqueInRect(
2530 const PhysicalRect& local_rect,
2531 unsigned max_depth_to_test) const {
2532 NOT_DESTROYED();
2533 if (!max_depth_to_test)
2534 return false;
2535 for (LayoutObject* child = SlowFirstChild(); child;
2536 child = child->NextSibling()) {
2537 if (!child->IsBox())
2538 continue;
2539 auto* child_box = To<LayoutBox>(child);
2540 if (!IsCandidateForOpaquenessTest(*child_box))
2541 continue;
2542 DCHECK(!child_box->IsPositioned());
2543 PhysicalRect child_local_rect = local_rect;
2544 child_local_rect.Move(-child_box->PhysicalLocation());
2545 if (child_local_rect.Y() < 0 || child_local_rect.X() < 0) {
2546 // If there is unobscured area above/left of a static positioned box then
2547 // the rect is probably not covered. This can cause false-negative in
2548 // non-horizontal-tb writing mode but is allowed.
2549 return false;
2550 }
2551 if (child_local_rect.Bottom() > child_box->Size().Height() ||
2552 child_local_rect.Right() > child_box->Size().Width())
2553 continue;
2554 if (child_box->BackgroundIsKnownToBeOpaqueInRect(child_local_rect))
2555 return true;
2556 if (child_box->ForegroundIsKnownToBeOpaqueInRect(child_local_rect,
2557 max_depth_to_test - 1))
2558 return true;
2559 }
2560 return false;
2561 }
2562
2563 DISABLE_CFI_PERF
ComputeBackgroundIsKnownToBeObscured() const2564 bool LayoutBox::ComputeBackgroundIsKnownToBeObscured() const {
2565 NOT_DESTROYED();
2566 if (ScrollsOverflow())
2567 return false;
2568 // Test to see if the children trivially obscure the background.
2569 if (!StyleRef().HasBackground())
2570 return false;
2571 // Root background painting is special.
2572 if (IsA<LayoutView>(this))
2573 return false;
2574 if (StyleRef().BoxShadow())
2575 return false;
2576 PhysicalRect background_rect;
2577 if (!GetBackgroundPaintedExtent(background_rect))
2578 return false;
2579 return ForegroundIsKnownToBeOpaqueInRect(background_rect,
2580 kBackgroundObscurationTestMaxDepth);
2581 }
2582
PaintMask(const PaintInfo & paint_info,const PhysicalOffset & paint_offset) const2583 void LayoutBox::PaintMask(const PaintInfo& paint_info,
2584 const PhysicalOffset& paint_offset) const {
2585 NOT_DESTROYED();
2586 BoxPainter(*this).PaintMask(paint_info, paint_offset);
2587 }
2588
ImageChanged(WrappedImagePtr image,CanDeferInvalidation defer)2589 void LayoutBox::ImageChanged(WrappedImagePtr image,
2590 CanDeferInvalidation defer) {
2591 NOT_DESTROYED();
2592 bool is_box_reflect_image =
2593 (StyleRef().BoxReflect() && StyleRef().BoxReflect()->Mask().GetImage() &&
2594 StyleRef().BoxReflect()->Mask().GetImage()->Data() == image);
2595
2596 if (is_box_reflect_image && HasLayer()) {
2597 Layer()->SetFilterOnEffectNodeDirty();
2598 SetNeedsPaintPropertyUpdate();
2599 }
2600
2601 // TODO(chrishtr): support delayed paint invalidation for animated border
2602 // images.
2603 if ((StyleRef().BorderImage().GetImage() &&
2604 StyleRef().BorderImage().GetImage()->Data() == image) ||
2605 (StyleRef().MaskBoxImage().GetImage() &&
2606 StyleRef().MaskBoxImage().GetImage()->Data() == image) ||
2607 is_box_reflect_image) {
2608 SetShouldDoFullPaintInvalidationWithoutGeometryChange(
2609 PaintInvalidationReason::kImage);
2610 } else {
2611 for (const FillLayer* layer = &StyleRef().MaskLayers(); layer;
2612 layer = layer->Next()) {
2613 if (layer->GetImage() && image == layer->GetImage()->Data()) {
2614 SetShouldDoFullPaintInvalidationWithoutGeometryChange(
2615 PaintInvalidationReason::kImage);
2616 break;
2617 }
2618 }
2619 }
2620
2621 if (!BackgroundTransfersToView()) {
2622 for (const FillLayer* layer = &StyleRef().BackgroundLayers(); layer;
2623 layer = layer->Next()) {
2624 if (layer->GetImage() && image == layer->GetImage()->Data()) {
2625 bool maybe_animated =
2626 layer->GetImage()->CachedImage() &&
2627 layer->GetImage()->CachedImage()->GetImage() &&
2628 layer->GetImage()->CachedImage()->GetImage()->MaybeAnimated();
2629 if (defer == CanDeferInvalidation::kYes && maybe_animated)
2630 SetMayNeedPaintInvalidationAnimatedBackgroundImage();
2631 else
2632 SetBackgroundNeedsFullPaintInvalidation();
2633 break;
2634 }
2635 }
2636 }
2637
2638 ShapeValue* shape_outside_value = StyleRef().ShapeOutside();
2639 if (!GetFrameView()->IsInPerformLayout() && IsFloating() &&
2640 shape_outside_value && shape_outside_value->GetImage() &&
2641 shape_outside_value->GetImage()->Data() == image) {
2642 ShapeOutsideInfo& info = ShapeOutsideInfo::EnsureInfo(*this);
2643 if (!info.IsComputingShape()) {
2644 info.MarkShapeAsDirty();
2645 MarkShapeOutsideDependentsForLayout();
2646 }
2647 }
2648 }
2649
ComputeResourcePriority() const2650 ResourcePriority LayoutBox::ComputeResourcePriority() const {
2651 NOT_DESTROYED();
2652 PhysicalRect view_bounds = ViewRect();
2653 PhysicalRect object_bounds = PhysicalContentBoxRect();
2654 // TODO(japhet): Is this IgnoreTransforms correct? Would it be better to use
2655 // the visual rect (which has ancestor clips and transforms applied)? Should
2656 // we map to the top-level viewport instead of the current (sub) frame?
2657 object_bounds.Move(LocalToAbsolutePoint(PhysicalOffset(), kIgnoreTransforms));
2658
2659 // The object bounds might be empty right now, so intersects will fail since
2660 // it doesn't deal with empty rects. Use LayoutRect::contains in that case.
2661 bool is_visible;
2662 if (!object_bounds.IsEmpty())
2663 is_visible = view_bounds.Intersects(object_bounds);
2664 else
2665 is_visible = view_bounds.Contains(object_bounds);
2666
2667 PhysicalRect screen_rect;
2668 if (!object_bounds.IsEmpty()) {
2669 screen_rect = view_bounds;
2670 screen_rect.Intersect(object_bounds);
2671 }
2672
2673 int screen_area = 0;
2674 if (!screen_rect.IsEmpty() && is_visible)
2675 screen_area = (screen_rect.Width() * screen_rect.Height()).ToInt();
2676 return ResourcePriority(
2677 is_visible ? ResourcePriority::kVisible : ResourcePriority::kNotVisible,
2678 screen_area);
2679 }
2680
LocationChanged()2681 void LayoutBox::LocationChanged() {
2682 NOT_DESTROYED();
2683 // The location may change because of layout of other objects. Should check
2684 // this object for paint invalidation.
2685 if (!NeedsLayout())
2686 SetShouldCheckForPaintInvalidation();
2687 }
2688
SizeChanged()2689 void LayoutBox::SizeChanged() {
2690 NOT_DESTROYED();
2691 // The size may change because of layout of other objects. Should check this
2692 // object for paint invalidation.
2693 if (!NeedsLayout())
2694 SetShouldCheckForPaintInvalidation();
2695 }
2696
IntersectsVisibleViewport() const2697 bool LayoutBox::IntersectsVisibleViewport() const {
2698 NOT_DESTROYED();
2699 PhysicalRect rect = PhysicalVisualOverflowRect();
2700 LayoutView* layout_view = View();
2701 while (layout_view->GetFrame()->OwnerLayoutObject())
2702 layout_view = layout_view->GetFrame()->OwnerLayoutObject()->View();
2703 MapToVisualRectInAncestorSpace(layout_view, rect);
2704 return rect.Intersects(PhysicalRect(
2705 layout_view->GetFrameView()->GetScrollableArea()->VisibleContentRect()));
2706 }
2707
EnsureIsReadyForPaintInvalidation()2708 void LayoutBox::EnsureIsReadyForPaintInvalidation() {
2709 NOT_DESTROYED();
2710 LayoutBoxModelObject::EnsureIsReadyForPaintInvalidation();
2711
2712 bool new_obscured = ComputeBackgroundIsKnownToBeObscured();
2713 if (BackgroundIsKnownToBeObscured() != new_obscured) {
2714 SetBackgroundIsKnownToBeObscured(new_obscured);
2715 SetBackgroundNeedsFullPaintInvalidation();
2716 }
2717
2718 if (MayNeedPaintInvalidationAnimatedBackgroundImage() &&
2719 !BackgroundIsKnownToBeObscured()) {
2720 SetBackgroundNeedsFullPaintInvalidation();
2721 SetShouldDelayFullPaintInvalidation();
2722 }
2723
2724 if (ShouldDelayFullPaintInvalidation() && IntersectsVisibleViewport()) {
2725 // Do regular full paint invalidation if the object with delayed paint
2726 // invalidation is on screen.
2727 ClearShouldDelayFullPaintInvalidation();
2728 DCHECK(ShouldDoFullPaintInvalidation());
2729 }
2730 }
2731
InvalidatePaintRectangle(const PhysicalRect & dirty_rect)2732 void LayoutBox::InvalidatePaintRectangle(const PhysicalRect& dirty_rect) {
2733 NOT_DESTROYED();
2734 DCHECK_NE(GetDocument().Lifecycle().GetState(), DocumentLifecycle::kInPaint);
2735
2736 if (dirty_rect.IsEmpty())
2737 return;
2738
2739 EnsureRareData().partial_invalidation_rect_.Unite(dirty_rect);
2740 SetShouldCheckForPaintInvalidationWithoutGeometryChange();
2741 }
2742
ClearPartialInvalidationVisualRect() const2743 void LayoutBox::ClearPartialInvalidationVisualRect() const {
2744 NOT_DESTROYED();
2745 if (rare_data_)
2746 rare_data_->partial_invalidation_rect_ = PhysicalRect();
2747 }
2748
PartialInvalidationVisualRect() const2749 IntRect LayoutBox::PartialInvalidationVisualRect() const {
2750 NOT_DESTROYED();
2751 if (!rare_data_)
2752 return IntRect();
2753 PhysicalRect rect = rare_data_->partial_invalidation_rect_;
2754 if (rect.IsEmpty())
2755 return IntRect();
2756 rect.Move(FirstFragment().PaintOffset());
2757 return EnclosingIntRect(rect);
2758 }
2759
InvalidatePaint(const PaintInvalidatorContext & context) const2760 void LayoutBox::InvalidatePaint(const PaintInvalidatorContext& context) const {
2761 NOT_DESTROYED();
2762 BoxPaintInvalidator(*this, context).InvalidatePaint();
2763 }
2764
ClearPaintFlags()2765 void LayoutBox::ClearPaintFlags() {
2766 NOT_DESTROYED();
2767 LayoutObject::ClearPaintFlags();
2768
2769 if (auto* scrollable_area = GetScrollableArea()) {
2770 if (auto* scrollbar =
2771 DynamicTo<CustomScrollbar>(scrollable_area->HorizontalScrollbar()))
2772 scrollbar->ClearPaintFlags();
2773 if (auto* scrollbar =
2774 DynamicTo<CustomScrollbar>(scrollable_area->VerticalScrollbar()))
2775 scrollbar->ClearPaintFlags();
2776 }
2777 }
2778
OverflowClipRect(const PhysicalOffset & location,OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const2779 PhysicalRect LayoutBox::OverflowClipRect(
2780 const PhysicalOffset& location,
2781 OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
2782 NOT_DESTROYED();
2783 PhysicalRect clip_rect;
2784
2785 if (IsEffectiveRootScroller()) {
2786 // If this box is the effective root scroller, use the viewport clipping
2787 // rect since it will account for the URL bar correctly which the border
2788 // box does not. We can do this because the effective root scroller is
2789 // restricted such that it exactly fills the viewport. See
2790 // RootScrollerController::IsValidRootScroller()
2791 clip_rect = PhysicalRect(location, View()->ViewRect().size);
2792 } else {
2793 clip_rect = PhysicalBorderBoxRect();
2794 clip_rect.Contract(BorderBoxOutsets());
2795 clip_rect.Move(location);
2796 if (HasNonVisibleOverflow()) {
2797 const auto overflow_clip = GetOverflowClipAxes();
2798 if (overflow_clip == kOverflowClipBothAxis) {
2799 clip_rect.Inflate(StyleRef().OverflowClipMargin());
2800 } else {
2801 auto infinite_rect = LayoutRect::InfiniteIntRect();
2802 if ((overflow_clip & kOverflowClipX) == kNoOverflowClip) {
2803 clip_rect.offset.left = LayoutUnit(infinite_rect.X());
2804 clip_rect.size.width = LayoutUnit(infinite_rect.Width());
2805 }
2806 if ((overflow_clip & kOverflowClipY) == kNoOverflowClip) {
2807 clip_rect.offset.top = LayoutUnit(infinite_rect.Y());
2808 clip_rect.size.height = LayoutUnit(infinite_rect.Height());
2809 }
2810 }
2811 }
2812 }
2813
2814 if (IsScrollContainer())
2815 ExcludeScrollbars(clip_rect, overlay_scrollbar_clip_behavior);
2816
2817 auto* input = DynamicTo<HTMLInputElement>(GetNode());
2818 if (UNLIKELY(input)) {
2819 // As for LayoutButton, ControlClip is to for not BUTTONs but INPUT
2820 // buttons for IE/Firefox compatibility.
2821 if (IsTextFieldIncludingNG() || IsButtonIncludingNG()) {
2822 DCHECK(HasControlClip());
2823 PhysicalRect control_clip = PhysicalPaddingBoxRect();
2824 control_clip.Move(location);
2825 clip_rect.Intersect(control_clip);
2826 }
2827 } else if (UNLIKELY(IsMenuList(this))) {
2828 DCHECK(HasControlClip());
2829 PhysicalRect control_clip = PhysicalContentBoxRect();
2830 control_clip.Move(location);
2831 clip_rect.Intersect(control_clip);
2832 } else {
2833 DCHECK(!HasControlClip());
2834 }
2835
2836 return clip_rect;
2837 }
2838
HasControlClip() const2839 bool LayoutBox::HasControlClip() const {
2840 NOT_DESTROYED();
2841 return UNLIKELY(IsTextFieldIncludingNG() || IsFileUploadControl() ||
2842 IsMenuList(this) ||
2843 (IsButtonIncludingNG() && IsA<HTMLInputElement>(GetNode())));
2844 }
2845
ExcludeScrollbars(PhysicalRect & rect,OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const2846 void LayoutBox::ExcludeScrollbars(
2847 PhysicalRect& rect,
2848 OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
2849 NOT_DESTROYED();
2850 if (CanSkipComputeScrollbars())
2851 return;
2852
2853 NGPhysicalBoxStrut scrollbars = ComputeScrollbarsInternal(
2854 kDoNotClampToContentBox, overlay_scrollbar_clip_behavior);
2855 rect.offset.top += scrollbars.top;
2856 rect.offset.left += scrollbars.left;
2857 rect.size.width -= scrollbars.HorizontalSum();
2858 rect.size.height -= scrollbars.VerticalSum();
2859 rect.size.ClampNegativeToZero();
2860 }
2861
ClipRect(const PhysicalOffset & location) const2862 PhysicalRect LayoutBox::ClipRect(const PhysicalOffset& location) const {
2863 NOT_DESTROYED();
2864 PhysicalRect clip_rect(location, Size());
2865 LayoutUnit width = Size().Width();
2866 LayoutUnit height = Size().Height();
2867
2868 if (!StyleRef().ClipLeft().IsAuto()) {
2869 LayoutUnit c = ValueForLength(StyleRef().ClipLeft(), width);
2870 clip_rect.offset.left += c;
2871 clip_rect.size.width -= c;
2872 }
2873
2874 if (!StyleRef().ClipRight().IsAuto()) {
2875 clip_rect.size.width -=
2876 width - ValueForLength(StyleRef().ClipRight(), width);
2877 }
2878
2879 if (!StyleRef().ClipTop().IsAuto()) {
2880 LayoutUnit c = ValueForLength(StyleRef().ClipTop(), height);
2881 clip_rect.offset.top += c;
2882 clip_rect.size.height -= c;
2883 }
2884
2885 if (!StyleRef().ClipBottom().IsAuto()) {
2886 clip_rect.size.height -=
2887 height - ValueForLength(StyleRef().ClipBottom(), height);
2888 }
2889
2890 return clip_rect;
2891 }
2892
PortionOfMarginNotConsumedByFloat(LayoutUnit child_margin,LayoutUnit content_side,LayoutUnit offset)2893 static LayoutUnit PortionOfMarginNotConsumedByFloat(LayoutUnit child_margin,
2894 LayoutUnit content_side,
2895 LayoutUnit offset) {
2896 if (child_margin <= 0)
2897 return LayoutUnit();
2898 LayoutUnit content_side_with_margin = content_side + child_margin;
2899 if (offset > content_side_with_margin)
2900 return child_margin;
2901 return offset - content_side;
2902 }
2903
ShrinkLogicalWidthToAvoidFloats(LayoutUnit child_margin_start,LayoutUnit child_margin_end,const LayoutBlockFlow * cb) const2904 LayoutUnit LayoutBox::ShrinkLogicalWidthToAvoidFloats(
2905 LayoutUnit child_margin_start,
2906 LayoutUnit child_margin_end,
2907 const LayoutBlockFlow* cb) const {
2908 NOT_DESTROYED();
2909 LayoutUnit logical_top_position = LogicalTop();
2910 LayoutUnit start_offset_for_content = cb->StartOffsetForContent();
2911 LayoutUnit end_offset_for_content = cb->EndOffsetForContent();
2912
2913 // NOTE: This call to LogicalHeightForChild is bad, as it may contain data
2914 // from a previous layout.
2915 LayoutUnit logical_height = cb->LogicalHeightForChild(*this);
2916 LayoutUnit start_offset_for_avoiding_floats =
2917 cb->StartOffsetForAvoidingFloats(logical_top_position, logical_height);
2918 LayoutUnit end_offset_for_avoiding_floats =
2919 cb->EndOffsetForAvoidingFloats(logical_top_position, logical_height);
2920
2921 // If there aren't any floats constraining us then allow the margins to
2922 // shrink/expand the width as much as they want.
2923 if (start_offset_for_content == start_offset_for_avoiding_floats &&
2924 end_offset_for_content == end_offset_for_avoiding_floats)
2925 return cb->AvailableLogicalWidthForAvoidingFloats(logical_top_position,
2926 logical_height) -
2927 child_margin_start - child_margin_end;
2928
2929 LayoutUnit width = cb->AvailableLogicalWidthForAvoidingFloats(
2930 logical_top_position, logical_height);
2931 width -= std::max(LayoutUnit(), child_margin_start);
2932 width -= std::max(LayoutUnit(), child_margin_end);
2933
2934 // We need to see if margins on either the start side or the end side can
2935 // contain the floats in question. If they can, then just using the line width
2936 // is inaccurate. In the case where a float completely fits, we don't need to
2937 // use the line offset at all, but can instead push all the way to the content
2938 // edge of the containing block. In the case where the float doesn't fit, we
2939 // can use the line offset, but we need to grow it by the margin to reflect
2940 // the fact that the margin was "consumed" by the float. Negative margins
2941 // aren't consumed by the float, and so we ignore them.
2942 width += PortionOfMarginNotConsumedByFloat(child_margin_start,
2943 start_offset_for_content,
2944 start_offset_for_avoiding_floats);
2945 width += PortionOfMarginNotConsumedByFloat(
2946 child_margin_end, end_offset_for_content, end_offset_for_avoiding_floats);
2947 return width;
2948 }
2949
ContainingBlockLogicalHeightForGetComputedStyle() const2950 LayoutUnit LayoutBox::ContainingBlockLogicalHeightForGetComputedStyle() const {
2951 NOT_DESTROYED();
2952 if (HasOverrideContainingBlockContentLogicalHeight())
2953 return OverrideContainingBlockContentLogicalHeight();
2954
2955 if (!IsPositioned())
2956 return ContainingBlockLogicalHeightForContent(kExcludeMarginBorderPadding);
2957
2958 auto* cb = To<LayoutBoxModelObject>(Container());
2959 LayoutUnit height = ContainingBlockLogicalHeightForPositioned(
2960 cb, /* check_for_perpendicular_writing_mode */ false);
2961 if (IsInFlowPositioned())
2962 height -= cb->PaddingLogicalHeight();
2963 return height;
2964 }
2965
ContainingBlockLogicalWidthForContent() const2966 LayoutUnit LayoutBox::ContainingBlockLogicalWidthForContent() const {
2967 NOT_DESTROYED();
2968 if (HasOverrideContainingBlockContentLogicalWidth())
2969 return OverrideContainingBlockContentLogicalWidth();
2970
2971 LayoutBlock* cb = ContainingBlock();
2972 if (IsOutOfFlowPositioned())
2973 return cb->ClientLogicalWidth();
2974 return cb->AvailableLogicalWidth();
2975 }
2976
ContainingBlockLogicalHeightForContent(AvailableLogicalHeightType height_type) const2977 LayoutUnit LayoutBox::ContainingBlockLogicalHeightForContent(
2978 AvailableLogicalHeightType height_type) const {
2979 NOT_DESTROYED();
2980 if (HasOverrideContainingBlockContentLogicalHeight())
2981 return OverrideContainingBlockContentLogicalHeight();
2982
2983 LayoutBlock* cb = ContainingBlock();
2984 return cb->AvailableLogicalHeight(height_type);
2985 }
2986
ContainingBlockAvailableLineWidth() const2987 LayoutUnit LayoutBox::ContainingBlockAvailableLineWidth() const {
2988 NOT_DESTROYED();
2989 LayoutBlock* cb = ContainingBlock();
2990 auto* child_block_flow = DynamicTo<LayoutBlockFlow>(cb);
2991 if (child_block_flow) {
2992 return child_block_flow->AvailableLogicalWidthForAvoidingFloats(
2993 LogicalTop(), AvailableLogicalHeight(kIncludeMarginBorderPadding));
2994 }
2995 return LayoutUnit();
2996 }
2997
PerpendicularContainingBlockLogicalHeight() const2998 LayoutUnit LayoutBox::PerpendicularContainingBlockLogicalHeight() const {
2999 NOT_DESTROYED();
3000 if (HasOverrideContainingBlockContentLogicalHeight())
3001 return OverrideContainingBlockContentLogicalHeight();
3002
3003 LayoutBlock* cb = ContainingBlock();
3004 if (cb->HasOverrideLogicalHeight())
3005 return cb->OverrideContentLogicalHeight();
3006
3007 const ComputedStyle& containing_block_style = cb->StyleRef();
3008 const Length& logical_height_length = containing_block_style.LogicalHeight();
3009
3010 // FIXME: For now just support fixed heights. Eventually should support
3011 // percentage heights as well.
3012 if (!logical_height_length.IsFixed()) {
3013 LayoutUnit fill_fallback_extent =
3014 LayoutUnit(containing_block_style.IsHorizontalWritingMode()
3015 ? View()->GetFrameView()->Size().Height()
3016 : View()->GetFrameView()->Size().Width());
3017 LayoutUnit fill_available_extent =
3018 ContainingBlock()->AvailableLogicalHeight(kExcludeMarginBorderPadding);
3019 if (fill_available_extent == -1)
3020 return fill_fallback_extent;
3021 return std::min(fill_available_extent, fill_fallback_extent);
3022 }
3023
3024 // Use the content box logical height as specified by the style.
3025 return cb->AdjustContentBoxLogicalHeightForBoxSizing(
3026 LayoutUnit(logical_height_length.Value()));
3027 }
3028
MapLocalToAncestor(const LayoutBoxModelObject * ancestor,TransformState & transform_state,MapCoordinatesFlags mode) const3029 void LayoutBox::MapLocalToAncestor(const LayoutBoxModelObject* ancestor,
3030 TransformState& transform_state,
3031 MapCoordinatesFlags mode) const {
3032 NOT_DESTROYED();
3033 bool is_fixed_pos = StyleRef().GetPosition() == EPosition::kFixed;
3034
3035 // If this box has a transform or contains paint, it acts as a fixed position
3036 // container for fixed descendants, and may itself also be fixed position. So
3037 // propagate 'fixed' up only if this box is fixed position.
3038 if (CanContainFixedPositionObjects() && !is_fixed_pos)
3039 mode &= ~kIsFixed;
3040 else if (is_fixed_pos)
3041 mode |= kIsFixed;
3042
3043 LayoutBoxModelObject::MapLocalToAncestor(ancestor, transform_state, mode);
3044 }
3045
MapAncestorToLocal(const LayoutBoxModelObject * ancestor,TransformState & transform_state,MapCoordinatesFlags mode) const3046 void LayoutBox::MapAncestorToLocal(const LayoutBoxModelObject* ancestor,
3047 TransformState& transform_state,
3048 MapCoordinatesFlags mode) const {
3049 NOT_DESTROYED();
3050 if (this == ancestor)
3051 return;
3052
3053 bool is_fixed_pos = StyleRef().GetPosition() == EPosition::kFixed;
3054
3055 // If this box has a transform or contains paint, it acts as a fixed position
3056 // container for fixed descendants, and may itself also be fixed position. So
3057 // propagate 'fixed' up only if this box is fixed position.
3058 if (CanContainFixedPositionObjects() && !is_fixed_pos)
3059 mode &= ~kIsFixed;
3060 else if (is_fixed_pos)
3061 mode |= kIsFixed;
3062
3063 LayoutBoxModelObject::MapAncestorToLocal(ancestor, transform_state, mode);
3064 }
3065
OffsetFromContainerInternal(const LayoutObject * o,bool ignore_scroll_offset) const3066 PhysicalOffset LayoutBox::OffsetFromContainerInternal(
3067 const LayoutObject* o,
3068 bool ignore_scroll_offset) const {
3069 NOT_DESTROYED();
3070 DCHECK_EQ(o, Container());
3071
3072 PhysicalOffset offset;
3073 if (IsInFlowPositioned())
3074 offset += OffsetForInFlowPosition();
3075
3076 offset += PhysicalLocation();
3077
3078 if (o->IsScrollContainer())
3079 offset += OffsetFromScrollableContainer(o, ignore_scroll_offset);
3080
3081 if (IsOutOfFlowPositioned() && o->IsLayoutInline() &&
3082 o->CanContainOutOfFlowPositionedElement(StyleRef().GetPosition())) {
3083 offset += To<LayoutInline>(o)->OffsetForInFlowPositionedInline(*this);
3084 }
3085
3086 return offset;
3087 }
3088
CreateInlineBox()3089 InlineBox* LayoutBox::CreateInlineBox() {
3090 NOT_DESTROYED();
3091 return new InlineBox(LineLayoutItem(this));
3092 }
3093
DirtyLineBoxes(bool full_layout)3094 void LayoutBox::DirtyLineBoxes(bool full_layout) {
3095 NOT_DESTROYED();
3096 if (!IsInLayoutNGInlineFormattingContext() && inline_box_wrapper_) {
3097 if (full_layout) {
3098 inline_box_wrapper_->Destroy();
3099 inline_box_wrapper_ = nullptr;
3100 } else {
3101 inline_box_wrapper_->DirtyLineBoxes();
3102 }
3103 }
3104 }
3105
HasInlineFragments() const3106 bool LayoutBox::HasInlineFragments() const {
3107 NOT_DESTROYED();
3108 if (!IsInLayoutNGInlineFormattingContext())
3109 return inline_box_wrapper_;
3110 if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
3111 return first_paint_fragment_;
3112 return first_fragment_item_index_;
3113 }
3114
SetFirstInlineFragment(NGPaintFragment * fragment)3115 void LayoutBox::SetFirstInlineFragment(NGPaintFragment* fragment) {
3116 NOT_DESTROYED();
3117 CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
3118 DCHECK(!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
3119 first_paint_fragment_ = fragment;
3120 }
3121
ClearFirstInlineFragmentItemIndex()3122 void LayoutBox::ClearFirstInlineFragmentItemIndex() {
3123 NOT_DESTROYED();
3124 CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
3125 DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
3126 first_fragment_item_index_ = 0u;
3127 }
3128
SetFirstInlineFragmentItemIndex(wtf_size_t index)3129 void LayoutBox::SetFirstInlineFragmentItemIndex(wtf_size_t index) {
3130 NOT_DESTROYED();
3131 CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
3132 DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
3133 DCHECK_NE(index, 0u);
3134 first_fragment_item_index_ = index;
3135 }
3136
InLayoutNGInlineFormattingContextWillChange(bool new_value)3137 void LayoutBox::InLayoutNGInlineFormattingContextWillChange(bool new_value) {
3138 NOT_DESTROYED();
3139 if (IsInLayoutNGInlineFormattingContext()) {
3140 if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) {
3141 SetFirstInlineFragment(nullptr);
3142 } else {
3143 ClearFirstInlineFragmentItemIndex();
3144 }
3145 } else {
3146 DeleteLineBoxWrapper();
3147 }
3148
3149 // Because |first_paint_fragment_| and |inline_box_wrapper_| are union, when
3150 // one is deleted, the other should be initialized to nullptr.
3151 DCHECK(new_value ? (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()
3152 ? !first_fragment_item_index_
3153 : !first_paint_fragment_)
3154 : !inline_box_wrapper_);
3155 }
3156
HasFragmentItems() const3157 bool LayoutBox::NGPhysicalFragmentList::HasFragmentItems() const {
3158 for (const NGPhysicalBoxFragment& fragment : *this) {
3159 if (fragment.HasItems())
3160 return true;
3161 }
3162 return false;
3163 }
3164
SetCachedLayoutResult(scoped_refptr<const NGLayoutResult> result)3165 void LayoutBox::SetCachedLayoutResult(
3166 scoped_refptr<const NGLayoutResult> result) {
3167 NOT_DESTROYED();
3168 DCHECK(!result->PhysicalFragment().BreakToken());
3169 DCHECK(!result->IsSingleUse());
3170
3171 if (result->GetConstraintSpaceForCaching().CacheSlot() ==
3172 NGCacheSlot::kMeasure) {
3173 // We don't early return here, when setting the "measure" result we also
3174 // set the "layout" result.
3175 if (measure_result_)
3176 InvalidateItems(*measure_result_);
3177 if (IsTableCell() && !IsTableCellLegacy())
3178 To<LayoutNGTableCell>(this)->InvalidateLayoutResultCacheAfterMeasure();
3179 measure_result_ = result;
3180 } else {
3181 // We have a "layout" result, and we may need to clear the old "measure"
3182 // result if we needed non-simplified layout.
3183 if (measure_result_ && NeedsLayout() && !NeedsSimplifiedLayoutOnly()) {
3184 InvalidateItems(*measure_result_);
3185 measure_result_ = nullptr;
3186 }
3187 }
3188
3189 AddLayoutResult(std::move(result), 0);
3190 }
3191
AddLayoutResult(scoped_refptr<const NGLayoutResult> result,wtf_size_t index)3192 void LayoutBox::AddLayoutResult(scoped_refptr<const NGLayoutResult> result,
3193 wtf_size_t index) {
3194 NOT_DESTROYED();
3195 DCHECK_EQ(result->Status(), NGLayoutResult::kSuccess);
3196 if (index != WTF::kNotFound && layout_results_.size() > index) {
3197 if (layout_results_.size() > index + 1)
3198 ShrinkLayoutResults(index + 1);
3199 ReplaceLayoutResult(std::move(result), index);
3200 return;
3201 }
3202
3203 DCHECK_EQ(index, layout_results_.size());
3204 const auto& fragment = To<NGPhysicalBoxFragment>(result->PhysicalFragment());
3205 layout_results_.push_back(std::move(result));
3206 CheckDidAddFragment(*this, fragment);
3207
3208 // If this is the last fragment for the node, and its node establishes an
3209 // inline formatting context, we have some finalization to do.
3210 if (!fragment.BreakToken() && HasFragmentItems())
3211 NGFragmentItems::FinalizeAfterLayout(layout_results_);
3212 }
3213
ReplaceLayoutResult(scoped_refptr<const NGLayoutResult> result,wtf_size_t index)3214 void LayoutBox::ReplaceLayoutResult(scoped_refptr<const NGLayoutResult> result,
3215 wtf_size_t index) {
3216 NOT_DESTROYED();
3217 DCHECK_LE(index, layout_results_.size());
3218 const NGLayoutResult* old_result = layout_results_[index].get();
3219 if (old_result == result.get())
3220 return;
3221 const auto& fragment = To<NGPhysicalBoxFragment>(result->PhysicalFragment());
3222 bool got_new_fragment = &old_result->PhysicalFragment() != &fragment;
3223 if (got_new_fragment) {
3224 // Clear associations with the LayoutObjects and the items in the *first*
3225 // box fragment. We only ever associate LayoutObjects with the items in the
3226 // first box fragment, but this doesn't take place until we have added all
3227 // the fragments for the node. Therefore we need to clean up this now,
3228 // regardless of which index we're at. This means that we're potentially
3229 // doing duplicate work here (for each fragment we replace), but only if
3230 // we're split into multiple box fragments.
3231 // TODO(layout-dev): Make this work for multiple box fragments (block
3232 // fragmentation).
3233 if (To<NGPhysicalBoxFragment>(layout_results_[0]->PhysicalFragment())
3234 .HasItems()) {
3235 if (!index)
3236 InvalidateItems(*old_result);
3237 NGFragmentItems::ClearAssociatedFragments(this);
3238 }
3239 }
3240 layout_results_[index] = std::move(result);
3241 CheckDidAddFragment(*this, fragment);
3242
3243 // If this is the last fragment for the node, and its node establishes an
3244 // inline formatting context, we have some finalization to do.
3245 if (got_new_fragment && !fragment.BreakToken() && HasFragmentItems())
3246 NGFragmentItems::FinalizeAfterLayout(layout_results_);
3247 }
3248
ClearLayoutResults()3249 void LayoutBox::ClearLayoutResults() {
3250 NOT_DESTROYED();
3251 if (measure_result_)
3252 InvalidateItems(*measure_result_);
3253 measure_result_ = nullptr;
3254
3255 ShrinkLayoutResults(0);
3256 }
3257
ShrinkLayoutResults(wtf_size_t results_to_keep)3258 void LayoutBox::ShrinkLayoutResults(wtf_size_t results_to_keep) {
3259 NOT_DESTROYED();
3260 DCHECK_GE(layout_results_.size(), results_to_keep);
3261 // Invalidate if inline |DisplayItemClient|s will be destroyed.
3262 for (wtf_size_t i = results_to_keep; i < layout_results_.size(); i++)
3263 InvalidateItems(*layout_results_[i]);
3264 if (results_to_keep == 0 && !layout_results_.IsEmpty()) {
3265 if (To<NGPhysicalBoxFragment>(layout_results_[0]->PhysicalFragment())
3266 .HasItems()) {
3267 NGFragmentItems::ClearAssociatedFragments(this);
3268 }
3269 }
3270 layout_results_.Shrink(results_to_keep);
3271 }
3272
InvalidateItems(const NGLayoutResult & result)3273 void LayoutBox::InvalidateItems(const NGLayoutResult& result) {
3274 NOT_DESTROYED();
3275 // Invalidate if inline |DisplayItemClient|s will be destroyed.
3276 const auto& box_fragment =
3277 To<NGPhysicalBoxFragment>(result.PhysicalFragment());
3278 if (!box_fragment.HasItems())
3279 return;
3280 #if DCHECK_IS_ON()
3281 // Column fragments are not really associated with a layout object.
3282 if (IsLayoutFlowThread())
3283 DCHECK(box_fragment.IsColumnBox());
3284 else
3285 DCHECK_EQ(this, box_fragment.GetLayoutObject());
3286 #endif
3287 ObjectPaintInvalidator(*this).SlowSetPaintingLayerNeedsRepaint();
3288 }
3289
GetCachedLayoutResult() const3290 const NGLayoutResult* LayoutBox::GetCachedLayoutResult() const {
3291 NOT_DESTROYED();
3292 if (layout_results_.IsEmpty())
3293 return nullptr;
3294 // Only return re-usable results.
3295 const NGLayoutResult* result = layout_results_[0].get();
3296 if (result->IsSingleUse())
3297 return nullptr;
3298 DCHECK(!result->PhysicalFragment().IsLayoutObjectDestroyedOrMoved() ||
3299 BeingDestroyed());
3300 DCHECK_EQ(layout_results_.size(), 1u);
3301 return result;
3302 }
3303
GetCachedMeasureResult() const3304 const NGLayoutResult* LayoutBox::GetCachedMeasureResult() const {
3305 NOT_DESTROYED();
3306 if (!measure_result_)
3307 return nullptr;
3308
3309 if (measure_result_->IsSingleUse())
3310 return nullptr;
3311
3312 return measure_result_.get();
3313 }
3314
CachedLayoutResult(const NGConstraintSpace & new_space,const NGBreakToken * break_token,const NGEarlyBreak * early_break,base::Optional<NGFragmentGeometry> * initial_fragment_geometry,NGLayoutCacheStatus * out_cache_status)3315 scoped_refptr<const NGLayoutResult> LayoutBox::CachedLayoutResult(
3316 const NGConstraintSpace& new_space,
3317 const NGBreakToken* break_token,
3318 const NGEarlyBreak* early_break,
3319 base::Optional<NGFragmentGeometry>* initial_fragment_geometry,
3320 NGLayoutCacheStatus* out_cache_status) {
3321 NOT_DESTROYED();
3322 *out_cache_status = NGLayoutCacheStatus::kNeedsLayout;
3323
3324 const bool use_layout_cache_slot =
3325 new_space.CacheSlot() == NGCacheSlot::kLayout &&
3326 !layout_results_.IsEmpty();
3327 const NGLayoutResult* cached_layout_result = use_layout_cache_slot
3328 ? GetCachedLayoutResult()
3329 : GetCachedMeasureResult();
3330
3331 if (!cached_layout_result)
3332 return nullptr;
3333
3334 // TODO(cbiesinger): Support caching fragmented boxes.
3335 if (break_token)
3336 return nullptr;
3337
3338 if (early_break)
3339 return nullptr;
3340
3341 // We'll never re-use the layout result of a fieldset contents wrapper. If the
3342 // current rendered legend ceased to be one, and any next legend child became
3343 // the rendered legend instead, we need to lay out the fieldset contents
3344 // wrapper, to get rid of the fragment for the former regular block child
3345 // legend (now rendered legend). When this happens, the contents wrapper won't
3346 // necessarily be marked for layout, since we don't detect that anything in
3347 // there has changed (and detecting that would be more expensive). So just
3348 // refuse to hit the cache, so that we force re-layout.
3349 if (UNLIKELY(IsAnonymous() && Parent()->IsLayoutNGFieldset()))
3350 return nullptr;
3351
3352 DCHECK_EQ(cached_layout_result->Status(), NGLayoutResult::kSuccess);
3353
3354 // Set our initial temporary cache status to "hit".
3355 NGLayoutCacheStatus cache_status = NGLayoutCacheStatus::kHit;
3356
3357 // If the display-lock blocked child layout, then we don't clear child needs
3358 // layout bits. However, we can still use the cached result, since we will
3359 // re-layout when unlocking.
3360 bool child_needs_layout_unless_locked =
3361 !ChildLayoutBlockedByDisplayLock() &&
3362 (PosChildNeedsLayout() || NormalChildNeedsLayout());
3363
3364 const NGPhysicalBoxFragment& physical_fragment =
3365 To<NGPhysicalBoxFragment>(cached_layout_result->PhysicalFragment());
3366 if (SelfNeedsLayoutForStyle() || child_needs_layout_unless_locked ||
3367 NeedsSimplifiedNormalFlowLayout() ||
3368 (NeedsPositionedMovementLayout() &&
3369 !NeedsPositionedMovementLayoutOnly())) {
3370 if (!ChildrenInline()) {
3371 // Check if we only need "simplified" layout. We don't abort yet, as we
3372 // need to check if other things (like floats) will require us to perform
3373 // a full layout.
3374 if (!NeedsSimplifiedLayoutOnly())
3375 return nullptr;
3376
3377 cache_status = NGLayoutCacheStatus::kNeedsSimplifiedLayout;
3378 } else if (!NeedsSimplifiedLayoutOnly() ||
3379 NeedsSimplifiedNormalFlowLayout()) {
3380 // We don't regenerate any lineboxes during our "simplified" layout pass.
3381 // If something needs "simplified" layout within a linebox, (e.g. an
3382 // atomic-inline) we miss the cache.
3383
3384 if (!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
3385 return nullptr;
3386
3387 // Check if some of line boxes are reusable.
3388
3389 // Only for the layout cache slot. Measure has several special
3390 // optimizations that makes reusing lines complicated.
3391 if (!use_layout_cache_slot)
3392 return nullptr;
3393
3394 if (SelfNeedsLayout())
3395 return nullptr;
3396
3397 if (!physical_fragment.HasItems())
3398 return nullptr;
3399
3400 // Propagating OOF needs re-layout.
3401 if (physical_fragment.HasOutOfFlowPositionedDescendants())
3402 return nullptr;
3403
3404 // Any floats might need to move, causing lines to wrap differently,
3405 // needing re-layout, either in cached result or in new constraint space.
3406 if (!cached_layout_result->ExclusionSpace().IsEmpty() ||
3407 new_space.HasFloats())
3408 return nullptr;
3409
3410 cache_status = NGLayoutCacheStatus::kCanReuseLines;
3411 } else {
3412 cache_status = NGLayoutCacheStatus::kNeedsSimplifiedLayout;
3413 }
3414 }
3415
3416 DCHECK(!physical_fragment.BreakToken());
3417
3418 NGBlockNode node(this);
3419 NGLayoutCacheStatus size_cache_status = CalculateSizeBasedLayoutCacheStatus(
3420 node, *cached_layout_result, new_space, initial_fragment_geometry);
3421
3422 // If our size may change (or we know a descendants size may change), we miss
3423 // the cache.
3424 if (size_cache_status == NGLayoutCacheStatus::kNeedsLayout)
3425 return nullptr;
3426
3427 // Update our temporary cache status, if the size cache check indicated we
3428 // might need simplified layout.
3429 if (size_cache_status == NGLayoutCacheStatus::kNeedsSimplifiedLayout &&
3430 cache_status == NGLayoutCacheStatus::kHit)
3431 cache_status = NGLayoutCacheStatus::kNeedsSimplifiedLayout;
3432
3433 LayoutUnit bfc_line_offset = new_space.BfcOffset().line_offset;
3434 base::Optional<LayoutUnit> bfc_block_offset =
3435 cached_layout_result->BfcBlockOffset();
3436 LayoutUnit block_offset_delta;
3437 NGMarginStrut end_margin_strut = cached_layout_result->EndMarginStrut();
3438
3439 const NGConstraintSpace& old_space =
3440 cached_layout_result->GetConstraintSpaceForCaching();
3441
3442 // Check the BFC offset. Even if they don't match, there're some cases we can
3443 // still reuse the fragment.
3444 bool are_bfc_offsets_equal =
3445 new_space.BfcOffset() == old_space.BfcOffset() &&
3446 new_space.ExpectedBfcBlockOffset() ==
3447 old_space.ExpectedBfcBlockOffset() &&
3448 new_space.ForcedBfcBlockOffset() == old_space.ForcedBfcBlockOffset();
3449
3450 // Even for the first fragment, when block fragmentation is enabled, block
3451 // offset changes should cause re-layout, since we will fragment at other
3452 // locations than before.
3453 if (UNLIKELY(!are_bfc_offsets_equal && new_space.HasBlockFragmentation())) {
3454 DCHECK(old_space.HasBlockFragmentation());
3455 return nullptr;
3456 }
3457
3458 bool is_margin_strut_equal =
3459 new_space.MarginStrut() == old_space.MarginStrut();
3460 bool is_exclusion_space_equal =
3461 new_space.ExclusionSpace() == old_space.ExclusionSpace();
3462
3463 bool is_new_formatting_context = physical_fragment.IsFormattingContextRoot();
3464
3465 // If a node *doesn't* establish a new formatting context it may be affected
3466 // by floats, or clearance.
3467 // If anything has changed prior to us (different exclusion space, etc), we
3468 // need to perform a series of additional checks if we can still reuse this
3469 // layout result.
3470 if (!is_new_formatting_context &&
3471 (!are_bfc_offsets_equal || !is_exclusion_space_equal ||
3472 !is_margin_strut_equal ||
3473 new_space.ClearanceOffset() != old_space.ClearanceOffset())) {
3474 DCHECK(!CreatesNewFormattingContext());
3475
3476 // If we have a different BFC offset, or exclusion space we can't perform
3477 // "simplified" layout.
3478 // This may occur if our %-block-size has changed (allowing "simplified"
3479 // layout), and we've been pushed down in the BFC coordinate space by a
3480 // sibling.
3481 // The "simplified" layout algorithm doesn't have the required logic to
3482 // shift any added exclusions within the output exclusion space.
3483 if (cache_status == NGLayoutCacheStatus::kNeedsSimplifiedLayout ||
3484 cache_status == NGLayoutCacheStatus::kCanReuseLines)
3485 return nullptr;
3486
3487 DCHECK_EQ(cache_status, NGLayoutCacheStatus::kHit);
3488
3489 if (!MaySkipLayoutWithinBlockFormattingContext(
3490 *cached_layout_result, new_space, &bfc_block_offset,
3491 &block_offset_delta, &end_margin_strut))
3492 return nullptr;
3493 }
3494
3495 // We've performed all of the cache checks at this point. If we need
3496 // "simplified" layout then abort now.
3497 *out_cache_status = cache_status;
3498 if (cache_status == NGLayoutCacheStatus::kNeedsSimplifiedLayout ||
3499 cache_status == NGLayoutCacheStatus::kCanReuseLines)
3500 return cached_layout_result;
3501
3502 physical_fragment.CheckType();
3503
3504 DCHECK_EQ(*out_cache_status, NGLayoutCacheStatus::kHit);
3505
3506 // We can safely re-use this fragment if we are positioned, and only our
3507 // position constraints changed (left/top/etc). However we need to clear the
3508 // dirty layout bit(s). Note that we may be here because we are display locked
3509 // and have cached a locked layout result. In that case, this function will
3510 // not clear the child dirty bits.
3511 ClearNeedsLayout();
3512
3513 // Optimization: TableConstraintSpaceData can be large, and it is shared
3514 // between all the rows in a table. Make constraint space table data for
3515 // reused row fragment be identical to the one used by other row fragments.
3516 if (IsTableRow() && IsLayoutNGMixin()) {
3517 const_cast<NGConstraintSpace&>(old_space).ReplaceTableConstraintSpaceData(
3518 *new_space.TableData());
3519 }
3520
3521 // OOF-positioned nodes have to two-tier cache. The additional cache check
3522 // runs before the OOF-positioned sizing, and positioning calculations.
3523 //
3524 // This additional check compares the percentage resolution size.
3525 //
3526 // As a result, the cached layout result always needs to contain the previous
3527 // percentage resolution size in order for the first-tier cache to work.
3528 // See |NGBlockNode::CachedLayoutResultForOutOfFlowPositioned|.
3529 bool needs_cached_result_update =
3530 node.IsOutOfFlowPositioned() && new_space.PercentageResolutionSize() !=
3531 old_space.PercentageResolutionSize();
3532
3533 // We can safely reuse this result if our BFC and "input" exclusion spaces
3534 // were equal.
3535 if (are_bfc_offsets_equal && is_exclusion_space_equal &&
3536 is_margin_strut_equal && !needs_cached_result_update) {
3537 // In order not to rebuild the internal derived-geometry "cache" of float
3538 // data, we need to move this to the new "output" exclusion space.
3539 cached_layout_result->ExclusionSpace().MoveAndUpdateDerivedGeometry(
3540 new_space.ExclusionSpace());
3541 return cached_layout_result;
3542 }
3543
3544 scoped_refptr<const NGLayoutResult> new_result =
3545 base::AdoptRef(new NGLayoutResult(*cached_layout_result, new_space,
3546 end_margin_strut, bfc_line_offset,
3547 bfc_block_offset, block_offset_delta));
3548
3549 if (needs_cached_result_update)
3550 SetCachedLayoutResult(new_result);
3551
3552 return new_result;
3553 }
3554
GetPhysicalFragment(wtf_size_t index) const3555 const NGPhysicalBoxFragment* LayoutBox::GetPhysicalFragment(
3556 wtf_size_t index) const {
3557 NOT_DESTROYED();
3558 return &To<NGPhysicalBoxFragment>(layout_results_[index]->PhysicalFragment());
3559 }
3560
3561 const NGPhysicalBoxFragment&
operator *() const3562 LayoutBox::NGPhysicalFragmentList::Iterator::operator*() const {
3563 return To<NGPhysicalBoxFragment>((*iterator_)->PhysicalFragment());
3564 }
3565
FragmentDataFromPhysicalFragment(const NGPhysicalBoxFragment & physical_fragment) const3566 const FragmentData* LayoutBox::FragmentDataFromPhysicalFragment(
3567 const NGPhysicalBoxFragment& physical_fragment) const {
3568 NOT_DESTROYED();
3569 const FragmentData* fragment_data = &FirstFragment();
3570 for (const auto& result : layout_results_) {
3571 if (&result->PhysicalFragment() == &physical_fragment)
3572 return fragment_data;
3573 DCHECK(fragment_data->NextFragment());
3574 fragment_data = fragment_data->NextFragment();
3575 }
3576 NOTREACHED();
3577 return fragment_data;
3578 }
3579
PositionLineBox(InlineBox * box)3580 void LayoutBox::PositionLineBox(InlineBox* box) {
3581 NOT_DESTROYED();
3582 if (IsOutOfFlowPositioned()) {
3583 // Cache the x position only if we were an INLINE type originally.
3584 bool originally_inline = StyleRef().IsOriginalDisplayInlineType();
3585 if (originally_inline) {
3586 // The value is cached in the xPos of the box. We only need this value if
3587 // our object was inline originally, since otherwise it would have ended
3588 // up underneath the inlines.
3589 RootInlineBox& root = box->Root();
3590 root.Block().SetStaticInlinePositionForChild(LineLayoutBox(this),
3591 box->LogicalLeft());
3592 } else {
3593 // Our object was a block originally, so we make our normal flow position
3594 // be just below the line box (as though all the inlines that came before
3595 // us got wrapped in an anonymous block, which is what would have happened
3596 // had we been in flow). This value was cached in the y() of the box.
3597 Layer()->SetStaticBlockPosition(box->LogicalTop());
3598 }
3599
3600 if (Container()->IsLayoutInline())
3601 MoveWithEdgeOfInlineContainerIfNecessary(box->IsHorizontal());
3602
3603 // Nuke the box.
3604 box->Remove(kDontMarkLineBoxes);
3605 box->Destroy();
3606 } else if (IsAtomicInlineLevel()) {
3607 SetLocationAndUpdateOverflowControlsIfNeeded(box->Location());
3608 SetInlineBoxWrapper(box);
3609 }
3610 }
3611
MoveWithEdgeOfInlineContainerIfNecessary(bool is_horizontal)3612 void LayoutBox::MoveWithEdgeOfInlineContainerIfNecessary(bool is_horizontal) {
3613 NOT_DESTROYED();
3614 DCHECK(IsOutOfFlowPositioned());
3615 DCHECK(Container()->IsLayoutInline());
3616 DCHECK(Container()->CanContainOutOfFlowPositionedElement(
3617 StyleRef().GetPosition()));
3618 // If this object is inside a relative positioned inline and its inline
3619 // position is an explicit offset from the edge of its container then it will
3620 // need to move if its inline container has changed width. We do not track if
3621 // the width has changed but if we are here then we are laying out lines
3622 // inside it, so it probably has - mark our object for layout so that it can
3623 // move to the new offset created by the new width.
3624 if (!NormalChildNeedsLayout() &&
3625 !StyleRef().HasStaticInlinePosition(is_horizontal))
3626 SetChildNeedsLayout(kMarkOnlyThis);
3627 }
3628
DeleteLineBoxWrapper()3629 void LayoutBox::DeleteLineBoxWrapper() {
3630 NOT_DESTROYED();
3631 if (!IsInLayoutNGInlineFormattingContext() && inline_box_wrapper_) {
3632 if (!DocumentBeingDestroyed())
3633 inline_box_wrapper_->Remove();
3634 inline_box_wrapper_->Destroy();
3635 inline_box_wrapper_ = nullptr;
3636 }
3637 }
3638
SetSpannerPlaceholder(LayoutMultiColumnSpannerPlaceholder & placeholder)3639 void LayoutBox::SetSpannerPlaceholder(
3640 LayoutMultiColumnSpannerPlaceholder& placeholder) {
3641 NOT_DESTROYED();
3642 // Not expected to change directly from one spanner to another.
3643 CHECK(!rare_data_ || !rare_data_->spanner_placeholder_);
3644 EnsureRareData().spanner_placeholder_ = &placeholder;
3645 }
3646
ClearSpannerPlaceholder()3647 void LayoutBox::ClearSpannerPlaceholder() {
3648 NOT_DESTROYED();
3649 if (!rare_data_)
3650 return;
3651 rare_data_->spanner_placeholder_ = nullptr;
3652 }
3653
SetPaginationStrut(LayoutUnit strut)3654 void LayoutBox::SetPaginationStrut(LayoutUnit strut) {
3655 NOT_DESTROYED();
3656 if (!strut && !rare_data_)
3657 return;
3658 EnsureRareData().pagination_strut_ = strut;
3659 }
3660
IsBreakBetweenControllable(EBreakBetween break_value) const3661 bool LayoutBox::IsBreakBetweenControllable(EBreakBetween break_value) const {
3662 NOT_DESTROYED();
3663 if (break_value == EBreakBetween::kAuto)
3664 return true;
3665 // We currently only support non-auto break-before and break-after values on
3666 // in-flow block level elements, which is the minimum requirement according to
3667 // the spec.
3668 if (IsInline() || IsFloatingOrOutOfFlowPositioned())
3669 return false;
3670 const LayoutBlock* curr = ContainingBlock();
3671 if (!curr || !curr->IsLayoutBlockFlow())
3672 return false;
3673 const LayoutView* layout_view = View();
3674 bool view_is_paginated = layout_view->FragmentationContext();
3675 if (!view_is_paginated && !FlowThreadContainingBlock())
3676 return false;
3677 while (curr) {
3678 if (curr == layout_view) {
3679 return view_is_paginated && break_value != EBreakBetween::kColumn &&
3680 break_value != EBreakBetween::kAvoidColumn;
3681 }
3682 if (curr->IsLayoutFlowThread()) {
3683 if (break_value ==
3684 EBreakBetween::kAvoid) // Valid in any kind of fragmentation context.
3685 return true;
3686 bool is_multicol_value = break_value == EBreakBetween::kColumn ||
3687 break_value == EBreakBetween::kAvoidColumn;
3688 if (is_multicol_value)
3689 return true;
3690 // If this is a flow thread for a multicol container, and we have a break
3691 // value for paged, we need to keep looking.
3692 }
3693 if (curr->IsOutOfFlowPositioned())
3694 return false;
3695 curr = curr->ContainingBlock();
3696 }
3697 NOTREACHED();
3698 return false;
3699 }
3700
IsBreakInsideControllable(EBreakInside break_value) const3701 bool LayoutBox::IsBreakInsideControllable(EBreakInside break_value) const {
3702 NOT_DESTROYED();
3703 if (break_value == EBreakInside::kAuto)
3704 return true;
3705 // First check multicol.
3706 const LayoutFlowThread* flow_thread = FlowThreadContainingBlock();
3707 // 'avoid-column' is only valid in a multicol context.
3708 if (break_value == EBreakInside::kAvoidColumn)
3709 return flow_thread;
3710 // 'avoid' is valid in any kind of fragmentation context.
3711 if (break_value == EBreakInside::kAvoid && flow_thread)
3712 return true;
3713 DCHECK(break_value == EBreakInside::kAvoidPage ||
3714 break_value == EBreakInside::kAvoid);
3715 if (View()->FragmentationContext())
3716 return true; // The view is paginated, probably because we're printing.
3717 if (!flow_thread)
3718 return false; // We're not inside any pagination context
3719 return false;
3720 }
3721
BreakAfter() const3722 EBreakBetween LayoutBox::BreakAfter() const {
3723 NOT_DESTROYED();
3724 EBreakBetween break_value = StyleRef().BreakAfter();
3725 if (break_value == EBreakBetween::kAuto ||
3726 IsBreakBetweenControllable(break_value))
3727 return break_value;
3728 return EBreakBetween::kAuto;
3729 }
3730
BreakBefore() const3731 EBreakBetween LayoutBox::BreakBefore() const {
3732 NOT_DESTROYED();
3733 EBreakBetween break_value = StyleRef().BreakBefore();
3734 if (break_value == EBreakBetween::kAuto ||
3735 IsBreakBetweenControllable(break_value))
3736 return break_value;
3737 return EBreakBetween::kAuto;
3738 }
3739
BreakInside() const3740 EBreakInside LayoutBox::BreakInside() const {
3741 NOT_DESTROYED();
3742 EBreakInside break_value = StyleRef().BreakInside();
3743 if (break_value == EBreakInside::kAuto ||
3744 IsBreakInsideControllable(break_value))
3745 return break_value;
3746 return EBreakInside::kAuto;
3747 }
3748
ClassABreakPointValue(EBreakBetween previous_break_after_value) const3749 EBreakBetween LayoutBox::ClassABreakPointValue(
3750 EBreakBetween previous_break_after_value) const {
3751 NOT_DESTROYED();
3752 // First assert that we're at a class A break point.
3753 DCHECK(IsBreakBetweenControllable(previous_break_after_value));
3754
3755 return JoinFragmentainerBreakValues(previous_break_after_value,
3756 BreakBefore());
3757 }
3758
NeedsForcedBreakBefore(EBreakBetween previous_break_after_value) const3759 bool LayoutBox::NeedsForcedBreakBefore(
3760 EBreakBetween previous_break_after_value) const {
3761 NOT_DESTROYED();
3762 // Forced break values are only honored when specified on in-flow objects, but
3763 // floats and out-of-flow positioned objects may be affected by a break-after
3764 // value of the previous in-flow object, even though we're not at a class A
3765 // break point.
3766 EBreakBetween break_value =
3767 IsFloatingOrOutOfFlowPositioned()
3768 ? previous_break_after_value
3769 : ClassABreakPointValue(previous_break_after_value);
3770 return IsForcedFragmentainerBreakValue(break_value);
3771 }
3772
StartPageName() const3773 const AtomicString LayoutBox::StartPageName() const {
3774 NOT_DESTROYED();
3775 return StyleRef().Page();
3776 }
3777
EndPageName() const3778 const AtomicString LayoutBox::EndPageName() const {
3779 NOT_DESTROYED();
3780 return StyleRef().Page();
3781 }
3782
LocalVisualRectIgnoringVisibility() const3783 PhysicalRect LayoutBox::LocalVisualRectIgnoringVisibility() const {
3784 NOT_DESTROYED();
3785 return PhysicalSelfVisualOverflowRect();
3786 }
3787
InflateVisualRectForFilterUnderContainer(TransformState & transform_state,const LayoutObject & container,const LayoutBoxModelObject * ancestor_to_stop_at) const3788 void LayoutBox::InflateVisualRectForFilterUnderContainer(
3789 TransformState& transform_state,
3790 const LayoutObject& container,
3791 const LayoutBoxModelObject* ancestor_to_stop_at) const {
3792 NOT_DESTROYED();
3793 transform_state.Flatten();
3794 // Apply visual overflow caused by reflections and filters defined on objects
3795 // between this object and container (not included) or ancestorToStopAt
3796 // (included).
3797 PhysicalOffset offset_from_container = OffsetFromContainer(&container);
3798 transform_state.Move(offset_from_container);
3799 for (LayoutObject* parent = Parent(); parent && parent != container;
3800 parent = parent->Parent()) {
3801 if (parent->IsBox()) {
3802 // Convert rect into coordinate space of parent to apply parent's
3803 // reflection and filter.
3804 PhysicalOffset parent_offset = parent->OffsetFromAncestor(&container);
3805 transform_state.Move(-parent_offset);
3806 To<LayoutBox>(parent)->InflateVisualRectForFilter(transform_state);
3807 transform_state.Move(parent_offset);
3808 }
3809 if (parent == ancestor_to_stop_at)
3810 break;
3811 }
3812 transform_state.Move(-offset_from_container);
3813 }
3814
MapToVisualRectInAncestorSpaceInternal(const LayoutBoxModelObject * ancestor,TransformState & transform_state,VisualRectFlags visual_rect_flags) const3815 bool LayoutBox::MapToVisualRectInAncestorSpaceInternal(
3816 const LayoutBoxModelObject* ancestor,
3817 TransformState& transform_state,
3818 VisualRectFlags visual_rect_flags) const {
3819 NOT_DESTROYED();
3820 InflateVisualRectForFilter(transform_state);
3821
3822 if (ancestor == this)
3823 return true;
3824
3825 AncestorSkipInfo skip_info(ancestor, true);
3826 LayoutObject* container = Container(&skip_info);
3827 LayoutBox* table_row_container = nullptr;
3828 // Skip table row because cells and rows are in the same coordinate space (see
3829 // below, however for more comments about when |ancestor| is the table row).
3830 if ((IsTableCell() && !IsLayoutNGObject()) || IsTableCellLegacy()) {
3831 DCHECK(container->IsTableRow());
3832 DCHECK_EQ(ParentBox(), container);
3833 if (container != ancestor)
3834 container = container->Parent();
3835 else
3836 table_row_container = To<LayoutBox>(container);
3837 }
3838 if (!container)
3839 return true;
3840
3841 PhysicalOffset container_offset;
3842 if (auto* box = DynamicTo<LayoutBox>(container)) {
3843 container_offset += PhysicalLocation(box);
3844
3845 // If the row is the ancestor, however, add its offset back in. In effect,
3846 // this passes from the joint <td> / <tr> coordinate space to the parent
3847 // space, then back to <tr> / <td>.
3848 if (table_row_container)
3849 container_offset -= table_row_container->PhysicalLocation(box);
3850 } else {
3851 container_offset += PhysicalLocation();
3852 }
3853
3854 const ComputedStyle& style_to_use = StyleRef();
3855 EPosition position = style_to_use.GetPosition();
3856 if (IsOutOfFlowPositioned() && container->IsLayoutInline() &&
3857 container->CanContainOutOfFlowPositionedElement(position)) {
3858 container_offset +=
3859 To<LayoutInline>(container)->OffsetForInFlowPositionedInline(*this);
3860 } else if (style_to_use.HasInFlowPosition() && Layer()) {
3861 // Apply the relative position offset when invalidating a rectangle. The
3862 // layer is translated, but the layout box isn't, so we need to do this to
3863 // get the right dirty rect. Since this is called from
3864 // LayoutObject::setStyle, the relative position flag on the LayoutObject
3865 // has been cleared, so use the one on the style().
3866 container_offset += OffsetForInFlowPosition();
3867 }
3868
3869 if (skip_info.FilterSkipped()) {
3870 InflateVisualRectForFilterUnderContainer(transform_state, *container,
3871 ancestor);
3872 }
3873
3874 if (!MapVisualRectToContainer(container, container_offset, ancestor,
3875 visual_rect_flags, transform_state))
3876 return false;
3877
3878 if (skip_info.AncestorSkipped()) {
3879 bool preserve3D = container->StyleRef().Preserves3D();
3880 TransformState::TransformAccumulation accumulation =
3881 preserve3D ? TransformState::kAccumulateTransform
3882 : TransformState::kFlattenTransform;
3883
3884 // If the ancestor is below the container, then we need to map the rect into
3885 // ancestor's coordinates.
3886 PhysicalOffset ancestor_container_offset =
3887 ancestor->OffsetFromAncestor(container);
3888 transform_state.Move(-ancestor_container_offset, accumulation);
3889 return true;
3890 }
3891
3892 if (auto* layout_view = DynamicTo<LayoutView>(container)) {
3893 bool use_fixed_position_adjustment =
3894 position == EPosition::kFixed && container == ancestor;
3895 return layout_view->MapToVisualRectInAncestorSpaceInternal(
3896 ancestor, transform_state, use_fixed_position_adjustment ? kIsFixed : 0,
3897 visual_rect_flags);
3898 } else {
3899 return container->MapToVisualRectInAncestorSpaceInternal(
3900 ancestor, transform_state, visual_rect_flags);
3901 }
3902 }
3903
InflateVisualRectForFilter(TransformState & transform_state) const3904 void LayoutBox::InflateVisualRectForFilter(
3905 TransformState& transform_state) const {
3906 NOT_DESTROYED();
3907 if (!Layer() || !Layer()->PaintsWithFilters())
3908 return;
3909
3910 transform_state.Flatten();
3911 PhysicalRect rect = PhysicalRect::EnclosingRect(
3912 transform_state.LastPlanarQuad().BoundingBox());
3913 transform_state.SetQuad(
3914 FloatQuad(FloatRect(Layer()->MapRectForFilter(rect))));
3915 }
3916
ShouldRecalculateMinMaxWidthsAffectedByAncestor(const LayoutBox * box)3917 static bool ShouldRecalculateMinMaxWidthsAffectedByAncestor(
3918 const LayoutBox* box) {
3919 if (box->IntrinsicLogicalWidthsDirty()) {
3920 // If the preferred widths are already dirty at this point (during layout),
3921 // it actually means that we never need to calculate them, since that should
3922 // have been carried out by an ancestor that's sized based on preferred
3923 // widths (a shrink-to-fit container, for instance). In such cases the
3924 // object will be left as dirty indefinitely, and it would just be a waste
3925 // of time to calculate the preferred withs when nobody needs them.
3926 return false;
3927 }
3928 if (const LayoutBox* containing_block = box->ContainingBlock()) {
3929 if (containing_block->NeedsPreferredWidthsRecalculation() &&
3930 !containing_block->IntrinsicLogicalWidthsDirty()) {
3931 // If our containing block also has min/max widths that are affected by
3932 // the ancestry, we have already dealt with this object as well. Avoid
3933 // unnecessary work and O(n^2) time complexity.
3934 return false;
3935 }
3936 }
3937 return true;
3938 }
3939
UpdateLogicalWidth()3940 void LayoutBox::UpdateLogicalWidth() {
3941 NOT_DESTROYED();
3942 if (NeedsPreferredWidthsRecalculation()) {
3943 if (ShouldRecalculateMinMaxWidthsAffectedByAncestor(this)) {
3944 // Laying out this object means that its containing block is also being
3945 // laid out. This object is special, in that its min/max widths depend on
3946 // the ancestry (min/max width calculation should ideally be strictly
3947 // bottom-up, but that's not always the case), so since the containing
3948 // block size may have changed, we need to recalculate the min/max widths
3949 // of this object, and every child that has the same issue, recursively.
3950 SetIntrinsicLogicalWidthsDirty(kMarkOnlyThis);
3951
3952 // Since all this takes place during actual layout, instead of being part
3953 // of min/max the width calculation machinery, we need to enter said
3954 // machinery here, to make sure that what was dirtied is actually
3955 // recalculated. Leaving things dirty would mean that any subsequent
3956 // dirtying of descendants would fail.
3957 UpdateCachedIntrinsicLogicalWidthsIfNeeded();
3958 }
3959 }
3960
3961 LogicalExtentComputedValues computed_values;
3962 ComputeLogicalWidth(computed_values);
3963
3964 SetLogicalWidth(computed_values.extent_);
3965 SetLogicalLeft(computed_values.position_);
3966 SetMarginStart(computed_values.margins_.start_);
3967 SetMarginEnd(computed_values.margins_.end_);
3968 }
3969
GetMaxWidthListMarker(const LayoutBox * layout_object)3970 static float GetMaxWidthListMarker(const LayoutBox* layout_object) {
3971 #if DCHECK_IS_ON()
3972 DCHECK(layout_object);
3973 Node* parent_node = layout_object->GeneratingNode();
3974 DCHECK(parent_node);
3975 DCHECK(IsA<HTMLOListElement>(parent_node) ||
3976 IsA<HTMLUListElement>(parent_node));
3977 DCHECK_NE(layout_object->StyleRef().TextAutosizingMultiplier(), 1);
3978 #endif
3979 float max_width = 0;
3980 for (LayoutObject* child = layout_object->SlowFirstChild(); child;
3981 child = child->NextSibling()) {
3982 if (!child->IsListItem())
3983 continue;
3984
3985 auto* list_item = To<LayoutBox>(child);
3986 for (LayoutObject* item_child = list_item->SlowFirstChild(); item_child;
3987 item_child = item_child->NextSibling()) {
3988 if (!item_child->IsListMarkerForNormalContent())
3989 continue;
3990 auto* item_marker = To<LayoutBox>(item_child);
3991 // Make sure to compute the autosized width.
3992 if (item_marker->NeedsLayout())
3993 item_marker->UpdateLayout();
3994 max_width = std::max<float>(
3995 max_width,
3996 To<LayoutListMarker>(item_marker)->LogicalWidth().ToFloat());
3997 break;
3998 }
3999 }
4000 return max_width;
4001 }
4002
ContainerWidthInInlineDirection() const4003 LayoutUnit LayoutBox::ContainerWidthInInlineDirection() const {
4004 NOT_DESTROYED();
4005 LayoutBlock* cb = ContainingBlock();
4006
4007 if (IsParallelWritingMode(cb->StyleRef().GetWritingMode(),
4008 StyleRef().GetWritingMode())) {
4009 return std::max(LayoutUnit(), ContainingBlockLogicalWidthForContent());
4010 }
4011
4012 // PerpendicularContainingBlockLogicalHeight() can return -1 in some
4013 // situations but we cannot have a negative width, that's why we clamp it to
4014 // zero.
4015 return PerpendicularContainingBlockLogicalHeight().ClampNegativeToZero();
4016 }
4017
ShouldComputeLogicalWidthFromAspectRatio(LayoutUnit * out_logical_height) const4018 bool LayoutBox::ShouldComputeLogicalWidthFromAspectRatio(
4019 LayoutUnit* out_logical_height) const {
4020 NOT_DESTROYED();
4021 if (StyleRef().AspectRatio().IsAuto())
4022 return false;
4023
4024 if (!HasOverrideLogicalHeight() &&
4025 !ShouldComputeLogicalWidthFromAspectRatioAndInsets() &&
4026 !StyleRef().LogicalHeight().IsFixed() &&
4027 !StyleRef().LogicalHeight().IsPercentOrCalc()) {
4028 return false;
4029 }
4030
4031 LogicalExtentComputedValues values;
4032 values.extent_ = kIndefiniteSize;
4033 ComputeLogicalHeight(values);
4034 if (values.extent_ == kIndefiniteSize)
4035 return false;
4036
4037 if (out_logical_height)
4038 *out_logical_height = values.extent_;
4039 return true;
4040 }
4041
ComputeLogicalWidthFromAspectRatio(LayoutUnit * out_logical_width) const4042 bool LayoutBox::ComputeLogicalWidthFromAspectRatio(
4043 LayoutUnit* out_logical_width) const {
4044 NOT_DESTROYED();
4045 LayoutUnit logical_height_for_ar;
4046 if (!ShouldComputeLogicalWidthFromAspectRatio(&logical_height_for_ar))
4047 return false;
4048
4049 LayoutUnit container_width_in_inline_direction =
4050 ContainerWidthInInlineDirection();
4051
4052 NGBoxStrut border_padding(BorderStart() + ComputedCSSPaddingStart(),
4053 BorderEnd() + ComputedCSSPaddingEnd(),
4054 BorderBefore() + ComputedCSSPaddingBefore(),
4055 BorderAfter() + ComputedCSSPaddingAfter());
4056 LayoutUnit logical_width =
4057 InlineSizeFromAspectRatio(border_padding, StyleRef().LogicalAspectRatio(),
4058 StyleRef().BoxSizing(), logical_height_for_ar);
4059 *out_logical_width = ConstrainLogicalWidthByMinMax(
4060 logical_width, container_width_in_inline_direction, ContainingBlock(),
4061 /* allow_intrinsic */ false);
4062 return true;
4063 }
4064
4065 DISABLE_CFI_PERF
ComputeLogicalWidth(LogicalExtentComputedValues & computed_values) const4066 void LayoutBox::ComputeLogicalWidth(
4067 LogicalExtentComputedValues& computed_values) const {
4068 NOT_DESTROYED();
4069 computed_values.position_ = LogicalLeft();
4070 computed_values.margins_.start_ = MarginStart();
4071 computed_values.margins_.end_ = MarginEnd();
4072
4073 // The parent box is flexing us, so it has increased or decreased our
4074 // width. Use the width from the style context.
4075 if (HasOverrideLogicalWidth()) {
4076 computed_values.extent_ = OverrideLogicalWidth();
4077 return;
4078 }
4079
4080 if (IsOutOfFlowPositioned()) {
4081 ComputePositionedLogicalWidth(computed_values);
4082 return;
4083 }
4084
4085 // FIXME: Account for writing-mode in flexible boxes.
4086 // https://bugs.webkit.org/show_bug.cgi?id=46418
4087 bool in_vertical_box =
4088 Parent()->IsDeprecatedFlexibleBox() &&
4089 (Parent()->StyleRef().BoxOrient() == EBoxOrient::kVertical);
4090 bool stretching =
4091 (Parent()->StyleRef().BoxAlign() == EBoxAlignment::kStretch);
4092 // TODO (lajava): Stretching is the only reason why we don't want the box to
4093 // be treated as a replaced element, so we could perhaps refactor all this
4094 // logic, not only for flex and grid since alignment is intended to be applied
4095 // to any block.
4096 bool treat_as_replaced = ShouldComputeSizeAsReplaced() &&
4097 (!in_vertical_box || !stretching) &&
4098 (!IsGridItem() || !HasStretchedLogicalWidth());
4099 const ComputedStyle& style_to_use = StyleRef();
4100 LayoutUnit container_logical_width =
4101 std::max(LayoutUnit(), ContainingBlockLogicalWidthForContent());
4102
4103 if (IsInline() && !IsInlineBlockOrInlineTable()) {
4104 // just calculate margins
4105 computed_values.margins_.start_ = MinimumValueForLength(
4106 style_to_use.MarginStart(), container_logical_width);
4107 computed_values.margins_.end_ = MinimumValueForLength(
4108 style_to_use.MarginEnd(), container_logical_width);
4109 if (treat_as_replaced) {
4110 computed_values.extent_ = std::max(
4111 ComputeReplacedLogicalWidth() + BorderAndPaddingLogicalWidth(),
4112 PreferredLogicalWidths().min_size);
4113 }
4114 return;
4115 }
4116
4117 LayoutUnit container_width_in_inline_direction =
4118 ContainerWidthInInlineDirection();
4119 LayoutBlock* cb = ContainingBlock();
4120
4121 if (StyleRef().LogicalWidth().IsAuto() && !treat_as_replaced &&
4122 ComputeLogicalWidthFromAspectRatio(&computed_values.extent_)) {
4123 /* we're good */
4124 } else if (treat_as_replaced) {
4125 computed_values.extent_ =
4126 ComputeReplacedLogicalWidth() + BorderAndPaddingLogicalWidth();
4127 } else {
4128 LayoutUnit preferred_width = ComputeLogicalWidthUsing(
4129 kMainOrPreferredSize, style_to_use.LogicalWidth(),
4130 container_width_in_inline_direction, cb);
4131 computed_values.extent_ = ConstrainLogicalWidthByMinMax(
4132 preferred_width, container_width_in_inline_direction, cb);
4133 }
4134
4135 // Margin calculations.
4136 ComputeMarginsForDirection(
4137 kInlineDirection, cb, container_logical_width, computed_values.extent_,
4138 computed_values.margins_.start_, computed_values.margins_.end_,
4139 StyleRef().MarginStart(), StyleRef().MarginEnd());
4140
4141 bool has_perpendicular_containing_block =
4142 cb->IsHorizontalWritingMode() != IsHorizontalWritingMode();
4143 if (!has_perpendicular_containing_block && container_logical_width &&
4144 container_logical_width !=
4145 (computed_values.extent_ + computed_values.margins_.start_ +
4146 computed_values.margins_.end_) &&
4147 !IsFloating() && !IsInline() &&
4148 !cb->IsFlexibleBoxIncludingDeprecatedAndNG() && !cb->IsLayoutGrid()) {
4149 LayoutUnit new_margin_total =
4150 container_logical_width - computed_values.extent_;
4151 bool has_inverted_direction = cb->StyleRef().IsLeftToRightDirection() !=
4152 StyleRef().IsLeftToRightDirection();
4153 if (has_inverted_direction) {
4154 computed_values.margins_.start_ =
4155 new_margin_total - computed_values.margins_.end_;
4156 } else {
4157 computed_values.margins_.end_ =
4158 new_margin_total - computed_values.margins_.start_;
4159 }
4160 }
4161
4162 if (style_to_use.TextAutosizingMultiplier() != 1 &&
4163 style_to_use.MarginStart().IsFixed()) {
4164 Node* parent_node = GeneratingNode();
4165 if (parent_node && (IsA<HTMLOListElement>(*parent_node) ||
4166 IsA<HTMLUListElement>(*parent_node))) {
4167 // Make sure the markers in a list are properly positioned (i.e. not
4168 // chopped off) when autosized.
4169 const float adjusted_margin =
4170 (1 - 1.0 / style_to_use.TextAutosizingMultiplier()) *
4171 GetMaxWidthListMarker(this);
4172 bool has_inverted_direction = cb->StyleRef().IsLeftToRightDirection() !=
4173 StyleRef().IsLeftToRightDirection();
4174 if (has_inverted_direction)
4175 computed_values.margins_.end_ += adjusted_margin;
4176 else
4177 computed_values.margins_.start_ += adjusted_margin;
4178 }
4179 }
4180 }
4181
FillAvailableMeasure(LayoutUnit available_logical_width) const4182 LayoutUnit LayoutBox::FillAvailableMeasure(
4183 LayoutUnit available_logical_width) const {
4184 NOT_DESTROYED();
4185 LayoutUnit margin_start;
4186 LayoutUnit margin_end;
4187 return FillAvailableMeasure(available_logical_width, margin_start,
4188 margin_end);
4189 }
4190
FillAvailableMeasure(LayoutUnit available_logical_width,LayoutUnit & margin_start,LayoutUnit & margin_end) const4191 LayoutUnit LayoutBox::FillAvailableMeasure(LayoutUnit available_logical_width,
4192 LayoutUnit& margin_start,
4193 LayoutUnit& margin_end) const {
4194 NOT_DESTROYED();
4195 DCHECK_GE(available_logical_width, 0);
4196
4197 bool isOrthogonalElement =
4198 IsHorizontalWritingMode() != ContainingBlock()->IsHorizontalWritingMode();
4199 LayoutUnit available_size_for_resolving_margin =
4200 isOrthogonalElement ? ContainingBlockLogicalWidthForContent()
4201 : available_logical_width;
4202 margin_start = MinimumValueForLength(StyleRef().MarginStart(),
4203 available_size_for_resolving_margin);
4204 margin_end = MinimumValueForLength(StyleRef().MarginEnd(),
4205 available_size_for_resolving_margin);
4206
4207 if (HasOverrideAvailableInlineSize())
4208 available_logical_width = OverrideAvailableInlineSize();
4209
4210 LayoutUnit available = available_logical_width - margin_start - margin_end;
4211 available = std::max(available, LayoutUnit());
4212 return available;
4213 }
4214
4215 DISABLE_CFI_PERF
ComputeIntrinsicLogicalWidthUsing(const Length & logical_width_length,LayoutUnit available_logical_width) const4216 LayoutUnit LayoutBox::ComputeIntrinsicLogicalWidthUsing(
4217 const Length& logical_width_length,
4218 LayoutUnit available_logical_width) const {
4219 NOT_DESTROYED();
4220 if (logical_width_length.IsFillAvailable()) {
4221 if (!IsA<HTMLMarqueeElement>(GetNode())) {
4222 UseCounter::Count(GetDocument(),
4223 WebFeature::kCSSFillAvailableLogicalWidth);
4224 }
4225 return std::max(BorderAndPaddingLogicalWidth(),
4226 FillAvailableMeasure(available_logical_width));
4227 }
4228
4229 MinMaxSizesType type = MinMaxSizesType::kContent;
4230 if (logical_width_length.IsMinIntrinsic())
4231 type = MinMaxSizesType::kIntrinsic;
4232 MinMaxSizes sizes = IntrinsicLogicalWidths(type);
4233
4234 if (logical_width_length.IsMinContent() ||
4235 logical_width_length.IsMinIntrinsic())
4236 return sizes.min_size;
4237
4238 if (logical_width_length.IsMaxContent())
4239 return sizes.max_size;
4240
4241 if (logical_width_length.IsFitContent()) {
4242 return sizes.ClampSizeToMinAndMax(
4243 FillAvailableMeasure(available_logical_width));
4244 }
4245
4246 NOTREACHED();
4247 return LayoutUnit();
4248 }
4249
4250 DISABLE_CFI_PERF
ComputeLogicalWidthUsing(SizeType width_type,const Length & logical_width,LayoutUnit available_logical_width,const LayoutBlock * cb) const4251 LayoutUnit LayoutBox::ComputeLogicalWidthUsing(
4252 SizeType width_type,
4253 const Length& logical_width,
4254 LayoutUnit available_logical_width,
4255 const LayoutBlock* cb) const {
4256 NOT_DESTROYED();
4257 DCHECK(width_type == kMinSize || width_type == kMainOrPreferredSize ||
4258 !logical_width.IsAuto());
4259 if (width_type == kMinSize && logical_width.IsAuto())
4260 return AdjustBorderBoxLogicalWidthForBoxSizing(0);
4261
4262 if (logical_width.IsSpecified()) {
4263 // FIXME: If the containing block flow is perpendicular to our direction we
4264 // need to use the available logical height instead.
4265 return AdjustBorderBoxLogicalWidthForBoxSizing(
4266 ValueForLength(logical_width, available_logical_width));
4267 }
4268
4269 if (logical_width.IsContentOrIntrinsicOrFillAvailable()) {
4270 return ComputeIntrinsicLogicalWidthUsing(logical_width,
4271 available_logical_width);
4272 }
4273
4274 LayoutUnit margin_start;
4275 LayoutUnit margin_end;
4276 LayoutUnit logical_width_result =
4277 FillAvailableMeasure(available_logical_width, margin_start, margin_end);
4278
4279 auto* child_block_flow = DynamicTo<LayoutBlockFlow>(cb);
4280 if (ShrinkToAvoidFloats() && child_block_flow &&
4281 child_block_flow->ContainsFloats()) {
4282 logical_width_result = std::min(
4283 logical_width_result, ShrinkLogicalWidthToAvoidFloats(
4284 margin_start, margin_end, child_block_flow));
4285 }
4286
4287 if (width_type == kMainOrPreferredSize &&
4288 SizesLogicalWidthToFitContent(logical_width)) {
4289 // Reset width so that any percent margins on inline children do not
4290 // use it when calculating min/max preferred width.
4291 // TODO(crbug.com/710026): Remove const_cast
4292 LayoutUnit w = LogicalWidth();
4293 const_cast<LayoutBox*>(this)->SetLogicalWidth(LayoutUnit());
4294 MinMaxSizes preferred_logical_widths = PreferredLogicalWidths();
4295 LayoutUnit result =
4296 preferred_logical_widths.ClampSizeToMinAndMax(logical_width_result);
4297 const_cast<LayoutBox*>(this)->SetLogicalWidth(w);
4298 return result;
4299 }
4300 return logical_width_result;
4301 }
4302
ColumnFlexItemHasStretchAlignment() const4303 bool LayoutBox::ColumnFlexItemHasStretchAlignment() const {
4304 NOT_DESTROYED();
4305 // auto margins mean we don't stretch. Note that this function will only be
4306 // used for widths, so we don't have to check marginBefore/marginAfter.
4307 const auto& parent_style = Parent()->StyleRef();
4308 DCHECK(parent_style.ResolvedIsColumnFlexDirection());
4309 if (StyleRef().MarginStart().IsAuto() || StyleRef().MarginEnd().IsAuto())
4310 return false;
4311 return StyleRef()
4312 .ResolvedAlignSelf(
4313 ContainingBlock()->SelfAlignmentNormalBehavior(),
4314 &parent_style)
4315 .GetPosition() == ItemPosition::kStretch;
4316 }
4317
IsStretchingColumnFlexItem() const4318 bool LayoutBox::IsStretchingColumnFlexItem() const {
4319 NOT_DESTROYED();
4320 LayoutObject* parent = Parent();
4321 if (parent->StyleRef().IsDeprecatedWebkitBox() &&
4322 parent->StyleRef().BoxOrient() == EBoxOrient::kVertical &&
4323 parent->StyleRef().BoxAlign() == EBoxAlignment::kStretch)
4324 return true;
4325
4326 // We don't stretch multiline flexboxes because they need to apply line
4327 // spacing (align-content) first.
4328 if (parent->IsFlexibleBoxIncludingNG() &&
4329 parent->StyleRef().FlexWrap() == EFlexWrap::kNowrap &&
4330 parent->StyleRef().ResolvedIsColumnFlexDirection() &&
4331 ColumnFlexItemHasStretchAlignment())
4332 return true;
4333 return false;
4334 }
4335
4336 // TODO (lajava) Can/Should we move this inside specific layout classes (flex.
4337 // grid)? Can we refactor columnFlexItemHasStretchAlignment logic?
HasStretchedLogicalWidth() const4338 bool LayoutBox::HasStretchedLogicalWidth() const {
4339 NOT_DESTROYED();
4340 const ComputedStyle& style = StyleRef();
4341 if (!style.LogicalWidth().IsAuto() || style.MarginStart().IsAuto() ||
4342 style.MarginEnd().IsAuto())
4343 return false;
4344 LayoutBlock* cb = ContainingBlock();
4345 if (!cb) {
4346 // We are evaluating align-self/justify-self, which default to 'normal' for
4347 // the root element. The 'normal' value behaves like 'start' except for
4348 // Flexbox Items, which obviously should have a container.
4349 return false;
4350 }
4351 if (cb->IsHorizontalWritingMode() != IsHorizontalWritingMode()) {
4352 return style
4353 .ResolvedAlignSelf(cb->SelfAlignmentNormalBehavior(this),
4354 cb->Style())
4355 .GetPosition() == ItemPosition::kStretch;
4356 }
4357 return style
4358 .ResolvedJustifySelf(cb->SelfAlignmentNormalBehavior(this),
4359 cb->Style())
4360 .GetPosition() == ItemPosition::kStretch;
4361 }
4362
SizesLogicalWidthToFitContent(const Length & logical_width) const4363 bool LayoutBox::SizesLogicalWidthToFitContent(
4364 const Length& logical_width) const {
4365 NOT_DESTROYED();
4366 if (IsFloating() || IsInlineBlockOrInlineTable() ||
4367 StyleRef().HasOutOfFlowPosition())
4368 return true;
4369
4370 if (IsGridItem())
4371 return !HasStretchedLogicalWidth();
4372
4373 // Flexible box items should shrink wrap, so we lay them out at their
4374 // intrinsic widths. In the case of columns that have a stretch alignment, we
4375 // go ahead and layout at the stretched size to avoid an extra layout when
4376 // applying alignment.
4377 if (Parent()->IsFlexibleBoxIncludingNG()) {
4378 // For multiline columns, we need to apply align-content first, so we can't
4379 // stretch now.
4380 if (!Parent()->StyleRef().ResolvedIsColumnFlexDirection() ||
4381 Parent()->StyleRef().FlexWrap() != EFlexWrap::kNowrap)
4382 return true;
4383 if (!ColumnFlexItemHasStretchAlignment())
4384 return true;
4385 }
4386
4387 // Flexible horizontal boxes lay out children at their intrinsic widths. Also
4388 // vertical boxes that don't stretch their kids lay out their children at
4389 // their intrinsic widths.
4390 // FIXME: Think about writing-mode here.
4391 // https://bugs.webkit.org/show_bug.cgi?id=46473
4392 if ((Parent()->IsDeprecatedFlexibleBox() ||
4393 (Parent()->StyleRef().IsDeprecatedWebkitBox() &&
4394 Parent()->IsFlexibleBox())) &&
4395 (Parent()->StyleRef().BoxOrient() == EBoxOrient::kHorizontal ||
4396 Parent()->StyleRef().BoxAlign() != EBoxAlignment::kStretch))
4397 return true;
4398
4399 // Button, input, select, textarea, and legend treat width value of 'auto' as
4400 // 'intrinsic' unless it's in a stretching column flexbox.
4401 // FIXME: Think about writing-mode here.
4402 // https://bugs.webkit.org/show_bug.cgi?id=46473
4403 if (logical_width.IsAuto() && !IsStretchingColumnFlexItem() &&
4404 AutoWidthShouldFitContent())
4405 return true;
4406
4407 if (IsHorizontalWritingMode() != ContainingBlock()->IsHorizontalWritingMode())
4408 return true;
4409
4410 if (IsCustomItem())
4411 return IsCustomItemShrinkToFit();
4412
4413 return false;
4414 }
4415
AutoWidthShouldFitContent() const4416 bool LayoutBox::AutoWidthShouldFitContent() const {
4417 NOT_DESTROYED();
4418 return GetNode() &&
4419 (IsA<HTMLInputElement>(*GetNode()) ||
4420 IsA<HTMLSelectElement>(*GetNode()) ||
4421 IsA<HTMLButtonElement>(*GetNode()) ||
4422 IsA<HTMLTextAreaElement>(*GetNode()) || IsRenderedLegend());
4423 }
4424
ComputeMarginsForDirection(MarginDirection flow_direction,const LayoutBlock * containing_block,LayoutUnit container_width,LayoutUnit child_width,LayoutUnit & margin_start,LayoutUnit & margin_end,Length margin_start_length,Length margin_end_length) const4425 void LayoutBox::ComputeMarginsForDirection(MarginDirection flow_direction,
4426 const LayoutBlock* containing_block,
4427 LayoutUnit container_width,
4428 LayoutUnit child_width,
4429 LayoutUnit& margin_start,
4430 LayoutUnit& margin_end,
4431 Length margin_start_length,
4432 Length margin_end_length) const {
4433 NOT_DESTROYED();
4434 // First assert that we're not calling this method on box types that don't
4435 // support margins.
4436 DCHECK(!IsTableCell());
4437 DCHECK(!IsTableRow());
4438 DCHECK(!IsTableSection());
4439 DCHECK(!IsLayoutTableCol());
4440 if (flow_direction == kBlockDirection || IsFloating() || IsInline()) {
4441 // Margins are calculated with respect to the logical width of
4442 // the containing block (8.3)
4443 // Inline blocks/tables and floats don't have their margins increased.
4444 margin_start = MinimumValueForLength(margin_start_length, container_width);
4445 margin_end = MinimumValueForLength(margin_end_length, container_width);
4446 return;
4447 }
4448
4449 if (containing_block->IsFlexibleBoxIncludingNG()) {
4450 // We need to let flexbox handle the margin adjustment - otherwise, flexbox
4451 // will think we're wider than we actually are and calculate line sizes
4452 // wrong. See also https://drafts.csswg.org/css-flexbox/#auto-margins
4453 if (margin_start_length.IsAuto())
4454 margin_start_length = Length::Fixed(0);
4455 if (margin_end_length.IsAuto())
4456 margin_end_length = Length::Fixed(0);
4457 }
4458
4459 LayoutUnit margin_start_width =
4460 MinimumValueForLength(margin_start_length, container_width);
4461 LayoutUnit margin_end_width =
4462 MinimumValueForLength(margin_end_length, container_width);
4463
4464 LayoutUnit available_width = container_width;
4465 auto* containing_block_flow = DynamicTo<LayoutBlockFlow>(containing_block);
4466 if (CreatesNewFormattingContext() && containing_block_flow &&
4467 containing_block_flow->ContainsFloats()) {
4468 available_width = ContainingBlockAvailableLineWidth();
4469 if (ShrinkToAvoidFloats() && available_width < container_width) {
4470 margin_start = std::max(LayoutUnit(), margin_start_width);
4471 margin_end = std::max(LayoutUnit(), margin_end_width);
4472 }
4473 }
4474
4475 // CSS 2.1 (10.3.3): "If 'width' is not 'auto' and 'border-left-width' +
4476 // 'padding-left' + 'width' + 'padding-right' + 'border-right-width' (plus any
4477 // of 'margin-left' or 'margin-right' that are not 'auto') is larger than the
4478 // width of the containing block, then any 'auto' values for 'margin-left' or
4479 // 'margin-right' are, for the following rules, treated as zero.
4480 LayoutUnit margin_box_width =
4481 child_width + (!StyleRef().Width().IsAuto()
4482 ? margin_start_width + margin_end_width
4483 : LayoutUnit());
4484
4485 if (margin_box_width < available_width) {
4486 // CSS 2.1: "If both 'margin-left' and 'margin-right' are 'auto', their used
4487 // values are equal. This horizontally centers the element with respect to
4488 // the edges of the containing block."
4489 const ComputedStyle& containing_block_style = containing_block->StyleRef();
4490 if ((margin_start_length.IsAuto() && margin_end_length.IsAuto()) ||
4491 (!margin_start_length.IsAuto() && !margin_end_length.IsAuto() &&
4492 containing_block_style.GetTextAlign() == ETextAlign::kWebkitCenter)) {
4493 // Other browsers center the margin box for align=center elements so we
4494 // match them here.
4495 LayoutUnit centered_margin_box_start =
4496 std::max(LayoutUnit(), (available_width - child_width -
4497 margin_start_width - margin_end_width) /
4498 2);
4499 margin_start = centered_margin_box_start + margin_start_width;
4500 margin_end =
4501 available_width - child_width - margin_start + margin_end_width;
4502 return;
4503 }
4504
4505 // Adjust margins for the align attribute
4506 if ((!containing_block_style.IsLeftToRightDirection() &&
4507 containing_block_style.GetTextAlign() == ETextAlign::kWebkitLeft) ||
4508 (containing_block_style.IsLeftToRightDirection() &&
4509 containing_block_style.GetTextAlign() == ETextAlign::kWebkitRight)) {
4510 if (containing_block_style.IsLeftToRightDirection() !=
4511 StyleRef().IsLeftToRightDirection()) {
4512 if (!margin_start_length.IsAuto())
4513 margin_end_length = Length::Auto();
4514 } else {
4515 if (!margin_end_length.IsAuto())
4516 margin_start_length = Length::Auto();
4517 }
4518 }
4519
4520 // CSS 2.1: "If there is exactly one value specified as 'auto', its used
4521 // value follows from the equality."
4522 if (margin_end_length.IsAuto()) {
4523 margin_start = margin_start_width;
4524 margin_end = available_width - child_width - margin_start;
4525 return;
4526 }
4527
4528 if (margin_start_length.IsAuto()) {
4529 margin_end = margin_end_width;
4530 margin_start = available_width - child_width - margin_end;
4531 return;
4532 }
4533 }
4534
4535 // Either no auto margins, or our margin box width is >= the container width,
4536 // auto margins will just turn into 0.
4537 margin_start = margin_start_width;
4538 margin_end = margin_end_width;
4539 }
4540
4541 DISABLE_CFI_PERF
UpdateLogicalHeight()4542 void LayoutBox::UpdateLogicalHeight() {
4543 NOT_DESTROYED();
4544 if (!HasOverrideLogicalHeight()) {
4545 // If we have an override height, our children will have sized themselves
4546 // relative to our override height, which would make our intrinsic size
4547 // incorrect (too big).
4548 intrinsic_content_logical_height_ = ContentLogicalHeight();
4549 }
4550
4551 LogicalExtentComputedValues computed_values;
4552 ComputeLogicalHeight(computed_values);
4553
4554 SetLogicalHeight(computed_values.extent_);
4555 SetLogicalTop(computed_values.position_);
4556 SetMarginBefore(computed_values.margins_.before_);
4557 SetMarginAfter(computed_values.margins_.after_);
4558 }
4559
HeightForDocumentElement(const Document & document)4560 static inline const Length& HeightForDocumentElement(const Document& document) {
4561 return document.documentElement()
4562 ->GetLayoutObject()
4563 ->StyleRef()
4564 .LogicalHeight();
4565 }
4566
ComputeLogicalHeight(LogicalExtentComputedValues & computed_values) const4567 void LayoutBox::ComputeLogicalHeight(
4568 LogicalExtentComputedValues& computed_values) const {
4569 NOT_DESTROYED();
4570 LayoutUnit height;
4571 if (HasOverrideIntrinsicContentLogicalHeight()) {
4572 height = OverrideIntrinsicContentLogicalHeight() +
4573 BorderAndPaddingLogicalHeight() +
4574 ComputeLogicalScrollbars().BlockSum();
4575 } else {
4576 LayoutUnit default_height = DefaultIntrinsicContentBlockSize();
4577 if (default_height != kIndefiniteSize) {
4578 height = default_height + BorderAndPaddingLogicalHeight();
4579 // <textarea>'s intrinsic size should ignore scrollbar existence.
4580 if (!IsTextAreaIncludingNG())
4581 height += ComputeLogicalScrollbars().BlockSum();
4582 // FIXME: The logical height of the inner editor box should have been
4583 // added before calling ComputeLogicalHeight to avoid this hack.
4584 if (IsTextControlIncludingNG())
4585 SetIntrinsicContentLogicalHeight(default_height);
4586 } else if (ShouldApplySizeContainment() && !IsLayoutGrid()) {
4587 height = BorderAndPaddingLogicalHeight() +
4588 ComputeLogicalScrollbars().BlockSum();
4589 } else {
4590 height = LogicalHeight();
4591 }
4592 }
4593 ComputeLogicalHeight(height, LogicalTop(), computed_values);
4594 }
4595
ComputeLogicalHeight(LayoutUnit logical_height,LayoutUnit logical_top,LogicalExtentComputedValues & computed_values) const4596 void LayoutBox::ComputeLogicalHeight(
4597 LayoutUnit logical_height,
4598 LayoutUnit logical_top,
4599 LogicalExtentComputedValues& computed_values) const {
4600 NOT_DESTROYED();
4601 computed_values.extent_ = logical_height;
4602 computed_values.position_ = logical_top;
4603
4604 // Cell height is managed by the table.
4605 if (IsTableCell())
4606 return;
4607
4608 Length h;
4609 if (IsOutOfFlowPositioned()) {
4610 ComputePositionedLogicalHeight(computed_values);
4611 if (HasOverrideLogicalHeight())
4612 computed_values.extent_ = OverrideLogicalHeight();
4613 } else {
4614 LayoutBlock* cb = ContainingBlock();
4615
4616 // If we are perpendicular to our containing block then we need to resolve
4617 // our block-start and block-end margins so that if they are 'auto' we are
4618 // centred or aligned within the inline flow containing block: this is done
4619 // by computing the margins as though they are inline.
4620 // Note that as this is the 'sizing phase' we are using our own writing mode
4621 // rather than the containing block's. We use the containing block's writing
4622 // mode when figuring out the block-direction margins for positioning in
4623 // |computeAndSetBlockDirectionMargins| (i.e. margin collapsing etc.).
4624 // http://www.w3.org/TR/2014/CR-css-writing-modes-3-20140320/#orthogonal-flows
4625 MarginDirection flow_direction =
4626 IsHorizontalWritingMode() != cb->IsHorizontalWritingMode()
4627 ? kInlineDirection
4628 : kBlockDirection;
4629
4630 // For tables, calculate margins only.
4631 if (IsTable()) {
4632 ComputeMarginsForDirection(
4633 flow_direction, cb, ContainingBlockLogicalWidthForContent(),
4634 computed_values.extent_, computed_values.margins_.before_,
4635 computed_values.margins_.after_, StyleRef().MarginBefore(),
4636 StyleRef().MarginAfter());
4637 return;
4638 }
4639
4640 bool check_min_max_height = false;
4641 bool compute_size_as_replaced = ShouldComputeSizeAsReplaced();
4642
4643 // The parent box is flexing us, so it has increased or decreased our
4644 // height. We have to grab our cached flexible height.
4645 if (HasOverrideLogicalHeight()) {
4646 h = Length::Fixed(OverrideLogicalHeight());
4647 } else if (compute_size_as_replaced) {
4648 h = Length::Fixed(ComputeReplacedLogicalHeight() +
4649 BorderAndPaddingLogicalHeight());
4650 } else {
4651 h = StyleRef().LogicalHeight();
4652 check_min_max_height = true;
4653 }
4654
4655 LayoutUnit height_result;
4656 if (check_min_max_height) {
4657 if (!compute_size_as_replaced &&
4658 ShouldComputeLogicalHeightFromAspectRatio()) {
4659 NGBoxStrut border_padding(BorderStart() + ComputedCSSPaddingStart(),
4660 BorderEnd() + ComputedCSSPaddingEnd(),
4661 BorderBefore() + ComputedCSSPaddingBefore(),
4662 BorderAfter() + ComputedCSSPaddingAfter());
4663 height_result = BlockSizeFromAspectRatio(
4664 border_padding, StyleRef().LogicalAspectRatio(),
4665 StyleRef().BoxSizing(), LogicalWidth());
4666 } else {
4667 height_result = ComputeLogicalHeightUsing(
4668 kMainOrPreferredSize, h,
4669 computed_values.extent_ - BorderAndPaddingLogicalHeight());
4670 }
4671 if (height_result == -1)
4672 height_result = computed_values.extent_;
4673 height_result = ConstrainLogicalHeightByMinMax(
4674 height_result,
4675 computed_values.extent_ - BorderAndPaddingLogicalHeight());
4676 } else {
4677 DCHECK(h.IsFixed());
4678 height_result = LayoutUnit(h.Value());
4679 }
4680
4681 computed_values.extent_ = height_result;
4682 ComputeMarginsForDirection(
4683 flow_direction, cb, ContainingBlockLogicalWidthForContent(),
4684 computed_values.extent_, computed_values.margins_.before_,
4685 computed_values.margins_.after_, StyleRef().MarginBefore(),
4686 StyleRef().MarginAfter());
4687 }
4688
4689 // WinIE quirk: The <html> block always fills the entire canvas in quirks
4690 // mode. The <body> always fills the <html> block in quirks mode. Only apply
4691 // this quirk if the block is normal flow and no height is specified. When
4692 // we're printing, we also need this quirk if the body or root has a
4693 // percentage height since we don't set a height in LayoutView when we're
4694 // printing. So without this quirk, the height has nothing to be a percentage
4695 // of, and it ends up being 0. That is bad.
4696 bool paginated_content_needs_base_height =
4697 GetDocument().Printing() && h.IsPercentOrCalc() &&
4698 (IsDocumentElement() ||
4699 (IsBody() &&
4700 HeightForDocumentElement(GetDocument()).IsPercentOrCalc())) &&
4701 !IsInline();
4702 if (StretchesToViewport() || paginated_content_needs_base_height) {
4703 LayoutUnit margins = CollapsedMarginBefore() + CollapsedMarginAfter();
4704 LayoutUnit visible_height = View()->ViewLogicalHeightForPercentages();
4705 if (IsDocumentElement()) {
4706 computed_values.extent_ =
4707 std::max(computed_values.extent_, visible_height - margins);
4708 } else {
4709 LayoutUnit margins_borders_padding =
4710 margins + ParentBox()->MarginBefore() + ParentBox()->MarginAfter() +
4711 ParentBox()->BorderAndPaddingLogicalHeight();
4712 computed_values.extent_ = std::max(
4713 computed_values.extent_, visible_height - margins_borders_padding);
4714 }
4715 }
4716 }
4717
ComputeLogicalHeightWithoutLayout() const4718 LayoutUnit LayoutBox::ComputeLogicalHeightWithoutLayout() const {
4719 NOT_DESTROYED();
4720 LogicalExtentComputedValues computed_values;
4721
4722 if (!SelfNeedsLayout() && HasOverrideIntrinsicContentLogicalHeight()) {
4723 ComputeLogicalHeight(OverrideIntrinsicContentLogicalHeight() +
4724 BorderAndPaddingLogicalHeight(),
4725 LayoutUnit(), computed_values);
4726 } else {
4727 // TODO(cbiesinger): We should probably return something other than just
4728 // border + padding, but for now we have no good way to do anything else
4729 // without layout, so we just use that.
4730 ComputeLogicalHeight(BorderAndPaddingLogicalHeight(), LayoutUnit(),
4731 computed_values);
4732 }
4733 return computed_values.extent_;
4734 }
4735
ComputeLogicalHeightUsing(SizeType height_type,const Length & height,LayoutUnit intrinsic_content_height) const4736 LayoutUnit LayoutBox::ComputeLogicalHeightUsing(
4737 SizeType height_type,
4738 const Length& height,
4739 LayoutUnit intrinsic_content_height) const {
4740 NOT_DESTROYED();
4741 LayoutUnit logical_height = ComputeContentAndScrollbarLogicalHeightUsing(
4742 height_type, height, intrinsic_content_height);
4743 if (logical_height != -1) {
4744 if (height.IsSpecified())
4745 logical_height = AdjustBorderBoxLogicalHeightForBoxSizing(logical_height);
4746 else
4747 logical_height += BorderAndPaddingLogicalHeight();
4748 }
4749 return logical_height;
4750 }
4751
ComputeContentLogicalHeight(SizeType height_type,const Length & height,LayoutUnit intrinsic_content_height) const4752 LayoutUnit LayoutBox::ComputeContentLogicalHeight(
4753 SizeType height_type,
4754 const Length& height,
4755 LayoutUnit intrinsic_content_height) const {
4756 NOT_DESTROYED();
4757 LayoutUnit height_including_scrollbar =
4758 ComputeContentAndScrollbarLogicalHeightUsing(height_type, height,
4759 intrinsic_content_height);
4760 if (height_including_scrollbar == -1)
4761 return LayoutUnit(-1);
4762 LayoutUnit adjusted = height_including_scrollbar;
4763 if (height.IsSpecified()) {
4764 // Keywords don't get adjusted for box-sizing
4765 adjusted =
4766 AdjustContentBoxLogicalHeightForBoxSizing(height_including_scrollbar);
4767 }
4768 return std::max(LayoutUnit(),
4769 adjusted - ComputeLogicalScrollbars().BlockSum());
4770 }
4771
ComputeIntrinsicLogicalContentHeightUsing(SizeType height_type,const Length & logical_height_length,LayoutUnit intrinsic_content_height,LayoutUnit border_and_padding) const4772 LayoutUnit LayoutBox::ComputeIntrinsicLogicalContentHeightUsing(
4773 SizeType height_type,
4774 const Length& logical_height_length,
4775 LayoutUnit intrinsic_content_height,
4776 LayoutUnit border_and_padding) const {
4777 NOT_DESTROYED();
4778 // FIXME(cbiesinger): The css-sizing spec is considering changing what
4779 // min-content/max-content should resolve to.
4780 // If that happens, this code will have to change.
4781 if (logical_height_length.IsMinContent() ||
4782 logical_height_length.IsMaxContent() ||
4783 logical_height_length.IsMinIntrinsic() ||
4784 logical_height_length.IsFitContent()) {
4785 if (IsAtomicInlineLevel() && !IsFlexibleBoxIncludingNG() && !IsLayoutGrid())
4786 return IntrinsicSize().Height();
4787 return intrinsic_content_height;
4788 }
4789 if (logical_height_length.IsFillAvailable()) {
4790 if (!IsA<HTMLMarqueeElement>(GetNode())) {
4791 UseCounter::Count(GetDocument(),
4792 WebFeature::kCSSFillAvailableLogicalHeight);
4793 }
4794 const LayoutUnit available_logical_height =
4795 LayoutBoxUtils::AvailableLogicalHeight(*this, ContainingBlock());
4796 // If the available logical-height is indefinite fallback to the "default"
4797 // depending on the |SizeType|.
4798 if (available_logical_height == -1) {
4799 if (height_type == kMinSize)
4800 return LayoutUnit();
4801 if (height_type == kMainOrPreferredSize)
4802 return intrinsic_content_height;
4803 return LayoutUnit::Max();
4804 }
4805 return available_logical_height - border_and_padding;
4806 }
4807 NOTREACHED();
4808 return LayoutUnit();
4809 }
4810
ComputeContentAndScrollbarLogicalHeightUsing(SizeType height_type,const Length & height,LayoutUnit intrinsic_content_height) const4811 LayoutUnit LayoutBox::ComputeContentAndScrollbarLogicalHeightUsing(
4812 SizeType height_type,
4813 const Length& height,
4814 LayoutUnit intrinsic_content_height) const {
4815 NOT_DESTROYED();
4816 if (height.IsAuto())
4817 return height_type == kMinSize ? LayoutUnit() : LayoutUnit(-1);
4818 // FIXME(cbiesinger): The css-sizing spec is considering changing what
4819 // min-content/max-content should resolve to.
4820 // If that happens, this code will have to change.
4821 if (height.IsContentOrIntrinsicOrFillAvailable()) {
4822 if (intrinsic_content_height == -1)
4823 return LayoutUnit(-1); // Intrinsic height isn't available.
4824 return ComputeIntrinsicLogicalContentHeightUsing(
4825 height_type, height, intrinsic_content_height,
4826 BorderAndPaddingLogicalHeight()) +
4827 ComputeLogicalScrollbars().BlockSum();
4828 }
4829 if (height.IsFixed())
4830 return LayoutUnit(height.Value());
4831 if (height.IsPercentOrCalc())
4832 return ComputePercentageLogicalHeight(height);
4833 return LayoutUnit(-1);
4834 }
4835
StretchesToViewportInQuirksMode() const4836 bool LayoutBox::StretchesToViewportInQuirksMode() const {
4837 NOT_DESTROYED();
4838 if (!IsDocumentElement() && !IsBody())
4839 return false;
4840 return StyleRef().LogicalHeight().IsAuto() &&
4841 !IsFloatingOrOutOfFlowPositioned() && !IsInline() &&
4842 !ShouldComputeLogicalHeightFromAspectRatio() &&
4843 !FlowThreadContainingBlock();
4844 }
4845
SkipContainingBlockForPercentHeightCalculation(const LayoutBox * containing_block)4846 bool LayoutBox::SkipContainingBlockForPercentHeightCalculation(
4847 const LayoutBox* containing_block) {
4848 const bool in_quirks_mode = containing_block->GetDocument().InQuirksMode();
4849 // Anonymous blocks should not impede percentage resolution on a child.
4850 // Examples of such anonymous blocks are blocks wrapped around inlines that
4851 // have block siblings (from the CSS spec) and multicol flow threads (an
4852 // implementation detail). Another implementation detail, ruby runs, create
4853 // anonymous inline-blocks, so skip those too. All other types of anonymous
4854 // objects, such as table-cells, will be treated just as if they were
4855 // non-anonymous.
4856 if (containing_block->IsAnonymous()) {
4857 if (!in_quirks_mode && containing_block->Parent() &&
4858 containing_block->Parent()->IsLayoutNGFieldset())
4859 return false;
4860 EDisplay display = containing_block->StyleRef().Display();
4861 return display == EDisplay::kBlock || display == EDisplay::kInlineBlock ||
4862 display == EDisplay::kFlowRoot;
4863 }
4864
4865 // For quirks mode, we skip most auto-height containing blocks when computing
4866 // percentages.
4867 if (!in_quirks_mode || !containing_block->StyleRef().LogicalHeight().IsAuto())
4868 return false;
4869
4870 const Node* node = containing_block->GetNode();
4871 if (UNLIKELY(node->IsInUserAgentShadowRoot())) {
4872 const Element* host = node->OwnerShadowHost();
4873 if (const auto* input = DynamicTo<HTMLInputElement>(host)) {
4874 // In web_tests/fast/forms/range/range-thumb-height-percentage.html, a
4875 // percent height for the slider thumb element should refer to the height
4876 // of the INPUT box.
4877 if (input->type() == input_type_names::kRange)
4878 return true;
4879 }
4880 }
4881
4882 return !containing_block->IsTableCell() &&
4883 !containing_block->IsOutOfFlowPositioned() &&
4884 !containing_block->HasOverridePercentageResolutionBlockSize() &&
4885 !containing_block->IsLayoutGrid() &&
4886 !containing_block->IsFlexibleBoxIncludingDeprecatedAndNG() &&
4887 !containing_block->IsLayoutNGCustom();
4888 }
4889
ContainingBlockLogicalHeightForPercentageResolution(LayoutBlock ** out_cb,bool * out_skipped_auto_height_containing_block) const4890 LayoutUnit LayoutBox::ContainingBlockLogicalHeightForPercentageResolution(
4891 LayoutBlock** out_cb,
4892 bool* out_skipped_auto_height_containing_block) const {
4893 NOT_DESTROYED();
4894 LayoutBlock* cb = ContainingBlock();
4895 const LayoutBlock* const real_cb = cb;
4896 const LayoutBox* containing_block_child = this;
4897 bool skipped_auto_height_containing_block = false;
4898 LayoutUnit root_margin_border_padding_height;
4899 while (!IsA<LayoutView>(cb) &&
4900 (IsHorizontalWritingMode() == cb->IsHorizontalWritingMode() &&
4901 SkipContainingBlockForPercentHeightCalculation(cb))) {
4902 if ((cb->IsBody() || cb->IsDocumentElement()) &&
4903 !HasOverrideContainingBlockContentLogicalHeight())
4904 root_margin_border_padding_height += cb->MarginBefore() +
4905 cb->MarginAfter() +
4906 cb->BorderAndPaddingLogicalHeight();
4907 skipped_auto_height_containing_block = true;
4908 containing_block_child = cb;
4909 cb = cb->ContainingBlock();
4910 }
4911
4912 if (out_cb)
4913 *out_cb = cb;
4914
4915 if (out_skipped_auto_height_containing_block) {
4916 *out_skipped_auto_height_containing_block =
4917 skipped_auto_height_containing_block;
4918 }
4919
4920 LayoutUnit available_height(-1);
4921 if (containing_block_child->HasOverridePercentageResolutionBlockSize()) {
4922 available_height =
4923 containing_block_child->OverridePercentageResolutionBlockSize();
4924 } else if (cb->HasOverridePercentageResolutionBlockSize()) {
4925 available_height = cb->OverridePercentageResolutionBlockSize();
4926 } else if (HasOverrideContainingBlockContentLogicalWidth() &&
4927 IsHorizontalWritingMode() != real_cb->IsHorizontalWritingMode()) {
4928 available_height = OverrideContainingBlockContentLogicalWidth();
4929 } else if (HasOverrideContainingBlockContentLogicalHeight() &&
4930 IsHorizontalWritingMode() == real_cb->IsHorizontalWritingMode()) {
4931 available_height = OverrideContainingBlockContentLogicalHeight();
4932 } else if (IsHorizontalWritingMode() != cb->IsHorizontalWritingMode()) {
4933 available_height =
4934 containing_block_child->ContainingBlockLogicalWidthForContent();
4935 } else if (cb->IsTableCell()) {
4936 if (!skipped_auto_height_containing_block) {
4937 // Table cells violate what the CSS spec says to do with heights.
4938 // Basically we don't care if the cell specified a height or not. We just
4939 // always make ourselves be a percentage of the cell's current content
4940 // height.
4941 if (!cb->HasOverrideLogicalHeight()) {
4942 // https://drafts.csswg.org/css-tables-3/#row-layout:
4943 // For the purpose of calculating [the minimum height of a row],
4944 // descendants of table cells whose height depends on percentages
4945 // of their parent cell's height are considered to have an auto
4946 // height if they have overflow set to visible or hidden or if
4947 // they are replaced elements, and a 0px height if they have not.
4948 const LayoutNGTableCellInterface* cell =
4949 ToInterface<LayoutNGTableCellInterface>(cb);
4950 if (StyleRef().OverflowY() != EOverflow::kVisible &&
4951 StyleRef().OverflowY() != EOverflow::kHidden &&
4952 !ShouldBeConsideredAsReplaced() &&
4953 (!cb->StyleRef().LogicalHeight().IsAuto() || !cell->TableInterface()
4954 ->ToLayoutObject()
4955 ->StyleRef()
4956 .LogicalHeight()
4957 .IsAuto()))
4958 return LayoutUnit();
4959 return LayoutUnit(-1);
4960 }
4961 available_height = cb->OverrideLogicalHeight() -
4962 cb->CollapsedBorderAndCSSPaddingLogicalHeight() -
4963 cb->ComputeLogicalScrollbars().BlockSum();
4964 }
4965 } else {
4966 available_height = cb->AvailableLogicalHeightForPercentageComputation();
4967 }
4968
4969 if (available_height == -1)
4970 return available_height;
4971
4972 available_height -= root_margin_border_padding_height;
4973
4974 // LayoutNG already includes padding in
4975 // OverrideContainingBlockContentLogicalHeight so we only need to add it here
4976 // for legacy containing blocks.
4977 if (IsTable() && IsOutOfFlowPositioned() && !cb->IsLayoutNGObject())
4978 available_height += cb->PaddingLogicalHeight();
4979
4980 return available_height;
4981 }
4982
ComputePercentageLogicalHeight(const Length & height) const4983 LayoutUnit LayoutBox::ComputePercentageLogicalHeight(
4984 const Length& height) const {
4985 NOT_DESTROYED();
4986 bool skipped_auto_height_containing_block = false;
4987 LayoutBlock* cb = nullptr;
4988 LayoutUnit available_height =
4989 ContainingBlockLogicalHeightForPercentageResolution(
4990 &cb, &skipped_auto_height_containing_block);
4991
4992 DCHECK(cb);
4993 cb->AddPercentHeightDescendant(const_cast<LayoutBox*>(this));
4994
4995 if (available_height == -1)
4996 return available_height;
4997
4998 LayoutUnit result = ValueForLength(height, available_height);
4999
5000 // |OverrideLogicalHeight| is the maximum height made available by the
5001 // cell to its percent height children when we decide they can determine the
5002 // height of the cell. If the percent height child is box-sizing:content-box
5003 // then we must subtract the border and padding from the cell's
5004 // |available_height| (given by |OverrideLogicalHeight|) to arrive
5005 // at the child's computed height.
5006 bool subtract_border_and_padding =
5007 IsTable() ||
5008 (!RuntimeEnabledFeatures::TableCellNewPercentsEnabled() &&
5009 cb->IsTableCell() && !skipped_auto_height_containing_block &&
5010 cb->HasOverrideLogicalHeight() &&
5011 StyleRef().BoxSizing() == EBoxSizing::kContentBox);
5012 if (subtract_border_and_padding) {
5013 result -= BorderAndPaddingLogicalHeight();
5014 return std::max(LayoutUnit(), result);
5015 }
5016 return result;
5017 }
5018
ComputeReplacedLogicalWidth(ShouldComputePreferred should_compute_preferred) const5019 LayoutUnit LayoutBox::ComputeReplacedLogicalWidth(
5020 ShouldComputePreferred should_compute_preferred) const {
5021 NOT_DESTROYED();
5022 return ComputeReplacedLogicalWidthRespectingMinMaxWidth(
5023 ComputeReplacedLogicalWidthUsing(kMainOrPreferredSize,
5024 StyleRef().LogicalWidth()),
5025 should_compute_preferred);
5026 }
5027
ComputeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logical_width,ShouldComputePreferred should_compute_preferred) const5028 LayoutUnit LayoutBox::ComputeReplacedLogicalWidthRespectingMinMaxWidth(
5029 LayoutUnit logical_width,
5030 ShouldComputePreferred should_compute_preferred) const {
5031 NOT_DESTROYED();
5032 LayoutUnit min_logical_width =
5033 (should_compute_preferred == kComputePreferred &&
5034 StyleRef().LogicalMinWidth().IsPercentOrCalc())
5035 ? logical_width
5036 : ComputeReplacedLogicalWidthUsing(kMinSize,
5037 StyleRef().LogicalMinWidth());
5038 LayoutUnit max_logical_width =
5039 (should_compute_preferred == kComputePreferred &&
5040 StyleRef().LogicalMaxWidth().IsPercentOrCalc()) ||
5041 StyleRef().LogicalMaxWidth().IsNone()
5042 ? logical_width
5043 : ComputeReplacedLogicalWidthUsing(kMaxSize,
5044 StyleRef().LogicalMaxWidth());
5045 return std::max(min_logical_width,
5046 std::min(logical_width, max_logical_width));
5047 }
5048
ComputeReplacedLogicalWidthUsing(SizeType size_type,const Length & logical_width) const5049 LayoutUnit LayoutBox::ComputeReplacedLogicalWidthUsing(
5050 SizeType size_type,
5051 const Length& logical_width) const {
5052 NOT_DESTROYED();
5053 DCHECK(size_type == kMinSize || size_type == kMainOrPreferredSize ||
5054 !logical_width.IsAuto());
5055 if (size_type == kMinSize && logical_width.IsAuto())
5056 return AdjustContentBoxLogicalWidthForBoxSizing(LayoutUnit());
5057
5058 switch (logical_width.GetType()) {
5059 case Length::kFixed:
5060 return AdjustContentBoxLogicalWidthForBoxSizing(logical_width.Value());
5061 case Length::kMinContent:
5062 case Length::kMaxContent:
5063 case Length::kMinIntrinsic: {
5064 // MinContent/MaxContent don't need the availableLogicalWidth argument.
5065 LayoutUnit available_logical_width;
5066 return ComputeIntrinsicLogicalWidthUsing(logical_width,
5067 available_logical_width) -
5068 BorderAndPaddingLogicalWidth();
5069 }
5070 case Length::kFitContent:
5071 case Length::kFillAvailable:
5072 case Length::kPercent:
5073 case Length::kCalculated: {
5074 LayoutUnit cw;
5075 if (IsOutOfFlowPositioned()) {
5076 cw = ContainingBlockLogicalWidthForPositioned(
5077 To<LayoutBoxModelObject>(Container()));
5078 } else {
5079 cw = IsHorizontalWritingMode() ==
5080 ContainingBlock()->IsHorizontalWritingMode()
5081 ? ContainingBlockLogicalWidthForContent()
5082 : PerpendicularContainingBlockLogicalHeight();
5083 }
5084 const Length& container_logical_width =
5085 ContainingBlock()->StyleRef().LogicalWidth();
5086 // FIXME: Handle cases when containing block width is calculated or
5087 // viewport percent. https://bugs.webkit.org/show_bug.cgi?id=91071
5088 if (logical_width.IsContentOrIntrinsicOrFillAvailable())
5089 return ComputeIntrinsicLogicalWidthUsing(logical_width, cw) -
5090 BorderAndPaddingLogicalWidth();
5091 if (cw > 0 || (!cw && (container_logical_width.IsFixed() ||
5092 container_logical_width.IsPercentOrCalc())))
5093 return AdjustContentBoxLogicalWidthForBoxSizing(
5094 MinimumValueForLength(logical_width, cw));
5095 return LayoutUnit();
5096 }
5097 case Length::kAuto:
5098 case Length::kNone:
5099 return IntrinsicLogicalWidth();
5100 case Length::kExtendToZoom:
5101 case Length::kDeviceWidth:
5102 case Length::kDeviceHeight:
5103 break;
5104 }
5105
5106 NOTREACHED();
5107 return LayoutUnit();
5108 }
5109
ComputeReplacedLogicalHeight(LayoutUnit) const5110 LayoutUnit LayoutBox::ComputeReplacedLogicalHeight(LayoutUnit) const {
5111 NOT_DESTROYED();
5112 return ComputeReplacedLogicalHeightRespectingMinMaxHeight(
5113 ComputeReplacedLogicalHeightUsing(kMainOrPreferredSize,
5114 StyleRef().LogicalHeight()));
5115 }
5116
LogicalHeightComputesAsNone(SizeType size_type) const5117 bool LayoutBox::LogicalHeightComputesAsNone(SizeType size_type) const {
5118 NOT_DESTROYED();
5119 DCHECK(size_type == kMinSize || size_type == kMaxSize);
5120 const Length& logical_height = size_type == kMinSize
5121 ? StyleRef().LogicalMinHeight()
5122 : StyleRef().LogicalMaxHeight();
5123
5124 // Note that the values 'min-content', 'max-content' and 'fit-content' should
5125 // behave as the initial value if specified in the block direction.
5126 if (logical_height.IsMinContent() || logical_height.IsMaxContent() ||
5127 logical_height.IsMinIntrinsic() || logical_height.IsFitContent())
5128 return true;
5129
5130 Length initial_logical_height =
5131 size_type == kMinSize ? ComputedStyleInitialValues::InitialMinHeight()
5132 : ComputedStyleInitialValues::InitialMaxHeight();
5133
5134 if (logical_height == initial_logical_height)
5135 return true;
5136
5137 if (logical_height.IsPercentOrCalc() &&
5138 HasOverrideContainingBlockContentLogicalHeight()) {
5139 if (OverrideContainingBlockContentLogicalHeight() == kIndefiniteSize)
5140 return true;
5141 else if (!GetDocument().InQuirksMode())
5142 return false;
5143 }
5144
5145 // CustomLayout items can resolve their percentages against an available or
5146 // percentage size override.
5147 if (IsCustomItem() && (HasOverrideContainingBlockContentLogicalHeight() ||
5148 HasOverridePercentageResolutionBlockSize()))
5149 return false;
5150
5151 if (LayoutBlock* cb = ContainingBlockForAutoHeightDetection(logical_height))
5152 return cb->HasAutoHeightOrContainingBlockWithAutoHeight();
5153 return false;
5154 }
5155
ComputeReplacedLogicalHeightRespectingMinMaxHeight(LayoutUnit logical_height) const5156 LayoutUnit LayoutBox::ComputeReplacedLogicalHeightRespectingMinMaxHeight(
5157 LayoutUnit logical_height) const {
5158 NOT_DESTROYED();
5159 // If the height of the containing block is not specified explicitly (i.e., it
5160 // depends on content height), and this element is not absolutely positioned,
5161 // the percentage value is treated as '0' (for 'min-height') or 'none' (for
5162 // 'max-height').
5163 LayoutUnit min_logical_height;
5164 if (!LogicalHeightComputesAsNone(kMinSize)) {
5165 min_logical_height = ComputeReplacedLogicalHeightUsing(
5166 kMinSize, StyleRef().LogicalMinHeight());
5167 }
5168 LayoutUnit max_logical_height = logical_height;
5169 if (!LogicalHeightComputesAsNone(kMaxSize)) {
5170 max_logical_height = ComputeReplacedLogicalHeightUsing(
5171 kMaxSize, StyleRef().LogicalMaxHeight());
5172 }
5173 return std::max(min_logical_height,
5174 std::min(logical_height, max_logical_height));
5175 }
5176
ComputeReplacedLogicalHeightUsing(SizeType size_type,const Length & logical_height) const5177 LayoutUnit LayoutBox::ComputeReplacedLogicalHeightUsing(
5178 SizeType size_type,
5179 const Length& logical_height) const {
5180 NOT_DESTROYED();
5181 DCHECK(size_type == kMinSize || size_type == kMainOrPreferredSize ||
5182 !logical_height.IsAuto());
5183 if (size_type == kMinSize && logical_height.IsAuto())
5184 return AdjustContentBoxLogicalHeightForBoxSizing(LayoutUnit());
5185
5186 switch (logical_height.GetType()) {
5187 case Length::kFixed:
5188 return AdjustContentBoxLogicalHeightForBoxSizing(logical_height.Value());
5189 case Length::kPercent:
5190 case Length::kCalculated: {
5191 // TODO(rego): Check if we can somehow reuse
5192 // LayoutBox::computePercentageLogicalHeight() and/or
5193 // LayoutBlock::availableLogicalHeightForPercentageComputation() (see
5194 // http://crbug.com/635655).
5195 LayoutObject* cb =
5196 IsOutOfFlowPositioned() ? Container() : ContainingBlock();
5197 while (cb->IsAnonymous())
5198 cb = cb->ContainingBlock();
5199 bool has_perpendicular_containing_block =
5200 cb->IsHorizontalWritingMode() != IsHorizontalWritingMode();
5201 LayoutUnit stretched_height(-1);
5202 auto* block = DynamicTo<LayoutBlock>(cb);
5203 if (block) {
5204 block->AddPercentHeightDescendant(const_cast<LayoutBox*>(this));
5205 if (block->IsFlexItem()) {
5206 const LayoutFlexibleBox* flex_box =
5207 ToLayoutFlexibleBox(block->Parent());
5208 if (flex_box->UseOverrideLogicalHeightForPerentageResolution(*block))
5209 stretched_height = block->OverrideContentLogicalHeight();
5210 } else if (block->IsGridItem() && block->HasOverrideLogicalHeight() &&
5211 !has_perpendicular_containing_block) {
5212 stretched_height = block->OverrideContentLogicalHeight();
5213 }
5214 }
5215
5216 LayoutUnit available_height;
5217 if (IsOutOfFlowPositioned()) {
5218 available_height = ContainingBlockLogicalHeightForPositioned(
5219 To<LayoutBoxModelObject>(cb));
5220 } else if (stretched_height != -1) {
5221 available_height = stretched_height;
5222 } else if (HasOverridePercentageResolutionBlockSize()) {
5223 available_height = OverridePercentageResolutionBlockSize();
5224 } else {
5225 available_height = has_perpendicular_containing_block
5226 ? ContainingBlockLogicalWidthForContent()
5227 : ContainingBlockLogicalHeightForContent(
5228 kIncludeMarginBorderPadding);
5229
5230 // It is necessary to use the border-box to match WinIE's broken
5231 // box model. This is essential for sizing inside
5232 // table cells using percentage heights.
5233 // FIXME: This needs to be made writing-mode-aware. If the cell and
5234 // image are perpendicular writing-modes, this isn't right.
5235 // https://bugs.webkit.org/show_bug.cgi?id=46997
5236 while (!IsA<LayoutView>(cb) &&
5237 (cb->StyleRef().LogicalHeight().IsAuto() ||
5238 cb->StyleRef().LogicalHeight().IsPercentOrCalc())) {
5239 if (!RuntimeEnabledFeatures::TableCellNewPercentsEnabled() &&
5240 cb->IsTableCell()) {
5241 // Don't let table cells squeeze percent-height replaced elements
5242 // <http://bugs.webkit.org/show_bug.cgi?id=15359>
5243 available_height =
5244 std::max(available_height, IntrinsicLogicalHeight());
5245 return ValueForLength(
5246 logical_height,
5247 available_height - BorderAndPaddingLogicalHeight());
5248 }
5249 To<LayoutBlock>(cb)->AddPercentHeightDescendant(
5250 const_cast<LayoutBox*>(this));
5251 cb = cb->ContainingBlock();
5252 }
5253 }
5254
5255 return AdjustContentBoxLogicalHeightForBoxSizing(
5256 (RuntimeEnabledFeatures::TableCellNewPercentsEnabled() &&
5257 available_height == kIndefiniteSize)
5258 ? IntrinsicLogicalHeight()
5259 : ValueForLength(logical_height, available_height));
5260 }
5261 case Length::kMinContent:
5262 case Length::kMaxContent:
5263 case Length::kFitContent:
5264 case Length::kFillAvailable:
5265 return AdjustContentBoxLogicalHeightForBoxSizing(
5266 ComputeIntrinsicLogicalContentHeightUsing(size_type, logical_height,
5267 IntrinsicLogicalHeight(),
5268 BorderAndPaddingHeight()));
5269 default:
5270 return IntrinsicLogicalHeight();
5271 }
5272 }
5273
AvailableLogicalHeight(AvailableLogicalHeightType height_type) const5274 LayoutUnit LayoutBox::AvailableLogicalHeight(
5275 AvailableLogicalHeightType height_type) const {
5276 NOT_DESTROYED();
5277 if (RuntimeEnabledFeatures::LayoutNGEnabled()) {
5278 // LayoutNG code is correct, Legacy code incorrectly ConstrainsMinMax
5279 // when height is -1, and returns 0, not -1.
5280 // The reason this code is NG-only is that this code causes performance
5281 // regression for nested-percent-height-tables test case.
5282 // This code gets executed 740 times in the test case.
5283 // https://chromium-review.googlesource.com/c/chromium/src/+/1103289
5284 LayoutUnit height =
5285 AvailableLogicalHeightUsing(StyleRef().LogicalHeight(), height_type);
5286 if (UNLIKELY(height == -1))
5287 return height;
5288 return ConstrainContentBoxLogicalHeightByMinMax(height, LayoutUnit(-1));
5289 }
5290 // http://www.w3.org/TR/CSS2/visudet.html#propdef-height - We are interested
5291 // in the content height.
5292 // FIXME: Should we pass intrinsicContentLogicalHeight() instead of -1 here?
5293 return ConstrainContentBoxLogicalHeightByMinMax(
5294 AvailableLogicalHeightUsing(StyleRef().LogicalHeight(), height_type),
5295 LayoutUnit(-1));
5296 }
5297
AvailableLogicalHeightUsing(const Length & h,AvailableLogicalHeightType height_type) const5298 LayoutUnit LayoutBox::AvailableLogicalHeightUsing(
5299 const Length& h,
5300 AvailableLogicalHeightType height_type) const {
5301 NOT_DESTROYED();
5302 if (auto* layout_view = DynamicTo<LayoutView>(this)) {
5303 return LayoutUnit(IsHorizontalWritingMode()
5304 ? layout_view->GetFrameView()->Size().Height()
5305 : layout_view->GetFrameView()->Size().Width());
5306 }
5307
5308 // We need to stop here, since we don't want to increase the height of the
5309 // table artificially. We're going to rely on this cell getting expanded to
5310 // some new height, and then when we lay out again we'll use the calculation
5311 // below.
5312 if (IsTableCell() && (h.IsAuto() || h.IsPercentOrCalc())) {
5313 if (HasOverrideLogicalHeight()) {
5314 return OverrideLogicalHeight() -
5315 CollapsedBorderAndCSSPaddingLogicalHeight() -
5316 ComputeLogicalScrollbars().BlockSum();
5317 }
5318 return LogicalHeight() - BorderAndPaddingLogicalHeight();
5319 }
5320
5321 if (IsFlexItemIncludingNG()) {
5322 if (IsFlexItem()) {
5323 const LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent());
5324 if (flex_box.UseOverrideLogicalHeightForPerentageResolution(*this))
5325 return OverrideContentLogicalHeight();
5326 } else if (HasOverrideContainingBlockContentLogicalWidth() &&
5327 IsOrthogonalWritingModeRoot()) {
5328 return OverrideContainingBlockContentLogicalWidth();
5329 } else if (HasOverrideLogicalHeight() &&
5330 IsOverrideLogicalHeightDefinite()) {
5331 return OverrideContentLogicalHeight();
5332 } else if (!GetBoxLayoutExtraInput()) {
5333 // TODO(ikilpatrick): Remove this post M86.
5334 if (const auto* previous_result = GetCachedLayoutResult()) {
5335 const NGConstraintSpace& space =
5336 previous_result->GetConstraintSpaceForCaching();
5337 if (space.IsFixedBlockSize() && !space.IsFixedBlockSizeIndefinite())
5338 return space.AvailableSize().block_size;
5339 }
5340 }
5341 }
5342
5343 if (ShouldComputeLogicalHeightFromAspectRatio()) {
5344 NGBoxStrut border_padding(BorderStart() + ComputedCSSPaddingStart(),
5345 BorderEnd() + ComputedCSSPaddingEnd(),
5346 BorderBefore() + ComputedCSSPaddingBefore(),
5347 BorderAfter() + ComputedCSSPaddingAfter());
5348 return BlockSizeFromAspectRatio(border_padding,
5349 StyleRef().LogicalAspectRatio(),
5350 StyleRef().BoxSizing(), LogicalWidth());
5351 }
5352
5353 if (h.IsPercentOrCalc() && IsOutOfFlowPositioned()) {
5354 // FIXME: This is wrong if the containingBlock has a perpendicular writing
5355 // mode.
5356 LayoutUnit available_height =
5357 ContainingBlockLogicalHeightForPositioned(ContainingBlock());
5358 return AdjustContentBoxLogicalHeightForBoxSizing(
5359 ValueForLength(h, available_height));
5360 }
5361
5362 // FIXME: Should we pass intrinsicContentLogicalHeight() instead of -1 here?
5363 LayoutUnit height_including_scrollbar =
5364 ComputeContentAndScrollbarLogicalHeightUsing(kMainOrPreferredSize, h,
5365 LayoutUnit(-1));
5366 if (height_including_scrollbar != -1) {
5367 return std::max(LayoutUnit(), AdjustContentBoxLogicalHeightForBoxSizing(
5368 height_including_scrollbar) -
5369 ComputeLogicalScrollbars().BlockSum());
5370 }
5371
5372 // FIXME: Check logicalTop/logicalBottom here to correctly handle vertical
5373 // writing-mode.
5374 // https://bugs.webkit.org/show_bug.cgi?id=46500
5375 auto* curr_layout_block = DynamicTo<LayoutBlock>(this);
5376 if (curr_layout_block && IsOutOfFlowPositioned() &&
5377 StyleRef().Height().IsAuto() &&
5378 !(StyleRef().Top().IsAuto() || StyleRef().Bottom().IsAuto())) {
5379 LayoutBlock* block = const_cast<LayoutBlock*>(curr_layout_block);
5380 LogicalExtentComputedValues computed_values;
5381 block->ComputeLogicalHeight(block->LogicalHeight(), LayoutUnit(),
5382 computed_values);
5383 return computed_values.extent_ - block->BorderAndPaddingLogicalHeight() -
5384 block->ComputeLogicalScrollbars().BlockSum();
5385 }
5386
5387 // FIXME: This is wrong if the containingBlock has a perpendicular writing
5388 // mode.
5389 LayoutUnit available_height =
5390 ContainingBlockLogicalHeightForContent(height_type);
5391 // FIXME: This is incorrect if available_height == -1 || 0
5392 if (height_type == kExcludeMarginBorderPadding) {
5393 // FIXME: Margin collapsing hasn't happened yet, so this incorrectly removes
5394 // collapsed margins.
5395 available_height -=
5396 MarginBefore() + MarginAfter() + BorderAndPaddingLogicalHeight();
5397 }
5398 return available_height;
5399 }
5400
ComputeAndSetBlockDirectionMargins(const LayoutBlock * containing_block)5401 void LayoutBox::ComputeAndSetBlockDirectionMargins(
5402 const LayoutBlock* containing_block) {
5403 NOT_DESTROYED();
5404 LayoutUnit margin_before;
5405 LayoutUnit margin_after;
5406 DCHECK(containing_block);
5407 ComputeMarginsForDirection(
5408 kBlockDirection, containing_block,
5409 ContainingBlockLogicalWidthForContent(), LogicalHeight(), margin_before,
5410 margin_after, StyleRef().MarginBeforeUsing(containing_block->StyleRef()),
5411 StyleRef().MarginAfterUsing(containing_block->StyleRef()));
5412 // Note that in this 'positioning phase' of the layout we are using the
5413 // containing block's writing mode rather than our own when calculating
5414 // margins.
5415 // http://www.w3.org/TR/2014/CR-css-writing-modes-3-20140320/#orthogonal-flows
5416 containing_block->SetMarginBeforeForChild(*this, margin_before);
5417 containing_block->SetMarginAfterForChild(*this, margin_after);
5418 }
5419
ContainingBlockLogicalWidthForPositioned(const LayoutBoxModelObject * containing_block,bool check_for_perpendicular_writing_mode) const5420 LayoutUnit LayoutBox::ContainingBlockLogicalWidthForPositioned(
5421 const LayoutBoxModelObject* containing_block,
5422 bool check_for_perpendicular_writing_mode) const {
5423 NOT_DESTROYED();
5424 if (check_for_perpendicular_writing_mode &&
5425 containing_block->IsHorizontalWritingMode() != IsHorizontalWritingMode())
5426 return ContainingBlockLogicalHeightForPositioned(containing_block, false);
5427
5428 // Use viewport as container for top-level fixed-position elements.
5429 const auto* view = DynamicTo<LayoutView>(containing_block);
5430 if (StyleRef().GetPosition() == EPosition::kFixed && view &&
5431 !GetDocument().Printing()) {
5432 if (LocalFrameView* frame_view = view->GetFrameView()) {
5433 // Don't use visibleContentRect since the PaintLayer's size has not been
5434 // set yet.
5435 LayoutSize viewport_size(
5436 frame_view->LayoutViewport()->ExcludeScrollbars(frame_view->Size()));
5437 return LayoutUnit(containing_block->IsHorizontalWritingMode()
5438 ? viewport_size.Width()
5439 : viewport_size.Height());
5440 }
5441 }
5442
5443 if (HasOverrideContainingBlockContentLogicalWidth())
5444 return OverrideContainingBlockContentLogicalWidth();
5445
5446 if (containing_block->IsAnonymousBlock() &&
5447 containing_block->IsRelPositioned()) {
5448 // Ensure we compute our width based on the width of our rel-pos inline
5449 // container rather than any anonymous block created to manage a block-flow
5450 // ancestor of ours in the rel-pos inline's inline flow.
5451 containing_block = To<LayoutBox>(containing_block)->Continuation();
5452 // There may be nested parallel inline continuations. We have now found the
5453 // innermost inline (which may not be relatively positioned). Locate the
5454 // inline that serves as the containing block of this box.
5455 while (!containing_block->CanContainOutOfFlowPositionedElement(
5456 StyleRef().GetPosition())) {
5457 containing_block =
5458 To<LayoutBoxModelObject>(containing_block->Container());
5459 DCHECK(containing_block->IsLayoutInline());
5460 }
5461 } else if (containing_block->IsBox()) {
5462 return std::max(LayoutUnit(),
5463 To<LayoutBox>(containing_block)->ClientLogicalWidth());
5464 }
5465
5466 DCHECK(containing_block->IsLayoutInline());
5467 DCHECK(containing_block->CanContainOutOfFlowPositionedElement(
5468 StyleRef().GetPosition()));
5469
5470 const auto* flow = To<LayoutInline>(containing_block);
5471 InlineFlowBox* first = flow->FirstLineBox();
5472 InlineFlowBox* last = flow->LastLineBox();
5473
5474 // If the containing block is empty, return a width of 0.
5475 if (!first || !last)
5476 return LayoutUnit();
5477
5478 LayoutUnit from_left;
5479 LayoutUnit from_right;
5480 if (containing_block->StyleRef().IsLeftToRightDirection()) {
5481 from_left = first->LogicalLeft() + first->BorderLogicalLeft();
5482 from_right =
5483 last->LogicalLeft() + last->LogicalWidth() - last->BorderLogicalRight();
5484 } else {
5485 from_right = first->LogicalLeft() + first->LogicalWidth() -
5486 first->BorderLogicalRight();
5487 from_left = last->LogicalLeft() + last->BorderLogicalLeft();
5488 }
5489
5490 return std::max(LayoutUnit(), from_right - from_left);
5491 }
5492
ContainingBlockLogicalHeightForPositioned(const LayoutBoxModelObject * containing_block,bool check_for_perpendicular_writing_mode) const5493 LayoutUnit LayoutBox::ContainingBlockLogicalHeightForPositioned(
5494 const LayoutBoxModelObject* containing_block,
5495 bool check_for_perpendicular_writing_mode) const {
5496 NOT_DESTROYED();
5497 if (check_for_perpendicular_writing_mode &&
5498 containing_block->IsHorizontalWritingMode() != IsHorizontalWritingMode())
5499 return ContainingBlockLogicalWidthForPositioned(containing_block, false);
5500
5501 // Use viewport as container for top-level fixed-position elements.
5502 const auto* view = DynamicTo<LayoutView>(containing_block);
5503 if (StyleRef().GetPosition() == EPosition::kFixed && view &&
5504 !GetDocument().Printing()) {
5505 if (LocalFrameView* frame_view = view->GetFrameView()) {
5506 // Don't use visibleContentRect since the PaintLayer's size has not been
5507 // set yet.
5508 LayoutSize viewport_size(
5509 frame_view->LayoutViewport()->ExcludeScrollbars(frame_view->Size()));
5510 return containing_block->IsHorizontalWritingMode()
5511 ? viewport_size.Height()
5512 : viewport_size.Width();
5513 }
5514 }
5515
5516 if (HasOverrideContainingBlockContentLogicalHeight())
5517 return OverrideContainingBlockContentLogicalHeight();
5518
5519 if (containing_block->IsBox())
5520 return To<LayoutBox>(containing_block)->ClientLogicalHeight();
5521
5522 DCHECK(containing_block->IsLayoutInline());
5523 DCHECK(containing_block->CanContainOutOfFlowPositionedElement(
5524 StyleRef().GetPosition()));
5525
5526 const auto* flow = To<LayoutInline>(containing_block);
5527 // If the containing block is empty, return a height of 0.
5528 if (!flow->HasInlineFragments())
5529 return LayoutUnit();
5530
5531 LayoutUnit height_result;
5532 auto bounding_box_size = flow->PhysicalLinesBoundingBox().size;
5533 if (containing_block->IsHorizontalWritingMode())
5534 height_result = bounding_box_size.height;
5535 else
5536 height_result = bounding_box_size.width;
5537 height_result -=
5538 (containing_block->BorderBefore() + containing_block->BorderAfter());
5539 return height_result;
5540 }
5541
AccumulateStaticOffsetForFlowThread(LayoutBox & layout_box,LayoutUnit inline_position,LayoutUnit & block_position)5542 static LayoutUnit AccumulateStaticOffsetForFlowThread(
5543 LayoutBox& layout_box,
5544 LayoutUnit inline_position,
5545 LayoutUnit& block_position) {
5546 if (layout_box.IsLegacyTableRow())
5547 return LayoutUnit();
5548 block_position += layout_box.LogicalTop();
5549 if (!layout_box.IsLayoutFlowThread())
5550 return LayoutUnit();
5551 LayoutUnit previous_inline_position = inline_position;
5552 // We're walking out of a flowthread here. This flow thread is not in the
5553 // containing block chain, so we need to convert the position from the
5554 // coordinate space of this flowthread to the containing coordinate space.
5555 To<LayoutFlowThread>(layout_box)
5556 .FlowThreadToContainingCoordinateSpace(block_position, inline_position);
5557 return inline_position - previous_inline_position;
5558 }
5559
ComputeInlineStaticDistance(Length & logical_left,Length & logical_right,const LayoutBox * child,const LayoutBoxModelObject * container_block,LayoutUnit container_logical_width,const NGBoxFragmentBuilder * fragment_builder)5560 void LayoutBox::ComputeInlineStaticDistance(
5561 Length& logical_left,
5562 Length& logical_right,
5563 const LayoutBox* child,
5564 const LayoutBoxModelObject* container_block,
5565 LayoutUnit container_logical_width,
5566 const NGBoxFragmentBuilder* fragment_builder) {
5567 if (!logical_left.IsAuto() || !logical_right.IsAuto())
5568 return;
5569
5570 LayoutObject* parent = child->Parent();
5571 TextDirection parent_direction = parent->StyleRef().Direction();
5572
5573 // This method is using EnclosingBox() which is wrong for absolutely
5574 // positioned grid items, as they rely on the grid area. So for grid items if
5575 // both "left" and "right" properties are "auto", we can consider that one of
5576 // them (depending on the direction) is simply "0".
5577 if (parent->IsLayoutGrid() && parent == child->ContainingBlock()) {
5578 if (parent_direction == TextDirection::kLtr)
5579 logical_left = Length::Fixed(0);
5580 else
5581 logical_right = Length::Fixed(0);
5582 return;
5583 }
5584
5585 // For multicol we also need to keep track of the block position, since that
5586 // determines which column we're in and thus affects the inline position.
5587 LayoutUnit static_block_position = child->Layer()->StaticBlockPosition();
5588
5589 // FIXME: The static distance computation has not been patched for mixed
5590 // writing modes yet.
5591 if (parent_direction == TextDirection::kLtr) {
5592 LayoutUnit static_position = child->Layer()->StaticInlinePosition() -
5593 container_block->BorderLogicalLeft();
5594 for (LayoutObject* curr = child->Parent(); curr && curr != container_block;
5595 curr = curr->Container()) {
5596 if (auto* box = DynamicTo<LayoutBox>(curr)) {
5597 static_position +=
5598 (fragment_builder &&
5599 fragment_builder->GetLayoutObject() == curr->Parent())
5600 ? fragment_builder->GetChildOffset(curr).inline_offset
5601 : box->LogicalLeft();
5602 if (box->IsInFlowPositioned())
5603 static_position += box->OffsetForInFlowPosition().left;
5604 if (curr->IsInsideFlowThread()) {
5605 static_position += AccumulateStaticOffsetForFlowThread(
5606 *box, static_position, static_block_position);
5607 }
5608 } else if (curr->IsInline() && curr->IsInFlowPositioned()) {
5609 if (!curr->IsInLayoutNGInlineFormattingContext()) {
5610 if (!curr->StyleRef().LogicalLeft().IsAuto())
5611 static_position +=
5612 ValueForLength(curr->StyleRef().LogicalLeft(),
5613 curr->ContainingBlock()->AvailableWidth());
5614 else
5615 static_position -=
5616 ValueForLength(curr->StyleRef().LogicalRight(),
5617 curr->ContainingBlock()->AvailableWidth());
5618 }
5619 }
5620 }
5621 logical_left = Length::Fixed(static_position);
5622 } else {
5623 LayoutBox* enclosing_box = child->Parent()->EnclosingBox();
5624 LayoutUnit static_position = child->Layer()->StaticInlinePosition() +
5625 container_logical_width +
5626 container_block->BorderLogicalLeft();
5627 if (container_block->IsBox()) {
5628 static_position +=
5629 To<LayoutBox>(container_block)->LogicalLeftScrollbarWidth();
5630 }
5631 for (LayoutObject* curr = child->Parent(); curr; curr = curr->Container()) {
5632 if (auto* box = DynamicTo<LayoutBox>(curr)) {
5633 if (curr == enclosing_box)
5634 static_position -= enclosing_box->LogicalWidth();
5635 if (curr != container_block) {
5636 static_position -=
5637 (fragment_builder &&
5638 fragment_builder->GetLayoutObject() == curr->Parent())
5639 ? fragment_builder->GetChildOffset(curr).inline_offset
5640 : box->LogicalLeft();
5641 if (box->IsInFlowPositioned()) {
5642 static_position -= box->OffsetForInFlowPosition().left;
5643 }
5644 if (curr->IsInsideFlowThread()) {
5645 static_position -= AccumulateStaticOffsetForFlowThread(
5646 *box, static_position, static_block_position);
5647 }
5648 }
5649 } else if (curr->IsInline() && curr->IsInFlowPositioned()) {
5650 if (!curr->IsInLayoutNGInlineFormattingContext()) {
5651 if (!curr->StyleRef().LogicalLeft().IsAuto())
5652 static_position -=
5653 ValueForLength(curr->StyleRef().LogicalLeft(),
5654 curr->ContainingBlock()->AvailableWidth());
5655 else
5656 static_position +=
5657 ValueForLength(curr->StyleRef().LogicalRight(),
5658 curr->ContainingBlock()->AvailableWidth());
5659 }
5660 }
5661 if (curr == container_block)
5662 break;
5663 }
5664 logical_right = Length::Fixed(static_position);
5665 }
5666 }
5667
ComputePositionedLogicalWidth(LogicalExtentComputedValues & computed_values) const5668 void LayoutBox::ComputePositionedLogicalWidth(
5669 LogicalExtentComputedValues& computed_values) const {
5670 NOT_DESTROYED();
5671 // QUESTIONS
5672 // FIXME 1: Should we still deal with these the cases of 'left' or 'right'
5673 // having the type 'static' in determining whether to calculate the static
5674 // distance?
5675 // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1.
5676
5677 // FIXME 2: Can perhaps optimize out cases when max-width/min-width are
5678 // greater than or less than the computed width(). Be careful of box-sizing
5679 // and percentage issues.
5680
5681 // The following is based off of the W3C Working Draft from April 11, 2006 of
5682 // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements"
5683 // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width>
5684 // (block-style-comments in this function and in
5685 // computePositionedLogicalWidthUsing() correspond to text from the spec)
5686
5687 // We don't use containingBlock(), since we may be positioned by an enclosing
5688 // relative positioned inline.
5689 const auto* container_block = To<LayoutBoxModelObject>(Container());
5690
5691 const LayoutUnit container_logical_width =
5692 ContainingBlockLogicalWidthForPositioned(container_block);
5693
5694 // Use the container block's direction except when calculating the static
5695 // distance. This conforms with the reference results for
5696 // abspos-replaced-width-margin-000.htm of the CSS 2.1 test suite.
5697 TextDirection container_direction = container_block->StyleRef().Direction();
5698
5699 bool is_horizontal = IsHorizontalWritingMode();
5700 const LayoutUnit borders_plus_padding = BorderAndPaddingLogicalWidth();
5701 const Length& margin_logical_left =
5702 is_horizontal ? StyleRef().MarginLeft() : StyleRef().MarginTop();
5703 const Length& margin_logical_right =
5704 is_horizontal ? StyleRef().MarginRight() : StyleRef().MarginBottom();
5705
5706 Length logical_left_length = StyleRef().LogicalLeft();
5707 Length logical_right_length = StyleRef().LogicalRight();
5708 // ---------------------------------------------------------------------------
5709 // For the purposes of this section and the next, the term "static position"
5710 // (of an element) refers, roughly, to the position an element would have had
5711 // in the normal flow. More precisely:
5712 //
5713 // * The static position for 'left' is the distance from the left edge of the
5714 // containing block to the left margin edge of a hypothetical box that
5715 // would have been the first box of the element if its 'position' property
5716 // had been 'static' and 'float' had been 'none'. The value is negative if
5717 // the hypothetical box is to the left of the containing block.
5718 // * The static position for 'right' is the distance from the right edge of
5719 // the containing block to the right margin edge of the same hypothetical
5720 // box as above. The value is positive if the hypothetical box is to the
5721 // left of the containing block's edge.
5722 //
5723 // But rather than actually calculating the dimensions of that hypothetical
5724 // box, user agents are free to make a guess at its probable position.
5725 //
5726 // For the purposes of calculating the static position, the containing block
5727 // of fixed positioned elements is the initial containing block instead of
5728 // the viewport, and all scrollable boxes should be assumed to be scrolled to
5729 // their origin.
5730 // ---------------------------------------------------------------------------
5731 // see FIXME 1
5732 // Calculate the static distance if needed.
5733 ComputeInlineStaticDistance(logical_left_length, logical_right_length, this,
5734 container_block, container_logical_width);
5735
5736 // Calculate constraint equation values for 'width' case.
5737 ComputePositionedLogicalWidthUsing(
5738 kMainOrPreferredSize, StyleRef().LogicalWidth(), container_block,
5739 container_direction, container_logical_width, borders_plus_padding,
5740 logical_left_length, logical_right_length, margin_logical_left,
5741 margin_logical_right, computed_values);
5742
5743 MinMaxSizes transferred_min_max{LayoutUnit(), LayoutUnit::Max()};
5744 if (ShouldComputeLogicalHeightFromAspectRatio())
5745 transferred_min_max = ComputeMinMaxLogicalWidthFromAspectRatio();
5746
5747 // Calculate constraint equation values for 'max-width' case.
5748 LogicalExtentComputedValues max_values;
5749 max_values.extent_ = LayoutUnit::Max();
5750 if (!StyleRef().LogicalMaxWidth().IsNone()) {
5751 ComputePositionedLogicalWidthUsing(
5752 kMaxSize, StyleRef().LogicalMaxWidth(), container_block,
5753 container_direction, container_logical_width, borders_plus_padding,
5754 logical_left_length, logical_right_length, margin_logical_left,
5755 margin_logical_right, max_values);
5756 }
5757 if (transferred_min_max.max_size < max_values.extent_) {
5758 ComputePositionedLogicalWidthUsing(
5759 kMaxSize, Length::Fixed(transferred_min_max.max_size), container_block,
5760 container_direction, container_logical_width, borders_plus_padding,
5761 logical_left_length, logical_right_length, margin_logical_left,
5762 margin_logical_right, max_values);
5763 }
5764
5765 if (computed_values.extent_ > max_values.extent_)
5766 max_values.CopyExceptBlockMargins(&computed_values);
5767
5768 LogicalExtentComputedValues min_values;
5769 // Calculate constraint equation values for 'min-width' case.
5770 if (!StyleRef().LogicalMinWidth().IsZero() ||
5771 StyleRef().LogicalMinWidth().IsContentOrIntrinsicOrFillAvailable()) {
5772 ComputePositionedLogicalWidthUsing(
5773 kMinSize, StyleRef().LogicalMinWidth(), container_block,
5774 container_direction, container_logical_width, borders_plus_padding,
5775 logical_left_length, logical_right_length, margin_logical_left,
5776 margin_logical_right, min_values);
5777 }
5778 if (transferred_min_max.min_size > min_values.extent_) {
5779 ComputePositionedLogicalWidthUsing(
5780 kMinSize, Length::Fixed(transferred_min_max.min_size), container_block,
5781 container_direction, container_logical_width, borders_plus_padding,
5782 logical_left_length, logical_right_length, margin_logical_left,
5783 margin_logical_right, min_values);
5784 }
5785 if (computed_values.extent_ < min_values.extent_)
5786 min_values.CopyExceptBlockMargins(&computed_values);
5787
5788 computed_values.extent_ += borders_plus_padding;
5789 }
5790
ComputeLogicalLeftPositionedOffset(LayoutUnit & logical_left_pos,const LayoutBox * child,LayoutUnit logical_width_value,const LayoutBoxModelObject * container_block,LayoutUnit container_logical_width)5791 void LayoutBox::ComputeLogicalLeftPositionedOffset(
5792 LayoutUnit& logical_left_pos,
5793 const LayoutBox* child,
5794 LayoutUnit logical_width_value,
5795 const LayoutBoxModelObject* container_block,
5796 LayoutUnit container_logical_width) {
5797 if (child->IsHorizontalWritingMode()) {
5798 if (container_block->HasFlippedBlocksWritingMode()) {
5799 // Deal with differing writing modes here. Our offset needs to be in the
5800 // containing block's coordinate space. If the containing block is flipped
5801 // along this axis, then we need to flip the coordinate. This can only
5802 // happen if the containing block has flipped mode and is perpendicular
5803 // to us.
5804 logical_left_pos =
5805 container_logical_width - logical_width_value - logical_left_pos;
5806 logical_left_pos += container_block->BorderRight();
5807 if (container_block->IsBox() &&
5808 !To<LayoutBox>(container_block)->CanSkipComputeScrollbars()) {
5809 logical_left_pos += To<LayoutBox>(container_block)
5810 ->ComputeScrollbarsInternal(kClampToContentBox)
5811 .right;
5812 }
5813 } else {
5814 logical_left_pos += container_block->BorderLeft();
5815 if (container_block->IsBox() &&
5816 !To<LayoutBox>(container_block)->CanSkipComputeScrollbars()) {
5817 logical_left_pos += To<LayoutBox>(container_block)
5818 ->ComputeScrollbarsInternal(kClampToContentBox)
5819 .left;
5820 }
5821 }
5822 } else {
5823 logical_left_pos += container_block->BorderTop();
5824 if (container_block->IsBox() &&
5825 !To<LayoutBox>(container_block)->CanSkipComputeScrollbars()) {
5826 logical_left_pos += To<LayoutBox>(container_block)
5827 ->ComputeScrollbarsInternal(kClampToContentBox)
5828 .top;
5829 }
5830 }
5831 }
5832
ShrinkToFitLogicalWidth(LayoutUnit available_logical_width,LayoutUnit borders_plus_padding) const5833 LayoutUnit LayoutBox::ShrinkToFitLogicalWidth(
5834 LayoutUnit available_logical_width,
5835 LayoutUnit borders_plus_padding) const {
5836 NOT_DESTROYED();
5837 MinMaxSizes sizes = PreferredLogicalWidths();
5838 sizes -= borders_plus_padding;
5839 return sizes.ShrinkToFit(available_logical_width);
5840 }
5841
ComputePositionedLogicalWidthUsing(SizeType width_size_type,const Length & logical_width,const LayoutBoxModelObject * container_block,TextDirection container_direction,LayoutUnit container_logical_width,LayoutUnit borders_plus_padding,const Length & logical_left,const Length & logical_right,const Length & margin_logical_left,const Length & margin_logical_right,LogicalExtentComputedValues & computed_values) const5842 void LayoutBox::ComputePositionedLogicalWidthUsing(
5843 SizeType width_size_type,
5844 const Length& logical_width,
5845 const LayoutBoxModelObject* container_block,
5846 TextDirection container_direction,
5847 LayoutUnit container_logical_width,
5848 LayoutUnit borders_plus_padding,
5849 const Length& logical_left,
5850 const Length& logical_right,
5851 const Length& margin_logical_left,
5852 const Length& margin_logical_right,
5853 LogicalExtentComputedValues& computed_values) const {
5854 NOT_DESTROYED();
5855 LayoutUnit logical_width_value;
5856
5857 DCHECK(width_size_type == kMinSize ||
5858 width_size_type == kMainOrPreferredSize || !logical_width.IsAuto());
5859 if (width_size_type == kMinSize && logical_width.IsAuto()) {
5860 if (ShouldComputeLogicalWidthFromAspectRatio()) {
5861 logical_width_value =
5862 IntrinsicLogicalWidths(MinMaxSizesType::kIntrinsic).min_size;
5863 } else {
5864 logical_width_value = LayoutUnit();
5865 }
5866 } else if (width_size_type == kMainOrPreferredSize &&
5867 logical_width.IsAuto() &&
5868 ComputeLogicalWidthFromAspectRatio(&logical_width_value)) {
5869 // We're good.
5870 } else if (logical_width.IsContentOrIntrinsicOrFillAvailable()) {
5871 logical_width_value = ComputeIntrinsicLogicalWidthUsing(
5872 logical_width, container_logical_width) -
5873 borders_plus_padding;
5874 } else {
5875 logical_width_value = AdjustContentBoxLogicalWidthForBoxSizing(
5876 ValueForLength(logical_width, container_logical_width));
5877 }
5878
5879 // 'left' and 'right' cannot both be 'auto' because one would of been
5880 // converted to the static position already
5881 DCHECK(!(logical_left.IsAuto() && logical_right.IsAuto()));
5882
5883 // minimumValueForLength will convert 'auto' to 0 so that it doesn't impact
5884 // the available space computation below.
5885 LayoutUnit logical_left_value =
5886 MinimumValueForLength(logical_left, container_logical_width);
5887 LayoutUnit logical_right_value =
5888 MinimumValueForLength(logical_right, container_logical_width);
5889
5890 const LayoutUnit container_relative_logical_width =
5891 ContainingBlockLogicalWidthForPositioned(container_block, false);
5892
5893 // If we are using aspect-ratio, the width is effectively not auto.
5894 bool logical_width_is_auto =
5895 logical_width.IsAuto() && !ShouldComputeLogicalWidthFromAspectRatio();
5896 bool logical_left_is_auto = logical_left.IsAuto();
5897 bool logical_right_is_auto = logical_right.IsAuto();
5898 LayoutUnit& margin_logical_left_value = StyleRef().IsLeftToRightDirection()
5899 ? computed_values.margins_.start_
5900 : computed_values.margins_.end_;
5901 LayoutUnit& margin_logical_right_value =
5902 StyleRef().IsLeftToRightDirection() ? computed_values.margins_.end_
5903 : computed_values.margins_.start_;
5904 if (!logical_left_is_auto && !logical_width_is_auto &&
5905 !logical_right_is_auto) {
5906 // -------------------------------------------------------------------------
5907 // If none of the three is 'auto': If both 'margin-left' and 'margin-
5908 // right' are 'auto', solve the equation under the extra constraint that
5909 // the two margins get equal values, unless this would make them negative,
5910 // in which case when direction of the containing block is 'ltr' ('rtl'),
5911 // set 'margin-left' ('margin-right') to zero and solve for 'margin-right'
5912 // ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto',
5913 // solve the equation for that value. If the values are over-constrained,
5914 // ignore the value for 'left' (in case the 'direction' property of the
5915 // containing block is 'rtl') or 'right' (in case 'direction' is 'ltr')
5916 // and solve for that value.
5917 // -------------------------------------------------------------------------
5918 // NOTE: It is not necessary to solve for 'right' in the over constrained
5919 // case because the value is not used for any further calculations.
5920
5921 computed_values.extent_ = logical_width_value;
5922
5923 const LayoutUnit available_space =
5924 container_logical_width -
5925 (logical_left_value + computed_values.extent_ + logical_right_value +
5926 borders_plus_padding);
5927
5928 // Margins are now the only unknown
5929 if (margin_logical_left.IsAuto() && margin_logical_right.IsAuto()) {
5930 // Both margins auto, solve for equality
5931 if (available_space >= 0) {
5932 margin_logical_left_value =
5933 available_space / 2; // split the difference
5934 margin_logical_right_value =
5935 available_space -
5936 margin_logical_left_value; // account for odd valued differences
5937 } else {
5938 // Use the containing block's direction rather than the parent block's
5939 // per CSS 2.1 reference test abspos-non-replaced-width-margin-000.
5940 if (container_direction == TextDirection::kLtr) {
5941 margin_logical_left_value = LayoutUnit();
5942 margin_logical_right_value = available_space; // will be negative
5943 } else {
5944 margin_logical_left_value = available_space; // will be negative
5945 margin_logical_right_value = LayoutUnit();
5946 }
5947 }
5948 } else if (margin_logical_left.IsAuto()) {
5949 // Solve for left margin
5950 margin_logical_right_value = ValueForLength(
5951 margin_logical_right, container_relative_logical_width);
5952 margin_logical_left_value = available_space - margin_logical_right_value;
5953 } else if (margin_logical_right.IsAuto()) {
5954 // Solve for right margin
5955 margin_logical_left_value =
5956 ValueForLength(margin_logical_left, container_relative_logical_width);
5957 margin_logical_right_value = available_space - margin_logical_left_value;
5958 } else {
5959 // Over-constrained, solve for left if direction is RTL
5960 margin_logical_left_value =
5961 ValueForLength(margin_logical_left, container_relative_logical_width);
5962 margin_logical_right_value = ValueForLength(
5963 margin_logical_right, container_relative_logical_width);
5964
5965 // Use the containing block's direction rather than the parent block's
5966 // per CSS 2.1 reference test abspos-non-replaced-width-margin-000.
5967 if (container_direction == TextDirection::kRtl)
5968 logical_left_value = (available_space + logical_left_value) -
5969 margin_logical_left_value -
5970 margin_logical_right_value;
5971 }
5972 } else {
5973 // -------------------------------------------------------------------------
5974 // Otherwise, set 'auto' values for 'margin-left' and 'margin-right'
5975 // to 0, and pick the one of the following six rules that applies.
5976 //
5977 // 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the
5978 // width is shrink-to-fit. Then solve for 'left'
5979 //
5980 // OMIT RULE 2 AS IT SHOULD NEVER BE HIT
5981 // ------------------------------------------------------------------
5982 // 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if
5983 // the 'direction' property of the containing block is 'ltr' set
5984 // 'left' to the static position, otherwise set 'right' to the
5985 // static position. Then solve for 'left' (if 'direction is 'rtl')
5986 // or 'right' (if 'direction' is 'ltr').
5987 // ------------------------------------------------------------------
5988 //
5989 // 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the
5990 // width is shrink-to-fit . Then solve for 'right'
5991 // 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve
5992 // for 'left'
5993 // 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve
5994 // for 'width'
5995 // 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve
5996 // for 'right'
5997 //
5998 // Calculation of the shrink-to-fit width is similar to calculating the
5999 // width of a table cell using the automatic table layout algorithm.
6000 // Roughly: calculate the preferred width by formatting the content without
6001 // breaking lines other than where explicit line breaks occur, and also
6002 // calculate the preferred minimum width, e.g., by trying all possible line
6003 // breaks. CSS 2.1 does not define the exact algorithm.
6004 // Thirdly, calculate the available width: this is found by solving for
6005 // 'width' after setting 'left' (in case 1) or 'right' (in case 3) to 0.
6006 //
6007 // Then the shrink-to-fit width is:
6008 // min(max(preferred minimum width, available width), preferred width).
6009 // -------------------------------------------------------------------------
6010 // NOTE: For rules 3 and 6 it is not necessary to solve for 'right'
6011 // because the value is not used for any further calculations.
6012
6013 // Calculate margins, 'auto' margins are ignored.
6014 margin_logical_left_value = MinimumValueForLength(
6015 margin_logical_left, container_relative_logical_width);
6016 margin_logical_right_value = MinimumValueForLength(
6017 margin_logical_right, container_relative_logical_width);
6018
6019 const LayoutUnit available_space =
6020 container_logical_width -
6021 (margin_logical_left_value + margin_logical_right_value +
6022 logical_left_value + logical_right_value + borders_plus_padding);
6023
6024 // FIXME: Is there a faster way to find the correct case?
6025 // Use rule/case that applies.
6026 if (logical_left_is_auto && logical_width_is_auto &&
6027 !logical_right_is_auto) {
6028 // RULE 1: (use shrink-to-fit for width, and solve of left)
6029 computed_values.extent_ =
6030 ShrinkToFitLogicalWidth(available_space, borders_plus_padding);
6031 logical_left_value = available_space - computed_values.extent_;
6032 } else if (!logical_left_is_auto && logical_width_is_auto &&
6033 logical_right_is_auto) {
6034 // RULE 3: (use shrink-to-fit for width, and no need solve of right)
6035 computed_values.extent_ =
6036 ShrinkToFitLogicalWidth(available_space, borders_plus_padding);
6037 } else if (logical_left_is_auto && !logical_width_is_auto &&
6038 !logical_right_is_auto) {
6039 // RULE 4: (solve for left)
6040 computed_values.extent_ = logical_width_value;
6041 logical_left_value = available_space - computed_values.extent_;
6042 } else if (!logical_left_is_auto && logical_width_is_auto &&
6043 !logical_right_is_auto) {
6044 // RULE 5: (solve for width)
6045 if (AutoWidthShouldFitContent())
6046 computed_values.extent_ =
6047 ShrinkToFitLogicalWidth(available_space, borders_plus_padding);
6048 else
6049 computed_values.extent_ = std::max(LayoutUnit(), available_space);
6050 } else if (!logical_left_is_auto && !logical_width_is_auto &&
6051 logical_right_is_auto) {
6052 // RULE 6: (no need solve for right)
6053 computed_values.extent_ = logical_width_value;
6054 }
6055 }
6056
6057 // Use computed values to calculate the horizontal position.
6058
6059 // FIXME: This hack is needed to calculate the logical left position for a
6060 // 'rtl' relatively positioned, inline because right now, it is using the
6061 // logical left position of the first line box when really it should use the
6062 // last line box. When this is fixed elsewhere, this block should be removed.
6063 if (container_block->IsLayoutInline() &&
6064 !container_block->StyleRef().IsLeftToRightDirection()) {
6065 const auto* flow = To<LayoutInline>(container_block);
6066 InlineFlowBox* first_line = flow->FirstLineBox();
6067 InlineFlowBox* last_line = flow->LastLineBox();
6068 if (first_line && last_line && first_line != last_line) {
6069 computed_values.position_ =
6070 logical_left_value + margin_logical_left_value +
6071 last_line->BorderLogicalLeft() +
6072 (last_line->LogicalLeft() - first_line->LogicalLeft());
6073 return;
6074 }
6075 }
6076
6077 computed_values.position_ = logical_left_value + margin_logical_left_value;
6078 ComputeLogicalLeftPositionedOffset(computed_values.position_, this,
6079 computed_values.extent_, container_block,
6080 container_logical_width);
6081 }
6082
ComputeBlockStaticDistance(Length & logical_top,Length & logical_bottom,const LayoutBox * child,const LayoutBoxModelObject * container_block,const NGBoxFragmentBuilder * fragment_builder)6083 void LayoutBox::ComputeBlockStaticDistance(
6084 Length& logical_top,
6085 Length& logical_bottom,
6086 const LayoutBox* child,
6087 const LayoutBoxModelObject* container_block,
6088 const NGBoxFragmentBuilder* fragment_builder) {
6089 if (!logical_top.IsAuto() || !logical_bottom.IsAuto())
6090 return;
6091
6092 // FIXME: The static distance computation has not been patched for mixed
6093 // writing modes.
6094 LayoutUnit static_logical_top = child->Layer()->StaticBlockPosition();
6095 for (LayoutObject* curr = child->Parent(); curr && curr != container_block;
6096 curr = curr->Container()) {
6097 if (!curr->IsBox() || curr->IsLegacyTableRow())
6098 continue;
6099 const auto& box = *To<LayoutBox>(curr);
6100 static_logical_top +=
6101 (fragment_builder &&
6102 fragment_builder->GetLayoutObject() == box.Parent())
6103 ? fragment_builder->GetChildOffset(&box).block_offset
6104 : box.LogicalTop();
6105 if (box.IsInFlowPositioned())
6106 static_logical_top += box.OffsetForInFlowPosition().top;
6107 if (!box.IsLayoutFlowThread())
6108 continue;
6109 // We're walking out of a flowthread here. This flow thread is not in the
6110 // containing block chain, so we need to convert the position from the
6111 // coordinate space of this flowthread to the containing coordinate space.
6112 // The inline position cannot affect the block position, so we don't bother
6113 // calculating it.
6114 LayoutUnit dummy_inline_position;
6115 To<LayoutFlowThread>(box).FlowThreadToContainingCoordinateSpace(
6116 static_logical_top, dummy_inline_position);
6117 }
6118
6119 // Now static_logical_top is relative to container_block's logical top.
6120 // Convert it to be relative to containing_block's logical client top.
6121 static_logical_top -= container_block->BorderBefore();
6122 if (auto* box = DynamicTo<LayoutBox>(container_block))
6123 static_logical_top -= box->LogicalTopScrollbarHeight();
6124 logical_top = Length::Fixed(static_logical_top);
6125 }
6126
ComputePositionedLogicalHeight(LogicalExtentComputedValues & computed_values) const6127 void LayoutBox::ComputePositionedLogicalHeight(
6128 LogicalExtentComputedValues& computed_values) const {
6129 NOT_DESTROYED();
6130 // The following is based off of the W3C Working Draft from April 11, 2006 of
6131 // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements"
6132 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height>
6133 // (block-style-comments in this function and in
6134 // computePositionedLogicalHeightUsing()
6135 // correspond to text from the spec)
6136
6137 // We don't use containingBlock(), since we may be positioned by an enclosing
6138 // relpositioned inline.
6139 const auto* container_block = To<LayoutBoxModelObject>(Container());
6140
6141 const LayoutUnit container_logical_height =
6142 ContainingBlockLogicalHeightForPositioned(container_block);
6143
6144 const ComputedStyle& style_to_use = StyleRef();
6145 const LayoutUnit borders_plus_padding = BorderAndPaddingLogicalHeight();
6146 const Length& margin_before = style_to_use.MarginBefore();
6147 const Length& margin_after = style_to_use.MarginAfter();
6148 Length logical_top_length = style_to_use.LogicalTop();
6149 Length logical_bottom_length = style_to_use.LogicalBottom();
6150
6151 // ---------------------------------------------------------------------------
6152 // For the purposes of this section and the next, the term "static position"
6153 // (of an element) refers, roughly, to the position an element would have had
6154 // in the normal flow. More precisely, the static position for 'top' is the
6155 // distance from the top edge of the containing block to the top margin edge
6156 // of a hypothetical box that would have been the first box of the element if
6157 // its 'position' property had been 'static' and 'float' had been 'none'. The
6158 // value is negative if the hypothetical box is above the containing block.
6159 //
6160 // But rather than actually calculating the dimensions of that hypothetical
6161 // box, user agents are free to make a guess at its probable position.
6162 //
6163 // For the purposes of calculating the static position, the containing block
6164 // of fixed positioned elements is the initial containing block instead of
6165 // the viewport.
6166 // ---------------------------------------------------------------------------
6167 // see FIXME 1
6168 // Calculate the static distance if needed.
6169 ComputeBlockStaticDistance(logical_top_length, logical_bottom_length, this,
6170 container_block);
6171
6172 // Calculate constraint equation values for 'height' case.
6173 LayoutUnit logical_height = computed_values.extent_;
6174 ComputePositionedLogicalHeightUsing(
6175 kMainOrPreferredSize, style_to_use.LogicalHeight(), container_block,
6176 container_logical_height, borders_plus_padding, logical_height,
6177 logical_top_length, logical_bottom_length, margin_before, margin_after,
6178 computed_values);
6179
6180 // Avoid doing any work in the common case (where the values of min-height and
6181 // max-height are their defaults).
6182 // see FIXME 2
6183
6184 // Calculate constraint equation values for 'max-height' case.
6185 const Length& logical_max_height = style_to_use.LogicalMaxHeight();
6186 if (!logical_max_height.IsNone() && !logical_max_height.IsMinContent() &&
6187 !logical_max_height.IsMaxContent() &&
6188 !logical_max_height.IsMinIntrinsic() &&
6189 !logical_max_height.IsFitContent()) {
6190 LogicalExtentComputedValues max_values;
6191
6192 ComputePositionedLogicalHeightUsing(
6193 kMaxSize, logical_max_height, container_block, container_logical_height,
6194 borders_plus_padding, logical_height, logical_top_length,
6195 logical_bottom_length, margin_before, margin_after, max_values);
6196
6197 if (computed_values.extent_ > max_values.extent_) {
6198 computed_values.extent_ = max_values.extent_;
6199 computed_values.position_ = max_values.position_;
6200 computed_values.margins_.before_ = max_values.margins_.before_;
6201 computed_values.margins_.after_ = max_values.margins_.after_;
6202 }
6203 }
6204
6205 // Calculate constraint equation values for 'min-height' case.
6206 Length logical_min_height = style_to_use.LogicalMinHeight();
6207 if (logical_min_height.IsMinContent() || logical_min_height.IsMaxContent() ||
6208 logical_min_height.IsMinIntrinsic() || logical_min_height.IsFitContent())
6209 logical_min_height = Length::Auto();
6210 // auto is considered to be zero, so we need to check for it explicitly.
6211 if (logical_min_height.IsAuto() || !logical_min_height.IsZero() ||
6212 logical_min_height.IsFillAvailable()) {
6213 LogicalExtentComputedValues min_values;
6214
6215 ComputePositionedLogicalHeightUsing(
6216 kMinSize, logical_min_height, container_block, container_logical_height,
6217 borders_plus_padding, logical_height, logical_top_length,
6218 logical_bottom_length, margin_before, margin_after, min_values);
6219
6220 if (computed_values.extent_ < min_values.extent_) {
6221 computed_values.extent_ = min_values.extent_;
6222 computed_values.position_ = min_values.position_;
6223 computed_values.margins_.before_ = min_values.margins_.before_;
6224 computed_values.margins_.after_ = min_values.margins_.after_;
6225 }
6226 }
6227
6228 // Set final height value.
6229 computed_values.extent_ += borders_plus_padding;
6230 }
6231
ComputeLogicalTopPositionedOffset(LayoutUnit & logical_top_pos,const LayoutBox * child,LayoutUnit logical_height_value,const LayoutBoxModelObject * container_block,LayoutUnit container_logical_height)6232 void LayoutBox::ComputeLogicalTopPositionedOffset(
6233 LayoutUnit& logical_top_pos,
6234 const LayoutBox* child,
6235 LayoutUnit logical_height_value,
6236 const LayoutBoxModelObject* container_block,
6237 LayoutUnit container_logical_height) {
6238 // Deal with differing writing modes here. Our offset needs to be in the
6239 // containing block's coordinate space. If the containing block is flipped
6240 // along this axis, then we need to flip the coordinate. This can only happen
6241 // if the containing block is both a flipped mode and perpendicular to us.
6242 if ((child->StyleRef().IsFlippedBlocksWritingMode() &&
6243 child->IsHorizontalWritingMode() !=
6244 container_block->IsHorizontalWritingMode()) ||
6245 (child->StyleRef().IsFlippedBlocksWritingMode() !=
6246 container_block->StyleRef().IsFlippedBlocksWritingMode() &&
6247 child->IsHorizontalWritingMode() ==
6248 container_block->IsHorizontalWritingMode())) {
6249 logical_top_pos =
6250 container_logical_height - logical_height_value - logical_top_pos;
6251 }
6252
6253 // Convert logical_top_pos from container's client space to container's border
6254 // box space.
6255 if (child->IsHorizontalWritingMode()) {
6256 logical_top_pos += container_block->BorderTop();
6257 if (container_block->IsBox() &&
6258 !To<LayoutBox>(container_block)->CanSkipComputeScrollbars()) {
6259 logical_top_pos += To<LayoutBox>(container_block)
6260 ->ComputeScrollbarsInternal(kClampToContentBox)
6261 .top;
6262 }
6263 } else if (container_block->HasFlippedBlocksWritingMode()) {
6264 logical_top_pos += container_block->BorderRight();
6265 if (container_block->IsBox() &&
6266 !To<LayoutBox>(container_block)->CanSkipComputeScrollbars()) {
6267 logical_top_pos += To<LayoutBox>(container_block)
6268 ->ComputeScrollbarsInternal(kClampToContentBox)
6269 .right;
6270 }
6271 } else {
6272 logical_top_pos += container_block->BorderLeft();
6273 if (container_block->IsBox() &&
6274 !To<LayoutBox>(container_block)->CanSkipComputeScrollbars()) {
6275 logical_top_pos += To<LayoutBox>(container_block)
6276 ->ComputeScrollbarsInternal(kClampToContentBox)
6277 .left;
6278 }
6279 }
6280 }
6281
ComputePositionedLogicalHeightUsing(SizeType height_size_type,Length logical_height_length,const LayoutBoxModelObject * container_block,LayoutUnit container_logical_height,LayoutUnit borders_plus_padding,LayoutUnit logical_height,const Length & logical_top,const Length & logical_bottom,const Length & margin_before,const Length & margin_after,LogicalExtentComputedValues & computed_values) const6282 void LayoutBox::ComputePositionedLogicalHeightUsing(
6283 SizeType height_size_type,
6284 Length logical_height_length,
6285 const LayoutBoxModelObject* container_block,
6286 LayoutUnit container_logical_height,
6287 LayoutUnit borders_plus_padding,
6288 LayoutUnit logical_height,
6289 const Length& logical_top,
6290 const Length& logical_bottom,
6291 const Length& margin_before,
6292 const Length& margin_after,
6293 LogicalExtentComputedValues& computed_values) const {
6294 NOT_DESTROYED();
6295 DCHECK(height_size_type == kMinSize ||
6296 height_size_type == kMainOrPreferredSize ||
6297 !logical_height_length.IsAuto());
6298 if (height_size_type == kMinSize && logical_height_length.IsAuto()) {
6299 if (ShouldComputeLogicalHeightFromAspectRatio())
6300 logical_height_length = Length::Fixed(logical_height);
6301 else
6302 logical_height_length = Length::Fixed(0);
6303 }
6304
6305 // 'top' and 'bottom' cannot both be 'auto' because 'top would of been
6306 // converted to the static position in computePositionedLogicalHeight()
6307 DCHECK(!(logical_top.IsAuto() && logical_bottom.IsAuto()));
6308
6309 LayoutUnit logical_height_value;
6310 LayoutUnit content_logical_height = logical_height - borders_plus_padding;
6311
6312 const LayoutUnit container_relative_logical_width =
6313 ContainingBlockLogicalWidthForPositioned(container_block, false);
6314
6315 LayoutUnit logical_top_value;
6316
6317 bool from_aspect_ratio = height_size_type == kMainOrPreferredSize &&
6318 ShouldComputeLogicalHeightFromAspectRatio();
6319 bool logical_height_is_auto =
6320 logical_height_length.IsAuto() && !from_aspect_ratio;
6321 bool logical_top_is_auto = logical_top.IsAuto();
6322 bool logical_bottom_is_auto = logical_bottom.IsAuto();
6323
6324 LayoutUnit resolved_logical_height;
6325 // Height is never unsolved for tables.
6326 if (IsTable()) {
6327 resolved_logical_height = content_logical_height;
6328 logical_height_is_auto = false;
6329 } else {
6330 if (logical_height_length.IsContentOrIntrinsicOrFillAvailable()) {
6331 resolved_logical_height = ComputeIntrinsicLogicalContentHeightUsing(
6332 height_size_type, logical_height_length, content_logical_height,
6333 borders_plus_padding);
6334 } else if (from_aspect_ratio) {
6335 NGBoxStrut border_padding(BorderStart() + ComputedCSSPaddingStart(),
6336 BorderEnd() + ComputedCSSPaddingEnd(),
6337 BorderBefore() + ComputedCSSPaddingBefore(),
6338 BorderAfter() + ComputedCSSPaddingAfter());
6339 resolved_logical_height = BlockSizeFromAspectRatio(
6340 border_padding, StyleRef().LogicalAspectRatio(),
6341 StyleRef().BoxSizing(), LogicalWidth());
6342 resolved_logical_height = std::max(
6343 LayoutUnit(), resolved_logical_height - borders_plus_padding);
6344 } else {
6345 resolved_logical_height = AdjustContentBoxLogicalHeightForBoxSizing(
6346 ValueForLength(logical_height_length, container_logical_height));
6347 }
6348 }
6349
6350 if (!logical_top_is_auto && !logical_height_is_auto &&
6351 !logical_bottom_is_auto) {
6352 // -------------------------------------------------------------------------
6353 // If none of the three are 'auto': If both 'margin-top' and 'margin-bottom'
6354 // are 'auto', solve the equation under the extra constraint that the two
6355 // margins get equal values. If one of 'margin-top' or 'margin- bottom' is
6356 // 'auto', solve the equation for that value. If the values are over-
6357 // constrained, ignore the value for 'bottom' and solve for that value.
6358 // -------------------------------------------------------------------------
6359 // NOTE: It is not necessary to solve for 'bottom' in the over constrained
6360 // case because the value is not used for any further calculations.
6361
6362 logical_height_value = resolved_logical_height;
6363 logical_top_value = ValueForLength(logical_top, container_logical_height);
6364
6365 const LayoutUnit available_space =
6366 container_logical_height -
6367 (logical_top_value + logical_height_value +
6368 ValueForLength(logical_bottom, container_logical_height) +
6369 borders_plus_padding);
6370
6371 // Margins are now the only unknown
6372 if (margin_before.IsAuto() && margin_after.IsAuto()) {
6373 // Both margins auto, solve for equality
6374 // NOTE: This may result in negative values.
6375 computed_values.margins_.before_ =
6376 available_space / 2; // split the difference
6377 computed_values.margins_.after_ =
6378 available_space - computed_values.margins_
6379 .before_; // account for odd valued differences
6380 } else if (margin_before.IsAuto()) {
6381 // Solve for top margin
6382 computed_values.margins_.after_ =
6383 ValueForLength(margin_after, container_relative_logical_width);
6384 computed_values.margins_.before_ =
6385 available_space - computed_values.margins_.after_;
6386 } else if (margin_after.IsAuto()) {
6387 // Solve for bottom margin
6388 computed_values.margins_.before_ =
6389 ValueForLength(margin_before, container_relative_logical_width);
6390 computed_values.margins_.after_ =
6391 available_space - computed_values.margins_.before_;
6392 } else {
6393 // Over-constrained, (no need solve for bottom)
6394 computed_values.margins_.before_ =
6395 ValueForLength(margin_before, container_relative_logical_width);
6396 computed_values.margins_.after_ =
6397 ValueForLength(margin_after, container_relative_logical_width);
6398 }
6399 } else {
6400 // -------------------------------------------------------------------------
6401 // Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom'
6402 // to 0, and pick the one of the following six rules that applies.
6403 //
6404 // 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then
6405 // the height is based on the content, and solve for 'top'.
6406 //
6407 // OMIT RULE 2 AS IT SHOULD NEVER BE HIT
6408 // ------------------------------------------------------------------
6409 // 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then
6410 // set 'top' to the static position, and solve for 'bottom'.
6411 // ------------------------------------------------------------------
6412 //
6413 // 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then
6414 // the height is based on the content, and solve for 'bottom'.
6415 // 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and
6416 // solve for 'top'.
6417 // 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and
6418 // solve for 'height'.
6419 // 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and
6420 // solve for 'bottom'.
6421 // -------------------------------------------------------------------------
6422 // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom'
6423 // because the value is not used for any further calculations.
6424
6425 // Calculate margins, 'auto' margins are ignored.
6426 computed_values.margins_.before_ =
6427 MinimumValueForLength(margin_before, container_relative_logical_width);
6428 computed_values.margins_.after_ =
6429 MinimumValueForLength(margin_after, container_relative_logical_width);
6430
6431 const LayoutUnit available_space =
6432 container_logical_height -
6433 (computed_values.margins_.before_ + computed_values.margins_.after_ +
6434 borders_plus_padding);
6435
6436 // Use rule/case that applies.
6437 if (logical_top_is_auto && logical_height_is_auto &&
6438 !logical_bottom_is_auto) {
6439 // RULE 1: (height is content based, solve of top)
6440 logical_height_value = content_logical_height;
6441 logical_top_value =
6442 available_space -
6443 (logical_height_value +
6444 ValueForLength(logical_bottom, container_logical_height));
6445 } else if (!logical_top_is_auto && logical_height_is_auto &&
6446 logical_bottom_is_auto) {
6447 // RULE 3: (height is content based, no need solve of bottom)
6448 logical_top_value = ValueForLength(logical_top, container_logical_height);
6449 logical_height_value = content_logical_height;
6450 } else if (logical_top_is_auto && !logical_height_is_auto &&
6451 !logical_bottom_is_auto) {
6452 // RULE 4: (solve of top)
6453 logical_height_value = resolved_logical_height;
6454 logical_top_value =
6455 available_space -
6456 (logical_height_value +
6457 ValueForLength(logical_bottom, container_logical_height));
6458 } else if (!logical_top_is_auto && logical_height_is_auto &&
6459 !logical_bottom_is_auto) {
6460 // RULE 5: (solve of height)
6461 logical_top_value = ValueForLength(logical_top, container_logical_height);
6462 logical_height_value = std::max(
6463 LayoutUnit(),
6464 available_space -
6465 (logical_top_value +
6466 ValueForLength(logical_bottom, container_logical_height)));
6467 } else if (!logical_top_is_auto && !logical_height_is_auto &&
6468 logical_bottom_is_auto) {
6469 // RULE 6: (no need solve of bottom)
6470 logical_height_value = resolved_logical_height;
6471 logical_top_value = ValueForLength(logical_top, container_logical_height);
6472 }
6473 }
6474 computed_values.extent_ = logical_height_value;
6475
6476 // Use computed values to calculate the vertical position.
6477 computed_values.position_ =
6478 logical_top_value + computed_values.margins_.before_;
6479 ComputeLogicalTopPositionedOffset(computed_values.position_, this,
6480 logical_height_value, container_block,
6481 container_logical_height);
6482 }
6483
LocalCaretRect(const InlineBox * box,int caret_offset,LayoutUnit * extra_width_to_end_of_line) const6484 LayoutRect LayoutBox::LocalCaretRect(
6485 const InlineBox* box,
6486 int caret_offset,
6487 LayoutUnit* extra_width_to_end_of_line) const {
6488 NOT_DESTROYED();
6489 // VisiblePositions at offsets inside containers either a) refer to the
6490 // positions before/after those containers (tables and select elements) or
6491 // b) refer to the position inside an empty block.
6492 // They never refer to children.
6493 // FIXME: Paint the carets inside empty blocks differently than the carets
6494 // before/after elements.
6495 LayoutUnit caret_width = GetFrameView()->CaretWidth();
6496 LayoutRect rect(Location(), LayoutSize(caret_width, Size().Height()));
6497 bool ltr =
6498 box ? box->IsLeftToRightDirection() : StyleRef().IsLeftToRightDirection();
6499
6500 if ((!caret_offset) ^ ltr)
6501 rect.Move(LayoutSize(Size().Width() - caret_width, LayoutUnit()));
6502
6503 if (box) {
6504 const RootInlineBox& root_box = box->Root();
6505 LayoutUnit top = root_box.LineTop();
6506 rect.SetY(top);
6507 rect.SetHeight(root_box.LineBottom() - top);
6508 }
6509
6510 // If height of box is smaller than font height, use the latter one,
6511 // otherwise the caret might become invisible.
6512 //
6513 // Also, if the box is not an atomic inline-level element, always use the font
6514 // height. This prevents the "big caret" bug described in:
6515 // <rdar://problem/3777804> Deleting all content in a document can result in
6516 // giant tall-as-window insertion point
6517 //
6518 // FIXME: ignoring :first-line, missing good reason to take care of
6519 const SimpleFontData* font_data = StyleRef().GetFont().PrimaryFont();
6520 LayoutUnit font_height =
6521 LayoutUnit(font_data ? font_data->GetFontMetrics().Height() : 0);
6522 if (font_height > rect.Height() || (!IsAtomicInlineLevel() && !IsTable()))
6523 rect.SetHeight(font_height);
6524
6525 if (extra_width_to_end_of_line)
6526 *extra_width_to_end_of_line = Location().X() + Size().Width() - rect.MaxX();
6527
6528 // Move to local coords
6529 rect.MoveBy(-Location());
6530
6531 // FIXME: Border/padding should be added for all elements but this workaround
6532 // is needed because we use offsets inside an "atomic" element to represent
6533 // positions before and after the element in deprecated editing offsets.
6534 if (GetNode() &&
6535 !(EditingIgnoresContent(*GetNode()) || IsDisplayInsideTable(GetNode()))) {
6536 rect.SetX(rect.X() + BorderLeft() + PaddingLeft());
6537 rect.SetY(rect.Y() + PaddingTop() + BorderTop());
6538 }
6539
6540 if (!IsHorizontalWritingMode())
6541 return rect.TransposedRect();
6542
6543 return rect;
6544 }
6545
PositionForPoint(const PhysicalOffset & point) const6546 PositionWithAffinity LayoutBox::PositionForPoint(
6547 const PhysicalOffset& point) const {
6548 NOT_DESTROYED();
6549 // no children...return this layout object's element, if there is one, and
6550 // offset 0
6551 LayoutObject* first_child = SlowFirstChild();
6552 if (!first_child)
6553 return CreatePositionWithAffinity(
6554 NonPseudoNode() ? FirstPositionInOrBeforeNode(*NonPseudoNode())
6555 : Position());
6556
6557 if (IsTable() && NonPseudoNode()) {
6558 const Node& node = *NonPseudoNode();
6559 LayoutUnit x_in_block_direction = FlipForWritingMode(point.left);
6560 if (x_in_block_direction < 0 || x_in_block_direction > Size().Width() ||
6561 point.top < 0 || point.top > Size().Height()) {
6562 if (x_in_block_direction <= Size().Width() / 2) {
6563 return CreatePositionWithAffinity(FirstPositionInOrBeforeNode(node));
6564 }
6565 return CreatePositionWithAffinity(LastPositionInOrAfterNode(node));
6566 }
6567 }
6568
6569 // Pass off to the closest child.
6570 LayoutUnit min_dist = LayoutUnit::Max();
6571 LayoutBox* closest_layout_object = nullptr;
6572 PhysicalOffset adjusted_point = point;
6573 if (IsLegacyTableRow())
6574 adjusted_point += PhysicalLocation();
6575
6576 for (LayoutObject* layout_object = first_child; layout_object;
6577 layout_object = layout_object->NextSibling()) {
6578 if ((!layout_object->SlowFirstChild() && !layout_object->IsInline() &&
6579 !layout_object->IsLayoutBlockFlow()) ||
6580 layout_object->StyleRef().Visibility() != EVisibility::kVisible)
6581 continue;
6582
6583 if (!layout_object->IsBox())
6584 continue;
6585
6586 auto* layout_box = To<LayoutBox>(layout_object);
6587
6588 LayoutUnit top =
6589 layout_box->BorderTop() + layout_box->PaddingTop() +
6590 (IsLegacyTableRow() ? LayoutUnit() : layout_box->Location().Y());
6591 LayoutUnit bottom = top + layout_box->ContentHeight();
6592 LayoutUnit left =
6593 layout_box->BorderLeft() + layout_box->PaddingLeft() +
6594 (IsLegacyTableRow() ? LayoutUnit()
6595 : layout_box->PhysicalLocation().left);
6596 LayoutUnit right = left + layout_box->ContentWidth();
6597
6598 if (point.left <= right && point.left >= left && point.top <= top &&
6599 point.top >= bottom) {
6600 if (layout_box->IsLegacyTableRow()) {
6601 return layout_box->PositionForPoint(point + adjusted_point -
6602 layout_box->PhysicalLocation());
6603 }
6604 return layout_box->PositionForPoint(point -
6605 layout_box->PhysicalLocation());
6606 }
6607
6608 // Find the distance from (x, y) to the box. Split the space around the box
6609 // into 8 pieces and use a different compare depending on which piece (x, y)
6610 // is in.
6611 PhysicalOffset cmp;
6612 if (point.left > right) {
6613 if (point.top < top)
6614 cmp = PhysicalOffset(right, top);
6615 else if (point.top > bottom)
6616 cmp = PhysicalOffset(right, bottom);
6617 else
6618 cmp = PhysicalOffset(right, point.top);
6619 } else if (point.left < left) {
6620 if (point.top < top)
6621 cmp = PhysicalOffset(left, top);
6622 else if (point.top > bottom)
6623 cmp = PhysicalOffset(left, bottom);
6624 else
6625 cmp = PhysicalOffset(left, point.top);
6626 } else {
6627 if (point.top < top)
6628 cmp = PhysicalOffset(point.left, top);
6629 else
6630 cmp = PhysicalOffset(point.left, bottom);
6631 }
6632
6633 PhysicalOffset difference = cmp - point;
6634
6635 LayoutUnit dist =
6636 difference.left * difference.left + difference.top * difference.top;
6637 if (dist < min_dist) {
6638 closest_layout_object = layout_box;
6639 min_dist = dist;
6640 }
6641 }
6642
6643 if (closest_layout_object) {
6644 return closest_layout_object->PositionForPoint(
6645 adjusted_point - closest_layout_object->PhysicalLocation());
6646 }
6647 return CreatePositionWithAffinity(
6648 NonPseudoNode() ? FirstPositionInOrBeforeNode(*NonPseudoNode())
6649 : Position());
6650 }
6651
6652 DISABLE_CFI_PERF
ShrinkToAvoidFloats() const6653 bool LayoutBox::ShrinkToAvoidFloats() const {
6654 NOT_DESTROYED();
6655 // Floating objects don't shrink. Objects that don't avoid floats don't
6656 // shrink.
6657 if (IsInline() || !CreatesNewFormattingContext() || IsFloating())
6658 return false;
6659
6660 // Only auto width objects can possibly shrink to avoid floats.
6661 if (!StyleRef().Width().IsAuto())
6662 return false;
6663
6664 // If the containing block is LayoutNG, we will not let legacy layout deal
6665 // with positioning of floats or sizing of auto-width new formatting context
6666 // block level objects adjacent to them.
6667 if (const auto* containing_block = ContainingBlock()) {
6668 if (containing_block->IsLayoutNGMixin())
6669 return false;
6670 }
6671
6672 // Legends are taken out of the normal flow, and are laid out at the very
6673 // start of the fieldset, and are therefore not affected by floats (that may
6674 // appear earlier in the DOM).
6675 if (IsRenderedLegend())
6676 return false;
6677
6678 return true;
6679 }
6680
6681 DISABLE_CFI_PERF
ShouldBeConsideredAsReplaced() const6682 bool LayoutBox::ShouldBeConsideredAsReplaced() const {
6683 NOT_DESTROYED();
6684 if (IsAtomicInlineLevel())
6685 return true;
6686 // We need to detect all types of objects that should be treated as replaced.
6687 // Callers of this method will use the result for various things, such as
6688 // determining how to size the object, or whether it needs to avoid adjacent
6689 // floats, just like objects that establish a new formatting context.
6690 // IsAtomicInlineLevel() will not catch all the cases. Objects may be
6691 // block-level and still replaced, and we cannot deduce this from the
6692 // LayoutObject type. Checkboxes and radio buttons are such examples. We need
6693 // to check the Element type. This also applies to images, since we may have
6694 // created a block-flow LayoutObject for the ALT text (which still counts as
6695 // replaced).
6696 auto* element = DynamicTo<Element>(GetNode());
6697 if (!element)
6698 return false;
6699 if (element->IsFormControlElement()) {
6700 // Form control elements are generally replaced objects. Fieldsets are not,
6701 // though. A fieldset is (almost) a regular block container, and should be
6702 // treated as such.
6703 return !IsA<HTMLFieldSetElement>(element);
6704 }
6705 return IsA<HTMLImageElement>(element);
6706 }
6707
UpdateFragmentationInfoForChild(LayoutBox & child)6708 void LayoutBox::UpdateFragmentationInfoForChild(LayoutBox& child) {
6709 NOT_DESTROYED();
6710 LayoutState* layout_state = View()->GetLayoutState();
6711 DCHECK(layout_state->IsPaginated());
6712 child.SetOffsetToNextPage(LayoutUnit());
6713 if (!IsPageLogicalHeightKnown())
6714 return;
6715
6716 LayoutUnit logical_top = child.LogicalTop();
6717 LayoutUnit logical_height = child.LogicalHeightWithVisibleOverflow();
6718 LayoutUnit space_left = PageRemainingLogicalHeightForOffset(
6719 logical_top, kAssociateWithLatterPage);
6720 if (space_left < logical_height)
6721 child.SetOffsetToNextPage(space_left);
6722 }
6723
ChildNeedsRelayoutForPagination(const LayoutBox & child) const6724 bool LayoutBox::ChildNeedsRelayoutForPagination(const LayoutBox& child) const {
6725 NOT_DESTROYED();
6726 // TODO(mstensho): Should try to get this to work for floats too, instead of
6727 // just marking and bailing here.
6728 if (child.IsFloating())
6729 return true;
6730 const LayoutFlowThread* flow_thread = child.FlowThreadContainingBlock();
6731 // Figure out if we really need to force re-layout of the child. We only need
6732 // to do this if there's a chance that we need to recalculate pagination
6733 // struts inside.
6734 if (IsPageLogicalHeightKnown()) {
6735 LayoutUnit logical_top = child.LogicalTop();
6736 LayoutUnit logical_height = child.LogicalHeightWithVisibleOverflow();
6737 LayoutUnit remaining_space = PageRemainingLogicalHeightForOffset(
6738 logical_top, kAssociateWithLatterPage);
6739 if (child.OffsetToNextPage()) {
6740 // We need to relayout unless we're going to break at the exact same
6741 // location as before.
6742 if (child.OffsetToNextPage() != remaining_space)
6743 return true;
6744 // If column height isn't guaranteed to be uniform, we have no way of
6745 // telling what has happened after the first break.
6746 if (flow_thread && flow_thread->MayHaveNonUniformPageLogicalHeight())
6747 return true;
6748 } else if (logical_height > remaining_space) {
6749 // Last time we laid out this child, we didn't need to break, but now we
6750 // have to. So we need to relayout.
6751 return true;
6752 }
6753 } else if (child.OffsetToNextPage()) {
6754 // This child did previously break, but it won't anymore, because we no
6755 // longer have a known fragmentainer height.
6756 return true;
6757 }
6758
6759 // It seems that we can skip layout of this child, but we need to ask the flow
6760 // thread for permission first. We currently cannot skip over objects
6761 // containing column spanners.
6762 return flow_thread && !flow_thread->CanSkipLayout(child);
6763 }
6764
MarkChildForPaginationRelayoutIfNeeded(LayoutBox & child,SubtreeLayoutScope & layout_scope)6765 void LayoutBox::MarkChildForPaginationRelayoutIfNeeded(
6766 LayoutBox& child,
6767 SubtreeLayoutScope& layout_scope) {
6768 NOT_DESTROYED();
6769 DCHECK(!child.NeedsLayout() || child.ChildLayoutBlockedByDisplayLock());
6770 LayoutState* layout_state = View()->GetLayoutState();
6771
6772 if (layout_state->PaginationStateChanged() ||
6773 (layout_state->IsPaginated() && ChildNeedsRelayoutForPagination(child)))
6774 layout_scope.SetChildNeedsLayout(&child);
6775 }
6776
MarkOrthogonalWritingModeRoot()6777 void LayoutBox::MarkOrthogonalWritingModeRoot() {
6778 NOT_DESTROYED();
6779 DCHECK(GetFrameView());
6780 GetFrameView()->AddOrthogonalWritingModeRoot(*this);
6781 }
6782
UnmarkOrthogonalWritingModeRoot()6783 void LayoutBox::UnmarkOrthogonalWritingModeRoot() {
6784 NOT_DESTROYED();
6785 DCHECK(GetFrameView());
6786 GetFrameView()->RemoveOrthogonalWritingModeRoot(*this);
6787 }
6788
6789 // Children of LayoutCustom object's are only considered "items" when it has a
6790 // loaded algorithm.
IsCustomItem() const6791 bool LayoutBox::IsCustomItem() const {
6792 NOT_DESTROYED();
6793 auto* parent_layout_box = DynamicTo<LayoutNGCustom>(Parent());
6794 return parent_layout_box && parent_layout_box->IsLoaded();
6795 }
6796
6797 // LayoutCustom items are only shrink-to-fit during the web-developer defined
6798 // layout phase (not during fallback).
IsCustomItemShrinkToFit() const6799 bool LayoutBox::IsCustomItemShrinkToFit() const {
6800 NOT_DESTROYED();
6801 DCHECK(IsCustomItem());
6802 return To<LayoutNGCustom>(Parent())->IsLoaded();
6803 }
6804
AddVisualEffectOverflow()6805 void LayoutBox::AddVisualEffectOverflow() {
6806 NOT_DESTROYED();
6807 if (!StyleRef().HasVisualOverflowingEffect())
6808 return;
6809
6810 // Add in the final overflow with shadows, outsets and outline combined.
6811 PhysicalRect visual_effect_overflow = PhysicalBorderBoxRect();
6812 LayoutRectOutsets outsets = ComputeVisualEffectOverflowOutsets();
6813 visual_effect_overflow.Expand(outsets);
6814 AddSelfVisualOverflow(visual_effect_overflow);
6815
6816 if (VisualOverflowIsSet()) {
6817 overflow_->visual_overflow->SetHasSubpixelVisualEffectOutsets(
6818 !IsIntegerValue(outsets.Top()) || !IsIntegerValue(outsets.Right()) ||
6819 !IsIntegerValue(outsets.Bottom()) || !IsIntegerValue(outsets.Left()));
6820 }
6821 }
6822
ComputeVisualEffectOverflowOutsets()6823 LayoutRectOutsets LayoutBox::ComputeVisualEffectOverflowOutsets() {
6824 NOT_DESTROYED();
6825 const ComputedStyle& style = StyleRef();
6826 DCHECK(style.HasVisualOverflowingEffect());
6827
6828 LayoutRectOutsets outsets = style.BoxDecorationOutsets();
6829
6830 if (style.HasOutline()) {
6831 Vector<PhysicalRect> outline_rects = OutlineRects(
6832 PhysicalOffset(), OutlineRectsShouldIncludeBlockVisualOverflow());
6833 PhysicalRect rect = UnionRect(outline_rects);
6834 bool outline_affected = rect.size != PhysicalSizeToBeNoop(Size());
6835 SetOutlineMayBeAffectedByDescendants(outline_affected);
6836 rect.Inflate(LayoutUnit(style.OutlineOutsetExtent()));
6837 outsets.Unite(LayoutRectOutsets(-rect.Y(), rect.Right() - Size().Width(),
6838 rect.Bottom() - Size().Height(),
6839 -rect.X()));
6840 }
6841
6842 return outsets;
6843 }
6844
AddVisualOverflowFromChild(const LayoutBox & child,const LayoutSize & delta)6845 void LayoutBox::AddVisualOverflowFromChild(const LayoutBox& child,
6846 const LayoutSize& delta) {
6847 NOT_DESTROYED();
6848 // Never allow flow threads to propagate overflow up to a parent.
6849 if (child.IsLayoutFlowThread())
6850 return;
6851
6852 // Add in visual overflow from the child. Even if the child clips its
6853 // overflow, it may still have visual overflow of its own set from box shadows
6854 // or reflections. It is unnecessary to propagate this overflow if we are
6855 // clipping our own overflow.
6856 if (child.HasSelfPaintingLayer())
6857 return;
6858 LayoutRect child_visual_overflow_rect =
6859 child.VisualOverflowRectForPropagation();
6860 child_visual_overflow_rect.Move(delta);
6861 AddContentsVisualOverflow(child_visual_overflow_rect);
6862 }
6863
6864 DISABLE_CFI_PERF
AddLayoutOverflowFromChild(const LayoutBox & child,const LayoutSize & delta)6865 void LayoutBox::AddLayoutOverflowFromChild(const LayoutBox& child,
6866 const LayoutSize& delta) {
6867 NOT_DESTROYED();
6868 DCHECK(!ChildLayoutBlockedByDisplayLock());
6869
6870 // Never allow flow threads to propagate overflow up to a parent.
6871 if (child.IsLayoutFlowThread())
6872 return;
6873
6874 // Only propagate layout overflow from the child if the child isn't clipping
6875 // its overflow. If it is, then its overflow is internal to it, and we don't
6876 // care about it. LayoutOverflowRectForPropagation takes care of this and just
6877 // propagates the border box rect instead.
6878 LayoutRect child_layout_overflow_rect =
6879 child.LayoutOverflowRectForPropagation(this);
6880 child_layout_overflow_rect.Move(delta);
6881 AddLayoutOverflow(child_layout_overflow_rect);
6882 }
6883
SetLayoutClientAfterEdge(LayoutUnit client_after_edge)6884 void LayoutBox::SetLayoutClientAfterEdge(LayoutUnit client_after_edge) {
6885 NOT_DESTROYED();
6886 if (LayoutOverflowIsSet())
6887 overflow_->layout_overflow->SetLayoutClientAfterEdge(client_after_edge);
6888 }
6889
LayoutClientAfterEdge() const6890 LayoutUnit LayoutBox::LayoutClientAfterEdge() const {
6891 NOT_DESTROYED();
6892 return LayoutOverflowIsSet()
6893 ? overflow_->layout_overflow->LayoutClientAfterEdge()
6894 : ClientLogicalBottom();
6895 }
6896
PhysicalVisualOverflowRectIncludingFilters() const6897 PhysicalRect LayoutBox::PhysicalVisualOverflowRectIncludingFilters() const {
6898 NOT_DESTROYED();
6899 PhysicalRect bounds_rect = PhysicalVisualOverflowRect();
6900 if (!StyleRef().HasFilter())
6901 return bounds_rect;
6902 FloatRect float_rect = Layer()->MapRectForFilter(FloatRect(bounds_rect));
6903 float_rect.UniteIfNonZero(Layer()->FilterReferenceBox());
6904 bounds_rect = PhysicalRect::EnclosingRect(float_rect);
6905 return bounds_rect;
6906 }
6907
HasTopOverflow() const6908 bool LayoutBox::HasTopOverflow() const {
6909 NOT_DESTROYED();
6910 return !StyleRef().IsLeftToRightDirection() && !IsHorizontalWritingMode();
6911 }
6912
HasLeftOverflow() const6913 bool LayoutBox::HasLeftOverflow() const {
6914 NOT_DESTROYED();
6915 if (IsHorizontalWritingMode())
6916 return !StyleRef().IsLeftToRightDirection();
6917 return StyleRef().GetWritingMode() == WritingMode::kVerticalRl;
6918 }
6919
SetLayoutOverflowFromLayoutResults()6920 void LayoutBox::SetLayoutOverflowFromLayoutResults() {
6921 NOT_DESTROYED();
6922 DCHECK(RuntimeEnabledFeatures::LayoutNGLayoutOverflowEnabled());
6923 ClearSelfNeedsLayoutOverflowRecalc();
6924 ClearChildNeedsLayoutOverflowRecalc();
6925 ClearLayoutOverflow();
6926
6927 const WritingMode writing_mode = StyleRef().GetWritingMode();
6928 base::Optional<PhysicalRect> layout_overflow;
6929 LayoutUnit consumed_block_size;
6930
6931 // Iterate over all the fragments and unite their individual layout-overflow
6932 // to determine the final layout-overflow.
6933 for (const auto& layout_result : layout_results_) {
6934 const auto& fragment =
6935 To<NGPhysicalBoxFragment>(layout_result->PhysicalFragment());
6936
6937 // In order to correctly unite the overflow, we need to shift an individual
6938 // fragment's layout-overflow by previously consumed block-size so far.
6939 PhysicalOffset offset_adjust;
6940 switch (writing_mode) {
6941 case WritingMode::kHorizontalTb:
6942 offset_adjust = {LayoutUnit(), consumed_block_size};
6943 break;
6944 case WritingMode::kVerticalRl:
6945 case WritingMode::kSidewaysRl:
6946 offset_adjust = {-fragment.Size().width - consumed_block_size,
6947 LayoutUnit()};
6948 break;
6949 case WritingMode::kVerticalLr:
6950 case WritingMode::kSidewaysLr:
6951 offset_adjust = {consumed_block_size, LayoutUnit()};
6952 break;
6953 default:
6954 NOTREACHED();
6955 break;
6956 }
6957
6958 PhysicalRect fragment_layout_overflow = fragment.LayoutOverflow();
6959 fragment_layout_overflow.offset += offset_adjust;
6960
6961 // If we are the first fragment just set the layout-overflow.
6962 if (!layout_overflow)
6963 layout_overflow = fragment_layout_overflow;
6964 else
6965 layout_overflow->UniteEvenIfEmpty(fragment_layout_overflow);
6966
6967 if (const auto* break_token = fragment.BreakToken()) {
6968 consumed_block_size =
6969 To<NGBlockBreakToken>(break_token)->ConsumedBlockSize();
6970 }
6971 }
6972
6973 if (!layout_overflow)
6974 return;
6975
6976 // layout-overflow is stored respecting flipped-blocks.
6977 if (IsFlippedBlocksWritingMode(writing_mode)) {
6978 layout_overflow->offset.left =
6979 -layout_overflow->offset.left - layout_overflow->size.width;
6980 }
6981
6982 if (layout_overflow->IsEmpty() ||
6983 PhysicalPaddingBoxRect().Contains(*layout_overflow))
6984 return;
6985
6986 DCHECK(!LayoutOverflowIsSet());
6987 if (!overflow_)
6988 overflow_ = std::make_unique<BoxOverflowModel>();
6989 overflow_->layout_overflow.emplace(layout_overflow->ToLayoutRect());
6990 }
6991
6992 DISABLE_CFI_PERF
AddLayoutOverflow(const LayoutRect & rect)6993 void LayoutBox::AddLayoutOverflow(const LayoutRect& rect) {
6994 NOT_DESTROYED();
6995 if (rect.IsEmpty())
6996 return;
6997
6998 LayoutRect client_box = NoOverflowRect();
6999 if (client_box.Contains(rect))
7000 return;
7001
7002 // For overflow clip objects, we don't want to propagate overflow into
7003 // unreachable areas.
7004 LayoutRect overflow_rect(rect);
7005 if (IsScrollContainer() || IsA<LayoutView>(this)) {
7006 // Overflow is in the block's coordinate space and thus is flipped for
7007 // vertical-rl writing
7008 // mode. At this stage that is actually a simplification, since we can
7009 // treat vertical-lr/rl
7010 // as the same.
7011 if (HasTopOverflow()) {
7012 overflow_rect.ShiftMaxYEdgeTo(
7013 std::min(overflow_rect.MaxY(), client_box.MaxY()));
7014 } else {
7015 overflow_rect.ShiftYEdgeTo(std::max(overflow_rect.Y(), client_box.Y()));
7016 }
7017 if (HasLeftOverflow() !=
7018 IsFlippedBlocksWritingMode(StyleRef().GetWritingMode())) {
7019 overflow_rect.ShiftMaxXEdgeTo(
7020 std::min(overflow_rect.MaxX(), client_box.MaxX()));
7021 } else {
7022 overflow_rect.ShiftXEdgeTo(std::max(overflow_rect.X(), client_box.X()));
7023 }
7024
7025 // Now re-test with the adjusted rectangle and see if it has become
7026 // unreachable or fully
7027 // contained.
7028 if (client_box.Contains(overflow_rect) || overflow_rect.IsEmpty())
7029 return;
7030 }
7031
7032 if (!LayoutOverflowIsSet()) {
7033 if (!overflow_)
7034 overflow_ = std::make_unique<BoxOverflowModel>();
7035 overflow_->layout_overflow.emplace(client_box);
7036 }
7037
7038 overflow_->layout_overflow->AddLayoutOverflow(overflow_rect);
7039 }
7040
AddSelfVisualOverflow(const LayoutRect & rect)7041 void LayoutBox::AddSelfVisualOverflow(const LayoutRect& rect) {
7042 NOT_DESTROYED();
7043 if (rect.IsEmpty())
7044 return;
7045
7046 LayoutRect border_box = BorderBoxRect();
7047 if (border_box.Contains(rect))
7048 return;
7049
7050 if (!VisualOverflowIsSet()) {
7051 if (!overflow_)
7052 overflow_ = std::make_unique<BoxOverflowModel>();
7053
7054 overflow_->visual_overflow.emplace(border_box);
7055 }
7056
7057 overflow_->visual_overflow->AddSelfVisualOverflow(rect);
7058 }
7059
AddContentsVisualOverflow(const LayoutRect & rect)7060 void LayoutBox::AddContentsVisualOverflow(const LayoutRect& rect) {
7061 NOT_DESTROYED();
7062 if (rect.IsEmpty())
7063 return;
7064
7065 // If hasOverflowClip() we always save contents visual overflow because we
7066 // need it
7067 // e.g. to determine whether to apply rounded corner clip on contents.
7068 // Otherwise we save contents visual overflow only if it overflows the border
7069 // box.
7070 LayoutRect border_box = BorderBoxRect();
7071 if (!HasNonVisibleOverflow() && border_box.Contains(rect))
7072 return;
7073
7074 if (!VisualOverflowIsSet()) {
7075 if (!overflow_)
7076 overflow_ = std::make_unique<BoxOverflowModel>();
7077
7078 overflow_->visual_overflow.emplace(border_box);
7079 }
7080 overflow_->visual_overflow->AddContentsVisualOverflow(rect);
7081 }
7082
ClearLayoutOverflow()7083 void LayoutBox::ClearLayoutOverflow() {
7084 NOT_DESTROYED();
7085 if (overflow_)
7086 overflow_->layout_overflow.reset();
7087 // overflow_ will be reset by MutableForPainting::ClearPreviousOverflowData()
7088 // if we don't need it to store previous overflow data.
7089 }
7090
ClearVisualOverflow()7091 void LayoutBox::ClearVisualOverflow() {
7092 NOT_DESTROYED();
7093 if (overflow_)
7094 overflow_->visual_overflow.reset();
7095 // overflow_ will be reset by MutableForPainting::ClearPreviousOverflowData()
7096 // if we don't need it to store previous overflow data.
7097 }
7098
PercentageLogicalHeightIsResolvable() const7099 bool LayoutBox::PercentageLogicalHeightIsResolvable() const {
7100 NOT_DESTROYED();
7101 Length fake_length = Length::Percent(100);
7102 return ComputePercentageLogicalHeight(fake_length) != -1;
7103 }
7104
7105 DISABLE_CFI_PERF
HasUnsplittableScrollingOverflow() const7106 bool LayoutBox::HasUnsplittableScrollingOverflow() const {
7107 NOT_DESTROYED();
7108 // We will paginate as long as we don't scroll overflow in the pagination
7109 // direction.
7110 bool is_horizontal = IsHorizontalWritingMode();
7111 if ((is_horizontal && !ScrollsOverflowY()) ||
7112 (!is_horizontal && !ScrollsOverflowX()))
7113 return false;
7114
7115 // Fragmenting scrollbars is only problematic in interactive media, e.g.
7116 // multicol on a screen. If we're printing, which is non-interactive media, we
7117 // should allow objects with non-visible overflow to be paginated as normally.
7118 if (GetDocument().Printing())
7119 return false;
7120
7121 // We do have overflow. We'll still be willing to paginate as long as the
7122 // block has auto logical height, auto or undefined max-logical-height and a
7123 // zero or auto min-logical-height.
7124 // Note this is just a heuristic, and it's still possible to have overflow
7125 // under these conditions, but it should work out to be good enough for common
7126 // cases. Paginating overflow with scrollbars present is not the end of the
7127 // world and is what we used to do in the old model anyway.
7128 return StyleRef().LogicalHeight().IsSpecified() ||
7129 (StyleRef().LogicalMaxHeight().IsSpecified() &&
7130 (!StyleRef().LogicalMaxHeight().IsPercentOrCalc() ||
7131 PercentageLogicalHeightIsResolvable())) ||
7132 (StyleRef().LogicalMinHeight().IsSpecified() &&
7133 (!StyleRef().LogicalMinHeight().IsPercentOrCalc() ||
7134 PercentageLogicalHeightIsResolvable()));
7135 }
7136
GetPaginationBreakability(FragmentationEngine engine) const7137 LayoutBox::PaginationBreakability LayoutBox::GetPaginationBreakability(
7138 FragmentationEngine engine) const {
7139 NOT_DESTROYED();
7140 if (ShouldBeConsideredAsReplaced() || HasUnsplittableScrollingOverflow() ||
7141 (Parent() && IsWritingModeRoot()) ||
7142 (IsOutOfFlowPositioned() &&
7143 StyleRef().GetPosition() == EPosition::kFixed) ||
7144 ShouldApplySizeContainment() || IsFrameSet())
7145 return kForbidBreaks;
7146
7147 if (engine != kUnknownFragmentationEngine) {
7148 // If the object isn't using the same engine as the fragmentation context,
7149 // it must be treated as monolithic.
7150 if (IsLayoutNGObject() != (engine == kNGFragmentationEngine))
7151 return kForbidBreaks;
7152 }
7153
7154 EBreakInside break_value = BreakInside();
7155 if (break_value == EBreakInside::kAvoid ||
7156 break_value == EBreakInside::kAvoidPage ||
7157 break_value == EBreakInside::kAvoidColumn)
7158 return kAvoidBreaks;
7159 return kAllowAnyBreaks;
7160 }
7161
LineHeight(bool,LineDirectionMode direction,LinePositionMode) const7162 LayoutUnit LayoutBox::LineHeight(bool /*firstLine*/,
7163 LineDirectionMode direction,
7164 LinePositionMode /*linePositionMode*/) const {
7165 if (IsAtomicInlineLevel()) {
7166 return direction == kHorizontalLine ? MarginHeight() + Size().Height()
7167 : MarginWidth() + Size().Width();
7168 }
7169 return LayoutUnit();
7170 }
7171
7172 DISABLE_CFI_PERF
BaselinePosition(FontBaseline baseline_type,bool,LineDirectionMode direction,LinePositionMode line_position_mode) const7173 LayoutUnit LayoutBox::BaselinePosition(
7174 FontBaseline baseline_type,
7175 bool /*firstLine*/,
7176 LineDirectionMode direction,
7177 LinePositionMode line_position_mode) const {
7178 DCHECK_EQ(line_position_mode, kPositionOnContainingLine);
7179 if (IsAtomicInlineLevel()) {
7180 LayoutUnit result = direction == kHorizontalLine
7181 ? MarginHeight() + Size().Height()
7182 : MarginWidth() + Size().Width();
7183 if (baseline_type == kAlphabeticBaseline)
7184 return result;
7185 return result - result / 2;
7186 }
7187 return LayoutUnit();
7188 }
7189
EnclosingFloatPaintingLayer() const7190 PaintLayer* LayoutBox::EnclosingFloatPaintingLayer() const {
7191 NOT_DESTROYED();
7192 const LayoutObject* curr = this;
7193 while (curr) {
7194 PaintLayer* layer = curr->HasLayer() && curr->IsBox()
7195 ? To<LayoutBox>(curr)->Layer()
7196 : nullptr;
7197 if (layer && layer->IsSelfPaintingLayer())
7198 return layer;
7199 curr = curr->Parent();
7200 }
7201 return nullptr;
7202 }
7203
EnclosingScrollportBox() const7204 const LayoutBlock& LayoutBox::EnclosingScrollportBox() const {
7205 NOT_DESTROYED();
7206 const LayoutBlock* ancestor = ContainingBlock();
7207 for (; ancestor; ancestor = ancestor->ContainingBlock()) {
7208 if (ancestor->IsScrollContainer())
7209 return *ancestor;
7210 }
7211 NOTREACHED();
7212 return *ancestor;
7213 }
7214
LogicalVisualOverflowRectForPropagation() const7215 LayoutRect LayoutBox::LogicalVisualOverflowRectForPropagation() const {
7216 NOT_DESTROYED();
7217 LayoutRect rect = VisualOverflowRectForPropagation();
7218 if (!Parent()->StyleRef().IsHorizontalWritingMode())
7219 return rect.TransposedRect();
7220 return rect;
7221 }
7222
7223 DISABLE_CFI_PERF
RectForOverflowPropagation(const LayoutRect & rect) const7224 LayoutRect LayoutBox::RectForOverflowPropagation(const LayoutRect& rect) const {
7225 NOT_DESTROYED();
7226 // If the child and parent are in the same blocks direction, then we don't
7227 // have to do anything fancy. Just return the rect.
7228 if (Parent()->StyleRef().IsFlippedBlocksWritingMode() ==
7229 StyleRef().IsFlippedBlocksWritingMode())
7230 return rect;
7231
7232 // Convert the rect into parent's blocks direction by flipping along the y
7233 // axis.
7234 LayoutRect result = rect;
7235 result.SetX(Size().Width() - rect.MaxX());
7236 return result;
7237 }
7238
7239 DISABLE_CFI_PERF
LogicalLayoutOverflowRectForPropagation(LayoutObject * container) const7240 LayoutRect LayoutBox::LogicalLayoutOverflowRectForPropagation(
7241 LayoutObject* container) const {
7242 NOT_DESTROYED();
7243 LayoutRect rect = LayoutOverflowRectForPropagation(container);
7244 if (!Parent()->StyleRef().IsHorizontalWritingMode())
7245 return rect.TransposedRect();
7246 return rect;
7247 }
7248
7249 DISABLE_CFI_PERF
LayoutOverflowRectForPropagation(LayoutObject * container) const7250 LayoutRect LayoutBox::LayoutOverflowRectForPropagation(
7251 LayoutObject* container) const {
7252 NOT_DESTROYED();
7253 // Only propagate interior layout overflow if we don't clip it.
7254 LayoutRect rect = BorderBoxRect();
7255
7256 if (!ShouldApplyLayoutContainment() &&
7257 (!ShouldClipOverflowAlongBothAxis() ||
7258 StyleRef().OverflowClipMargin() != LayoutUnit())) {
7259 rect.Unite(LayoutOverflowRect());
7260 }
7261
7262 bool has_transform = HasLayer() && Layer()->Transform();
7263 if (IsInFlowPositioned() || has_transform) {
7264 // If we are relatively positioned or if we have a transform, then we have
7265 // to convert this rectangle into physical coordinates, apply relative
7266 // positioning and transforms to it, and then convert it back.
7267 DeprecatedFlipForWritingMode(rect);
7268
7269 PhysicalOffset container_offset;
7270
7271 if (IsRelPositioned())
7272 container_offset = RelativePositionOffset();
7273
7274 if (ShouldUseTransformFromContainer(container)) {
7275 TransformationMatrix t;
7276 GetTransformFromContainer(container ? container : Container(),
7277 container_offset, t);
7278 rect = t.MapRect(rect);
7279 } else {
7280 rect.Move(container_offset.ToLayoutSize());
7281 }
7282
7283 // Now we need to flip back.
7284 DeprecatedFlipForWritingMode(rect);
7285 }
7286
7287 return RectForOverflowPropagation(rect);
7288 }
7289
7290 DISABLE_CFI_PERF
NoOverflowRect() const7291 LayoutRect LayoutBox::NoOverflowRect() const {
7292 NOT_DESTROYED();
7293 return FlipForWritingMode(PhysicalPaddingBoxRect());
7294 }
7295
VisualOverflowRect() const7296 LayoutRect LayoutBox::VisualOverflowRect() const {
7297 NOT_DESTROYED();
7298 if (!VisualOverflowIsSet())
7299 return BorderBoxRect();
7300
7301 const LayoutRect& self_visual_overflow_rect =
7302 overflow_->visual_overflow->SelfVisualOverflowRect();
7303 if (HasMask())
7304 return self_visual_overflow_rect;
7305
7306 const OverflowClipAxes overflow_clip_axes = GetOverflowClipAxes();
7307 const LayoutUnit overflow_clip_margin = StyleRef().OverflowClipMargin();
7308 if (overflow_clip_margin != LayoutUnit()) {
7309 // overflow_clip_margin should only be set if 'overflow' is 'clip' along
7310 // both axis.
7311 DCHECK_EQ(overflow_clip_axes, kOverflowClipBothAxis);
7312 const LayoutRect& contents_visual_overflow_rect =
7313 overflow_->visual_overflow->ContentsVisualOverflowRect();
7314 if (!contents_visual_overflow_rect.IsEmpty()) {
7315 LayoutRect result = BorderBoxRect();
7316 result.Inflate(overflow_clip_margin);
7317 result.Intersect(contents_visual_overflow_rect);
7318 result.Unite(self_visual_overflow_rect);
7319 return result;
7320 }
7321 }
7322
7323 if (overflow_clip_axes == kOverflowClipBothAxis)
7324 return self_visual_overflow_rect;
7325
7326 LayoutRect result = overflow_->visual_overflow->ContentsVisualOverflowRect();
7327 result.Unite(self_visual_overflow_rect);
7328 ApplyOverflowClip(overflow_clip_axes, self_visual_overflow_rect, result);
7329 return result;
7330 }
7331
OffsetPoint(const Element * parent) const7332 PhysicalOffset LayoutBox::OffsetPoint(const Element* parent) const {
7333 NOT_DESTROYED();
7334 return AdjustedPositionRelativeTo(PhysicalLocation(), parent);
7335 }
7336
OffsetLeft(const Element * parent) const7337 LayoutUnit LayoutBox::OffsetLeft(const Element* parent) const {
7338 NOT_DESTROYED();
7339 return OffsetPoint(parent).left;
7340 }
7341
OffsetTop(const Element * parent) const7342 LayoutUnit LayoutBox::OffsetTop(const Element* parent) const {
7343 NOT_DESTROYED();
7344 return OffsetPoint(parent).top;
7345 }
7346
LocationContainer() const7347 LayoutBox* LayoutBox::LocationContainer() const {
7348 NOT_DESTROYED();
7349 // Location of a non-root SVG object derived from LayoutBox should not be
7350 // affected by writing-mode of the containing box (SVGRoot).
7351 if (IsSVGChild())
7352 return nullptr;
7353
7354 // Normally the box's location is relative to its containing box.
7355 LayoutObject* container = Container();
7356 while (container && !container->IsBox())
7357 container = container->Container();
7358 return To<LayoutBox>(container);
7359 }
7360
HasRelativeLogicalWidth() const7361 bool LayoutBox::HasRelativeLogicalWidth() const {
7362 NOT_DESTROYED();
7363 return StyleRef().LogicalWidth().IsPercentOrCalc() ||
7364 StyleRef().LogicalMinWidth().IsPercentOrCalc() ||
7365 StyleRef().LogicalMaxWidth().IsPercentOrCalc();
7366 }
7367
HasRelativeLogicalHeight() const7368 bool LayoutBox::HasRelativeLogicalHeight() const {
7369 NOT_DESTROYED();
7370 return StyleRef().LogicalHeight().IsPercentOrCalc() ||
7371 StyleRef().LogicalMinHeight().IsPercentOrCalc() ||
7372 StyleRef().LogicalMaxHeight().IsPercentOrCalc();
7373 }
7374
MarkBoxForRelayoutAfterSplit(LayoutBox * box)7375 static void MarkBoxForRelayoutAfterSplit(LayoutBox* box) {
7376 // FIXME: The table code should handle that automatically. If not,
7377 // we should fix it and remove the table part checks.
7378 if (box->IsTable()) {
7379 // Because we may have added some sections with already computed column
7380 // structures, we need to sync the table structure with them now. This
7381 // avoids crashes when adding new cells to the table.
7382 ToInterface<LayoutNGTableInterface>(box)->ForceSectionsRecalc();
7383 } else if (box->IsTableSection()) {
7384 ToInterface<LayoutNGTableSectionInterface>(box)->SetNeedsCellRecalc();
7385 }
7386
7387 box->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
7388 layout_invalidation_reason::kAnonymousBlockChange);
7389 }
7390
CollapseLoneAnonymousBlockChild(LayoutBox * parent,LayoutObject * child)7391 static void CollapseLoneAnonymousBlockChild(LayoutBox* parent,
7392 LayoutObject* child) {
7393 auto* child_block_flow = DynamicTo<LayoutBlockFlow>(child);
7394 auto* parent_block_flow = DynamicTo<LayoutBlockFlow>(parent);
7395 if (!child->IsAnonymousBlock() || !child_block_flow)
7396 return;
7397 if (!parent_block_flow)
7398 return;
7399 parent_block_flow->CollapseAnonymousBlockChild(child_block_flow);
7400 }
7401
SplitAnonymousBoxesAroundChild(LayoutObject * before_child)7402 LayoutObject* LayoutBox::SplitAnonymousBoxesAroundChild(
7403 LayoutObject* before_child) {
7404 NOT_DESTROYED();
7405 LayoutBox* box_at_top_of_new_branch = nullptr;
7406
7407 while (before_child->Parent() != this) {
7408 auto* box_to_split = To<LayoutBox>(before_child->Parent());
7409 if (box_to_split->SlowFirstChild() != before_child &&
7410 box_to_split->IsAnonymous()) {
7411 // We have to split the parent box into two boxes and move children
7412 // from |beforeChild| to end into the new post box.
7413 LayoutBox* post_box =
7414 box_to_split->CreateAnonymousBoxWithSameTypeAs(this);
7415 post_box->SetChildrenInline(box_to_split->ChildrenInline());
7416 auto* parent_box = To<LayoutBox>(box_to_split->Parent());
7417 // We need to invalidate the |parentBox| before inserting the new node
7418 // so that the table paint invalidation logic knows the structure is
7419 // dirty. See for example LayoutTableCell:localVisualRect().
7420 MarkBoxForRelayoutAfterSplit(parent_box);
7421 parent_box->VirtualChildren()->InsertChildNode(
7422 parent_box, post_box, box_to_split->NextSibling());
7423 box_to_split->MoveChildrenTo(post_box, before_child, nullptr, true);
7424
7425 LayoutObject* child = post_box->SlowFirstChild();
7426 DCHECK(child);
7427 if (child && !child->NextSibling())
7428 CollapseLoneAnonymousBlockChild(post_box, child);
7429 child = box_to_split->SlowFirstChild();
7430 DCHECK(child);
7431 if (child && !child->NextSibling())
7432 CollapseLoneAnonymousBlockChild(box_to_split, child);
7433
7434 MarkBoxForRelayoutAfterSplit(box_to_split);
7435 MarkBoxForRelayoutAfterSplit(post_box);
7436 box_at_top_of_new_branch = post_box;
7437
7438 before_child = post_box;
7439 } else {
7440 before_child = box_to_split;
7441 }
7442 }
7443
7444 // Splitting the box means the left side of the container chain will lose any
7445 // percent height descendants below |boxAtTopOfNewBranch| on the right hand
7446 // side.
7447 if (box_at_top_of_new_branch) {
7448 box_at_top_of_new_branch->ClearPercentHeightDescendants();
7449 MarkBoxForRelayoutAfterSplit(this);
7450 }
7451
7452 DCHECK_EQ(before_child->Parent(), this);
7453 return before_child;
7454 }
7455
OffsetFromLogicalTopOfFirstPage() const7456 LayoutUnit LayoutBox::OffsetFromLogicalTopOfFirstPage() const {
7457 NOT_DESTROYED();
7458 LayoutState* layout_state = View()->GetLayoutState();
7459 if (!layout_state || !layout_state->IsPaginated())
7460 return LayoutUnit();
7461
7462 if (layout_state->GetLayoutObject() == this) {
7463 LayoutSize offset = layout_state->PaginationOffset();
7464 return IsHorizontalWritingMode() ? offset.Height() : offset.Width();
7465 }
7466
7467 // A LayoutBlock always establishes a layout state, and this method is only
7468 // meant to be called on the object currently being laid out.
7469 DCHECK(!IsLayoutBlock());
7470
7471 // In case this box doesn't establish a layout state, try the containing
7472 // block.
7473 LayoutBlock* container_block = ContainingBlock();
7474 DCHECK(layout_state->GetLayoutObject() == container_block);
7475 return container_block->OffsetFromLogicalTopOfFirstPage() + LogicalTop();
7476 }
7477
SetOffsetToNextPage(LayoutUnit offset)7478 void LayoutBox::SetOffsetToNextPage(LayoutUnit offset) {
7479 NOT_DESTROYED();
7480 if (!rare_data_ && !offset)
7481 return;
7482 EnsureRareData().offset_to_next_page_ = offset;
7483 }
7484
LogicalExtentAfterUpdatingLogicalWidth(const LayoutUnit & new_logical_top,LayoutBox::LogicalExtentComputedValues & computed_values)7485 void LayoutBox::LogicalExtentAfterUpdatingLogicalWidth(
7486 const LayoutUnit& new_logical_top,
7487 LayoutBox::LogicalExtentComputedValues& computed_values) {
7488 NOT_DESTROYED();
7489 // FIXME: None of this is right for perpendicular writing-mode children.
7490 LayoutUnit old_logical_width = LogicalWidth();
7491 LayoutUnit old_logical_left = LogicalLeft();
7492 LayoutUnit old_margin_left = MarginLeft();
7493 LayoutUnit old_margin_right = MarginRight();
7494 LayoutUnit old_logical_top = LogicalTop();
7495
7496 SetLogicalTop(new_logical_top);
7497 UpdateLogicalWidth();
7498
7499 computed_values.extent_ = LogicalWidth();
7500 computed_values.position_ = LogicalLeft();
7501 computed_values.margins_.start_ = MarginStart();
7502 computed_values.margins_.end_ = MarginEnd();
7503
7504 SetLogicalTop(old_logical_top);
7505 SetLogicalWidth(old_logical_width);
7506 SetLogicalLeft(old_logical_left);
7507 SetMarginLeft(old_margin_left);
7508 SetMarginRight(old_margin_right);
7509 }
7510
GetShapeOutsideInfo() const7511 ShapeOutsideInfo* LayoutBox::GetShapeOutsideInfo() const {
7512 NOT_DESTROYED();
7513 return ShapeOutsideInfo::Info(*this);
7514 }
7515
SetPercentHeightContainer(LayoutBlock * container)7516 void LayoutBox::SetPercentHeightContainer(LayoutBlock* container) {
7517 NOT_DESTROYED();
7518 DCHECK(!container || !PercentHeightContainer());
7519 if (!container && !rare_data_)
7520 return;
7521 EnsureRareData().percent_height_container_ = container;
7522 }
7523
RemoveFromPercentHeightContainer()7524 void LayoutBox::RemoveFromPercentHeightContainer() {
7525 NOT_DESTROYED();
7526 if (!PercentHeightContainer())
7527 return;
7528
7529 DCHECK(PercentHeightContainer()->HasPercentHeightDescendant(this));
7530 PercentHeightContainer()->RemovePercentHeightDescendant(this);
7531 // The above call should call this object's
7532 // setPercentHeightContainer(nullptr).
7533 DCHECK(!PercentHeightContainer());
7534 }
7535
ClearPercentHeightDescendants()7536 void LayoutBox::ClearPercentHeightDescendants() {
7537 NOT_DESTROYED();
7538 for (LayoutObject* curr = SlowFirstChild(); curr;
7539 curr = curr->NextInPreOrder(this)) {
7540 if (curr->IsBox())
7541 To<LayoutBox>(curr)->RemoveFromPercentHeightContainer();
7542 }
7543 }
7544
PageLogicalHeightForOffset(LayoutUnit offset) const7545 LayoutUnit LayoutBox::PageLogicalHeightForOffset(LayoutUnit offset) const {
7546 NOT_DESTROYED();
7547 // We need to have calculated some fragmentainer logical height (even a
7548 // tentative one will do, though) in order to tell how tall one fragmentainer
7549 // is.
7550 DCHECK(IsPageLogicalHeightKnown());
7551
7552 LayoutView* layout_view = View();
7553 LayoutFlowThread* flow_thread = FlowThreadContainingBlock();
7554 LayoutUnit page_logical_height;
7555 if (!flow_thread) {
7556 page_logical_height = layout_view->PageLogicalHeight();
7557 } else {
7558 page_logical_height = flow_thread->PageLogicalHeightForOffset(
7559 offset + OffsetFromLogicalTopOfFirstPage());
7560 }
7561 DCHECK_GT(page_logical_height, LayoutUnit());
7562 return page_logical_height;
7563 }
7564
IsPageLogicalHeightKnown() const7565 bool LayoutBox::IsPageLogicalHeightKnown() const {
7566 NOT_DESTROYED();
7567 if (const LayoutFlowThread* flow_thread = FlowThreadContainingBlock())
7568 return flow_thread->IsPageLogicalHeightKnown();
7569 return View()->PageLogicalHeight();
7570 }
7571
PageRemainingLogicalHeightForOffset(LayoutUnit offset,PageBoundaryRule page_boundary_rule) const7572 LayoutUnit LayoutBox::PageRemainingLogicalHeightForOffset(
7573 LayoutUnit offset,
7574 PageBoundaryRule page_boundary_rule) const {
7575 NOT_DESTROYED();
7576 DCHECK(IsPageLogicalHeightKnown());
7577 LayoutView* layout_view = View();
7578 offset += OffsetFromLogicalTopOfFirstPage();
7579
7580 LayoutUnit footer_height =
7581 View()->GetLayoutState()->HeightOffsetForTableFooters();
7582 LayoutFlowThread* flow_thread = FlowThreadContainingBlock();
7583 LayoutUnit remaining_height;
7584 if (!flow_thread) {
7585 LayoutUnit page_logical_height = layout_view->PageLogicalHeight();
7586 remaining_height =
7587 page_logical_height - IntMod(offset, page_logical_height);
7588 if (page_boundary_rule == kAssociateWithFormerPage) {
7589 // An offset exactly at a page boundary will act as being part of the
7590 // former page in question (i.e. no remaining space), rather than being
7591 // part of the latter (i.e. one whole page length of remaining space).
7592 remaining_height = IntMod(remaining_height, page_logical_height);
7593 }
7594 } else {
7595 remaining_height = flow_thread->PageRemainingLogicalHeightForOffset(
7596 offset, page_boundary_rule);
7597 }
7598 return remaining_height - footer_height;
7599 }
7600
CurrentPageNumber(LayoutUnit child_logical_top) const7601 int LayoutBox::CurrentPageNumber(LayoutUnit child_logical_top) const {
7602 NOT_DESTROYED();
7603 LayoutUnit offset = OffsetFromLogicalTopOfFirstPage() + child_logical_top;
7604 return (offset / View()->PageLogicalHeight()).Floor();
7605 }
7606
CrossesPageBoundary(LayoutUnit offset,LayoutUnit logical_height) const7607 bool LayoutBox::CrossesPageBoundary(LayoutUnit offset,
7608 LayoutUnit logical_height) const {
7609 NOT_DESTROYED();
7610 if (!IsPageLogicalHeightKnown())
7611 return false;
7612 return PageRemainingLogicalHeightForOffset(offset, kAssociateWithLatterPage) <
7613 logical_height;
7614 }
7615
CalculatePaginationStrutToFitContent(LayoutUnit offset,LayoutUnit content_logical_height) const7616 LayoutUnit LayoutBox::CalculatePaginationStrutToFitContent(
7617 LayoutUnit offset,
7618 LayoutUnit content_logical_height) const {
7619 NOT_DESTROYED();
7620 LayoutUnit strut_to_next_page =
7621 PageRemainingLogicalHeightForOffset(offset, kAssociateWithLatterPage);
7622
7623 LayoutState* layout_state = View()->GetLayoutState();
7624 strut_to_next_page += layout_state->HeightOffsetForTableFooters();
7625 // If we're inside a cell in a row that straddles a page then avoid the
7626 // repeating header group if necessary. If we're a table section we're
7627 // already accounting for it.
7628 if (!IsTableSection()) {
7629 strut_to_next_page += layout_state->HeightOffsetForTableHeaders();
7630 }
7631
7632 LayoutUnit next_page_logical_top = offset + strut_to_next_page;
7633 if (PageLogicalHeightForOffset(next_page_logical_top) >=
7634 content_logical_height)
7635 return strut_to_next_page; // Content fits just fine in the next page or
7636 // column.
7637
7638 // Moving to the top of the next page or column doesn't result in enough space
7639 // for the content that we're trying to fit. If we're in a nested
7640 // fragmentation context, we may find enough space if we move to a column
7641 // further ahead, by effectively breaking to the next outer fragmentainer.
7642 LayoutFlowThread* flow_thread = FlowThreadContainingBlock();
7643 if (!flow_thread) {
7644 // If there's no flow thread, we're not nested. All pages have the same
7645 // height. Give up.
7646 return strut_to_next_page;
7647 }
7648 // Start searching for a suitable offset at the top of the next page or
7649 // column.
7650 LayoutUnit flow_thread_offset =
7651 OffsetFromLogicalTopOfFirstPage() + next_page_logical_top;
7652 return strut_to_next_page +
7653 flow_thread->NextLogicalTopForUnbreakableContent(
7654 flow_thread_offset, content_logical_height) -
7655 flow_thread_offset;
7656 }
7657
SnapContainer() const7658 LayoutBox* LayoutBox::SnapContainer() const {
7659 NOT_DESTROYED();
7660 return rare_data_ ? rare_data_->snap_container_ : nullptr;
7661 }
7662
SetSnapContainer(LayoutBox * new_container)7663 void LayoutBox::SetSnapContainer(LayoutBox* new_container) {
7664 NOT_DESTROYED();
7665 LayoutBox* old_container = SnapContainer();
7666 if (old_container == new_container)
7667 return;
7668
7669 if (old_container)
7670 old_container->RemoveSnapArea(*this);
7671
7672 EnsureRareData().snap_container_ = new_container;
7673
7674 if (new_container)
7675 new_container->AddSnapArea(*this);
7676 }
7677
ClearSnapAreas()7678 void LayoutBox::ClearSnapAreas() {
7679 NOT_DESTROYED();
7680 if (SnapAreaSet* areas = SnapAreas()) {
7681 for (auto* const snap_area : *areas)
7682 snap_area->rare_data_->snap_container_ = nullptr;
7683 areas->clear();
7684 }
7685 }
7686
AddSnapArea(LayoutBox & snap_area)7687 void LayoutBox::AddSnapArea(LayoutBox& snap_area) {
7688 NOT_DESTROYED();
7689 EnsureRareData().EnsureSnapAreas().insert(&snap_area);
7690 }
7691
RemoveSnapArea(const LayoutBox & snap_area)7692 void LayoutBox::RemoveSnapArea(const LayoutBox& snap_area) {
7693 NOT_DESTROYED();
7694 // const_cast is safe here because we only need to modify the type to match
7695 // the key type, and not actually mutate the object.
7696 if (rare_data_ && rare_data_->snap_areas_)
7697 rare_data_->snap_areas_->erase(const_cast<LayoutBox*>(&snap_area));
7698 }
7699
ReassignSnapAreas(LayoutBox & new_container)7700 void LayoutBox::ReassignSnapAreas(LayoutBox& new_container) {
7701 NOT_DESTROYED();
7702 SnapAreaSet* areas = SnapAreas();
7703 if (!areas)
7704 return;
7705 for (auto* const snap_area : *areas) {
7706 snap_area->rare_data_->snap_container_ = &new_container;
7707 new_container.AddSnapArea(*snap_area);
7708 }
7709 areas->clear();
7710 }
7711
AllowedToPropagateRecursiveScrollToParentFrame(const mojom::blink::ScrollIntoViewParamsPtr & params)7712 bool LayoutBox::AllowedToPropagateRecursiveScrollToParentFrame(
7713 const mojom::blink::ScrollIntoViewParamsPtr& params) {
7714 NOT_DESTROYED();
7715 if (!GetFrameView()->SafeToPropagateScrollToParent())
7716 return false;
7717
7718 if (params->type != mojom::blink::ScrollType::kProgrammatic)
7719 return true;
7720
7721 return !GetDocument().IsVerticalScrollEnforced();
7722 }
7723
SnapAreas() const7724 SnapAreaSet* LayoutBox::SnapAreas() const {
7725 NOT_DESTROYED();
7726 return rare_data_ ? rare_data_->snap_areas_.get() : nullptr;
7727 }
7728
GetCustomLayoutChild() const7729 CustomLayoutChild* LayoutBox::GetCustomLayoutChild() const {
7730 NOT_DESTROYED();
7731 DCHECK(rare_data_);
7732 DCHECK(rare_data_->layout_child_);
7733 return rare_data_->layout_child_.Get();
7734 }
7735
AddCustomLayoutChildIfNeeded()7736 void LayoutBox::AddCustomLayoutChildIfNeeded() {
7737 NOT_DESTROYED();
7738 if (!IsCustomItem())
7739 return;
7740
7741 const AtomicString& name = Parent()->StyleRef().DisplayLayoutCustomName();
7742 LayoutWorklet* worklet = LayoutWorklet::From(*GetDocument().domWindow());
7743 const CSSLayoutDefinition* definition =
7744 worklet->Proxy()->FindDefinition(name);
7745
7746 // If there isn't a definition yet, the web developer defined layout isn't
7747 // loaded yet (or is invalid). The layout tree will get re-attached when
7748 // loaded, so don't bother creating a script representation of this node yet.
7749 if (!definition)
7750 return;
7751
7752 EnsureRareData().layout_child_ =
7753 MakeGarbageCollected<CustomLayoutChild>(*definition, NGBlockNode(this));
7754 }
7755
ClearCustomLayoutChild()7756 void LayoutBox::ClearCustomLayoutChild() {
7757 NOT_DESTROYED();
7758 if (!rare_data_)
7759 return;
7760
7761 if (rare_data_->layout_child_)
7762 rare_data_->layout_child_->ClearLayoutNode();
7763
7764 rare_data_->layout_child_ = nullptr;
7765 }
7766
DebugRect() const7767 PhysicalRect LayoutBox::DebugRect() const {
7768 NOT_DESTROYED();
7769 return PhysicalRect(PhysicalLocation(), Size());
7770 }
7771
ApplyOverflowClipToLayoutOverflowRect()7772 void LayoutBox::ApplyOverflowClipToLayoutOverflowRect() {
7773 NOT_DESTROYED();
7774 if (!HasNonVisibleOverflow() || IsScrollContainer() ||
7775 !LayoutOverflowIsSet()) {
7776 return;
7777 }
7778
7779 const OverflowClipAxes overflow_clip_axes = GetOverflowClipAxes();
7780 if (overflow_clip_axes == kNoOverflowClip)
7781 return;
7782
7783 LayoutRect no_overflow_rect = NoOverflowRect();
7784 LayoutRect overflow_rect = overflow_->layout_overflow->LayoutOverflowRect();
7785 const LayoutUnit overflow_clip_margin = StyleRef().OverflowClipMargin();
7786 if (overflow_clip_margin != LayoutUnit()) {
7787 // overflow_clip_margin should only be set if 'overflow' is 'clip' along
7788 // both axis.
7789 DCHECK_EQ(overflow_clip_axes, kOverflowClipBothAxis);
7790 no_overflow_rect.Inflate(overflow_clip_margin);
7791 overflow_rect.Intersect(no_overflow_rect);
7792 } else {
7793 ApplyOverflowClip(overflow_clip_axes, no_overflow_rect, overflow_rect);
7794 }
7795 overflow_->layout_overflow->SetLayoutOverflow(overflow_rect);
7796 }
7797
ComputeOverflowClipAxes() const7798 OverflowClipAxes LayoutBox::ComputeOverflowClipAxes() const {
7799 NOT_DESTROYED();
7800 if (ShouldApplyPaintContainment() || HasControlClip())
7801 return kOverflowClipBothAxis;
7802 if (!HasNonVisibleOverflow())
7803 return kNoOverflowClip;
7804 if (IsScrollContainer())
7805 return kOverflowClipBothAxis;
7806 return (StyleRef().OverflowX() == EOverflow::kVisible ? kNoOverflowClip
7807 : kOverflowClipX) |
7808 (StyleRef().OverflowY() == EOverflow::kVisible ? kNoOverflowClip
7809 : kOverflowClipY);
7810 }
7811
SavePreviousOverflowData()7812 void LayoutBox::MutableForPainting::SavePreviousOverflowData() {
7813 if (!GetLayoutBox().overflow_)
7814 GetLayoutBox().overflow_ = std::make_unique<BoxOverflowModel>();
7815 auto& previous_overflow = GetLayoutBox().overflow_->previous_overflow_data;
7816 if (!previous_overflow)
7817 previous_overflow.emplace();
7818 previous_overflow->previous_physical_layout_overflow_rect =
7819 GetLayoutBox().PhysicalLayoutOverflowRect();
7820 previous_overflow->previous_physical_visual_overflow_rect =
7821 GetLayoutBox().PhysicalVisualOverflowRect();
7822 previous_overflow->previous_physical_self_visual_overflow_rect =
7823 GetLayoutBox().PhysicalSelfVisualOverflowRect();
7824 }
7825
SetPreviousGeometryForLayoutShiftTracking(const PhysicalOffset & paint_offset,const LayoutSize & size,const PhysicalRect & visual_overflow_rect)7826 void LayoutBox::MutableForPainting::SetPreviousGeometryForLayoutShiftTracking(
7827 const PhysicalOffset& paint_offset,
7828 const LayoutSize& size,
7829 const PhysicalRect& visual_overflow_rect) {
7830 FirstFragment().SetPaintOffset(paint_offset);
7831 GetLayoutBox().previous_size_ = size;
7832 if (PhysicalRect(PhysicalOffset(), size).Contains(visual_overflow_rect))
7833 return;
7834
7835 if (!GetLayoutBox().overflow_)
7836 GetLayoutBox().overflow_ = std::make_unique<BoxOverflowModel>();
7837 auto& previous_overflow = GetLayoutBox().overflow_->previous_overflow_data;
7838 if (!previous_overflow)
7839 previous_overflow.emplace();
7840 previous_overflow->previous_physical_visual_overflow_rect =
7841 visual_overflow_rect;
7842 // Other previous rects don't matter because they are used for paint
7843 // invalidation and we always do full paint invalidation on reattachment.
7844 }
7845
VisualRectOutsetForRasterEffects() const7846 RasterEffectOutset LayoutBox::VisualRectOutsetForRasterEffects() const {
7847 NOT_DESTROYED();
7848 // If the box has subpixel visual effect outsets, as the visual effect may be
7849 // painted along the pixel-snapped border box, the pixels on the anti-aliased
7850 // edge of the effect may overflow the calculated visual rect. Expand visual
7851 // rect by one pixel in the case.
7852 return VisualOverflowIsSet() &&
7853 overflow_->visual_overflow->HasSubpixelVisualEffectOutsets()
7854 ? RasterEffectOutset::kWholePixel
7855 : RasterEffectOutset::kNone;
7856 }
7857
ResolvedDirection() const7858 TextDirection LayoutBox::ResolvedDirection() const {
7859 NOT_DESTROYED();
7860 if (IsInline() && IsAtomicInlineLevel()) {
7861 if (IsInLayoutNGInlineFormattingContext()) {
7862 NGInlineCursor cursor;
7863 cursor.MoveTo(*this);
7864 if (cursor)
7865 return cursor.Current().ResolvedDirection();
7866 }
7867 if (InlineBoxWrapper())
7868 return InlineBoxWrapper()->Direction();
7869 }
7870 return StyleRef().Direction();
7871 }
7872
NeedsScrollNode(CompositingReasons direct_compositing_reasons) const7873 bool LayoutBox::NeedsScrollNode(
7874 CompositingReasons direct_compositing_reasons) const {
7875 NOT_DESTROYED();
7876 if (!IsScrollContainer())
7877 return false;
7878
7879 if (direct_compositing_reasons & CompositingReason::kRootScroller)
7880 return true;
7881
7882 return GetScrollableArea()->ScrollsOverflow();
7883 }
7884
OverrideTickmarks(Vector<IntRect> tickmarks)7885 void LayoutBox::OverrideTickmarks(Vector<IntRect> tickmarks) {
7886 NOT_DESTROYED();
7887 GetScrollableArea()->SetTickmarksOverride(std::move(tickmarks));
7888 InvalidatePaintForTickmarks();
7889 }
7890
InvalidatePaintForTickmarks()7891 void LayoutBox::InvalidatePaintForTickmarks() {
7892 NOT_DESTROYED();
7893 ScrollableArea* scrollable_area = GetScrollableArea();
7894 if (!scrollable_area)
7895 return;
7896 Scrollbar* scrollbar = scrollable_area->VerticalScrollbar();
7897 if (!scrollbar)
7898 return;
7899 scrollbar->SetNeedsPaintInvalidation(static_cast<ScrollbarPart>(~kThumbPart));
7900 }
7901
7902 } // namespace blink
7903