mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
024a8dc8ff
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/36325 A lot of the code in YogaLayoutableShadowNode expects a 1:1 mapping from ShadowNode children to Yoga Node children, which is not always the case, e.g. for `RawTextShadowNode` (if text, or a number coerced to text, is rendered outside a Text component). In S323291 we saw this cause memory corruption due to later invalid static_cast. I changed the casting mechanism to terminte instead of corrupting memory, but there is logic here that we need to make permissive (I think D29894182 (https://github.com/facebook/react-native/commit/d3e836245b5fab2e0299dd06901de74dff767b63) also tried to previously do this?). Normally Text will be rendered inside of a ParagraphShadowNode, where "Text" from React is mapped to ParagraphShadowNode (which is layoutable), "VirtualText" is mapped to TextShadowNode (which is also not layoutable), then finally the text fragment is mapped to "RawTextShadowNode". Arguably React renderer behavior should be not to send anything to native, but we can provide a generalized solution in YogaLayoutableShadowNode to handle any other cases of this issue. This solution works by filtering ShadowNode children to those which are YogaLayoutable, then only ever operating on that list of children. This means a guaranteed invariant of the nodes we operate on being layoutable (vs the adhoc error handling right now for when they are not), and means we maintain the index based mapping of ShadowNode children to Yoga Node children. Note, there is another similar API, `getLayoutableChildNodes()` which is protected and returns a filtered list of LayoutableShadowNode. This is public, to allow querying/setting layout results, whearas the similar operations in YogaLayoutableShadowNode are instead an implementation detail. Changelog: [General][Fixed] - Fix YogaLayoutableShadowNode handling of non-layoutable children Reviewed By: sammy-SC Differential Revision: D43657405 fbshipit-source-id: 8ed136b03b4da15a5e7dfbdd5539b04a2952420d
207 lines
6.3 KiB
C++
207 lines
6.3 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include <yoga/YGNode.h>
|
|
|
|
#include <react/debug/react_native_assert.h>
|
|
#include <react/renderer/components/view/YogaStylableProps.h>
|
|
#include <react/renderer/core/LayoutableShadowNode.h>
|
|
#include <react/renderer/core/Sealable.h>
|
|
#include <react/renderer/core/ShadowNode.h>
|
|
#include <react/renderer/debug/DebugStringConvertible.h>
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
class YogaLayoutableShadowNode : public LayoutableShadowNode {
|
|
using CompactValue = facebook::yoga::detail::CompactValue;
|
|
|
|
public:
|
|
using Shared = std::shared_ptr<YogaLayoutableShadowNode const>;
|
|
using ListOfShared =
|
|
butter::small_vector<Shared, kShadowNodeChildrenSmallVectorSize>;
|
|
|
|
static ShadowNodeTraits BaseTraits();
|
|
static ShadowNodeTraits::Trait IdentifierTrait();
|
|
|
|
#pragma mark - Constructors
|
|
|
|
YogaLayoutableShadowNode(
|
|
ShadowNodeFragment const &fragment,
|
|
ShadowNodeFamily::Shared const &family,
|
|
ShadowNodeTraits traits);
|
|
|
|
YogaLayoutableShadowNode(
|
|
ShadowNode const &sourceShadowNode,
|
|
ShadowNodeFragment const &fragment);
|
|
|
|
#pragma mark - Mutating Methods
|
|
|
|
/*
|
|
* Connects `measureFunc` function of Yoga node with
|
|
* `LayoutableShadowNode::measure()` method.
|
|
*/
|
|
void enableMeasurement();
|
|
|
|
void appendChild(ShadowNode::Shared const &child) override;
|
|
void replaceChild(
|
|
ShadowNode const &oldChild,
|
|
ShadowNode::Shared const &newChild,
|
|
size_t suggestedIndex = -1) override;
|
|
|
|
void updateYogaChildren();
|
|
|
|
void updateYogaProps();
|
|
|
|
/*
|
|
* Sets layoutable size of node.
|
|
*/
|
|
void setSize(Size size) const;
|
|
|
|
void setPadding(RectangleEdges<Float> padding) const;
|
|
|
|
/*
|
|
* Sets position type of Yoga node (relative, absolute).
|
|
*/
|
|
void setPositionType(YGPositionType positionType) const;
|
|
|
|
#pragma mark - LayoutableShadowNode
|
|
|
|
void cleanLayout() override;
|
|
void dirtyLayout() override;
|
|
bool getIsLayoutClean() const override;
|
|
|
|
/*
|
|
* Computes layout using Yoga layout engine.
|
|
* See `LayoutableShadowNode` for more details.
|
|
*/
|
|
void layoutTree(
|
|
LayoutContext layoutContext,
|
|
LayoutConstraints layoutConstraints) override;
|
|
|
|
void layout(LayoutContext layoutContext) override;
|
|
|
|
protected:
|
|
/*
|
|
* Yoga config associated (only) with this particular node.
|
|
*/
|
|
YGConfig yogaConfig_;
|
|
|
|
/*
|
|
* All Yoga functions only accept non-const arguments, so we have to mark
|
|
* Yoga node as `mutable` here to avoid `static_cast`ing the pointer to this
|
|
* all the time.
|
|
*/
|
|
mutable YGNode yogaNode_;
|
|
|
|
private:
|
|
/*
|
|
* Goes over `yogaNode_.getChildren()` and in case child's owner is
|
|
* equal to address of `yogaNode_`, it sets child's owner address
|
|
* to `0xBADC0FFEE0DDF00D`. This is magic constant, the intention
|
|
* is to make debugging easier when the address pops up in debugger.
|
|
* This prevents ABA problem where child yoga node goes from owned -> unowned
|
|
* -> back to owned because its parent is allocated at the same address.
|
|
*/
|
|
void updateYogaChildrenOwnersIfNeeded();
|
|
|
|
/*
|
|
* Return true if child's yogaNode's owner is this->yogaNode_. Otherwise
|
|
* returns false.
|
|
*/
|
|
bool doesOwn(YogaLayoutableShadowNode const &child) const;
|
|
|
|
/*
|
|
* Appends a Yoga node to the Yoga node associated with this node.
|
|
* The method does *not* do anything besides that (no cloning or `owner` field
|
|
* adjustment).
|
|
*/
|
|
void appendYogaChild(YogaLayoutableShadowNode::Shared const &childNode);
|
|
|
|
/*
|
|
* Makes the child node with a given `index` (and Yoga node associated with) a
|
|
* valid child node satisfied requirements of the Concurrent Layout approach.
|
|
*/
|
|
void adoptYogaChild(size_t index);
|
|
|
|
static YGConfig &initializeYogaConfig(YGConfig &config);
|
|
static YGNode *yogaNodeCloneCallbackConnector(
|
|
YGNode *oldYogaNode,
|
|
YGNode *parentYogaNode,
|
|
int childIndex);
|
|
static YGSize yogaNodeMeasureCallbackConnector(
|
|
YGNode *yogaNode,
|
|
float width,
|
|
YGMeasureMode widthMode,
|
|
float height,
|
|
YGMeasureMode heightMode);
|
|
static YogaLayoutableShadowNode &shadowNodeFromContext(YGNode *yogaNode);
|
|
|
|
#pragma mark - RTL Legacy Autoflip
|
|
|
|
/*
|
|
* Walks though shadow node hierarchy and reassign following values:
|
|
* - (left|right) → (start|end)
|
|
* - margin(Left|Right) → margin(Start|End)
|
|
* - padding(Left|Right) → padding(Start|End)
|
|
* - borderTop(Left|Right)Radius → borderTop(Start|End)Radius
|
|
* - borderBottom(Left|Right)Radius → borderBottom(Start|End)Radius
|
|
* - border(Left|Right)Width → border(Start|End)Width
|
|
* - border(Left|Right)Color → border(Start|End)Color
|
|
* This is neccesarry to be backwards compatible with old renderer, it swaps
|
|
* the values as well in https://fburl.com/diffusion/kl7bjr3h
|
|
*/
|
|
static void swapLeftAndRightInTree(
|
|
YogaLayoutableShadowNode const &shadowNode);
|
|
/*
|
|
* In shadow node passed as argument, reassigns following values
|
|
* - borderTop(Left|Right)Radius → borderTop(Start|End)Radius
|
|
* - borderBottom(Left|Right)Radius → borderBottom(Start|End)Radius
|
|
* - border(Left|Right)Width → border(Start|End)Width
|
|
* - border(Left|Right)Color → border(Start|End)Color
|
|
*/
|
|
static void swapLeftAndRightInViewProps(
|
|
YogaLayoutableShadowNode const &shadowNode);
|
|
/*
|
|
* In yoga node passed as argument, reassigns following values
|
|
* - (left|right) → (start|end)
|
|
* - margin(Left|Right) → margin(Start|End)
|
|
* - padding(Left|Right) → padding(Start|End)
|
|
*/
|
|
static void swapLeftAndRightInYogaStyleProps(
|
|
YogaLayoutableShadowNode const &shadowNode);
|
|
|
|
/*
|
|
* Combine a base YGStyle with aliased properties which should be flattened
|
|
* into it. E.g. reconciling "marginInlineStart" and "marginStart".
|
|
*/
|
|
static YGStyle applyAliasedProps(
|
|
const YGStyle &baseStyle,
|
|
const YogaStylableProps &props);
|
|
|
|
#pragma mark - Consistency Ensuring Helpers
|
|
|
|
void ensureConsistency() const;
|
|
void ensureYogaChildrenAlignment() const;
|
|
void ensureYogaChildrenOwnersConsistency() const;
|
|
void ensureYogaChildrenLookFine() const;
|
|
|
|
#pragma mark - Private member variables
|
|
/*
|
|
* List of children which derive from YogaLayoutableShadowNode
|
|
*/
|
|
ListOfShared yogaLayoutableChildren_;
|
|
};
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|