Coverage Report

Created: 2026-09-28 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/yoga/yoga/node/Node.h
Line
Count
Source
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#pragma once
9
10
#include <cstdint>
11
#include <cstdio>
12
#include <vector>
13
14
#include <yoga/Yoga.h>
15
#include <yoga/node/LayoutableChildren.h>
16
17
#include <yoga/config/Config.h>
18
#include <yoga/enums/Dimension.h>
19
#include <yoga/enums/Direction.h>
20
#include <yoga/enums/Edge.h>
21
#include <yoga/enums/Errata.h>
22
#include <yoga/enums/MeasureMode.h>
23
#include <yoga/enums/NodeType.h>
24
#include <yoga/enums/PhysicalEdge.h>
25
#include <yoga/node/LayoutResults.h>
26
#include <yoga/style/Style.h>
27
28
// Tag struct used to form the opaque YGNodeRef for the public C API
29
struct YGNode {};
30
31
namespace facebook::yoga {
32
33
class YG_EXPORT Node : public ::YGNode {
34
 public:
35
  using LayoutableChildren = yoga::LayoutableChildren<Node>;
36
  Node();
37
  explicit Node(const Config* config);
38
39
  Node(Node&& node) noexcept;
40
41
  // Does not expose true value semantics, as children are not cloned eagerly.
42
  // Should we remove this?
43
0
  Node(const Node& node) = default;
44
45
  // assignment means potential leaks of existing children, or alternatively
46
  // freeing unowned memory, double free, or freeing stack memory.
47
  Node& operator=(const Node&) = delete;
48
49
  // Getters
50
0
  void* getContext() const {
51
0
    return context_;
52
0
  }
53
54
0
  bool alwaysFormsContainingBlock() const {
55
0
    return alwaysFormsContainingBlock_;
56
0
  }
57
58
0
  bool getHasNewLayout() const {
59
0
    return hasNewLayout_;
60
0
  }
61
62
1.66M
  NodeType getNodeType() const {
63
1.66M
    return nodeType_;
64
1.66M
  }
65
66
48.8M
  bool hasMeasureFunc() const noexcept {
67
48.8M
    return measureFunc_ != nullptr;
68
48.8M
  }
69
70
  YGSize measure(
71
      float availableWidth,
72
      MeasureMode widthMode,
73
      float availableHeight,
74
      MeasureMode heightMode);
75
76
0
  bool hasMinContentMeasureFunc() const noexcept {
77
0
    return minContentMeasureFunc_ != nullptr;
78
0
  }
79
80
  YGSize measureMinContent(
81
      float availableWidth,
82
      MeasureMode widthMode,
83
      float availableHeight,
84
      MeasureMode heightMode);
85
86
0
  FloatOptional getMinContentWidth() const noexcept {
87
0
    return minContentWidth_;
88
0
  }
89
90
0
  FloatOptional getMinContentHeight() const noexcept {
91
0
    return minContentHeight_;
92
0
  }
93
94
0
  bool hasBaselineFunc() const noexcept {
95
0
    return baselineFunc_ != nullptr;
96
0
  }
97
98
  float baseline(float width, float height) const;
99
100
  float dimensionWithMargin(FlexDirection axis, float widthSize);
101
102
  bool isLayoutDimensionDefined(FlexDirection axis);
103
104
  /**
105
   * Whether the node has a "definite length" along the given axis.
106
   * https://www.w3.org/TR/css-sizing-3/#definite
107
   */
108
64.5M
  inline bool hasDefiniteLength(Dimension dimension, float ownerSize) {
109
64.5M
    auto usedValue = getProcessedDimension(dimension).resolve(ownerSize);
110
64.5M
    return usedValue.isDefined() && usedValue.unwrap() >= 0.0f;
111
64.5M
  }
112
113
12.5M
  bool hasErrata(Errata errata) const {
114
12.5M
    return config_->hasErrata(errata);
115
12.5M
  }
116
117
19.4M
  bool hasContentsChildren() const {
118
19.4M
    return contentsChildrenCount_ != 0;
119
19.4M
  }
120
121
1.66M
  YGDirtiedFunc getDirtiedFunc() const {
122
1.66M
    return dirtiedFunc_;
123
1.66M
  }
124
125
  // For Performance reasons passing as reference.
126
686M
  Style& style() {
127
686M
    return style_;
128
686M
  }
129
130
679M
  const Style& style() const {
131
679M
    return style_;
132
679M
  }
133
134
  // For Performance reasons passing as reference.
135
220M
  LayoutResults& getLayout() {
136
220M
    return layout_;
137
220M
  }
138
139
1.01M
  const LayoutResults& getLayout() const {
140
1.01M
    return layout_;
141
1.01M
  }
142
143
0
  size_t getLineIndex() const {
144
0
    return lineIndex_;
145
0
  }
146
147
0
  bool isReferenceBaseline() const {
148
0
    return isReferenceBaseline_;
149
0
  }
150
151
  // returns the Node that owns this Node. An owner is used to identify
152
  // the YogaTree that a Node belongs to. This method will return the parent
153
  // of the Node when a Node only belongs to one YogaTree or nullptr when
154
  // the Node is shared between two or more YogaTrees.
155
23.9M
  Node* getOwner() const {
156
23.9M
    return owner_;
157
23.9M
  }
158
159
1.66M
  const std::vector<Node*>& getChildren() const {
160
1.66M
    return children_;
161
1.66M
  }
162
163
103M
  Node* getChild(size_t index) const {
164
103M
    return children_.at(index);
165
103M
  }
166
167
80.5M
  size_t getChildCount() const {
168
80.5M
    return children_.size();
169
80.5M
  }
170
171
32.5M
  LayoutableChildren getLayoutChildren() const {
172
32.5M
    return LayoutableChildren(this);
173
32.5M
  }
174
175
19.4M
  size_t getLayoutChildCount() const {
176
19.4M
    if (contentsChildrenCount_ == 0) {
177
19.4M
      return children_.size();
178
19.4M
    } else {
179
0
      size_t count = 0;
180
0
      for (auto iter = getLayoutChildren().begin();
181
0
           iter != getLayoutChildren().end();
182
0
           iter++) {
183
0
        count++;
184
0
      }
185
0
      return count;
186
0
    }
187
19.4M
  }
188
189
83.9M
  const Config* getConfig() const {
190
83.9M
    return config_;
191
83.9M
  }
192
193
27.6M
  bool isDirty() const {
194
27.6M
    return isDirty_;
195
27.6M
  }
196
197
68.0M
  Style::SizeLength getProcessedDimension(Dimension dimension) const {
198
68.0M
    return processedDimensions_[static_cast<size_t>(dimension)];
199
68.0M
  }
200
201
  FloatOptional getResolvedDimension(
202
      Direction direction,
203
      Dimension dimension,
204
      float referenceLength,
205
2.58M
      float ownerWidth) const {
206
2.58M
    FloatOptional value =
207
2.58M
        getProcessedDimension(dimension).resolve(referenceLength);
208
2.58M
    if (style_.boxSizing() == BoxSizing::BorderBox) {
209
2.58M
      return value;
210
2.58M
    }
211
212
0
    FloatOptional dimensionPaddingAndBorder =
213
0
        FloatOptional{style_.computePaddingAndBorderForDimension(
214
0
            direction, dimension, ownerWidth)};
215
216
0
    return value +
217
0
        (dimensionPaddingAndBorder.isDefined() ? dimensionPaddingAndBorder
218
0
                                               : FloatOptional{0.0});
219
2.58M
  }
220
221
  // Setters
222
223
0
  void setContext(void* context) {
224
0
    context_ = context;
225
0
  }
226
227
0
  void setAlwaysFormsContainingBlock(bool alwaysFormsContainingBlock) {
228
0
    alwaysFormsContainingBlock_ = alwaysFormsContainingBlock;
229
0
  }
230
231
1.66M
  void setHasNewLayout(bool hasNewLayout) {
232
1.66M
    hasNewLayout_ = hasNewLayout;
233
1.66M
  }
234
235
0
  void setNodeType(NodeType nodeType) {
236
0
    nodeType_ = nodeType;
237
0
  }
238
239
  void setMeasureFunc(YGMeasureFunc measureFunc);
240
241
0
  void setMinContentMeasureFunc(YGMinContentMeasureFunc minContentMeasureFunc) {
242
0
    minContentMeasureFunc_ = minContentMeasureFunc;
243
0
  }
244
245
0
  void setMinContentWidth(FloatOptional minContentWidth) noexcept {
246
0
    minContentWidth_ = minContentWidth;
247
0
  }
248
249
0
  void setMinContentHeight(FloatOptional minContentHeight) noexcept {
250
0
    minContentHeight_ = minContentHeight;
251
0
  }
252
253
0
  void setBaselineFunc(YGBaselineFunc baseLineFunc) {
254
0
    baselineFunc_ = baseLineFunc;
255
0
  }
256
257
3.33M
  void setDirtiedFunc(YGDirtiedFunc dirtiedFunc) {
258
3.33M
    dirtiedFunc_ = dirtiedFunc;
259
3.33M
  }
260
261
0
  void setStyle(const Style& style) {
262
0
    style_ = style;
263
0
  }
264
265
1.66M
  void setLayout(const LayoutResults& layout) {
266
1.66M
    layout_ = layout;
267
1.66M
  }
268
269
15.5M
  void setLineIndex(size_t lineIndex) {
270
15.5M
    lineIndex_ = lineIndex;
271
15.5M
  }
272
273
0
  void setIsReferenceBaseline(bool isReferenceBaseline) {
274
0
    isReferenceBaseline_ = isReferenceBaseline;
275
0
  }
276
277
3.33M
  void setOwner(Node* owner) {
278
3.33M
    owner_ = owner;
279
3.33M
  }
280
281
  // TODO: rvalue override for setChildren
282
283
  void setConfig(Config* config);
284
285
  void setDirty(bool isDirty);
286
  void setChildren(const std::vector<Node*>& children);
287
  void setLayoutLastOwnerDirection(Direction direction);
288
  void setLayoutComputedFlexBasis(FloatOptional computedFlexBasis);
289
  void setLayoutComputedFlexBasisGeneration(
290
      uint32_t computedFlexBasisGeneration);
291
  void setLayoutMeasuredDimension(float measuredDimension, Dimension dimension);
292
  void setLayoutHadOverflow(bool hadOverflow);
293
  void setLayoutDimension(float lengthValue, Dimension dimension);
294
  void setLayoutDirection(Direction direction);
295
  void setLayoutMargin(float margin, PhysicalEdge edge);
296
  void setLayoutBorder(float border, PhysicalEdge edge);
297
  void setLayoutPadding(float padding, PhysicalEdge edge);
298
  void setLayoutPosition(float position, PhysicalEdge edge);
299
  void setPosition(Direction direction, float ownerWidth, float ownerHeight);
300
301
  // Other methods
302
  Style::SizeLength processFlexBasis() const;
303
  FloatOptional resolveFlexBasis(
304
      Direction direction,
305
      FlexDirection flexDirection,
306
      float referenceLength,
307
      float ownerWidth) const;
308
  void processDimensions();
309
  Direction resolveDirection(Direction ownerDirection);
310
  void clearChildren();
311
  /// Replaces the occurrences of oldChild with newChild
312
  void replaceChild(Node* oldChild, Node* newChild);
313
  void replaceChild(Node* child, size_t index);
314
  void insertChild(Node* child, size_t index);
315
  /// Removes the first occurrence of child
316
  bool removeChild(Node* child);
317
  void removeChild(size_t index);
318
319
  void cloneChildrenIfNeeded();
320
  void cloneContentsChildrenIfNeeded();
321
  void markDirtyAndPropagate();
322
  float resolveFlexGrow() const;
323
  float resolveFlexShrink() const;
324
  bool isNodeFlexible();
325
  void reset();
326
327
  float relativePosition(
328
      FlexDirection axis,
329
      Direction direction,
330
      float axisSize) const;
331
332
 private:
333
  // Used to allow resetting the node
334
0
  Node& operator=(Node&&) noexcept = default;
335
336
0
  void useWebDefaults() {
337
0
    style_.setFlexDirection(FlexDirection::Row);
338
0
    style_.setAlignContent(Align::Stretch);
339
0
  }
340
341
  bool hasNewLayout_ : 1 = true;
342
  bool isReferenceBaseline_ : 1 = false;
343
  bool isDirty_ : 1 = true;
344
  bool alwaysFormsContainingBlock_ : 1 = false;
345
  NodeType nodeType_ : bitCount<NodeType>() = NodeType::Default;
346
  void* context_ = nullptr;
347
  YGMeasureFunc measureFunc_ = nullptr;
348
  YGMinContentMeasureFunc minContentMeasureFunc_ = nullptr;
349
  FloatOptional minContentWidth_{};
350
  FloatOptional minContentHeight_{};
351
  YGBaselineFunc baselineFunc_ = nullptr;
352
  YGDirtiedFunc dirtiedFunc_ = nullptr;
353
  Style style_;
354
  LayoutResults layout_;
355
  size_t lineIndex_ = 0;
356
  size_t contentsChildrenCount_ = 0;
357
  Node* owner_ = nullptr;
358
  std::vector<Node*> children_;
359
  const Config* config_;
360
  std::array<Style::SizeLength, 2> processedDimensions_{
361
      {StyleSizeLength::undefined(), StyleSizeLength::undefined()}};
362
};
363
364
25.8M
inline Node* resolveRef(const YGNodeRef ref) {
365
25.8M
  return static_cast<Node*>(ref);
366
25.8M
}
367
368
0
inline const Node* resolveRef(const YGNodeConstRef ref) {
369
0
  return static_cast<const Node*>(ref);
370
0
}
371
372
} // namespace facebook::yoga