summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h
blob: f1ffccc5169fe3139a0827b4c0988efff0f9ad40 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_OUT_OF_FLOW_POSITIONED_NODE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_OUT_OF_FLOW_POSITIONED_NODE_H_

#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_static_position.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"

namespace blink {

// If an out-of-flow positioned element is inside a fragmentation context, it
// will be laid out once it reaches the fragmentation context root rather than
// once it reaches its containing block. A containing block holds the
// containing block information needed to place these OOF positioned nodes once
// they reach the fragmentation context root. See
// NGPhysicalOOFNodeForFragmentation/NGLogicalOOFNodeForFragmentation for more
// details.
template <typename OffsetType>
class NGContainingBlock {
  DISALLOW_NEW();

 public:
  NGContainingBlock() = default;

  NGContainingBlock(OffsetType offset,
                    OffsetType relative_offset,
                    const NGPhysicalFragment* fragment,
                    bool is_inside_column_spanner,
                    bool requires_content_before_breaking)
      : offset_(offset),
        relative_offset_(relative_offset),
        fragment_(std::move(fragment)),
        is_inside_column_spanner_(is_inside_column_spanner),
        requires_content_before_breaking_(requires_content_before_breaking) {}

  OffsetType Offset() const { return offset_; }
  void IncreaseBlockOffset(LayoutUnit block_offset) {
    offset_.block_offset += block_offset;
  }
  OffsetType RelativeOffset() const { return relative_offset_; }
  const NGPhysicalFragment* Fragment() const { return fragment_; }
  bool IsInsideColumnSpanner() const { return is_inside_column_spanner_; }

  void SetRequiresContentBeforeBreaking(bool b) {
    requires_content_before_breaking_ = b;
  }
  bool RequiresContentBeforeBreaking() const {
    return requires_content_before_breaking_;
  }

  void Trace(Visitor* visitor) const { visitor->Trace(fragment_); }

 private:
  OffsetType offset_;
  // The relative offset is stored separately to ensure that it is applied after
  // fragmentation: https://www.w3.org/TR/css-break-3/#transforms.
  OffsetType relative_offset_;
  Member<const NGPhysicalFragment> fragment_;
  // True if there is a column spanner between the containing block and the
  // multicol container (or if the containing block is a column spanner).
  bool is_inside_column_spanner_ = false;
  // True if we need to keep some child content in the current fragmentainer
  // before breaking (even that overflows the fragmentainer). See
  // NGBoxFragmentBuilder::SetRequiresContentBeforeBreaking() for more details.
  bool requires_content_before_breaking_ = false;
};

// This holds the containing block for an out-of-flow positioned element
// if the containing block is a non-atomic inline. It is the continuation
// root (i.e. the first LayoutInline in the continuation chain for the same
// node) if continuations are involved.
template <typename OffsetType>
struct NGInlineContainer {
  DISALLOW_NEW();

 public:
  Member<const LayoutInline> container;
  // Store the relative offset so that it can be applied after fragmentation,
  // if inside a fragmentation context.
  OffsetType relative_offset;

  NGInlineContainer() = default;
  NGInlineContainer(const LayoutInline* container, OffsetType relative_offset)
      : container(container), relative_offset(relative_offset) {}

  void Trace(Visitor* visitor) const { visitor->Trace(container); }
};

// If an out-of-flow positioned element is inside a nested fragmentation
// context, it will be laid out once it reaches the outermost fragmentation
// context root. A multicol with pending OOFs is the inner multicol information
// needed to perform layout on the OOF descendants once they make their way to
// the outermost context.
template <typename OffsetType>
struct NGMulticolWithPendingOOFs
    : public GarbageCollected<NGMulticolWithPendingOOFs<OffsetType>> {
 public:
  // If no fixedpos containing block was found, |multicol_offset| will be
  // relative to the outer fragmentation context root. Otherwise, it will be
  // relative to the fixedpos containing block.
  OffsetType multicol_offset;
  // If an OOF node in a nested fragmentation context has fixedpos descendants,
  // those descendants will not find their containing block if the containing
  // block lives inside an outer fragmentation context. Thus, we also need to
  // store information on the containing block and inline container for any
  // fixedpos descendants, if one exists.
  NGContainingBlock<OffsetType> fixedpos_containing_block;
  NGInlineContainer<OffsetType> fixedpos_inline_container;

  NGMulticolWithPendingOOFs() = default;
  NGMulticolWithPendingOOFs(
      OffsetType multicol_offset,
      NGContainingBlock<OffsetType> fixedpos_containing_block,
      NGInlineContainer<OffsetType> fixedpos_inline_container)
      : multicol_offset(multicol_offset),
        fixedpos_containing_block(fixedpos_containing_block),
        fixedpos_inline_container(fixedpos_inline_container) {}

  void Trace(Visitor* visitor) const {
    visitor->Trace(fixedpos_containing_block);
    visitor->Trace(fixedpos_inline_container);
  }
};

// A physical out-of-flow positioned-node is an element with the style
// "postion: absolute" or "position: fixed" which hasn't been bubbled up to its
// containing block yet, (e.g. an element with "position: relative"). As soon
// as a positioned-node reaches its containing block, it gets placed, and
// doesn't bubble further up the tree.
//
// This needs its static position [1] to be placed correctly in its containing
// block.
//
// This is struct is allowed to be stored/persisted.
//
// [1] https://www.w3.org/TR/CSS2/visudet.html#abs-non-replaced-width
struct CORE_EXPORT NGPhysicalOutOfFlowPositionedNode {
  DISALLOW_NEW();

  using HorizontalEdge = NGPhysicalStaticPosition::HorizontalEdge;
  using VerticalEdge = NGPhysicalStaticPosition::VerticalEdge;

 public:
  Member<LayoutBox> box;
  // Unpacked NGPhysicalStaticPosition.
  PhysicalOffset static_position;
  unsigned static_position_horizontal_edge : 2;
  unsigned static_position_vertical_edge : 2;
  // Whether or not this is an NGPhysicalOOFNodeForFragmentation.
  unsigned is_for_fragmentation : 1;
  NGInlineContainer<PhysicalOffset> inline_container;

  NGPhysicalOutOfFlowPositionedNode(
      NGBlockNode node,
      NGPhysicalStaticPosition static_position,
      NGInlineContainer<PhysicalOffset> inline_container =
          NGInlineContainer<PhysicalOffset>())
      : box(node.GetLayoutBox()),
        static_position(static_position.offset),
        static_position_horizontal_edge(static_position.horizontal_edge),
        static_position_vertical_edge(static_position.vertical_edge),
        is_for_fragmentation(false),
        inline_container(inline_container) {
    DCHECK(!inline_container.container ||
           inline_container.container ==
               inline_container.container->ContinuationRoot());
    DCHECK(node.IsBlock());
  }

  NGBlockNode Node() const { return NGBlockNode(box); }
  HorizontalEdge GetStaticPositionHorizontalEdge() const {
    return static_cast<HorizontalEdge>(static_position_horizontal_edge);
  }
  VerticalEdge GetStaticPositionVerticalEdge() const {
    return static_cast<VerticalEdge>(static_position_vertical_edge);
  }
  NGPhysicalStaticPosition StaticPosition() const {
    return {static_position, GetStaticPositionHorizontalEdge(),
            GetStaticPositionVerticalEdge()};
  }

  void Trace(Visitor* visitor) const;
  void TraceAfterDispatch(Visitor*) const;
};

// The logical version of above. It is used within a an algorithm pass (within
// an |NGContainerFragmentBuilder|), and its logical coordinate system is wrt.
// the container builder's writing-mode.
//
// It is *only* used within an algorithm pass, (it is temporary, and should not
// be stored/persisted).
struct CORE_EXPORT NGLogicalOutOfFlowPositionedNode {
  DISALLOW_NEW();

 public:
  Member<LayoutBox> box;
  NGLogicalStaticPosition static_position;
  NGInlineContainer<LogicalOffset> inline_container;
  // Whether or not this is an NGLogicalOOFNodeForFragmentation.
  unsigned is_for_fragmentation : 1;

  NGLogicalOutOfFlowPositionedNode(
      NGBlockNode node,
      NGLogicalStaticPosition static_position,
      NGInlineContainer<LogicalOffset> inline_container =
          NGInlineContainer<LogicalOffset>())
      : box(node.GetLayoutBox()),
        static_position(static_position),
        inline_container(inline_container),
        is_for_fragmentation(false) {
    DCHECK(!inline_container.container ||
           inline_container.container ==
               inline_container.container->ContinuationRoot());
    DCHECK(node.IsBlock());
  }

  NGBlockNode Node() const { return NGBlockNode(box); }

  void Trace(Visitor* visitor) const;
  void TraceAfterDispatch(Visitor*) const;
};

// When fragmentation comes into play, we no longer place a positioned-node as
// soon as it reaches its containing block. Instead, we continue to bubble the
// positioned node up until it reaches the fragmentation context root. There, it
// will get placed and properly fragmented.
//
// In addition to the static position, we also needs the containing block
// fragment to be placed correctly within the fragmentation context root. In
// addition, the containing block offset is needed to compute the start offset
// and the initial fragmentainer of an out-of-flow positioned-node.
//
// If an OOF node in a fragmentation context has fixedpos descendants, those
// descendants will not find their containing block if the containing block
// lives inside the fragmentation context root. Thus, we also need to store
// information on the containing block and inline container for any fixedpos
// descendants, if one exists.
//
// This is struct is allowed to be stored/persisted.
struct CORE_EXPORT NGPhysicalOOFNodeForFragmentation final
    : public NGPhysicalOutOfFlowPositionedNode {
  DISALLOW_NEW();

 public:
  NGContainingBlock<PhysicalOffset> containing_block;
  NGContainingBlock<PhysicalOffset> fixedpos_containing_block;
  NGInlineContainer<PhysicalOffset> fixedpos_inline_container;

  NGPhysicalOOFNodeForFragmentation(
      NGBlockNode node,
      NGPhysicalStaticPosition static_position,
      NGInlineContainer<PhysicalOffset> inline_container =
          NGInlineContainer<PhysicalOffset>(),
      NGContainingBlock<PhysicalOffset> containing_block =
          NGContainingBlock<PhysicalOffset>(),
      NGContainingBlock<PhysicalOffset> fixedpos_containing_block =
          NGContainingBlock<PhysicalOffset>(),
      NGInlineContainer<PhysicalOffset> fixedpos_inline_container =
          NGInlineContainer<PhysicalOffset>())
      : NGPhysicalOutOfFlowPositionedNode(node,
                                          static_position,
                                          inline_container),
        containing_block(containing_block),
        fixedpos_containing_block(fixedpos_containing_block),
        fixedpos_inline_container(fixedpos_inline_container) {
    is_for_fragmentation = true;
  }

  void TraceAfterDispatch(Visitor* visitor) const;
};

// The logical version of the above. It is used within a an algorithm pass
// (within an |NGContainerFragmentBuilder|), and its logical coordinate system
// is wrt. the container builder's writing-mode.
//
// It is *only* used within an algorithm pass, (it is temporary, and should not
// be stored/persisted).
struct CORE_EXPORT NGLogicalOOFNodeForFragmentation final
    : public NGLogicalOutOfFlowPositionedNode {
  DISALLOW_NEW();

 public:
  NGContainingBlock<LogicalOffset> containing_block;
  NGContainingBlock<LogicalOffset> fixedpos_containing_block;
  NGInlineContainer<LogicalOffset> fixedpos_inline_container;

  NGLogicalOOFNodeForFragmentation(
      NGBlockNode node,
      NGLogicalStaticPosition static_position,
      NGInlineContainer<LogicalOffset> inline_container =
          NGInlineContainer<LogicalOffset>(),
      NGContainingBlock<LogicalOffset> containing_block =
          NGContainingBlock<LogicalOffset>(),
      NGContainingBlock<LogicalOffset> fixedpos_containing_block =
          NGContainingBlock<LogicalOffset>(),
      NGInlineContainer<LogicalOffset> fixedpos_inline_container =
          NGInlineContainer<LogicalOffset>())
      : NGLogicalOutOfFlowPositionedNode(node,
                                         static_position,
                                         inline_container),
        containing_block(containing_block),
        fixedpos_containing_block(fixedpos_containing_block),
        fixedpos_inline_container(fixedpos_inline_container) {
    is_for_fragmentation = true;
  }

  explicit NGLogicalOOFNodeForFragmentation(
      const NGLogicalOutOfFlowPositionedNode& oof_node)
      : NGLogicalOutOfFlowPositionedNode(oof_node.Node(),
                                         oof_node.static_position,
                                         oof_node.inline_container) {
    is_for_fragmentation = true;
  }

  const LayoutObject* CssContainingBlock() const { return box->Container(); }

  void TraceAfterDispatch(Visitor* visitor) const;
};

template <>
struct DowncastTraits<NGLogicalOOFNodeForFragmentation> {
  static bool AllowFrom(const NGLogicalOutOfFlowPositionedNode& oof_node) {
    return oof_node.is_for_fragmentation;
  }
};

// This is a sub class of |NGPhysicalFragment::OutOfFlowData| that can store OOF
// propagation data under the NG block fragmentation context.
//
// This class is defined here instead of |NGPhysicalFragment| because types
// needed for this class requires full definition of |NGPhysicalFragment|, and
// |NGPhysicalFragment| requires full definition of this class if this is put
// into |NGPhysicalFragment|.
struct NGFragmentedOutOfFlowData final : NGPhysicalFragment::OutOfFlowData {
  using MulticolCollection =
      HeapHashMap<Member<LayoutBox>,
                  Member<NGMulticolWithPendingOOFs<PhysicalOffset>>>;

  static bool HasOutOfFlowPositionedFragmentainerDescendants(
      const NGPhysicalFragment& fragment) {
    const NGFragmentedOutOfFlowData* oof_data =
        fragment.FragmentedOutOfFlowData();
    return oof_data &&
           !oof_data->oof_positioned_fragmentainer_descendants.empty();
  }

  bool NeedsOOFPositionedInfoPropagation() const {
    return !oof_positioned_fragmentainer_descendants.empty() ||
           !multicols_with_pending_oofs.empty();
  }

  static base::span<NGPhysicalOOFNodeForFragmentation>
  OutOfFlowPositionedFragmentainerDescendants(
      const NGPhysicalFragment& fragment) {
    const NGFragmentedOutOfFlowData* oof_data =
        fragment.FragmentedOutOfFlowData();
    if (!oof_data || oof_data->oof_positioned_fragmentainer_descendants.empty())
      return base::span<NGPhysicalOOFNodeForFragmentation>();
    HeapVector<NGPhysicalOOFNodeForFragmentation>& descendants =
        const_cast<HeapVector<NGPhysicalOOFNodeForFragmentation>&>(
            oof_data->oof_positioned_fragmentainer_descendants);
    return {descendants.data(), descendants.size()};
  }

  void Trace(Visitor* visitor) const override {
    visitor->Trace(oof_positioned_fragmentainer_descendants);
    visitor->Trace(multicols_with_pending_oofs);
    NGPhysicalFragment::OutOfFlowData::Trace(visitor);
  }

  HeapVector<NGPhysicalOOFNodeForFragmentation>
      oof_positioned_fragmentainer_descendants;
  MulticolCollection multicols_with_pending_oofs;
};

inline PhysicalOffset RelativeInsetToPhysical(
    LogicalOffset relative_inset,
    WritingDirectionMode writing_direction) {
  return relative_inset.ConvertToPhysical(writing_direction, PhysicalSize(),
                                          PhysicalSize());
}

inline LogicalOffset RelativeInsetToLogical(
    PhysicalOffset relative_inset,
    WritingDirectionMode writing_direction) {
  return relative_inset.ConvertToLogical(writing_direction, PhysicalSize(),
                                         PhysicalSize());
}

}  // namespace blink

WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(
    blink::NGPhysicalOutOfFlowPositionedNode)
WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(
    blink::NGPhysicalOOFNodeForFragmentation)
WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(
    blink::NGLogicalOutOfFlowPositionedNode)
WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(
    blink::NGLogicalOOFNodeForFragmentation)

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_OUT_OF_FLOW_POSITIONED_NODE_H_