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 Apple Inc. All rights reserved.
7 * Copyright (C) 2010 Google Inc. All rights reserved.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 *
24 */
25
26 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
27
28 #include "cc/input/main_thread_scrolling_reason.h"
29 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
30 #include "third_party/blink/renderer/core/frame/local_frame.h"
31 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
32 #include "third_party/blink/renderer/core/frame/settings.h"
33 #include "third_party/blink/renderer/core/html/html_body_element.h"
34 #include "third_party/blink/renderer/core/layout/geometry/transform_state.h"
35 #include "third_party/blink/renderer/core/layout/layout_block.h"
36 #include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
37 #include "third_party/blink/renderer/core/layout/layout_geometry_map.h"
38 #include "third_party/blink/renderer/core/layout/layout_inline.h"
39 #include "third_party/blink/renderer/core/layout/layout_view.h"
40 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space.h"
41 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
42 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
43 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
44 #include "third_party/blink/renderer/core/paint/object_paint_invalidator.h"
45 #include "third_party/blink/renderer/core/paint/paint_layer.h"
46 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
47 #include "third_party/blink/renderer/core/style/shadow_list.h"
48 #include "third_party/blink/renderer/platform/geometry/length_functions.h"
49
50 namespace blink {
51
52 namespace {
IsOutOfFlowPositionedWithImplicitHeight(const LayoutBoxModelObject * child)53 inline bool IsOutOfFlowPositionedWithImplicitHeight(
54 const LayoutBoxModelObject* child) {
55 return child->IsOutOfFlowPositioned() &&
56 !child->StyleRef().LogicalTop().IsAuto() &&
57 !child->StyleRef().LogicalBottom().IsAuto();
58 }
59
60 // Inclusive of |from|, exclusive of |to|.
FindFirstStickyBetween(LayoutObject * from,LayoutObject * to)61 PaintLayer* FindFirstStickyBetween(LayoutObject* from, LayoutObject* to) {
62 LayoutObject* maybe_sticky_ancestor = from;
63 while (maybe_sticky_ancestor && maybe_sticky_ancestor != to) {
64 if (maybe_sticky_ancestor->StyleRef().HasStickyConstrainedPosition()) {
65 return ToLayoutBoxModelObject(maybe_sticky_ancestor)->Layer();
66 }
67
68 maybe_sticky_ancestor =
69 maybe_sticky_ancestor->IsLayoutInline()
70 ? maybe_sticky_ancestor->Container()
71 : ToLayoutBox(maybe_sticky_ancestor)->LocationContainer();
72 }
73 return nullptr;
74 }
75 } // namespace
76
77 // The HashMap for storing continuation pointers.
78 // The continuation chain is a singly linked list. As such, the HashMap's value
79 // is the next pointer associated with the key.
80 typedef HashMap<const LayoutBoxModelObject*, LayoutBoxModelObject*>
81 ContinuationMap;
82 static ContinuationMap* g_continuation_map = nullptr;
83
ContentChanged(ContentChangeType change_type)84 void LayoutBoxModelObject::ContentChanged(ContentChangeType change_type) {
85 if (!HasLayer())
86 return;
87
88 Layer()->ContentChanged(change_type);
89 }
90
LayoutBoxModelObject(ContainerNode * node)91 LayoutBoxModelObject::LayoutBoxModelObject(ContainerNode* node)
92 : LayoutObject(node) {}
93
UsesCompositedScrolling() const94 bool LayoutBoxModelObject::UsesCompositedScrolling() const {
95 return HasOverflowClip() && HasLayer() &&
96 Layer()->GetScrollableArea()->UsesCompositedScrolling();
97 }
98
99 BackgroundPaintLocation
ComputeBackgroundPaintLocationIfComposited(uint32_t * main_thread_scrolling_reasons) const100 LayoutBoxModelObject::ComputeBackgroundPaintLocationIfComposited(
101 uint32_t* main_thread_scrolling_reasons) const {
102 bool may_have_scrolling_layers_without_scrolling = IsA<LayoutView>(this);
103 const auto* scrollable_area = GetScrollableArea();
104 bool scrolls_overflow = scrollable_area && scrollable_area->ScrollsOverflow();
105 if (!scrolls_overflow && !may_have_scrolling_layers_without_scrolling)
106 return kBackgroundPaintInGraphicsLayer;
107
108 // If we care about LCD text, paint root backgrounds into scrolling contents
109 // layer even if style suggests otherwise. (For non-root scrollers, we just
110 // avoid compositing - see PLSA::ComputeNeedsCompositedScrolling.)
111 if (IsA<LayoutView>(this)) {
112 if (!GetDocument().GetSettings()->GetPreferCompositingToLCDTextEnabled())
113 return kBackgroundPaintInScrollingContents;
114 }
115
116 // TODO(flackr): Detect opaque custom scrollbars which would cover up a
117 // border-box background.
118 bool has_custom_scrollbars =
119 scrollable_area &&
120 ((scrollable_area->HorizontalScrollbar() &&
121 scrollable_area->HorizontalScrollbar()->IsCustomScrollbar()) ||
122 (scrollable_area->VerticalScrollbar() &&
123 scrollable_area->VerticalScrollbar()->IsCustomScrollbar()));
124
125 // TODO(flackr): When we correctly clip the scrolling contents layer we can
126 // paint locally equivalent backgrounds into it. https://crbug.com/645957
127 if (HasClip())
128 return kBackgroundPaintInGraphicsLayer;
129
130 // TODO(flackr): Remove this when box shadows are still painted correctly when
131 // painting into the composited scrolling contents layer.
132 // https://crbug.com/646464
133 if (StyleRef().BoxShadow()) {
134 if (main_thread_scrolling_reasons) {
135 *main_thread_scrolling_reasons |=
136 cc::MainThreadScrollingReason::kHasBoxShadowFromNonRootLayer;
137 }
138 return kBackgroundPaintInGraphicsLayer;
139 }
140
141 // Assume optimistically that the background can be painted in the scrolling
142 // contents until we find otherwise.
143 BackgroundPaintLocation paint_location = kBackgroundPaintInScrollingContents;
144 const FillLayer* layer = &(StyleRef().BackgroundLayers());
145 for (; layer; layer = layer->Next()) {
146 if (layer->Attachment() == EFillAttachment::kLocal)
147 continue;
148
149 // Solid color layers with an effective background clip of the padding box
150 // can be treated as local.
151 if (!layer->GetImage() && !layer->Next() &&
152 ResolveColor(GetCSSPropertyBackgroundColor()).Alpha() > 0) {
153 EFillBox clip = layer->Clip();
154 if (clip == EFillBox::kPadding)
155 continue;
156 // A border box can be treated as a padding box if the border is opaque or
157 // there is no border and we don't have custom scrollbars.
158 if (clip == EFillBox::kBorder) {
159 if (!has_custom_scrollbars &&
160 (StyleRef().BorderTopWidth() == 0 ||
161 (!ResolveColor(GetCSSPropertyBorderTopColor()).HasAlpha() &&
162 StyleRef().BorderTopStyle() == EBorderStyle::kSolid)) &&
163 (StyleRef().BorderLeftWidth() == 0 ||
164 (!ResolveColor(GetCSSPropertyBorderLeftColor()).HasAlpha() &&
165 StyleRef().BorderLeftStyle() == EBorderStyle::kSolid)) &&
166 (StyleRef().BorderRightWidth() == 0 ||
167 (!ResolveColor(GetCSSPropertyBorderRightColor()).HasAlpha() &&
168 StyleRef().BorderRightStyle() == EBorderStyle::kSolid)) &&
169 (StyleRef().BorderBottomWidth() == 0 ||
170 (!ResolveColor(GetCSSPropertyBorderBottomColor()).HasAlpha() &&
171 StyleRef().BorderBottomStyle() == EBorderStyle::kSolid))) {
172 continue;
173 }
174 // If we have an opaque background color only, we can safely paint it
175 // into both the scrolling contents layer and the graphics layer to
176 // preserve LCD text.
177 if (layer == (&StyleRef().BackgroundLayers()) &&
178 ResolveColor(GetCSSPropertyBackgroundColor()).Alpha() < 255)
179 return kBackgroundPaintInGraphicsLayer;
180 paint_location |= kBackgroundPaintInGraphicsLayer;
181 continue;
182 }
183 // A content fill box can be treated as a padding fill box if there is no
184 // padding.
185 if (clip == EFillBox::kContent && StyleRef().PaddingTop().IsZero() &&
186 StyleRef().PaddingLeft().IsZero() &&
187 StyleRef().PaddingRight().IsZero() &&
188 StyleRef().PaddingBottom().IsZero()) {
189 continue;
190 }
191 }
192 return kBackgroundPaintInGraphicsLayer;
193 }
194 return paint_location;
195 }
196
~LayoutBoxModelObject()197 LayoutBoxModelObject::~LayoutBoxModelObject() {
198 // Our layer should have been destroyed and cleared by now
199 DCHECK(!HasLayer());
200 DCHECK(!Layer());
201 }
202
WillBeDestroyed()203 void LayoutBoxModelObject::WillBeDestroyed() {
204 // A continuation of this LayoutObject should be destroyed at subclasses.
205 DCHECK(!Continuation());
206
207 if (IsPositioned()) {
208 // Don't use view() because the document's layoutView has been set to
209 // 0 during destruction.
210 if (LocalFrame* frame = GetFrame()) {
211 if (LocalFrameView* frame_view = frame->View()) {
212 if (StyleRef().HasViewportConstrainedPosition() ||
213 StyleRef().HasStickyConstrainedPosition())
214 frame_view->RemoveViewportConstrainedObject(*this);
215 }
216 }
217 }
218
219 LayoutObject::WillBeDestroyed();
220
221 if (HasLayer())
222 DestroyLayer();
223 }
224
StyleWillChange(StyleDifference diff,const ComputedStyle & new_style)225 void LayoutBoxModelObject::StyleWillChange(StyleDifference diff,
226 const ComputedStyle& new_style) {
227 // SPv1:
228 // This object's layer may begin or cease to be stacked or stacking context,
229 // in which case the paint invalidation container of this object and
230 // descendants may change. Thus we need to invalidate paint eagerly for all
231 // such children. PaintLayerCompositor::paintInvalidationOnCompositingChange()
232 // doesn't work for the case because we can only see the new
233 // paintInvalidationContainer during compositing update.
234 // SPv1 and v2:
235 // Change of stacked/stacking context status may cause change of this or
236 // descendant PaintLayer's CompositingContainer, so we need to eagerly
237 // invalidate the current compositing container chain which may have painted
238 // cached subsequences containing this object or descendant objects.
239 if (Style() &&
240 (StyleRef().IsStacked() != new_style.IsStacked() ||
241 StyleRef().IsStackingContext() != new_style.IsStackingContext()) &&
242 // ObjectPaintInvalidator requires this.
243 IsRooted()) {
244 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
245 ObjectPaintInvalidator(*this).SlowSetPaintingLayerNeedsRepaint();
246 } else {
247 // We need to invalidate based on the current compositing status.
248 DisableCompositingQueryAsserts compositing_disabler;
249 ObjectPaintInvalidator(*this)
250 .InvalidatePaintIncludingNonCompositingDescendants();
251 }
252 }
253
254 if (HasLayer() && diff.CssClipChanged())
255 Layer()->ClearClipRects();
256
257 LayoutObject::StyleWillChange(diff, new_style);
258 }
259
260 DISABLE_CFI_PERF
StyleDidChange(StyleDifference diff,const ComputedStyle * old_style)261 void LayoutBoxModelObject::StyleDidChange(StyleDifference diff,
262 const ComputedStyle* old_style) {
263 bool had_transform_related_property = HasTransformRelatedProperty();
264 bool had_layer = HasLayer();
265 bool layer_was_self_painting = had_layer && Layer()->IsSelfPaintingLayer();
266 bool was_horizontal_writing_mode = IsHorizontalWritingMode();
267 bool could_contain_fixed = ComputeIsFixedContainer(old_style);
268 bool could_contain_absolute =
269 could_contain_fixed || ComputeIsAbsoluteContainer(old_style);
270
271 LayoutObject::StyleDidChange(diff, old_style);
272 UpdateFromStyle();
273
274 // When an out-of-flow-positioned element changes its display between block
275 // and inline-block, then an incremental layout on the element's containing
276 // block lays out the element through LayoutPositionedObjects, which skips
277 // laying out the element's parent.
278 // The element's parent needs to relayout so that it calls LayoutBlockFlow::
279 // setStaticInlinePositionForChild with the out-of-flow-positioned child, so
280 // that when it's laid out, its LayoutBox::computePositionedLogicalWidth/
281 // Height takes into account its new inline/block position rather than its old
282 // block/inline position.
283 // Position changes and other types of display changes are handled elsewhere.
284 if (old_style && IsOutOfFlowPositioned() && Parent() &&
285 (StyleRef().GetPosition() == old_style->GetPosition()) &&
286 (StyleRef().IsOriginalDisplayInlineType() !=
287 old_style->IsOriginalDisplayInlineType()))
288 Parent()->SetNeedsLayout(layout_invalidation_reason::kChildChanged,
289 kMarkContainerChain);
290
291 PaintLayerType type = LayerTypeRequired();
292 if (type != kNoPaintLayer) {
293 if (!Layer()) {
294 // In order to update this object properly, we need to lay it out again.
295 // However, if we have never laid it out, don't mark it for layout. If
296 // this is a new object, it may not yet have been inserted into the tree,
297 // and if we mark it for layout then, we risk upsetting the tree
298 // insertion machinery.
299 if (EverHadLayout())
300 SetChildNeedsLayout();
301
302 CreateLayerAfterStyleChange();
303 }
304 } else if (Layer() && Layer()->Parent()) {
305 PaintLayer* parent_layer = Layer()->Parent();
306 // Either a transform wasn't specified or the object doesn't support
307 // transforms, so just null out the bit.
308 SetHasTransformRelatedProperty(false);
309 SetHasReflection(false);
310 Layer()->UpdateFilters(old_style, StyleRef());
311 Layer()->UpdateBackdropFilters(old_style, StyleRef());
312 Layer()->UpdateClipPath(old_style, StyleRef());
313 // Calls DestroyLayer() which clears the layer.
314 Layer()->RemoveOnlyThisLayerAfterStyleChange(old_style);
315 if (EverHadLayout())
316 SetChildNeedsLayout();
317 if (had_transform_related_property) {
318 SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
319 layout_invalidation_reason::kStyleChange);
320 }
321 if (!NeedsLayout()) {
322 // FIXME: We should call a specialized version of this function.
323 parent_layer->UpdateLayerPositionsAfterLayout();
324 }
325 }
326
327 bool can_contain_fixed = CanContainFixedPositionObjects();
328 bool can_contain_absolute = CanContainAbsolutePositionObjects();
329
330 if (old_style && (could_contain_fixed != can_contain_fixed ||
331 could_contain_absolute != can_contain_absolute)) {
332 // If out of flow element containment changed, then we need to force a
333 // subtree paint property update, since the children elements may now be
334 // referencing a different container.
335 AddSubtreePaintPropertyUpdateReason(
336 SubtreePaintPropertyUpdateReason::kContainerChainMayChange);
337 } else if (had_layer == HasLayer() &&
338 had_transform_related_property != HasTransformRelatedProperty()) {
339 // This affects whether to create transform node. Note that if the
340 // HasLayer() value changed, then all of this was already set in
341 // CreateLayerAfterStyleChange() or DestroyLayer().
342 SetNeedsPaintPropertyUpdate();
343 if (Layer())
344 Layer()->SetNeedsCompositingInputsUpdate();
345 }
346
347 if (old_style && Parent()) {
348 LayoutBlock* block = FindNonAnonymousContainingBlock(this);
349
350 if ((could_contain_fixed && !can_contain_fixed) ||
351 (could_contain_absolute && !can_contain_absolute)) {
352 // Clear our positioned objects list. Our absolute and fixed positioned
353 // descendants will be inserted into our containing block's positioned
354 // objects list during layout.
355 block->RemovePositionedObjects(nullptr, kNewContainingBlock);
356 }
357 if (!could_contain_absolute && can_contain_absolute) {
358 // Remove our absolute positioned descendants from their current
359 // containing block.
360 // They will be inserted into our positioned objects list during layout.
361 if (LayoutBlock* cb = block->ContainingBlockForAbsolutePosition())
362 cb->RemovePositionedObjects(this, kNewContainingBlock);
363 }
364 if (!could_contain_fixed && can_contain_fixed) {
365 // Remove our fixed positioned descendants from their current containing
366 // block.
367 // They will be inserted into our positioned objects list during layout.
368 if (LayoutBlock* cb = block->ContainingBlockForFixedPosition())
369 cb->RemovePositionedObjects(this, kNewContainingBlock);
370 }
371 }
372
373 if (Layer()) {
374 Layer()->StyleDidChange(diff, old_style);
375 if (had_layer && Layer()->IsSelfPaintingLayer() != layer_was_self_painting)
376 SetChildNeedsLayout();
377 }
378
379 if (old_style && was_horizontal_writing_mode != IsHorizontalWritingMode()) {
380 // Changing the getWritingMode() may change isOrthogonalWritingModeRoot()
381 // of children. Make sure all children are marked/unmarked as orthogonal
382 // writing-mode roots.
383 bool new_horizontal_writing_mode = IsHorizontalWritingMode();
384 for (LayoutObject* child = SlowFirstChild(); child;
385 child = child->NextSibling()) {
386 if (!child->IsBox())
387 continue;
388 if (new_horizontal_writing_mode != child->IsHorizontalWritingMode())
389 ToLayoutBox(child)->MarkOrthogonalWritingModeRoot();
390 else
391 ToLayoutBox(child)->UnmarkOrthogonalWritingModeRoot();
392 }
393 }
394
395 // The used style for body background may change due to computed style change
396 // on the document element because of change of BackgroundTransfersToView()
397 // which depends on the document element style.
398 if (IsDocumentElement()) {
399 if (HTMLBodyElement* body = GetDocument().FirstBodyElement()) {
400 if (auto* body_object = body->GetLayoutObject()) {
401 if (body_object->IsBoxModelObject()) {
402 auto* body_box_model = ToLayoutBoxModelObject(body_object);
403 bool new_body_background_transfers =
404 body_box_model->BackgroundTransfersToView(Style());
405 bool old_body_background_transfers =
406 old_style && body_box_model->BackgroundTransfersToView(old_style);
407 if (new_body_background_transfers != old_body_background_transfers &&
408 body_object->Style() && body_object->StyleRef().HasBackground())
409 body_object->SetBackgroundNeedsFullPaintInvalidation();
410 }
411 }
412 }
413 }
414
415 if (LocalFrameView* frame_view = View()->GetFrameView()) {
416 bool new_style_is_viewport_constained =
417 StyleRef().GetPosition() == EPosition::kFixed;
418 bool old_style_is_viewport_constrained =
419 old_style && old_style->GetPosition() == EPosition::kFixed;
420 bool new_style_is_sticky = StyleRef().HasStickyConstrainedPosition();
421 bool old_style_is_sticky =
422 old_style && old_style->HasStickyConstrainedPosition();
423
424 if (new_style_is_sticky != old_style_is_sticky) {
425 if (new_style_is_sticky) {
426 // During compositing inputs update we'll have the scroll ancestor
427 // without having to walk up the tree and can compute the sticky
428 // position constraints then.
429 if (Layer())
430 Layer()->SetNeedsCompositingInputsUpdate();
431
432 // TODO(pdr): When CompositeAfterPaint is enabled, we will need to
433 // invalidate the scroll paint property subtree for this so main thread
434 // scroll reasons are recomputed.
435 } else {
436 // This may get re-added to viewport constrained objects if the object
437 // went from sticky to fixed.
438 frame_view->RemoveViewportConstrainedObject(*this);
439
440 // Remove sticky constraints for this layer.
441 if (Layer()) {
442 if (const PaintLayer* ancestor_overflow_layer =
443 Layer()->AncestorOverflowLayer()) {
444 if (PaintLayerScrollableArea* scrollable_area =
445 ancestor_overflow_layer->GetScrollableArea())
446 scrollable_area->InvalidateStickyConstraintsFor(Layer());
447 }
448 }
449
450 // TODO(pdr): When CompositeAfterPaint is enabled, we will need to
451 // invalidate the scroll paint property subtree for this so main thread
452 // scroll reasons are recomputed.
453 }
454 }
455
456 if (new_style_is_viewport_constained != old_style_is_viewport_constrained) {
457 if (new_style_is_viewport_constained && Layer())
458 frame_view->AddViewportConstrainedObject(*this);
459 else
460 frame_view->RemoveViewportConstrainedObject(*this);
461 }
462 }
463
464 if (old_style &&
465 old_style->BackfaceVisibility() != StyleRef().BackfaceVisibility()) {
466 SetNeedsPaintPropertyUpdate();
467 }
468
469 if (old_style && HasLayer() && !Layer()->SelfNeedsRepaint() &&
470 diff.TransformChanged() &&
471 (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
472 !Layer()->HasStyleDeterminedDirectCompositingReasons())) {
473 // PaintLayerPainter::PaintLayerWithAdjustedRoot skips painting of a layer
474 // whose transform is not invertible, so we need to repaint the layer when
475 // invertible status changes.
476 TransformationMatrix old_transform;
477 TransformationMatrix new_transform;
478 old_style->ApplyTransform(
479 old_transform, LayoutSize(), ComputedStyle::kExcludeTransformOrigin,
480 ComputedStyle::kExcludeMotionPath,
481 ComputedStyle::kIncludeIndependentTransformProperties);
482 StyleRef().ApplyTransform(
483 new_transform, LayoutSize(), ComputedStyle::kExcludeTransformOrigin,
484 ComputedStyle::kExcludeMotionPath,
485 ComputedStyle::kIncludeIndependentTransformProperties);
486 if (old_transform.IsInvertible() != new_transform.IsInvertible())
487 Layer()->SetNeedsRepaint();
488 }
489
490 // We can't squash across a layout containment boundary. So, if the
491 // containment changes, we need to update the compositing inputs.
492 if (old_style &&
493 ShouldApplyLayoutContainment(*old_style) !=
494 ShouldApplyLayoutContainment() &&
495 Layer()) {
496 Layer()->SetNeedsCompositingInputsUpdate();
497 }
498 }
499
InvalidateStickyConstraints()500 void LayoutBoxModelObject::InvalidateStickyConstraints() {
501 PaintLayer* enclosing = EnclosingLayer();
502
503 if (PaintLayerScrollableArea* scrollable_area =
504 enclosing->GetScrollableArea()) {
505 scrollable_area->InvalidateAllStickyConstraints();
506 // If this object doesn't have a layer and its enclosing layer is a scroller
507 // then we don't need to invalidate the sticky constraints on the ancestor
508 // scroller because the enclosing scroller won't have changed size.
509 if (!Layer())
510 return;
511 }
512
513 // This intentionally uses the stale ancestor overflow layer compositing input
514 // as if we have saved constraints for this layer they were saved in the
515 // previous frame.
516 if (const PaintLayer* ancestor_overflow_layer =
517 enclosing->AncestorOverflowLayer()) {
518 if (PaintLayerScrollableArea* ancestor_scrollable_area =
519 ancestor_overflow_layer->GetScrollableArea())
520 ancestor_scrollable_area->InvalidateAllStickyConstraints();
521 }
522 }
523
CreateLayerAfterStyleChange()524 void LayoutBoxModelObject::CreateLayerAfterStyleChange() {
525 DCHECK(!HasLayer() && !Layer());
526 GetMutableForPainting().FirstFragment().SetLayer(
527 std::make_unique<PaintLayer>(*this));
528 SetHasLayer(true);
529 Layer()->InsertOnlyThisLayerAfterStyleChange();
530 // Creating a layer may affect existence of the LocalBorderBoxProperties, so
531 // we need to ensure that we update paint properties.
532 SetNeedsPaintPropertyUpdate();
533 if (GetScrollableArea())
534 GetScrollableArea()->InvalidateScrollTimeline();
535 }
536
DestroyLayer()537 void LayoutBoxModelObject::DestroyLayer() {
538 DCHECK(HasLayer() && Layer());
539 SetHasLayer(false);
540 GetMutableForPainting().FirstFragment().SetLayer(nullptr);
541 // Removing a layer may affect existence of the LocalBorderBoxProperties, so
542 // we need to ensure that we update paint properties.
543 SetNeedsPaintPropertyUpdate();
544 SetBackgroundPaintLocation(kBackgroundPaintInGraphicsLayer);
545 }
546
HasSelfPaintingLayer() const547 bool LayoutBoxModelObject::HasSelfPaintingLayer() const {
548 return Layer() && Layer()->IsSelfPaintingLayer();
549 }
550
GetScrollableArea() const551 PaintLayerScrollableArea* LayoutBoxModelObject::GetScrollableArea() const {
552 return Layer() ? Layer()->GetScrollableArea() : nullptr;
553 }
554
AddOutlineRectsForNormalChildren(Vector<PhysicalRect> & rects,const PhysicalOffset & additional_offset,NGOutlineType include_block_overflows) const555 void LayoutBoxModelObject::AddOutlineRectsForNormalChildren(
556 Vector<PhysicalRect>& rects,
557 const PhysicalOffset& additional_offset,
558 NGOutlineType include_block_overflows) const {
559 for (LayoutObject* child = SlowFirstChild(); child;
560 child = child->NextSibling()) {
561 // Outlines of out-of-flow positioned descendants are handled in
562 // LayoutBlock::AddOutlineRects().
563 if (child->IsOutOfFlowPositioned())
564 continue;
565
566 // Outline of an element continuation or anonymous block continuation is
567 // added when we iterate the continuation chain.
568 // See LayoutBlock::AddOutlineRects() and LayoutInline::AddOutlineRects().
569 auto* child_block_flow = DynamicTo<LayoutBlockFlow>(child);
570 if (child->IsElementContinuation() ||
571 (child_block_flow && child_block_flow->IsAnonymousBlockContinuation()))
572 continue;
573
574 AddOutlineRectsForDescendant(*child, rects, additional_offset,
575 include_block_overflows);
576 }
577 }
578
AddOutlineRectsForDescendant(const LayoutObject & descendant,Vector<PhysicalRect> & rects,const PhysicalOffset & additional_offset,NGOutlineType include_block_overflows) const579 void LayoutBoxModelObject::AddOutlineRectsForDescendant(
580 const LayoutObject& descendant,
581 Vector<PhysicalRect>& rects,
582 const PhysicalOffset& additional_offset,
583 NGOutlineType include_block_overflows) const {
584 if (descendant.IsText() || descendant.IsListMarker())
585 return;
586
587 if (descendant.HasLayer()) {
588 Vector<PhysicalRect> layer_outline_rects;
589 descendant.AddOutlineRects(layer_outline_rects, PhysicalOffset(),
590 include_block_overflows);
591 descendant.LocalToAncestorRects(layer_outline_rects, this, PhysicalOffset(),
592 additional_offset);
593 rects.AppendVector(layer_outline_rects);
594 return;
595 }
596
597 if (descendant.IsBox()) {
598 descendant.AddOutlineRects(
599 rects, additional_offset + ToLayoutBox(descendant).PhysicalLocation(),
600 include_block_overflows);
601 return;
602 }
603
604 if (descendant.IsLayoutInline()) {
605 // As an optimization, an ancestor has added rects for its line boxes
606 // covering descendants' line boxes, so descendants don't need to add line
607 // boxes again. For example, if the parent is a LayoutBlock, it adds rects
608 // for its RootOutlineBoxes which cover the line boxes of this LayoutInline.
609 // So the LayoutInline needs to add rects for children and continuations
610 // only.
611 ToLayoutInline(descendant)
612 .AddOutlineRectsForChildrenAndContinuations(rects, additional_offset,
613 include_block_overflows);
614 return;
615 }
616
617 descendant.AddOutlineRects(rects, additional_offset, include_block_overflows);
618 }
619
AbsoluteQuadsForSelf(Vector<FloatQuad> & quads,MapCoordinatesFlags mode) const620 void LayoutBoxModelObject::AbsoluteQuadsForSelf(
621 Vector<FloatQuad>& quads,
622 MapCoordinatesFlags mode) const {
623 NOTREACHED();
624 }
625
AbsoluteQuads(Vector<FloatQuad> & quads,MapCoordinatesFlags mode) const626 void LayoutBoxModelObject::AbsoluteQuads(Vector<FloatQuad>& quads,
627 MapCoordinatesFlags mode) const {
628 AbsoluteQuadsForSelf(quads, mode);
629
630 // Iterate over continuations, avoiding recursion in case there are
631 // many of them. See crbug.com/653767.
632 for (const LayoutBoxModelObject* continuation_object = Continuation();
633 continuation_object;
634 continuation_object = continuation_object->Continuation()) {
635 auto* continuation_block_flow =
636 DynamicTo<LayoutBlockFlow>(continuation_object);
637 DCHECK(continuation_object->IsLayoutInline() ||
638 (continuation_block_flow &&
639 continuation_block_flow->IsAnonymousBlockContinuation()));
640 continuation_object->AbsoluteQuadsForSelf(quads, mode);
641 }
642 }
643
UpdateFromStyle()644 void LayoutBoxModelObject::UpdateFromStyle() {
645 const ComputedStyle& style_to_use = StyleRef();
646 SetHasBoxDecorationBackground(style_to_use.HasBoxDecorationBackground());
647 SetInline(style_to_use.IsDisplayInlineType());
648 SetPositionState(style_to_use.GetPosition());
649 SetHorizontalWritingMode(style_to_use.IsHorizontalWritingMode());
650 SetCanContainFixedPositionObjects(ComputeIsFixedContainer(&style_to_use));
651 }
652
ContainingBlockForAutoHeightDetection(const Length & logical_height) const653 LayoutBlock* LayoutBoxModelObject::ContainingBlockForAutoHeightDetection(
654 const Length& logical_height) const {
655 // For percentage heights: The percentage is calculated with respect to the
656 // height of the generated box's containing block. If the height of the
657 // containing block is not specified explicitly (i.e., it depends on content
658 // height), and this element is not absolutely positioned, the used height is
659 // calculated as if 'auto' was specified.
660 if (!logical_height.IsPercentOrCalc() || IsOutOfFlowPositioned())
661 return nullptr;
662
663 // Anonymous block boxes are ignored when resolving percentage values that
664 // would refer to it: the closest non-anonymous ancestor box is used instead.
665 LayoutBlock* cb = ContainingBlock();
666 while (cb->IsAnonymous())
667 cb = cb->ContainingBlock();
668
669 // Matching LayoutBox::percentageLogicalHeightIsResolvableFromBlock() by
670 // ignoring table cell's attribute value, where it says that table cells
671 // violate what the CSS spec says to do with heights. Basically we don't care
672 // if the cell specified a height or not.
673 if (cb->IsTableCell())
674 return nullptr;
675
676 // Match LayoutBox::availableLogicalHeightUsing by special casing the layout
677 // view. The available height is taken from the frame.
678 if (IsA<LayoutView>(cb))
679 return nullptr;
680
681 if (IsOutOfFlowPositionedWithImplicitHeight(cb))
682 return nullptr;
683
684 return cb;
685 }
686
HasAutoHeightOrContainingBlockWithAutoHeight(RegisterPercentageDescendant register_percentage_descendant) const687 bool LayoutBoxModelObject::HasAutoHeightOrContainingBlockWithAutoHeight(
688 RegisterPercentageDescendant register_percentage_descendant) const {
689 // TODO(rego): Check if we can somehow reuse LayoutBlock::
690 // availableLogicalHeightForPercentageComputation() (see crbug.com/635655).
691 const LayoutBox* this_box = IsBox() ? ToLayoutBox(this) : nullptr;
692 const Length& logical_height_length = StyleRef().LogicalHeight();
693 LayoutBlock* cb =
694 ContainingBlockForAutoHeightDetection(logical_height_length);
695 if (register_percentage_descendant == kRegisterPercentageDescendant &&
696 logical_height_length.IsPercentOrCalc() && cb && IsBox()) {
697 cb->AddPercentHeightDescendant(const_cast<LayoutBox*>(ToLayoutBox(this)));
698 }
699 if (this_box && this_box->IsFlexItemIncludingNG()) {
700 if (this_box->IsFlexItem()) {
701 const LayoutFlexibleBox& flex_box = ToLayoutFlexibleBox(*Parent());
702 if (flex_box.UseOverrideLogicalHeightForPerentageResolution(*this_box))
703 return false;
704 } else if (this_box->GetCachedLayoutResult()) {
705 const NGConstraintSpace& space =
706 this_box->GetCachedLayoutResult()->GetConstraintSpaceForCaching();
707 if (space.IsFixedBlockSize() && !space.IsFixedBlockSizeIndefinite())
708 return false;
709 }
710 }
711 if (this_box && this_box->IsGridItem() &&
712 this_box->HasOverrideContainingBlockContentLogicalHeight())
713 return false;
714 if (this_box && this_box->IsCustomItem() &&
715 (this_box->HasOverrideContainingBlockContentLogicalHeight() ||
716 this_box->HasOverridePercentageResolutionBlockSize()))
717 return false;
718
719 if (logical_height_length.IsIntrinsicOrAuto() &&
720 !IsOutOfFlowPositionedWithImplicitHeight(this))
721 return true;
722
723 if (cb) {
724 // We need the containing block to have a definite block-size in order to
725 // resolve the block-size of the descendant, except when in quirks mode.
726 // Flexboxes follow strict behavior even in quirks mode, though.
727 if (!GetDocument().InQuirksMode() ||
728 cb->IsFlexibleBoxIncludingDeprecatedAndNG()) {
729 if (this_box &&
730 this_box->HasOverrideContainingBlockContentLogicalHeight()) {
731 return this_box->OverrideContainingBlockContentLogicalHeight() ==
732 LayoutUnit(-1);
733 }
734 return !cb->HasDefiniteLogicalHeight();
735 }
736 }
737
738 return false;
739 }
740
RelativePositionOffset() const741 PhysicalOffset LayoutBoxModelObject::RelativePositionOffset() const {
742 DCHECK(IsRelPositioned());
743 PhysicalOffset offset = AccumulateInFlowPositionOffsets();
744
745 LayoutBlock* containing_block = ContainingBlock();
746
747 // Objects that shrink to avoid floats normally use available line width when
748 // computing containing block width. However in the case of relative
749 // positioning using percentages, we can't do this. The offset should always
750 // be resolved using the available width of the containing block. Therefore we
751 // don't use containingBlockLogicalWidthForContent() here, but instead
752 // explicitly call availableWidth on our containing block.
753 // https://drafts.csswg.org/css-position-3/#rel-pos
754 // However for grid items the containing block is the grid area, so offsets
755 // should be resolved against that:
756 // https://drafts.csswg.org/css-grid/#grid-item-sizing
757 base::Optional<LayoutUnit> left;
758 base::Optional<LayoutUnit> right;
759 if (!StyleRef().Left().IsAuto() || !StyleRef().Right().IsAuto()) {
760 LayoutUnit available_width = HasOverrideContainingBlockContentWidth()
761 ? OverrideContainingBlockContentWidth()
762 : containing_block->AvailableWidth();
763 if (!StyleRef().Left().IsAuto())
764 left = ValueForLength(StyleRef().Left(), available_width);
765 if (!StyleRef().Right().IsAuto())
766 right = ValueForLength(StyleRef().Right(), available_width);
767 }
768 if (!left && !right) {
769 left = LayoutUnit();
770 right = LayoutUnit();
771 }
772 if (!left)
773 left = -right.value();
774 if (!right)
775 right = -left.value();
776 bool is_ltr = containing_block->StyleRef().IsLeftToRightDirection();
777 WritingMode writing_mode = containing_block->StyleRef().GetWritingMode();
778 switch (writing_mode) {
779 case WritingMode::kHorizontalTb:
780 if (is_ltr)
781 offset.left += left.value();
782 else
783 offset.left = -right.value();
784 break;
785 case WritingMode::kVerticalRl:
786 offset.left = -right.value();
787 break;
788 case WritingMode::kVerticalLr:
789 offset.left += left.value();
790 break;
791 // TODO(layout-dev): Sideways-lr and sideways-rl are not yet supported.
792 default:
793 break;
794 }
795
796 // If the containing block of a relatively positioned element does not specify
797 // a height, a percentage top or bottom offset should be resolved as auto.
798 // An exception to this is if the containing block has the WinIE quirk where
799 // <html> and <body> assume the size of the viewport. In this case, calculate
800 // the percent offset based on this height.
801 // See <https://bugs.webkit.org/show_bug.cgi?id=26396>.
802 // Another exception is a grid item, as the containing block is the grid area:
803 // https://drafts.csswg.org/css-grid/#grid-item-sizing
804
805 base::Optional<LayoutUnit> top;
806 base::Optional<LayoutUnit> bottom;
807 bool has_override_containing_block_content_height =
808 HasOverrideContainingBlockContentHeight();
809 if (!StyleRef().Top().IsAuto() &&
810 (!containing_block->HasAutoHeightOrContainingBlockWithAutoHeight() ||
811 !StyleRef().Top().IsPercentOrCalc() ||
812 containing_block->StretchesToViewport() ||
813 has_override_containing_block_content_height)) {
814 // TODO(rego): The computation of the available height is repeated later for
815 // "bottom". We could refactor this and move it to some common code for both
816 // ifs, however moving it outside of the ifs is not possible as it'd cause
817 // performance regressions (see crbug.com/893884).
818 top = ValueForLength(StyleRef().Top(),
819 has_override_containing_block_content_height
820 ? OverrideContainingBlockContentHeight()
821 : containing_block->AvailableHeight());
822 }
823 if (!StyleRef().Bottom().IsAuto() &&
824 (!containing_block->HasAutoHeightOrContainingBlockWithAutoHeight() ||
825 !StyleRef().Bottom().IsPercentOrCalc() ||
826 containing_block->StretchesToViewport() ||
827 has_override_containing_block_content_height)) {
828 // TODO(rego): Check comment above for "top", it applies here too.
829 bottom = ValueForLength(StyleRef().Bottom(),
830 has_override_containing_block_content_height
831 ? OverrideContainingBlockContentHeight()
832 : containing_block->AvailableHeight());
833 }
834 if (!top && !bottom) {
835 top = LayoutUnit();
836 bottom = LayoutUnit();
837 }
838 if (!top)
839 top = -bottom.value();
840 if (!bottom)
841 bottom = -top.value();
842 switch (writing_mode) {
843 case WritingMode::kHorizontalTb:
844 offset.top += top.value();
845 break;
846 case WritingMode::kVerticalRl:
847 if (is_ltr)
848 offset.top += top.value();
849 else
850 offset.top = -bottom.value();
851 break;
852 case WritingMode::kVerticalLr:
853 if (is_ltr)
854 offset.top += top.value();
855 else
856 offset.top = -bottom.value();
857 break;
858 // TODO(layout-dev): Sideways-lr and sideways-rl are not yet supported.
859 default:
860 break;
861 }
862 return offset;
863 }
864
UpdateStickyPositionConstraints() const865 void LayoutBoxModelObject::UpdateStickyPositionConstraints() const {
866 DCHECK(StyleRef().HasStickyConstrainedPosition());
867
868 const PhysicalSize constraining_size = ComputeStickyConstrainingRect().size;
869
870 StickyPositionScrollingConstraints constraints;
871 PhysicalOffset skipped_containers_offset;
872 LayoutBlock* containing_block = ContainingBlock();
873 // The location container for boxes is not always the containing block.
874 LayoutObject* location_container =
875 IsLayoutInline() ? Container() : ToLayoutBox(this)->LocationContainer();
876 // Skip anonymous containing blocks.
877 while (containing_block->IsAnonymous()) {
878 containing_block = containing_block->ContainingBlock();
879 }
880
881 // The sticky position constraint rects should be independent of the current
882 // scroll position therefore we should ignore the scroll offset when
883 // calculating the quad.
884 // TODO(crbug.com/966131): Is kIgnoreTransforms correct here?
885 MapCoordinatesFlags flags =
886 kIgnoreTransforms | kIgnoreScrollOffset | kIgnoreStickyOffset;
887 skipped_containers_offset = location_container->LocalToAncestorPoint(
888 PhysicalOffset(), containing_block, flags);
889 LayoutBox& scroll_ancestor =
890 ToLayoutBox(Layer()->AncestorOverflowLayer()->GetLayoutObject());
891
892 LayoutUnit max_container_width =
893 IsA<LayoutView>(containing_block)
894 ? containing_block->LogicalWidth()
895 : containing_block->ContainingBlockLogicalWidthForContent();
896 // Sticky positioned element ignore any override logical width on the
897 // containing block, as they don't call containingBlockLogicalWidthForContent.
898 // It's unclear whether this is totally fine.
899 // Compute the container-relative area within which the sticky element is
900 // allowed to move.
901 LayoutUnit max_width = containing_block->AvailableLogicalWidth();
902
903 // Map the containing block to the inner corner of the scroll ancestor without
904 // transforms.
905 PhysicalRect scroll_container_relative_padding_box_rect(
906 containing_block->LayoutOverflowRect());
907 if (containing_block != &scroll_ancestor) {
908 PhysicalRect local_rect = containing_block->PhysicalPaddingBoxRect();
909 scroll_container_relative_padding_box_rect =
910 containing_block->LocalToAncestorRect(local_rect, &scroll_ancestor,
911 flags);
912 }
913
914 // Remove top-left border offset from overflow scroller.
915 PhysicalOffset scroll_container_border_offset(scroll_ancestor.BorderLeft(),
916 scroll_ancestor.BorderTop());
917 scroll_container_relative_padding_box_rect.Move(
918 -scroll_container_border_offset);
919
920 PhysicalRect scroll_container_relative_containing_block_rect(
921 scroll_container_relative_padding_box_rect);
922
923 // This is removing the padding of the containing block's overflow rect to get
924 // the flow box rectangle and removing the margin of the sticky element to
925 // ensure that space between the sticky element and its containing flow box.
926 // It is an open issue whether the margin should collapse.
927 // See https://www.w3.org/TR/css-position-3/#sticky-pos
928 scroll_container_relative_containing_block_rect.ContractEdges(
929 MinimumValueForLength(containing_block->StyleRef().PaddingTop(),
930 max_container_width) +
931 MinimumValueForLength(StyleRef().MarginTop(), max_width),
932 MinimumValueForLength(containing_block->StyleRef().PaddingRight(),
933 max_container_width) +
934 MinimumValueForLength(StyleRef().MarginRight(), max_width),
935 MinimumValueForLength(containing_block->StyleRef().PaddingBottom(),
936 max_container_width) +
937 MinimumValueForLength(StyleRef().MarginBottom(), max_width),
938 MinimumValueForLength(containing_block->StyleRef().PaddingLeft(),
939 max_container_width) +
940 MinimumValueForLength(StyleRef().MarginLeft(), max_width));
941
942 constraints.scroll_container_relative_containing_block_rect =
943 scroll_container_relative_containing_block_rect;
944
945 PhysicalRect sticky_box_rect;
946 if (IsLayoutInline()) {
947 sticky_box_rect = ToLayoutInline(this)->PhysicalLinesBoundingBox();
948 } else {
949 sticky_box_rect =
950 containing_block->FlipForWritingMode(ToLayoutBox(this)->FrameRect());
951 }
952 PhysicalOffset sticky_location =
953 sticky_box_rect.offset + skipped_containers_offset;
954
955 // The scrollContainerRelativePaddingBoxRect's position is the padding box so
956 // we need to remove the border when finding the position of the sticky box
957 // within the scroll ancestor if the container is not our scroll ancestor. If
958 // the container is our scroll ancestor, we also need to remove the border
959 // box because we want the position from within the scroller border.
960 PhysicalOffset container_border_offset(containing_block->BorderLeft(),
961 containing_block->BorderTop());
962 sticky_location -= container_border_offset;
963 constraints.scroll_container_relative_sticky_box_rect = PhysicalRect(
964 scroll_container_relative_padding_box_rect.offset + sticky_location,
965 sticky_box_rect.size);
966
967 // To correctly compute the offsets, the constraints need to know about any
968 // nested position:sticky elements between themselves and their
969 // containingBlock, and between the containingBlock and their scrollAncestor.
970 //
971 // The respective search ranges are [container, containingBlock) and
972 // [containingBlock, scrollAncestor).
973 constraints.nearest_sticky_layer_shifting_sticky_box =
974 FindFirstStickyBetween(location_container, containing_block);
975 // We cannot use |scrollAncestor| here as it disregards the root
976 // ancestorOverflowLayer(), which we should include.
977 constraints.nearest_sticky_layer_shifting_containing_block =
978 FindFirstStickyBetween(
979 containing_block,
980 &Layer()->AncestorOverflowLayer()->GetLayoutObject());
981
982 // We skip the right or top sticky offset if there is not enough space to
983 // honor both the left/right or top/bottom offsets.
984 LayoutUnit horizontal_offsets =
985 MinimumValueForLength(StyleRef().Right(), constraining_size.width) +
986 MinimumValueForLength(StyleRef().Left(), constraining_size.width);
987 bool skip_right = false;
988 bool skip_left = false;
989 if (!StyleRef().Left().IsAuto() && !StyleRef().Right().IsAuto()) {
990 if (horizontal_offsets >
991 scroll_container_relative_containing_block_rect.Width() ||
992 horizontal_offsets + sticky_box_rect.Width() >
993 constraining_size.width) {
994 skip_right = StyleRef().IsLeftToRightDirection();
995 skip_left = !skip_right;
996 }
997 }
998
999 if (!StyleRef().Left().IsAuto() && !skip_left) {
1000 constraints.left_offset =
1001 MinimumValueForLength(StyleRef().Left(), constraining_size.width);
1002 constraints.is_anchored_left = true;
1003 }
1004
1005 if (!StyleRef().Right().IsAuto() && !skip_right) {
1006 constraints.right_offset =
1007 MinimumValueForLength(StyleRef().Right(), constraining_size.width);
1008 constraints.is_anchored_right = true;
1009 }
1010
1011 bool skip_bottom = false;
1012 // TODO(flackr): Exclude top or bottom edge offset depending on the writing
1013 // mode when related sections are fixed in spec.
1014 // See http://lists.w3.org/Archives/Public/www-style/2014May/0286.html
1015 LayoutUnit vertical_offsets =
1016 MinimumValueForLength(StyleRef().Top(), constraining_size.height) +
1017 MinimumValueForLength(StyleRef().Bottom(), constraining_size.height);
1018 if (!StyleRef().Top().IsAuto() && !StyleRef().Bottom().IsAuto()) {
1019 if (vertical_offsets >
1020 scroll_container_relative_containing_block_rect.Height() ||
1021 vertical_offsets + sticky_box_rect.Height() >
1022 constraining_size.height) {
1023 skip_bottom = true;
1024 }
1025 }
1026
1027 if (!StyleRef().Top().IsAuto()) {
1028 constraints.top_offset =
1029 MinimumValueForLength(StyleRef().Top(), constraining_size.height);
1030 constraints.is_anchored_top = true;
1031 }
1032
1033 if (!StyleRef().Bottom().IsAuto() && !skip_bottom) {
1034 constraints.bottom_offset =
1035 MinimumValueForLength(StyleRef().Bottom(), constraining_size.height);
1036 constraints.is_anchored_bottom = true;
1037 }
1038 PaintLayerScrollableArea* scrollable_area =
1039 Layer()->AncestorOverflowLayer()->GetScrollableArea();
1040 scrollable_area->GetStickyConstraintsMap().Set(Layer(), constraints);
1041 }
1042
IsSlowRepaintConstrainedObject() const1043 bool LayoutBoxModelObject::IsSlowRepaintConstrainedObject() const {
1044 if (!HasLayer() || (StyleRef().GetPosition() != EPosition::kFixed &&
1045 StyleRef().GetPosition() != EPosition::kSticky)) {
1046 return false;
1047 }
1048
1049 PaintLayer* layer = Layer();
1050
1051 // Whether the Layer sticks to the viewport is a tree-depenent
1052 // property and our viewportConstrainedObjects collection is maintained
1053 // with only LayoutObject-level information.
1054 if (!layer->FixedToViewport() && !layer->SticksToScroller())
1055 return false;
1056
1057 // If the whole subtree is invisible, there's no reason to scroll on
1058 // the main thread because we don't need to generate invalidations
1059 // for invisible content.
1060 if (layer->SubtreeIsInvisible())
1061 return false;
1062
1063 // We're only smart enough to scroll viewport-constrainted objects
1064 // in the compositor if they have their own backing or they paint
1065 // into a grouped back (which necessarily all have the same viewport
1066 // constraints).
1067 return (layer->GetCompositingState() == kNotComposited);
1068 }
1069
ComputeStickyConstrainingRect() const1070 PhysicalRect LayoutBoxModelObject::ComputeStickyConstrainingRect() const {
1071 LayoutBox* enclosing_clipping_box =
1072 Layer()->AncestorOverflowLayer()->GetLayoutBox();
1073 DCHECK(enclosing_clipping_box);
1074 PhysicalRect constraining_rect;
1075 constraining_rect =
1076 PhysicalRect(enclosing_clipping_box->OverflowClipRect(LayoutPoint()));
1077 constraining_rect.Move(
1078 PhysicalOffset(-enclosing_clipping_box->BorderLeft() +
1079 enclosing_clipping_box->PaddingLeft(),
1080 -enclosing_clipping_box->BorderTop() +
1081 enclosing_clipping_box->PaddingTop()));
1082 constraining_rect.ContractEdges(LayoutUnit(),
1083 enclosing_clipping_box->PaddingLeft() +
1084 enclosing_clipping_box->PaddingRight(),
1085 enclosing_clipping_box->PaddingTop() +
1086 enclosing_clipping_box->PaddingBottom(),
1087 LayoutUnit());
1088 return constraining_rect;
1089 }
1090
StickyPositionOffset() const1091 PhysicalOffset LayoutBoxModelObject::StickyPositionOffset() const {
1092 // TODO(chrishtr): StickyPositionOffset depends on compositing at present,
1093 // but there are callsites within Layout for it.
1094
1095 const PaintLayer* ancestor_overflow_layer = Layer()->AncestorOverflowLayer();
1096 // TODO: Force compositing input update if we ask for offset before
1097 // compositing inputs have been computed?
1098 if (!ancestor_overflow_layer || !ancestor_overflow_layer->GetScrollableArea())
1099 return PhysicalOffset();
1100
1101 StickyConstraintsMap& constraints_map =
1102 ancestor_overflow_layer->GetScrollableArea()->GetStickyConstraintsMap();
1103 auto it = constraints_map.find(Layer());
1104 if (it == constraints_map.end())
1105 return PhysicalOffset();
1106 StickyPositionScrollingConstraints* constraints = &it->value;
1107
1108 // The sticky offset is physical, so we can just return the delta computed in
1109 // absolute coords (though it may be wrong with transforms).
1110 PhysicalRect constraining_rect = ComputeStickyConstrainingRect();
1111 constraining_rect.Move(PhysicalOffset::FromFloatPointRound(
1112 ancestor_overflow_layer->GetScrollableArea()->ScrollPosition()));
1113 return constraints->ComputeStickyOffset(constraining_rect, constraints_map);
1114 }
1115
AdjustedPositionRelativeTo(const PhysicalOffset & start_point,const Element * offset_parent) const1116 PhysicalOffset LayoutBoxModelObject::AdjustedPositionRelativeTo(
1117 const PhysicalOffset& start_point,
1118 const Element* offset_parent) const {
1119 // If the element is the HTML body element or doesn't have a parent
1120 // return 0 and stop this algorithm.
1121 if (IsBody() || !Parent())
1122 return PhysicalOffset();
1123
1124 PhysicalOffset reference_point = start_point;
1125
1126 // If the offsetParent is null, return the distance between the canvas origin
1127 // and the left/top border edge of the element and stop this algorithm.
1128 if (!offset_parent)
1129 return reference_point;
1130
1131 if (const LayoutBoxModelObject* offset_parent_object =
1132 offset_parent->GetLayoutBoxModelObject()) {
1133 if (!IsOutOfFlowPositioned()) {
1134 if (IsInFlowPositioned())
1135 reference_point += OffsetForInFlowPosition();
1136
1137 // Note that we may fail to find |offsetParent| while walking the
1138 // container chain, if |offsetParent| is an inline split into
1139 // continuations: <body style="display:inline;" id="offsetParent">
1140 // <div id="this">
1141 // This is why we have to do a nullptr check here.
1142 for (const LayoutObject* current = Container();
1143 current && current->GetNode() != offset_parent;
1144 current = current->Container()) {
1145 // FIXME: What are we supposed to do inside SVG content?
1146 reference_point += PhysicalOffsetToBeNoop(
1147 current->ColumnOffset(reference_point.ToLayoutPoint()));
1148 if (current->IsBox() && !current->IsTableRow())
1149 reference_point += ToLayoutBox(current)->PhysicalLocation();
1150 }
1151
1152 if (offset_parent_object->IsBox() && offset_parent_object->IsBody() &&
1153 !offset_parent_object->IsPositioned()) {
1154 reference_point +=
1155 ToLayoutBox(offset_parent_object)->PhysicalLocation();
1156 }
1157 }
1158
1159 if (offset_parent_object->IsLayoutInline()) {
1160 const LayoutInline* inline_parent = ToLayoutInline(offset_parent_object);
1161
1162 if (IsBox() && IsOutOfFlowPositioned() &&
1163 inline_parent->CanContainOutOfFlowPositionedElement(
1164 StyleRef().GetPosition())) {
1165 // Offset for out of flow positioned elements with inline containers is
1166 // a special case in the CSS spec
1167 reference_point +=
1168 inline_parent->OffsetForInFlowPositionedInline(*ToLayoutBox(this));
1169 }
1170
1171 reference_point -= inline_parent->FirstLineBoxTopLeft();
1172 }
1173
1174 if (offset_parent_object->IsBox() && !offset_parent_object->IsBody()) {
1175 reference_point -=
1176 PhysicalOffset(ToLayoutBox(offset_parent_object)->BorderLeft(),
1177 ToLayoutBox(offset_parent_object)->BorderTop());
1178 }
1179 }
1180
1181 return reference_point;
1182 }
1183
OffsetForInFlowPosition() const1184 PhysicalOffset LayoutBoxModelObject::OffsetForInFlowPosition() const {
1185 if (IsRelPositioned())
1186 return RelativePositionOffset();
1187
1188 if (IsStickyPositioned())
1189 return StickyPositionOffset();
1190
1191 return PhysicalOffset();
1192 }
1193
OffsetLeft(const Element * parent) const1194 LayoutUnit LayoutBoxModelObject::OffsetLeft(const Element* parent) const {
1195 // Note that LayoutInline and LayoutBox override this to pass a different
1196 // startPoint to adjustedPositionRelativeTo.
1197 return AdjustedPositionRelativeTo(PhysicalOffset(), parent).left;
1198 }
1199
OffsetTop(const Element * parent) const1200 LayoutUnit LayoutBoxModelObject::OffsetTop(const Element* parent) const {
1201 // Note that LayoutInline and LayoutBox override this to pass a different
1202 // startPoint to adjustedPositionRelativeTo.
1203 return AdjustedPositionRelativeTo(PhysicalOffset(), parent).top;
1204 }
1205
PixelSnappedOffsetWidth(const Element * parent) const1206 int LayoutBoxModelObject::PixelSnappedOffsetWidth(const Element* parent) const {
1207 return SnapSizeToPixel(OffsetWidth(), OffsetLeft(parent));
1208 }
1209
PixelSnappedOffsetHeight(const Element * parent) const1210 int LayoutBoxModelObject::PixelSnappedOffsetHeight(
1211 const Element* parent) const {
1212 return SnapSizeToPixel(OffsetHeight(), OffsetTop(parent));
1213 }
1214
ComputedCSSPadding(const Length & padding) const1215 LayoutUnit LayoutBoxModelObject::ComputedCSSPadding(
1216 const Length& padding) const {
1217 LayoutUnit w;
1218 if (padding.IsPercentOrCalc())
1219 w = ContainingBlockLogicalWidthForContent();
1220 return MinimumValueForLength(padding, w);
1221 }
1222
ContainingBlockLogicalWidthForContent() const1223 LayoutUnit LayoutBoxModelObject::ContainingBlockLogicalWidthForContent() const {
1224 return ContainingBlock()->AvailableLogicalWidth();
1225 }
1226
Continuation() const1227 LayoutBoxModelObject* LayoutBoxModelObject::Continuation() const {
1228 return (!g_continuation_map) ? nullptr : g_continuation_map->at(this);
1229 }
1230
SetContinuation(LayoutBoxModelObject * continuation)1231 void LayoutBoxModelObject::SetContinuation(LayoutBoxModelObject* continuation) {
1232 if (continuation) {
1233 DCHECK(continuation->IsLayoutInline() || continuation->IsLayoutBlockFlow());
1234 if (!g_continuation_map)
1235 g_continuation_map = new ContinuationMap;
1236 g_continuation_map->Set(this, continuation);
1237 } else {
1238 if (g_continuation_map)
1239 g_continuation_map->erase(this);
1240 }
1241 }
1242
LocalCaretRectForEmptyElement(LayoutUnit width,LayoutUnit text_indent_offset) const1243 LayoutRect LayoutBoxModelObject::LocalCaretRectForEmptyElement(
1244 LayoutUnit width,
1245 LayoutUnit text_indent_offset) const {
1246 DCHECK(!SlowFirstChild() || SlowFirstChild()->IsPseudoElement());
1247
1248 // FIXME: This does not take into account either :first-line or :first-letter
1249 // However, as soon as some content is entered, the line boxes will be
1250 // constructed and this kludge is not called any more. So only the caret size
1251 // of an empty :first-line'd block is wrong. I think we can live with that.
1252 const ComputedStyle& current_style = FirstLineStyleRef();
1253
1254 enum CaretAlignment { kAlignLeft, kAlignRight, kAlignCenter };
1255
1256 CaretAlignment alignment = kAlignLeft;
1257
1258 switch (current_style.GetTextAlign()) {
1259 case ETextAlign::kLeft:
1260 case ETextAlign::kWebkitLeft:
1261 break;
1262 case ETextAlign::kCenter:
1263 case ETextAlign::kWebkitCenter:
1264 alignment = kAlignCenter;
1265 break;
1266 case ETextAlign::kRight:
1267 case ETextAlign::kWebkitRight:
1268 alignment = kAlignRight;
1269 break;
1270 case ETextAlign::kJustify:
1271 case ETextAlign::kStart:
1272 if (!current_style.IsLeftToRightDirection())
1273 alignment = kAlignRight;
1274 break;
1275 case ETextAlign::kEnd:
1276 if (current_style.IsLeftToRightDirection())
1277 alignment = kAlignRight;
1278 break;
1279 }
1280
1281 LayoutUnit x = BorderLeft() + PaddingLeft();
1282 LayoutUnit max_x = width - BorderRight() - PaddingRight();
1283 LayoutUnit caret_width = GetFrameView()->CaretWidth();
1284
1285 switch (alignment) {
1286 case kAlignLeft:
1287 if (current_style.IsLeftToRightDirection())
1288 x += text_indent_offset;
1289 break;
1290 case kAlignCenter:
1291 x = (x + max_x) / 2;
1292 if (current_style.IsLeftToRightDirection())
1293 x += text_indent_offset / 2;
1294 else
1295 x -= text_indent_offset / 2;
1296 break;
1297 case kAlignRight:
1298 x = max_x - caret_width;
1299 if (!current_style.IsLeftToRightDirection())
1300 x -= text_indent_offset;
1301 break;
1302 }
1303 x = std::min(x, (max_x - caret_width).ClampNegativeToZero());
1304
1305 const Font& font = StyleRef().GetFont();
1306 const SimpleFontData* font_data = font.PrimaryFont();
1307 LayoutUnit height;
1308 // crbug.com/595692 This check should not be needed but sometimes
1309 // primaryFont is null.
1310 if (font_data)
1311 height = LayoutUnit(font_data->GetFontMetrics().Height());
1312 LayoutUnit vertical_space =
1313 LineHeight(true,
1314 current_style.IsHorizontalWritingMode() ? kHorizontalLine
1315 : kVerticalLine,
1316 kPositionOfInteriorLineBoxes) -
1317 height;
1318 LayoutUnit y = PaddingTop() + BorderTop() + (vertical_space / 2);
1319 return current_style.IsHorizontalWritingMode()
1320 ? LayoutRect(x, y, caret_width, height)
1321 : LayoutRect(y, x, height, caret_width);
1322 }
1323
PushMappingToContainer(const LayoutBoxModelObject * ancestor_to_stop_at,LayoutGeometryMap & geometry_map) const1324 const LayoutObject* LayoutBoxModelObject::PushMappingToContainer(
1325 const LayoutBoxModelObject* ancestor_to_stop_at,
1326 LayoutGeometryMap& geometry_map) const {
1327 DCHECK_NE(ancestor_to_stop_at, this);
1328
1329 AncestorSkipInfo skip_info(ancestor_to_stop_at);
1330 LayoutObject* container = Container(&skip_info);
1331 if (!container)
1332 return nullptr;
1333
1334 bool is_inline = IsLayoutInline();
1335 bool is_fixed_pos =
1336 !is_inline && StyleRef().GetPosition() == EPosition::kFixed;
1337 bool contains_fixed_position = CanContainFixedPositionObjects();
1338
1339 TransformationMatrix adjustment_for_skipped_ancestor;
1340 bool adjustment_for_skipped_ancestor_is_translate_2d = true;
1341 if (skip_info.AncestorSkipped()) {
1342 // There can't be a transform between container and ancestor_to_stop_at,
1343 // because transforms create containers, so it should be safe to just
1344 // subtract the delta between the container and ancestor_to_stop_at.
1345 PhysicalOffset ancestor_offset =
1346 ancestor_to_stop_at->OffsetFromAncestor(container);
1347 adjustment_for_skipped_ancestor.Translate(-ancestor_offset.left.ToFloat(),
1348 -ancestor_offset.top.ToFloat());
1349 }
1350
1351 PhysicalOffset container_offset = OffsetFromContainer(container);
1352 bool offset_depends_on_point;
1353 if (IsLayoutFlowThread()) {
1354 container_offset += PhysicalOffsetToBeNoop(ColumnOffset(LayoutPoint()));
1355 offset_depends_on_point = true;
1356 } else {
1357 offset_depends_on_point =
1358 container->StyleRef().IsFlippedBlocksWritingMode() &&
1359 container->IsBox();
1360 }
1361
1362 bool preserve3d =
1363 container->StyleRef().Preserves3D() || StyleRef().Preserves3D();
1364 GeometryInfoFlags flags = 0;
1365 if (preserve3d)
1366 flags |= kAccumulatingTransform;
1367 if (offset_depends_on_point)
1368 flags |= kIsNonUniform;
1369 if (is_fixed_pos)
1370 flags |= kIsFixedPosition;
1371 if (contains_fixed_position)
1372 flags |= kContainsFixedPosition;
1373 if (ShouldUseTransformFromContainer(container)) {
1374 TransformationMatrix t;
1375 GetTransformFromContainer(container, container_offset, t);
1376 adjustment_for_skipped_ancestor.Multiply(t);
1377 geometry_map.Push(this, adjustment_for_skipped_ancestor, flags,
1378 PhysicalOffset());
1379 } else if (adjustment_for_skipped_ancestor_is_translate_2d) {
1380 container_offset += PhysicalOffset::FromFloatSizeRound(
1381 adjustment_for_skipped_ancestor.To2DTranslation());
1382 geometry_map.Push(this, container_offset, flags, PhysicalOffset());
1383 } else {
1384 adjustment_for_skipped_ancestor.Translate(container_offset.left,
1385 container_offset.top);
1386 geometry_map.Push(this, adjustment_for_skipped_ancestor, flags,
1387 PhysicalOffset());
1388 }
1389
1390 return skip_info.AncestorSkipped() ? ancestor_to_stop_at : container;
1391 }
1392
MoveChildTo(LayoutBoxModelObject * to_box_model_object,LayoutObject * child,LayoutObject * before_child,bool full_remove_insert)1393 void LayoutBoxModelObject::MoveChildTo(
1394 LayoutBoxModelObject* to_box_model_object,
1395 LayoutObject* child,
1396 LayoutObject* before_child,
1397 bool full_remove_insert) {
1398 // We assume that callers have cleared their positioned objects list for child
1399 // moves (!fullRemoveInsert) so the positioned layoutObject maps don't become
1400 // stale. It would be too slow to do the map lookup on each call.
1401 DCHECK(!full_remove_insert || !IsLayoutBlock() ||
1402 !To<LayoutBlock>(this)->HasPositionedObjects());
1403
1404 DCHECK_EQ(this, child->Parent());
1405 DCHECK(!before_child || to_box_model_object == before_child->Parent());
1406
1407 // If a child is moving from a block-flow to an inline-flow parent then any
1408 // floats currently intruding into the child can no longer do so. This can
1409 // happen if a block becomes floating or out-of-flow and is moved to an
1410 // anonymous block. Remove all floats from their float-lists immediately as
1411 // markAllDescendantsWithFloatsForLayout won't attempt to remove floats from
1412 // parents that have inline-flow if we try later.
1413 auto* child_block_flow = DynamicTo<LayoutBlockFlow>(child);
1414 if (child_block_flow && to_box_model_object->ChildrenInline() &&
1415 !ChildrenInline()) {
1416 child_block_flow->RemoveFloatingObjectsFromDescendants();
1417 DCHECK(!child_block_flow->ContainsFloats());
1418 }
1419
1420 if (full_remove_insert && IsLayoutBlock() && child->IsBox())
1421 ToLayoutBox(child)->RemoveFromPercentHeightContainer();
1422
1423 if (full_remove_insert && (to_box_model_object->IsLayoutBlock() ||
1424 to_box_model_object->IsLayoutInline())) {
1425 // Takes care of adding the new child correctly if toBlock and fromBlock
1426 // have different kind of children (block vs inline).
1427 to_box_model_object->AddChild(
1428 VirtualChildren()->RemoveChildNode(this, child), before_child);
1429 } else {
1430 to_box_model_object->VirtualChildren()->InsertChildNode(
1431 to_box_model_object,
1432 VirtualChildren()->RemoveChildNode(this, child, full_remove_insert),
1433 before_child, full_remove_insert);
1434 }
1435 }
1436
MoveChildrenTo(LayoutBoxModelObject * to_box_model_object,LayoutObject * start_child,LayoutObject * end_child,LayoutObject * before_child,bool full_remove_insert)1437 void LayoutBoxModelObject::MoveChildrenTo(
1438 LayoutBoxModelObject* to_box_model_object,
1439 LayoutObject* start_child,
1440 LayoutObject* end_child,
1441 LayoutObject* before_child,
1442 bool full_remove_insert) {
1443 // This condition is rarely hit since this function is usually called on
1444 // anonymous blocks which can no longer carry positioned objects (see r120761)
1445 // or when fullRemoveInsert is false.
1446 auto* block = DynamicTo<LayoutBlock>(this);
1447 if (full_remove_insert && block) {
1448 block->RemovePositionedObjects(nullptr);
1449 block->RemoveFromPercentHeightContainer();
1450 auto* block_flow = DynamicTo<LayoutBlockFlow>(block);
1451 if (block_flow)
1452 block_flow->RemoveFloatingObjects();
1453 }
1454
1455 DCHECK(!before_child || to_box_model_object == before_child->Parent());
1456 for (LayoutObject* child = start_child; child && child != end_child;) {
1457 // Save our next sibling as moveChildTo will clear it.
1458 LayoutObject* next_sibling = child->NextSibling();
1459 MoveChildTo(to_box_model_object, child, before_child, full_remove_insert);
1460 child = next_sibling;
1461 }
1462 }
1463
BackgroundTransfersToView(const ComputedStyle * document_element_style) const1464 bool LayoutBoxModelObject::BackgroundTransfersToView(
1465 const ComputedStyle* document_element_style) const {
1466 // In our painter implementation, ViewPainter instead of the painter of the
1467 // layout object of the document element paints the view background.
1468 if (IsDocumentElement())
1469 return true;
1470
1471 // http://www.w3.org/TR/css3-background/#body-background
1472 // If the document element is <html> with no background, and a <body> child
1473 // element exists, the <body> element's background transfers to the document
1474 // element which in turn transfers to the view in our painter implementation.
1475 if (!IsBody())
1476 return false;
1477
1478 Element* document_element = GetDocument().documentElement();
1479 if (!IsA<HTMLHtmlElement>(document_element))
1480 return false;
1481
1482 if (!document_element_style)
1483 document_element_style = document_element->GetComputedStyle();
1484 DCHECK(document_element_style);
1485 if (document_element_style->HasBackground())
1486 return false;
1487
1488 if (GetNode() != GetDocument().FirstBodyElement())
1489 return false;
1490
1491 return true;
1492 }
1493
1494 } // namespace blink
1495