1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_EXCLUSIONS_NG_EXCLUSION_SPACE_H_
6 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_EXCLUSIONS_NG_EXCLUSION_SPACE_H_
7 
8 #include "third_party/blink/renderer/core/core_export.h"
9 #include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion.h"
10 #include "third_party/blink/renderer/core/layout/ng/exclusions/ng_layout_opportunity.h"
11 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h"
12 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_rect.h"
13 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
14 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
15 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
16 #include "third_party/blink/renderer/platform/wtf/vector.h"
17 
18 namespace blink {
19 
20 typedef Vector<NGLayoutOpportunity, 8> LayoutOpportunityVector;
21 typedef base::RefCountedData<WTF::Vector<scoped_refptr<const NGExclusion>>>
22     NGExclusionPtrArray;
23 
24 // This class is an implementation detail. For use of the exclusion space,
25 // see NGExclusionSpace below. NGExclusionSpace was designed to be cheap
26 // to construct and cheap to copy if empty.
27 class CORE_EXPORT NGExclusionSpaceInternal {
28   USING_FAST_MALLOC(NGExclusionSpaceInternal);
29 
30  public:
31   NGExclusionSpaceInternal();
32   NGExclusionSpaceInternal(const NGExclusionSpaceInternal&);
33   NGExclusionSpaceInternal(NGExclusionSpaceInternal&&) noexcept;
34   NGExclusionSpaceInternal& operator=(const NGExclusionSpaceInternal&);
35   NGExclusionSpaceInternal& operator=(NGExclusionSpaceInternal&&) noexcept;
~NGExclusionSpaceInternal()36   ~NGExclusionSpaceInternal() {}
37 
38   void Add(scoped_refptr<const NGExclusion> exclusion);
39 
FindLayoutOpportunity(const NGBfcOffset & offset,const LayoutUnit available_inline_size,const LayoutUnit minimum_inline_size)40   NGLayoutOpportunity FindLayoutOpportunity(
41       const NGBfcOffset& offset,
42       const LayoutUnit available_inline_size,
43       const LayoutUnit minimum_inline_size) const {
44     // If the area clears all floats, we can just return the layout opportunity
45     // which matches the available space.
46     if (offset.block_offset >=
47         std::max(left_clear_offset_, right_clear_offset_)) {
48       NGBfcOffset end_offset(
49           offset.line_offset + available_inline_size.ClampNegativeToZero(),
50           LayoutUnit::Max());
51       return NGLayoutOpportunity(NGBfcRect(offset, end_offset), nullptr);
52     }
53 
54     return GetDerivedGeometry(offset.block_offset)
55         .FindLayoutOpportunity(offset, available_inline_size,
56                                minimum_inline_size);
57   }
58 
AllLayoutOpportunities(const NGBfcOffset & offset,const LayoutUnit available_inline_size)59   LayoutOpportunityVector AllLayoutOpportunities(
60       const NGBfcOffset& offset,
61       const LayoutUnit available_inline_size) const {
62     // If the area clears all floats, we can just return a single layout
63     // opportunity which matches the available space.
64     if (offset.block_offset >=
65         std::max(left_clear_offset_, right_clear_offset_)) {
66       NGBfcOffset end_offset(
67           offset.line_offset + available_inline_size.ClampNegativeToZero(),
68           LayoutUnit::Max());
69       return LayoutOpportunityVector(
70           {NGLayoutOpportunity(NGBfcRect(offset, end_offset), nullptr)});
71     }
72 
73     return GetDerivedGeometry(offset.block_offset)
74         .AllLayoutOpportunities(offset, available_inline_size);
75   }
76 
ClearanceOffset(EClear clear_type)77   LayoutUnit ClearanceOffset(EClear clear_type) const {
78     switch (clear_type) {
79       case EClear::kNone:
80         return LayoutUnit::Min();
81       case EClear::kLeft:
82         return left_clear_offset_;
83       case EClear::kRight:
84         return right_clear_offset_;
85       case EClear::kBoth:
86         return std::max(left_clear_offset_, right_clear_offset_);
87       default:
88         NOTREACHED();
89         return LayoutUnit::Min();
90     }
91   }
92 
LastFloatBlockStart()93   LayoutUnit LastFloatBlockStart() const { return last_float_block_start_; }
94 
IsEmpty()95   bool IsEmpty() const { return !num_exclusions_; }
96 
97   // Pre-initializes the exclusions vector to something used in a previous
98   // layout pass, however keeps the number of exclusions as zero.
PreInitialize(const NGExclusionSpaceInternal & other)99   void PreInitialize(const NGExclusionSpaceInternal& other) {
100     DCHECK(exclusions_->data.IsEmpty());
101     DCHECK_GT(other.exclusions_->data.size(), 0u);
102 
103     exclusions_ = other.exclusions_;
104   }
105 
106   // See |NGExclusionSpace::MoveAndUpdateDerivedGeometry|.
MoveAndUpdateDerivedGeometry(const NGExclusionSpaceInternal & other)107   void MoveAndUpdateDerivedGeometry(const NGExclusionSpaceInternal& other) {
108     if (!other.derived_geometry_)
109       return;
110 
111     MoveDerivedGeometry(other);
112 
113     // Iterate through all the exclusions which were added by the layout, and
114     // update the DerivedGeometry.
115     for (wtf_size_t i = other.num_exclusions_; i < num_exclusions_; ++i) {
116       const NGExclusion& exclusion = *exclusions_->data.at(i);
117 
118       // If we come across an exclusion with shape data, we opt-out of this
119       // optimization.
120       if (!track_shape_exclusions_ && exclusion.shape_data) {
121         track_shape_exclusions_ = true;
122         derived_geometry_ = nullptr;
123         return;
124       }
125 
126       derived_geometry_->Add(exclusion);
127     }
128   }
129 
130   // See |NGExclusionSpace::MoveDerivedGeometry|.
MoveDerivedGeometry(const NGExclusionSpaceInternal & other)131   void MoveDerivedGeometry(const NGExclusionSpaceInternal& other) {
132     if (!other.derived_geometry_)
133       return;
134 
135     track_shape_exclusions_ = other.track_shape_exclusions_;
136     derived_geometry_ = std::move(other.derived_geometry_);
137     other.derived_geometry_ = nullptr;
138   }
139 
140   // See |NGExclusionSpace::MergeExclusionSpaces|.
MergeExclusionSpaces(const NGBfcDelta & offset_delta,const NGExclusionSpaceInternal & previous_output,const NGExclusionSpaceInternal * previous_input)141   void MergeExclusionSpaces(const NGBfcDelta& offset_delta,
142                             const NGExclusionSpaceInternal& previous_output,
143                             const NGExclusionSpaceInternal* previous_input) {
144     // We need to copy all the exclusions over which were added by the cached
145     // layout result.
146     for (wtf_size_t i = previous_input ? previous_input->num_exclusions_ : 0;
147          i < previous_output.num_exclusions_; ++i) {
148       Add(previous_output.exclusions_->data.at(i)->CopyWithOffset(
149           offset_delta));
150     }
151   }
152 
153   bool operator==(const NGExclusionSpaceInternal& other) const;
154   bool operator!=(const NGExclusionSpaceInternal& other) const {
155     return !(*this == other);
156   }
157 
158 #if DCHECK_IS_ON()
CheckSameForSimplifiedLayout(const NGExclusionSpaceInternal & other)159   void CheckSameForSimplifiedLayout(
160       const NGExclusionSpaceInternal& other) const {
161     DCHECK_EQ(num_exclusions_, other.num_exclusions_);
162     for (wtf_size_t i = 0; i < num_exclusions_; ++i) {
163       const auto& exclusion = *exclusions_->data.at(i);
164       const auto& other_exclusion = *other.exclusions_->data.at(i);
165       DCHECK(exclusion.rect == other_exclusion.rect);
166       DCHECK_EQ(exclusion.type, other_exclusion.type);
167       DCHECK_EQ((bool)exclusion.shape_data, (bool)other_exclusion.shape_data);
168     }
169   }
170 #endif
171 
172   // This struct represents the side of a float against the "edge" of a shelf.
173   struct NGShelfEdge {
NGShelfEdgeNGShelfEdge174     NGShelfEdge(LayoutUnit block_start, LayoutUnit block_end)
175         : block_start(block_start), block_end(block_end) {}
176 
177     LayoutUnit block_start;
178     LayoutUnit block_end;
179   };
180 
181   // The shelf is an internal data-structure representing the bottom of a
182   // float. A shelf has a inline-size which is defined by the line_left and
183   // line_right members. E.g.
184   //
185   //    0 1 2 3 4 5 6 7 8
186   // 0  +---++--+    +---+
187   //    |xxx||xx|    |xxx|
188   // 10 |xxx|X-------Xxxx|
189   //    +---+        +---+
190   // 20
191   //
192   // In the above diagram the shelf is at the block-end edge of the smallest
193   // float. It would have the internal values of:
194   // {
195   //   block_offset: 10,
196   //   line_left: 20,
197   //   line_right: 65,
198   //   line_left_edges: [{0, 15}],
199   //   line_right_edges: [{0, 15}],
200   // }
201   // The line_left_edges and line_right_edges are all the floats which are
202   // "against" the shelf at the line_left and line_right offset respectively.
203   //
204   // An opportunity has a "solid" edge if there is at least one float adjacent
205   // to the line-left or line-right edge. If an opportunity has no adjacent
206   // floats it is invalid.
207   //
208   // These are used for:
209   //  - When we create an opportunity, making sure it has "solid" edges.
210   //  - The opportunity also holds onto a list of these edges to support
211   //    css-shapes.
212   struct NGShelf {
NGShelfNGShelf213     NGShelf(LayoutUnit block_offset, bool track_shape_exclusions)
214         : block_offset(block_offset),
215           line_left(LayoutUnit::Min()),
216           line_right(LayoutUnit::Max()),
217           shape_exclusions(track_shape_exclusions
218                                ? base::AdoptRef(new NGShapeExclusions)
219                                : nullptr),
220           has_shape_exclusions(false) {}
221 
222     // The copy constructor explicitly copies the shape_exclusions member.
NGShelfNGShelf223     NGShelf(const NGShelf& other)
224         : block_offset(other.block_offset),
225           line_left(other.line_left),
226           line_right(other.line_right),
227           line_left_edges(other.line_left_edges),
228           line_right_edges(other.line_right_edges),
229           shape_exclusions(other.shape_exclusions
230                                ? base::AdoptRef(new NGShapeExclusions(
231                                      *other.shape_exclusions))
232                                : nullptr),
233           has_shape_exclusions(other.has_shape_exclusions) {}
234 
235     NGShelf(NGShelf&& other) noexcept = default;
236     NGShelf& operator=(NGShelf&& other) noexcept = default;
237 
238     LayoutUnit block_offset;
239     LayoutUnit line_left;
240     LayoutUnit line_right;
241 
242     Vector<NGShelfEdge, 1> line_left_edges;
243     Vector<NGShelfEdge, 1> line_right_edges;
244 
245     // shape_exclusions contains all the floats which sit below this shelf. The
246     // has_shape_exclusions member will be true if shape_exclusions contains an
247     // exclusion with shape-outside specified (and therefore should be copied
248     // to any layout opportunity).
249     scoped_refptr<NGShapeExclusions> shape_exclusions;
250     bool has_shape_exclusions;
251   };
252 
253   // The closed-off area is an internal data-structure representing an area
254   // above a float. It contains a layout opportunity, and two vectors of
255   // |NGShelfEdge|. E.g.
256   //
257   //    0 1 2 3 4 5 6 7 8
258   // 0  +---+.      .+---+
259   //    |xxx|.      .|xxx|
260   // 10 |xxx|.      .|xxx|
261   //    +---+.      .+---+
262   // 20      ........
263   //      +---+
264   // 30   |xxx|
265   //      |xxx|
266   // 40   +---+
267   //
268   // In the above example the closed-off area is represented with the dotted
269   // line.
270   //
271   // It has the internal values of:
272   // {
273   //   opportunity: {
274   //     start_offset: {20, LayoutUnit::Min()},
275   //     end_offset: {65, 25},
276   //   }
277   //   line_left_edges: [{0, 15}],
278   //   line_right_edges: [{0, 15}],
279   // }
280   //
281   // Once a closed-off area has been created, it can never be changed due to
282   // the property that floats always align their block-start edges.
283   struct NGClosedArea {
NGClosedAreaNGClosedArea284     NGClosedArea(NGLayoutOpportunity opportunity,
285                  const Vector<NGShelfEdge, 1>& line_left_edges,
286                  const Vector<NGShelfEdge, 1>& line_right_edges)
287         : opportunity(opportunity),
288           line_left_edges(line_left_edges),
289           line_right_edges(line_right_edges) {}
290 
291     const NGLayoutOpportunity opportunity;
292     const Vector<NGShelfEdge, 1> line_left_edges;
293     const Vector<NGShelfEdge, 1> line_right_edges;
294   };
295 
296  private:
297   // In order to reduce the amount of Vector copies, instances of a
298   // NGExclusionSpaceInternal can share the same exclusions_ Vector. See the
299   // copy constructor.
300   //
301   // We implement a copy-on-write behaviour when adding an exclusion (if
302   // exclusions_.size(), and num_exclusions_ differs).
303   //
304   // num_exclusions_ is how many exclusions *this* instance of an exclusion
305   // space has, which may differ to the number of exclusions in the Vector.
306   scoped_refptr<NGExclusionPtrArray> exclusions_;
307   wtf_size_t num_exclusions_;
308 
309   // These members are used for keeping track of the "lowest" offset for each
310   // type of float. This is used for implementing float clearance.
311   LayoutUnit left_clear_offset_ = LayoutUnit::Min();
312   LayoutUnit right_clear_offset_ = LayoutUnit::Min();
313 
314   // This member is used for implementing the "top edge alignment rule" for
315   // floats. Floats can be positioned at negative offsets, hence is initialized
316   // the minimum value.
317   LayoutUnit last_float_block_start_ = LayoutUnit::Min();
318 
319   // In order to reduce the amount of copies related to bookkeeping shape data,
320   // we initially ignore exclusions with shape data. When we first see an
321   // exclusion with shape data, we set this flag, and rebuild the
322   // DerivedGeometry data-structure, to perform the additional bookkeeping.
323   bool track_shape_exclusions_;
324 
325   // The derived geometry struct, is the data-structure which handles all of the
326   // queries on the exclusion space. It can always be rebuilt from exclusions_
327   // and num_exclusions_. This is mutable as it is passed down a chain of
328   // exclusion spaces inside the copy constructor. E.g.
329   //
330   // NGExclusionSpace space1;
331   // space1.Add(exclusion1);
332   // space1.FindLayoutOpportunity(); // Builds derived_geometry_.
333   //
334   // NGExclusionSpace space2(space1); // Moves derived_geometry_ to space2.
335   // space2.Add(exclusion2); // Modifies derived_geometry_.
336   //
337   // space1.FindLayoutOpportunity(); // Re-builds derived_geometry_.
338   //
339   // This is efficient (desirable) as the common usage pattern is only the last
340   // exclusion space in the copy-chain is used for answering queries. Only when
341   // we trigger a (rare) re-layout case will we need to rebuild the
342   // derived_geometry_ data-structure.
343   struct CORE_EXPORT DerivedGeometry {
344     USING_FAST_MALLOC(DerivedGeometry);
345 
346    public:
347     // |block_offset_limit| represents the highest block-offset for which the
348     // geometry is valid. |FindLayoutOpportunity| and |AllLayoutOpportunities|
349     // should not be called for a block-offset higher than this.
350     // If |NGExclusionSpaceInternal::GetDerivedGeometry| is called with a
351     // higher limit the geometry is rebuilt.
352     //
353     // |track_shape_exclusions| is used to tell the geometry to track shape
354     // exclusions. Tracking shape exclusions is expensive, and uncommon, so
355     // when an exclusion with a shape is added we rebuilt the geometry to track
356     // this.
357     DerivedGeometry(LayoutUnit block_offset_limit, bool track_shape_exclusions);
358     DerivedGeometry(DerivedGeometry&& o) noexcept = default;
359 
360     void Add(const NGExclusion& exclusion);
361 
362     NGLayoutOpportunity FindLayoutOpportunity(
363         const NGBfcOffset& offset,
364         const LayoutUnit available_inline_size,
365         const LayoutUnit minimum_inline_size) const;
366 
367     LayoutOpportunityVector AllLayoutOpportunities(
368         const NGBfcOffset& offset,
369         const LayoutUnit available_inline_size) const;
370 
371     template <typename LambdaFunc>
372     void IterateAllLayoutOpportunities(const NGBfcOffset& offset,
373                                        const LayoutUnit available_inline_size,
374                                        const LambdaFunc&) const;
375 
376     // See |NGShelf| for a broad description of what shelves are. We always
377     // begin with one, which has the internal value of:
378     // {
379     //   block_offset: LayoutUnit::Min(),
380     //   line_left: LayoutUnit::Min(),
381     //   line_right: LayoutUnit::Max(),
382     // }
383     //
384     Vector<NGShelf, 4> shelves_;
385 
386     // See |NGClosedArea| for a broad description of what closed-off areas are.
387     //
388     // Floats always align their block-start edges. We exploit this property by
389     // keeping a list of closed-off areas. Once a closed-off area has been
390     // created, it can never change.
391     Vector<NGClosedArea, 4> areas_;
392 
393     // This represents the highest block-offset for which the geometry is valid
394     // for. If |NGExclusionSpaceInternal::GetDerivedGeometry| is called with a
395     // higher limit it is rebuilt.
396     LayoutUnit block_offset_limit_;
397 
398     bool track_shape_exclusions_;
399   };
400 
401   // Returns the derived_geometry_ member, potentially re-built from the
402   // exclusions_, and num_exclusions_ members.
403   const DerivedGeometry& GetDerivedGeometry(
404       LayoutUnit block_offset_limit) const;
405 
406   // See DerivedGeometry struct description.
407   mutable std::unique_ptr<DerivedGeometry> derived_geometry_;
408 };
409 
410 // The exclusion space represents all of the exclusions within a block
411 // formatting context.
412 //
413 // The space is mutated simply by adding exclusions, and various information
414 // can be queried based on the exclusions.
415 class CORE_EXPORT NGExclusionSpace {
416   DISALLOW_NEW();
417 
418  public:
419   NGExclusionSpace() = default;
NGExclusionSpace(const NGExclusionSpace & other)420   NGExclusionSpace(const NGExclusionSpace& other)
421       : exclusion_space_(other.exclusion_space_ ? new NGExclusionSpaceInternal(
422                                                       *other.exclusion_space_)
423                                                 : nullptr) {}
424   NGExclusionSpace(NGExclusionSpace&& other) noexcept = default;
425 
426   NGExclusionSpace& operator=(const NGExclusionSpace& other) {
427     exclusion_space_ = other.exclusion_space_
428                            ? std::make_unique<NGExclusionSpaceInternal>(
429                                  *other.exclusion_space_)
430                            : nullptr;
431     return *this;
432   }
433   NGExclusionSpace& operator=(NGExclusionSpace&& other) = default;
434 
Add(scoped_refptr<const NGExclusion> exclusion)435   void Add(scoped_refptr<const NGExclusion> exclusion) {
436     if (!exclusion_space_)
437       exclusion_space_ = std::make_unique<NGExclusionSpaceInternal>();
438     exclusion_space_->Add(std::move(exclusion));
439   }
440 
441   // Returns a layout opportunity, within the BFC.
442   // The area to search for layout opportunities is defined by the given offset,
443   // and |available_inline_size|. The layout opportunity must be greater than
444   // the given |minimum_inline_size|.
445   NGLayoutOpportunity FindLayoutOpportunity(
446       const NGBfcOffset& offset,
447       const LayoutUnit available_inline_size,
448       const LayoutUnit minimum_inline_size = LayoutUnit()) const {
449     if (!exclusion_space_) {
450       NGBfcOffset end_offset(
451           offset.line_offset + available_inline_size.ClampNegativeToZero(),
452           LayoutUnit::Max());
453       return NGLayoutOpportunity(NGBfcRect(offset, end_offset), nullptr);
454     }
455     return exclusion_space_->FindLayoutOpportunity(
456         offset, available_inline_size, minimum_inline_size);
457   }
458 
459   // If possible prefer FindLayoutOpportunity over this function.
AllLayoutOpportunities(const NGBfcOffset & offset,const LayoutUnit available_inline_size)460   LayoutOpportunityVector AllLayoutOpportunities(
461       const NGBfcOffset& offset,
462       const LayoutUnit available_inline_size) const {
463     if (!exclusion_space_) {
464       NGBfcOffset end_offset(
465           offset.line_offset + available_inline_size.ClampNegativeToZero(),
466           LayoutUnit::Max());
467       return LayoutOpportunityVector(
468           {NGLayoutOpportunity(NGBfcRect(offset, end_offset), nullptr)});
469     }
470     return exclusion_space_->AllLayoutOpportunities(offset,
471                                                     available_inline_size);
472   }
473 
474   // Returns the clearance offset based on the provided {@code clear_type}.
ClearanceOffset(EClear clear_type)475   LayoutUnit ClearanceOffset(EClear clear_type) const {
476     if (!exclusion_space_)
477       return LayoutUnit::Min();
478     return exclusion_space_->ClearanceOffset(clear_type);
479   }
480 
481   // Returns the block start offset of the last float added.
LastFloatBlockStart()482   LayoutUnit LastFloatBlockStart() const {
483     if (!exclusion_space_)
484       return LayoutUnit::Min();
485     return exclusion_space_->LastFloatBlockStart();
486   }
487 
IsEmpty()488   bool IsEmpty() const {
489     return !exclusion_space_ || exclusion_space_->IsEmpty();
490   }
491 
492   // See |NGExclusionSpaceInternal::PreInitialize|.
PreInitialize(const NGExclusionSpace & other)493   void PreInitialize(const NGExclusionSpace& other) const {
494     // Don't pre-initialize if we've already got an exclusions vector.
495     if (exclusion_space_)
496       return;
497 
498     // Don't pre-initialize if the other exclusion space didn't have an
499     // exclusions vector.
500     if (!other.exclusion_space_)
501       return;
502 
503     exclusion_space_ = std::make_unique<NGExclusionSpaceInternal>();
504     exclusion_space_->PreInitialize(*other.exclusion_space_);
505   }
506 
507   // Shifts the |DerivedGeometry| data-structure to this exclusion space, and
508   // adds any new exclusions.
MoveAndUpdateDerivedGeometry(const NGExclusionSpace & other)509   void MoveAndUpdateDerivedGeometry(const NGExclusionSpace& other) const {
510     if (!exclusion_space_ || !other.exclusion_space_)
511       return;
512 
513     exclusion_space_->MoveAndUpdateDerivedGeometry(*other.exclusion_space_);
514   }
515 
516   // Shifts the |DerivedGeometry| data-structure to this exclusion space.
MoveDerivedGeometry(const NGExclusionSpace & other)517   void MoveDerivedGeometry(const NGExclusionSpace& other) const {
518     DCHECK(*this == other);
519     if (!exclusion_space_ || !other.exclusion_space_)
520       return;
521 
522     exclusion_space_->MoveDerivedGeometry(*other.exclusion_space_);
523   }
524 
525   // This produces a new exclusion space for a |NGLayoutResult| which is being
526   // re-used for caching purposes.
527   //
528   // It takes:
529   //  - |old_output| The exclusion space associated with the cached layout
530   //    result (the output of layout).
531   //  - |old_input| The exclusion space which produced the cached layout result
532   //    (the input into layout).
533   //  - |new_input| The exclusion space which is being used to produce a new
534   //    layout result (the new input into layout).
535   //  - |offset_delta| the amount that the layout result was moved in BFC
536   //    coordinate space.
537   //
538   // |old_output| should contain the *at least* same exclusions as |old_input|
539   // however may have added some more exclusions during its layout.
540   //
541   // This function takes those exclusions added by the cached layout-result
542   // (the difference between |old_output| and |old_input|), and adds them to
543   // |new_input|. It will additionally shift them by |offset_delta|.
544   //
545   // This produces the correct exclusion space "new_output" for the new reused
546   // layout result.
MergeExclusionSpaces(const NGExclusionSpace & old_output,const NGExclusionSpace & old_input,const NGExclusionSpace & new_input,const NGBfcDelta & offset_delta)547   static NGExclusionSpace MergeExclusionSpaces(
548       const NGExclusionSpace& old_output,
549       const NGExclusionSpace& old_input,
550       const NGExclusionSpace& new_input,
551       const NGBfcDelta& offset_delta) {
552     // We start building the new exclusion space from the new input, this
553     // (should) have the derived geometry which will move to |new_output|.
554     NGExclusionSpace new_output = new_input;
555 
556     // If we didn't have any floats previously, we don't need to add any new
557     // ones, just return the new output.
558     if (!old_output.exclusion_space_)
559       return new_output;
560 
561     // If the layout didn't add any new exclusions, we can just return the new
562     // output.
563     if (old_input == old_output)
564       return new_output;
565 
566     if (!new_output.exclusion_space_) {
567       new_output.exclusion_space_ =
568           std::make_unique<NGExclusionSpaceInternal>();
569     }
570 
571     new_output.exclusion_space_->MergeExclusionSpaces(
572         offset_delta, *old_output.exclusion_space_,
573         old_input.exclusion_space_.get());
574 
575     return new_output;
576   }
577 
578   bool operator==(const NGExclusionSpace& other) const {
579     if (exclusion_space_ == other.exclusion_space_)
580       return true;
581     if (exclusion_space_ && other.exclusion_space_)
582       return *exclusion_space_ == *other.exclusion_space_;
583     return false;
584   }
585   bool operator!=(const NGExclusionSpace& other) const {
586     return !(*this == other);
587   }
588 
589 #if DCHECK_IS_ON()
CheckSameForSimplifiedLayout(const NGExclusionSpace & other)590   void CheckSameForSimplifiedLayout(const NGExclusionSpace& other) const {
591     DCHECK_EQ((bool)exclusion_space_, (bool)other.exclusion_space_);
592     if (exclusion_space_)
593       exclusion_space_->CheckSameForSimplifiedLayout(*other.exclusion_space_);
594   }
595 #endif
596 
597  private:
598   mutable std::unique_ptr<NGExclusionSpaceInternal> exclusion_space_;
599 };
600 
601 }  // namespace blink
602 
603 WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
604     blink::NGExclusionSpaceInternal::NGShelfEdge)
605 
606 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_EXCLUSIONS_NG_EXCLUSION_SPACE_H_
607