Refactor mount out of Binding.cpp

Summary:
Binding.cpp shares many responsibilities, including surface management and mount. This change refactors mount (and mount items) into separate files, while retaining the same functionality and interface.

Changelog:
[Internal][Android] - Refactor mount into FabricMountingManager

Reviewed By: mdvacca

Differential Revision: D32977902

fbshipit-source-id: 07cdf5a19033fccaa81901e5b9a4d44192ec7853
This commit is contained in:
Andrei Shikov
2021-12-20 08:09:54 -08:00
committed by Facebook GitHub Bot
parent dc5cae5a6f
commit 0a3ddce928
7 changed files with 1129 additions and 951 deletions
@@ -28,6 +28,7 @@ rn_xplat_cxx_library(
react_native_xplat_target("react/renderer/animations:animations"),
react_native_xplat_target("react/renderer/uimanager:uimanager"),
react_native_xplat_target("react/renderer/scheduler:scheduler"),
react_native_xplat_target("react/renderer/mounting:mounting"),
react_native_xplat_target("react/renderer/componentregistry:componentregistry"),
react_native_xplat_target("react/renderer/components/scrollview:scrollview"),
react_native_xplat_target("runtimeexecutor:runtimeexecutor"),
File diff suppressed because it is too large Load Diff
@@ -7,6 +7,8 @@
#pragma once
#include "FabricMountingManager.h"
#include <fbjni/fbjni.h>
#include <react/jni/JRuntimeExecutor.h>
#include <react/jni/JRuntimeScheduler.h>
@@ -29,49 +31,6 @@ namespace react {
class Instance;
struct CppMountItem final {
#pragma mark - Designated Initializers
static CppMountItem CreateMountItem(ShadowView const &shadowView);
static CppMountItem DeleteMountItem(ShadowView const &shadowView);
static CppMountItem InsertMountItem(
ShadowView const &parentView,
ShadowView const &shadowView,
int index);
static CppMountItem RemoveMountItem(
ShadowView const &parentView,
ShadowView const &shadowView,
int index);
static CppMountItem UpdatePropsMountItem(ShadowView const &shadowView);
static CppMountItem UpdateStateMountItem(ShadowView const &shadowView);
static CppMountItem UpdateLayoutMountItem(ShadowView const &shadowView);
static CppMountItem UpdateEventEmitterMountItem(ShadowView const &shadowView);
static CppMountItem UpdatePaddingMountItem(ShadowView const &shadowView);
#pragma mark - Type
enum Type {
Undefined = -1,
Multiple = 1,
Create = 2,
Delete = 4,
Insert = 8,
Remove = 16,
UpdateProps = 32,
UpdateState = 64,
UpdateLayout = 128,
UpdateEventEmitter = 256,
UpdatePadding = 512
};
#pragma mark - Fields
Type type = {Create};
ShadowView parentShadowView = {};
ShadowView oldChildShadowView = {};
ShadowView newChildShadowView = {};
int index = {};
};
class Binding : public jni::HybridClass<Binding>,
public SchedulerDelegate,
public LayoutAnimationStatusDelegate {
@@ -79,18 +38,12 @@ class Binding : public jni::HybridClass<Binding>,
constexpr static const char *const kJavaDescriptor =
"Lcom/facebook/react/fabric/Binding;";
constexpr static auto UIManagerJavaDescriptor =
"com/facebook/react/fabric/FabricUIManager";
constexpr static auto ReactFeatureFlagsJavaDescriptor =
"com/facebook/react/config/ReactFeatureFlags";
static void registerNatives();
private:
jni::global_ref<jobject> getJavaUIManager();
std::shared_ptr<Scheduler> getScheduler();
void setConstraints(
jint surfaceId,
jfloat minWidth,
@@ -144,10 +97,6 @@ class Binding : public jni::HybridClass<Binding>,
void schedulerDidFinishTransaction(
MountingCoordinator::Shared const &mountingCoordinator) override;
void preallocateShadowView(
const SurfaceId surfaceId,
const ShadowView &shadowView);
void schedulerDidRequestPreliminaryViewAllocation(
const SurfaceId surfaceId,
const ShadowNode &shadowNode) override;
@@ -171,6 +120,8 @@ class Binding : public jni::HybridClass<Binding>,
bool isJSResponder,
bool blockNativeResponder) override;
void preallocateView(SurfaceId surfaceId, ShadowNode const &shadowNode);
void setPixelDensity(float pointScaleFactor);
void driveCxxAnimations();
@@ -179,27 +130,30 @@ class Binding : public jni::HybridClass<Binding>,
// Private member variables
better::shared_mutex installMutex_;
jni::global_ref<jobject> javaUIManager_;
std::shared_ptr<FabricMountingManager> mountingManager_;
std::shared_ptr<Scheduler> scheduler_;
std::shared_ptr<Scheduler> getScheduler();
std::shared_ptr<FabricMountingManager> verifyMountingManager(
std::string const &locationHint);
// LayoutAnimations
void onAnimationStarted() override;
void onAllAnimationsComplete() override;
std::shared_ptr<LayoutAnimationDriver> animationDriver_;
std::unique_ptr<JBackgroundExecutor> backgroundExecutor_;
better::map<SurfaceId, SurfaceHandler> surfaceHandlerRegistry_{};
better::shared_mutex
surfaceHandlerRegistryMutex_; // Protects `surfaceHandlerRegistry_`.
std::recursive_mutex commitMutex_;
float pointScaleFactor_ = 1;
std::shared_ptr<const ReactNativeConfig> reactNativeConfig_{nullptr};
bool disablePreallocateViews_{false};
bool enableFabricLogs_{false};
bool enableEarlyEventEmitterUpdate_{false};
bool disableRevisionCheckForPreallocation_{false};
bool enableEventEmitterRawPointer_{false};
bool dispatchPreallocationInBackground_{false};
@@ -0,0 +1,50 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "FabricMountItem.h"
namespace facebook {
namespace react {
CppMountItem CppMountItem::CreateMountItem(ShadowView const &shadowView) {
return {CppMountItem::Type::Create, {}, {}, shadowView, -1};
}
CppMountItem CppMountItem::DeleteMountItem(ShadowView const &shadowView) {
return {CppMountItem::Type::Delete, {}, shadowView, {}, -1};
}
CppMountItem CppMountItem::InsertMountItem(
ShadowView const &parentView,
ShadowView const &shadowView,
int index) {
return {CppMountItem::Type::Insert, parentView, {}, shadowView, index};
}
CppMountItem CppMountItem::RemoveMountItem(
ShadowView const &parentView,
ShadowView const &shadowView,
int index) {
return {CppMountItem::Type::Remove, parentView, shadowView, {}, index};
}
CppMountItem CppMountItem::UpdatePropsMountItem(ShadowView const &shadowView) {
return {CppMountItem::Type::UpdateProps, {}, {}, shadowView, -1};
}
CppMountItem CppMountItem::UpdateStateMountItem(ShadowView const &shadowView) {
return {CppMountItem::Type::UpdateState, {}, {}, shadowView, -1};
}
CppMountItem CppMountItem::UpdateLayoutMountItem(ShadowView const &shadowView) {
return {CppMountItem::Type::UpdateLayout, {}, {}, shadowView, -1};
}
CppMountItem CppMountItem::UpdateEventEmitterMountItem(
ShadowView const &shadowView) {
return {CppMountItem::Type::UpdateEventEmitter, {}, {}, shadowView, -1};
}
CppMountItem CppMountItem::UpdatePaddingMountItem(
ShadowView const &shadowView) {
return {CppMountItem::Type::UpdatePadding, {}, {}, shadowView, -1};
}
} // namespace react
} // namespace facebook
@@ -0,0 +1,74 @@
/*
* Copyright (c) Facebook, Inc. and its 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 <fbjni/fbjni.h>
#include <react/renderer/mounting/ShadowView.h>
namespace facebook {
namespace react {
struct JMountItem : public jni::JavaClass<JMountItem> {
static constexpr auto kJavaDescriptor =
"Lcom/facebook/react/fabric/mounting/mountitems/MountItem;";
};
struct CppMountItem final {
#pragma mark - Designated Initializers
static CppMountItem CreateMountItem(ShadowView const &shadowView);
static CppMountItem DeleteMountItem(ShadowView const &shadowView);
static CppMountItem InsertMountItem(
ShadowView const &parentView,
ShadowView const &shadowView,
int index);
static CppMountItem RemoveMountItem(
ShadowView const &parentView,
ShadowView const &shadowView,
int index);
static CppMountItem UpdatePropsMountItem(ShadowView const &shadowView);
static CppMountItem UpdateStateMountItem(ShadowView const &shadowView);
static CppMountItem UpdateLayoutMountItem(ShadowView const &shadowView);
static CppMountItem UpdateEventEmitterMountItem(ShadowView const &shadowView);
static CppMountItem UpdatePaddingMountItem(ShadowView const &shadowView);
#pragma mark - Type
enum Type {
Undefined = -1,
Multiple = 1,
Create = 2,
Delete = 4,
Insert = 8,
Remove = 16,
UpdateProps = 32,
UpdateState = 64,
UpdateLayout = 128,
UpdateEventEmitter = 256,
UpdatePadding = 512
};
#pragma mark - Fields
Type type = {Create};
ShadowView parentShadowView = {};
ShadowView oldChildShadowView = {};
ShadowView newChildShadowView = {};
int index = {};
};
} // namespace react
} // namespace facebook
@@ -0,0 +1,821 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "FabricMountingManager.h"
#include "EventEmitterWrapper.h"
#include "StateWrapperImpl.h"
#include <react/jni/ReadableNativeMap.h>
#include <react/renderer/components/scrollview/ScrollViewProps.h>
#include <react/renderer/core/conversions.h>
#include <react/renderer/debug/SystraceSection.h>
#include <react/renderer/mounting/ShadowViewMutation.h>
#include <fbjni/fbjni.h>
#include <glog/logging.h>
#include <cfenv>
#include <cmath>
#include <vector>
using namespace facebook::jni;
namespace facebook {
namespace react {
static inline int getIntBufferSizeForType(CppMountItem::Type mountItemType) {
switch (mountItemType) {
case CppMountItem::Type::Create:
return 2; // tag, isLayoutable
case CppMountItem::Type::Insert:
case CppMountItem::Type::Remove:
return 3; // tag, parentTag, index
case CppMountItem::Type::Delete:
case CppMountItem::Type::UpdateProps:
case CppMountItem::Type::UpdateState:
case CppMountItem::Type::UpdateEventEmitter:
return 1; // tag
case CppMountItem::Type::UpdatePadding:
return 5; // tag, top, left, bottom, right
case CppMountItem::Type::UpdateLayout:
return 6; // tag, x, y, w, h, DisplayType
case CppMountItem::Undefined:
case CppMountItem::Multiple:
return -1;
}
}
static inline void updateBufferSizes(
CppMountItem::Type mountItemType,
int numInstructions,
int &batchMountItemIntsSize,
int &batchMountItemObjectsSize) {
if (numInstructions == 0) {
return;
}
batchMountItemIntsSize +=
numInstructions == 1 ? 1 : 2; // instructionType[, numInstructions]
batchMountItemIntsSize +=
numInstructions * getIntBufferSizeForType(mountItemType);
if (mountItemType == CppMountItem::Type::UpdateProps) {
batchMountItemObjectsSize +=
numInstructions; // props object * numInstructions
} else if (mountItemType == CppMountItem::Type::UpdateState) {
batchMountItemObjectsSize +=
numInstructions; // state object * numInstructions
} else if (mountItemType == CppMountItem::Type::UpdateEventEmitter) {
batchMountItemObjectsSize +=
numInstructions; // EventEmitter object * numInstructions
}
}
static inline void computeBufferSizes(
int &batchMountItemIntsSize,
int &batchMountItemObjectsSize,
std::vector<CppMountItem> &cppCommonMountItems,
std::vector<CppMountItem> &cppDeleteMountItems,
std::vector<CppMountItem> &cppUpdatePropsMountItems,
std::vector<CppMountItem> &cppUpdateStateMountItems,
std::vector<CppMountItem> &cppUpdatePaddingMountItems,
std::vector<CppMountItem> &cppUpdateLayoutMountItems,
std::vector<CppMountItem> &cppUpdateEventEmitterMountItems) {
CppMountItem::Type lastType = CppMountItem::Type::Undefined;
int numSameType = 0;
for (auto const &mountItem : cppCommonMountItems) {
const auto &mountItemType = mountItem.type;
if (lastType == mountItemType) {
numSameType++;
if (numSameType == 2) {
batchMountItemIntsSize += 1; // numInstructions
}
} else {
numSameType = 1;
lastType = mountItemType;
batchMountItemIntsSize += 1; // instructionType
}
batchMountItemIntsSize += getIntBufferSizeForType(mountItemType);
if (mountItemType == CppMountItem::Type::Create) {
batchMountItemObjectsSize +=
4; // component name, props, state, event emitter
}
}
updateBufferSizes(
CppMountItem::Type::UpdateProps,
cppUpdatePropsMountItems.size(),
batchMountItemIntsSize,
batchMountItemObjectsSize);
updateBufferSizes(
CppMountItem::Type::UpdateState,
cppUpdateStateMountItems.size(),
batchMountItemIntsSize,
batchMountItemObjectsSize);
updateBufferSizes(
CppMountItem::Type::UpdatePadding,
cppUpdatePaddingMountItems.size(),
batchMountItemIntsSize,
batchMountItemObjectsSize);
updateBufferSizes(
CppMountItem::Type::UpdateLayout,
cppUpdateLayoutMountItems.size(),
batchMountItemIntsSize,
batchMountItemObjectsSize);
updateBufferSizes(
CppMountItem::Type::UpdateEventEmitter,
cppUpdateEventEmitterMountItems.size(),
batchMountItemIntsSize,
batchMountItemObjectsSize);
updateBufferSizes(
CppMountItem::Type::Delete,
cppDeleteMountItems.size(),
batchMountItemIntsSize,
batchMountItemObjectsSize);
}
static inline void writeIntBufferTypePreamble(
int mountItemType,
int numItems,
_JNIEnv *env,
jintArray &intBufferArray,
int &intBufferPosition) {
jint temp[2];
if (numItems == 1) {
temp[0] = mountItemType;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp);
intBufferPosition += 1;
} else {
temp[0] = mountItemType | CppMountItem::Type::Multiple;
temp[1] = numItems;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 2, temp);
intBufferPosition += 2;
}
}
inline local_ref<ReadableMap::javaobject> castReadableMap(
local_ref<ReadableNativeMap::javaobject> const &nativeMap) {
return make_local(reinterpret_cast<ReadableMap::javaobject>(nativeMap.get()));
}
inline local_ref<ReadableArray::javaobject> castReadableArray(
local_ref<ReadableNativeArray::javaobject> const &nativeArray) {
return make_local(
reinterpret_cast<ReadableArray::javaobject>(nativeArray.get()));
}
// TODO: this method will be removed when binding for components are code-gen
local_ref<JString> getPlatformComponentName(ShadowView const &shadowView) {
static std::string scrollViewComponentName = std::string("ScrollView");
local_ref<JString> componentName;
if (scrollViewComponentName == shadowView.componentName) {
auto newViewProps =
std::static_pointer_cast<const ScrollViewProps>(shadowView.props);
if (newViewProps->getProbablyMoreHorizontalThanVertical_DEPRECATED()) {
componentName = make_jstring("AndroidHorizontalScrollView");
return componentName;
}
}
componentName = make_jstring(shadowView.componentName);
return componentName;
}
static inline float scale(Float value, Float pointScaleFactor) {
std::feclearexcept(FE_ALL_EXCEPT);
float result = value * pointScaleFactor;
if (std::fetestexcept(FE_OVERFLOW)) {
LOG(ERROR) << "Binding::scale - FE_OVERFLOW - value: " << value
<< " pointScaleFactor: " << pointScaleFactor
<< " result: " << result;
}
if (std::fetestexcept(FE_UNDERFLOW)) {
LOG(ERROR) << "Binding::scale - FE_UNDERFLOW - value: " << value
<< " pointScaleFactor: " << pointScaleFactor
<< " result: " << result;
}
return result;
}
void FabricMountingManager::executeMount(
MountingCoordinator::Shared const &mountingCoordinator) {
std::lock_guard<std::recursive_mutex> lock(commitMutex_);
SystraceSection s(
"FabricUIManagerBinding::schedulerDidFinishTransactionIntBuffer");
auto finishTransactionStartTime = telemetryTimePointNow();
auto mountingTransaction = mountingCoordinator->pullTransaction();
if (!mountingTransaction.has_value()) {
return;
}
auto env = Environment::current();
auto telemetry = mountingTransaction->getTelemetry();
auto surfaceId = mountingTransaction->getSurfaceId();
auto &mutations = mountingTransaction->getMutations();
auto revisionNumber = telemetry.getRevisionNumber();
std::vector<CppMountItem> cppCommonMountItems;
std::vector<CppMountItem> cppDeleteMountItems;
std::vector<CppMountItem> cppUpdatePropsMountItems;
std::vector<CppMountItem> cppUpdateStateMountItems;
std::vector<CppMountItem> cppUpdatePaddingMountItems;
std::vector<CppMountItem> cppUpdateLayoutMountItems;
std::vector<CppMountItem> cppUpdateEventEmitterMountItems;
for (const auto &mutation : mutations) {
const auto &parentShadowView = mutation.parentShadowView;
const auto &oldChildShadowView = mutation.oldChildShadowView;
const auto &newChildShadowView = mutation.newChildShadowView;
auto &mutationType = mutation.type;
auto &index = mutation.index;
bool isVirtual = mutation.mutatedViewIsVirtual();
bool noRevisionCheck =
disablePreallocateViews_ || disableRevisionCheckForPreallocation_;
switch (mutationType) {
case ShadowViewMutation::Create: {
if (noRevisionCheck || newChildShadowView.props->revision > 1) {
cppCommonMountItems.push_back(
CppMountItem::CreateMountItem(newChildShadowView));
}
break;
}
case ShadowViewMutation::Remove: {
if (!isVirtual) {
cppCommonMountItems.push_back(CppMountItem::RemoveMountItem(
parentShadowView, oldChildShadowView, index));
}
break;
}
case ShadowViewMutation::Delete: {
cppDeleteMountItems.push_back(
CppMountItem::DeleteMountItem(oldChildShadowView));
break;
}
case ShadowViewMutation::Update: {
if (!isVirtual) {
if (oldChildShadowView.props != newChildShadowView.props) {
cppUpdatePropsMountItems.push_back(
CppMountItem::UpdatePropsMountItem(newChildShadowView));
}
if (oldChildShadowView.state != newChildShadowView.state) {
cppUpdateStateMountItems.push_back(
CppMountItem::UpdateStateMountItem(newChildShadowView));
}
// Padding: padding mountItems must be executed before layout props
// are updated in the view. This is necessary to ensure that events
// (resulting from layout changes) are dispatched with the correct
// padding information.
if (oldChildShadowView.layoutMetrics.contentInsets !=
newChildShadowView.layoutMetrics.contentInsets) {
cppUpdatePaddingMountItems.push_back(
CppMountItem::UpdatePaddingMountItem(newChildShadowView));
}
if (oldChildShadowView.layoutMetrics !=
newChildShadowView.layoutMetrics) {
cppUpdateLayoutMountItems.push_back(
CppMountItem::UpdateLayoutMountItem(
mutation.newChildShadowView));
}
}
if (oldChildShadowView.eventEmitter !=
newChildShadowView.eventEmitter) {
cppUpdateEventEmitterMountItems.push_back(
CppMountItem::UpdateEventEmitterMountItem(
mutation.newChildShadowView));
}
break;
}
case ShadowViewMutation::Insert: {
if (!isVirtual) {
// Insert item
cppCommonMountItems.push_back(CppMountItem::InsertMountItem(
parentShadowView, newChildShadowView, index));
if (noRevisionCheck || newChildShadowView.props->revision > 1) {
cppUpdatePropsMountItems.push_back(
CppMountItem::UpdatePropsMountItem(newChildShadowView));
}
// State
if (newChildShadowView.state) {
cppUpdateStateMountItems.push_back(
CppMountItem::UpdateStateMountItem(newChildShadowView));
}
// Padding: padding mountItems must be executed before layout props
// are updated in the view. This is necessary to ensure that events
// (resulting from layout changes) are dispatched with the correct
// padding information.
cppUpdatePaddingMountItems.push_back(
CppMountItem::UpdatePaddingMountItem(
mutation.newChildShadowView));
// Layout
cppUpdateLayoutMountItems.push_back(
CppMountItem::UpdateLayoutMountItem(mutation.newChildShadowView));
}
// EventEmitter
cppUpdateEventEmitterMountItems.push_back(
CppMountItem::UpdateEventEmitterMountItem(
mutation.newChildShadowView));
break;
}
default: {
break;
}
}
}
// We now have all the information we need, including ordering of mount items,
// to know exactly how much space must be allocated
int batchMountItemIntsSize = 0;
int batchMountItemObjectsSize = 0;
computeBufferSizes(
batchMountItemIntsSize,
batchMountItemObjectsSize,
cppCommonMountItems,
cppDeleteMountItems,
cppUpdatePropsMountItems,
cppUpdateStateMountItems,
cppUpdatePaddingMountItems,
cppUpdateLayoutMountItems,
cppUpdateEventEmitterMountItems);
static auto createMountItemsIntBufferBatchContainer =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(
jint, jintArray, jtypeArray<jobject>, jint)>(
"createIntBufferBatchMountItem");
static auto scheduleMountItem = jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void(
JMountItem::javaobject,
jint,
jlong,
jlong,
jlong,
jlong,
jlong,
jlong,
jlong)>("scheduleMountItem");
if (batchMountItemIntsSize == 0) {
auto finishTransactionEndTime = telemetryTimePointNow();
scheduleMountItem(
javaUIManager_,
nullptr,
telemetry.getRevisionNumber(),
telemetryTimePointToMilliseconds(telemetry.getCommitStartTime()),
telemetryTimePointToMilliseconds(telemetry.getDiffStartTime()),
telemetryTimePointToMilliseconds(telemetry.getDiffEndTime()),
telemetryTimePointToMilliseconds(telemetry.getLayoutStartTime()),
telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()),
telemetryTimePointToMilliseconds(finishTransactionStartTime),
telemetryTimePointToMilliseconds(finishTransactionEndTime));
return;
}
// Allocate the intBuffer and object array, now that we know exact sizes
// necessary
// TODO: don't allocate at all if size is zero
jintArray intBufferArray = env->NewIntArray(batchMountItemIntsSize);
local_ref<JArrayClass<jobject>> objBufferArray =
JArrayClass<jobject>::newArray(batchMountItemObjectsSize);
// Fill in arrays
int intBufferPosition = 0;
int objBufferPosition = 0;
int prevMountItemType = -1;
jint temp[7];
for (int i = 0; i < cppCommonMountItems.size(); i++) {
const auto &mountItem = cppCommonMountItems[i];
const auto &mountItemType = mountItem.type;
// Get type here, and count forward how many items of this type are in a
// row. Write preamble to any common type here.
if (prevMountItemType != mountItemType) {
int numSameItemTypes = 1;
for (int j = i + 1; j < cppCommonMountItems.size() &&
cppCommonMountItems[j].type == mountItemType;
j++) {
numSameItemTypes++;
}
writeIntBufferTypePreamble(
mountItemType,
numSameItemTypes,
env,
intBufferArray,
intBufferPosition);
}
prevMountItemType = mountItemType;
// TODO: multi-create, multi-insert, etc
if (mountItemType == CppMountItem::Type::Create) {
local_ref<JString> componentName =
getPlatformComponentName(mountItem.newChildShadowView);
int isLayoutable =
mountItem.newChildShadowView.layoutMetrics != EmptyLayoutMetrics ? 1
: 0;
local_ref<ReadableMap::javaobject> props =
castReadableMap(ReadableNativeMap::newObjectCxxArgs(
mountItem.newChildShadowView.props->rawProps));
// Do not hold onto Java object from C
// We DO want to hold onto C object from Java, since we don't know the
// lifetime of the Java object
local_ref<StateWrapperImpl::JavaPart> javaStateWrapper = nullptr;
if (mountItem.newChildShadowView.state != nullptr) {
javaStateWrapper = StateWrapperImpl::newObjectJavaArgs();
StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper);
cStateWrapper->state_ = mountItem.newChildShadowView.state;
}
// Do not hold a reference to javaEventEmitter from the C++ side.
SharedEventEmitter eventEmitter =
mountItem.newChildShadowView.eventEmitter;
auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs();
EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter);
if (enableEventEmitterRawPointer_) {
cEventEmitter->eventEmitterPointer = eventEmitter.get();
} else {
cEventEmitter->eventEmitter = eventEmitter;
}
temp[0] = mountItem.newChildShadowView.tag;
temp[1] = isLayoutable;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 2, temp);
intBufferPosition += 2;
(*objBufferArray)[objBufferPosition++] = componentName.get();
(*objBufferArray)[objBufferPosition++] = props.get();
(*objBufferArray)[objBufferPosition++] =
javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr;
(*objBufferArray)[objBufferPosition++] = javaEventEmitter.get();
} else if (mountItemType == CppMountItem::Type::Insert) {
temp[0] = mountItem.newChildShadowView.tag;
temp[1] = mountItem.parentShadowView.tag;
temp[2] = mountItem.index;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 3, temp);
intBufferPosition += 3;
} else if (mountItemType == CppMountItem::Remove) {
temp[0] = mountItem.oldChildShadowView.tag;
temp[1] = mountItem.parentShadowView.tag;
temp[2] = mountItem.index;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 3, temp);
intBufferPosition += 3;
} else {
LOG(ERROR) << "Unexpected CppMountItem type";
}
}
if (!cppUpdatePropsMountItems.empty()) {
writeIntBufferTypePreamble(
CppMountItem::Type::UpdateProps,
cppUpdatePropsMountItems.size(),
env,
intBufferArray,
intBufferPosition);
for (const auto &mountItem : cppUpdatePropsMountItems) {
temp[0] = mountItem.newChildShadowView.tag;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp);
intBufferPosition += 1;
auto newProps = mountItem.newChildShadowView.props->rawProps;
local_ref<ReadableMap::javaobject> newPropsReadableMap =
castReadableMap(ReadableNativeMap::newObjectCxxArgs(newProps));
(*objBufferArray)[objBufferPosition++] = newPropsReadableMap.get();
}
}
if (!cppUpdateStateMountItems.empty()) {
writeIntBufferTypePreamble(
CppMountItem::Type::UpdateState,
cppUpdateStateMountItems.size(),
env,
intBufferArray,
intBufferPosition);
for (const auto &mountItem : cppUpdateStateMountItems) {
temp[0] = mountItem.newChildShadowView.tag;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp);
intBufferPosition += 1;
auto state = mountItem.newChildShadowView.state;
// Do not hold onto Java object from C
// We DO want to hold onto C object from Java, since we don't know the
// lifetime of the Java object
local_ref<StateWrapperImpl::JavaPart> javaStateWrapper = nullptr;
if (state != nullptr) {
javaStateWrapper = StateWrapperImpl::newObjectJavaArgs();
StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper);
cStateWrapper->state_ = state;
}
(*objBufferArray)[objBufferPosition++] =
(javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr);
}
}
if (!cppUpdatePaddingMountItems.empty()) {
writeIntBufferTypePreamble(
CppMountItem::Type::UpdatePadding,
cppUpdatePaddingMountItems.size(),
env,
intBufferArray,
intBufferPosition);
for (const auto &mountItem : cppUpdatePaddingMountItems) {
auto layoutMetrics = mountItem.newChildShadowView.layoutMetrics;
auto pointScaleFactor = layoutMetrics.pointScaleFactor;
auto contentInsets = layoutMetrics.contentInsets;
int left = floor(scale(contentInsets.left, pointScaleFactor));
int top = floor(scale(contentInsets.top, pointScaleFactor));
int right = floor(scale(contentInsets.right, pointScaleFactor));
int bottom = floor(scale(contentInsets.bottom, pointScaleFactor));
temp[0] = mountItem.newChildShadowView.tag;
temp[1] = left;
temp[2] = top;
temp[3] = right;
temp[4] = bottom;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 5, temp);
intBufferPosition += 5;
}
}
if (!cppUpdateLayoutMountItems.empty()) {
writeIntBufferTypePreamble(
CppMountItem::Type::UpdateLayout,
cppUpdateLayoutMountItems.size(),
env,
intBufferArray,
intBufferPosition);
for (const auto &mountItem : cppUpdateLayoutMountItems) {
auto layoutMetrics = mountItem.newChildShadowView.layoutMetrics;
auto pointScaleFactor = layoutMetrics.pointScaleFactor;
auto frame = layoutMetrics.frame;
int x = round(scale(frame.origin.x, pointScaleFactor));
int y = round(scale(frame.origin.y, pointScaleFactor));
int w = round(scale(frame.size.width, pointScaleFactor));
int h = round(scale(frame.size.height, pointScaleFactor));
int displayType =
toInt(mountItem.newChildShadowView.layoutMetrics.displayType);
temp[0] = mountItem.newChildShadowView.tag;
temp[1] = x;
temp[2] = y;
temp[3] = w;
temp[4] = h;
temp[5] = displayType;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 6, temp);
intBufferPosition += 6;
}
}
if (!cppUpdateEventEmitterMountItems.empty()) {
writeIntBufferTypePreamble(
CppMountItem::Type::UpdateEventEmitter,
cppUpdateEventEmitterMountItems.size(),
env,
intBufferArray,
intBufferPosition);
for (const auto &mountItem : cppUpdateEventEmitterMountItems) {
temp[0] = mountItem.newChildShadowView.tag;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp);
intBufferPosition += 1;
SharedEventEmitter eventEmitter =
mountItem.newChildShadowView.eventEmitter;
// Do not hold a reference to javaEventEmitter from the C++ side.
auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs();
EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter);
if (enableEventEmitterRawPointer_) {
cEventEmitter->eventEmitterPointer = eventEmitter.get();
} else {
cEventEmitter->eventEmitter = eventEmitter;
}
(*objBufferArray)[objBufferPosition++] = javaEventEmitter.get();
}
}
// Write deletes last - so that all prop updates, etc, for the tag in the same
// batch don't fail. Without additional machinery, moving deletes here
// requires that the differ never produces "DELETE...CREATE" in that order for
// the same tag. It's nice to be able to batch all similar operations together
// for space efficiency.
if (!cppDeleteMountItems.empty()) {
writeIntBufferTypePreamble(
CppMountItem::Type::Delete,
cppDeleteMountItems.size(),
env,
intBufferArray,
intBufferPosition);
for (const auto &mountItem : cppDeleteMountItems) {
temp[0] = mountItem.oldChildShadowView.tag;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp);
intBufferPosition += 1;
}
}
// If there are no items, we pass a nullptr instead of passing the object
// through the JNI
auto batch = createMountItemsIntBufferBatchContainer(
javaUIManager_,
surfaceId,
batchMountItemIntsSize == 0 ? nullptr : intBufferArray,
batchMountItemObjectsSize == 0 ? nullptr : objBufferArray.get(),
revisionNumber);
auto finishTransactionEndTime = telemetryTimePointNow();
scheduleMountItem(
javaUIManager_,
batch.get(),
telemetry.getRevisionNumber(),
telemetryTimePointToMilliseconds(telemetry.getCommitStartTime()),
telemetryTimePointToMilliseconds(telemetry.getDiffStartTime()),
telemetryTimePointToMilliseconds(telemetry.getDiffEndTime()),
telemetryTimePointToMilliseconds(telemetry.getLayoutStartTime()),
telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()),
telemetryTimePointToMilliseconds(finishTransactionStartTime),
telemetryTimePointToMilliseconds(finishTransactionEndTime));
env->DeleteLocalRef(intBufferArray);
}
void FabricMountingManager::preallocateShadowView(
SurfaceId surfaceId,
ShadowView const &shadowView) {
bool isLayoutableShadowNode = shadowView.layoutMetrics != EmptyLayoutMetrics;
static auto preallocateView = jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void(
jint,
jint,
jstring,
ReadableMap::javaobject,
jobject,
jobject,
jboolean)>("preallocateView");
// Do not hold onto Java object from C
// We DO want to hold onto C object from Java, since we don't know the
// lifetime of the Java object
local_ref<StateWrapperImpl::JavaPart> javaStateWrapper = nullptr;
if (shadowView.state != nullptr) {
javaStateWrapper = StateWrapperImpl::newObjectJavaArgs();
StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper);
cStateWrapper->state_ = shadowView.state;
}
// Do not hold a reference to javaEventEmitter from the C++ side.
local_ref<EventEmitterWrapper::JavaPart> javaEventEmitter = nullptr;
if (enableEarlyEventEmitterUpdate_) {
SharedEventEmitter eventEmitter = shadowView.eventEmitter;
if (eventEmitter != nullptr) {
javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs();
EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter);
if (enableEventEmitterRawPointer_) {
cEventEmitter->eventEmitterPointer = eventEmitter.get();
} else {
cEventEmitter->eventEmitter = eventEmitter;
}
}
}
local_ref<ReadableMap::javaobject> props = castReadableMap(
ReadableNativeMap::newObjectCxxArgs(shadowView.props->rawProps));
auto component = getPlatformComponentName(shadowView);
preallocateView(
javaUIManager_,
surfaceId,
shadowView.tag,
component.get(),
props.get(),
(javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr),
(javaEventEmitter != nullptr ? javaEventEmitter.get() : nullptr),
isLayoutableShadowNode);
}
void FabricMountingManager::dispatchCommand(
ShadowView const &shadowView,
std::string const &commandName,
folly::dynamic const &args) {
static auto dispatchCommand =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void(jint, jint, jstring, ReadableArray::javaobject)>(
"dispatchCommand");
local_ref<JString> command = make_jstring(commandName);
local_ref<ReadableArray::javaobject> argsArray =
castReadableArray(ReadableNativeArray::newObjectCxxArgs(args));
dispatchCommand(
javaUIManager_,
shadowView.surfaceId,
shadowView.tag,
command.get(),
argsArray.get());
}
void FabricMountingManager::sendAccessibilityEvent(
ShadowView const &shadowView,
std::string const &eventType) {
local_ref<JString> eventTypeStr = make_jstring(eventType);
static auto sendAccessibilityEventFromJS =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void(jint, jint, jstring)>(
"sendAccessibilityEventFromJS");
sendAccessibilityEventFromJS(
javaUIManager_, shadowView.surfaceId, shadowView.tag, eventTypeStr.get());
}
void FabricMountingManager::setIsJSResponder(
ShadowView const &shadowView,
bool isJSResponder,
bool blockNativeResponder) {
static auto setJSResponder =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void(jint, jint, jint, jboolean)>("setJSResponder");
static auto clearJSResponder = jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void()>("clearJSResponder");
if (isJSResponder) {
setJSResponder(
javaUIManager_,
shadowView.surfaceId,
shadowView.tag,
// The closest non-flattened ancestor of the same value if the node is
// not flattened. For now, we don't support the case when the node can
// be flattened because the only component that uses this feature -
// ScrollView - cannot be flattened.
shadowView.tag,
(jboolean)blockNativeResponder);
} else {
clearJSResponder(javaUIManager_);
}
}
void FabricMountingManager::onAnimationStarted() {
static auto layoutAnimationsStartedJNI =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void()>("onAnimationStarted");
layoutAnimationsStartedJNI(javaUIManager_);
}
void FabricMountingManager::onAllAnimationsComplete() {
static auto allAnimationsCompleteJNI =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void()>("onAllAnimationsComplete");
allAnimationsCompleteJNI(javaUIManager_);
}
FabricMountingManager::FabricMountingManager(
std::shared_ptr<const ReactNativeConfig> &config,
global_ref<jobject> &javaUIManager)
: javaUIManager_(javaUIManager) {
enableEarlyEventEmitterUpdate_ =
config->getBool("react_fabric:enable_early_event_emitter_update");
enableEventEmitterRawPointer_ =
config->getBool("react_fabric:enable_event_emitter_wrapper_raw_pointer");
disablePreallocateViews_ =
config->getBool("react_fabric:disabled_view_preallocation_android");
disableRevisionCheckForPreallocation_ =
config->getBool("react_fabric:disable_revision_check_for_preallocation");
}
} // namespace react
} // namespace facebook
@@ -0,0 +1,71 @@
/*
* Copyright (c) Facebook, Inc. and its 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 "FabricMountItem.h"
#include <react/config/ReactNativeConfig.h>
#include <react/renderer/animations/LayoutAnimationDriver.h>
#include <react/renderer/mounting/MountingCoordinator.h>
#include <react/renderer/mounting/ShadowView.h>
#include <react/renderer/uimanager/LayoutAnimationStatusDelegate.h>
#include <react/utils/ContextContainer.h>
#include <fbjni/fbjni.h>
#include <mutex>
namespace facebook {
namespace react {
class FabricMountingManager {
public:
constexpr static auto UIManagerJavaDescriptor =
"com/facebook/react/fabric/FabricUIManager";
FabricMountingManager(
std::shared_ptr<const ReactNativeConfig> &config,
jni::global_ref<jobject> &javaUIManager);
void preallocateShadowView(SurfaceId surfaceId, const ShadowView &shadowView);
void executeMount(MountingCoordinator::Shared const &mountingCoordinator);
void dispatchCommand(
ShadowView const &shadowView,
std::string const &commandName,
folly::dynamic const &args);
void sendAccessibilityEvent(
const ShadowView &shadowView,
std::string const &eventType);
void setIsJSResponder(
ShadowView const &shadowView,
bool isJSResponder,
bool blockNativeResponder);
void onAnimationStarted();
void onAllAnimationsComplete();
virtual ~FabricMountingManager() = default;
private:
jni::global_ref<jobject> javaUIManager_;
std::recursive_mutex commitMutex_;
bool enableEventEmitterRawPointer_{false};
bool enableEarlyEventEmitterUpdate_{false};
bool disablePreallocateViews_{false};
bool disableRevisionCheckForPreallocation_{false};
};
} // namespace react
} // namespace facebook