This commit is contained in:
Yamozha
2021-04-02 02:24:13 +03:00
parent c23950b545
commit 7256d79e2c
31493 changed files with 3036630 additions and 0 deletions

View File

@ -0,0 +1,93 @@
load("@fbsource//tools/build_defs/apple:flag_defs.bzl", "get_preprocessor_flags_for_build_mode")
load(
"//tools/build_defs/oss:rn_defs.bzl",
"ANDROID",
"APPLE",
"CXX",
"fb_xplat_cxx_test",
"get_apple_compiler_flags",
"get_apple_inspector_flags",
"react_native_xplat_target",
"rn_xplat_cxx_library",
"subdir_glob",
)
APPLE_COMPILER_FLAGS = get_apple_compiler_flags()
rn_xplat_cxx_library(
name = "uimanager",
srcs = glob(
["**/*.cpp"],
exclude = glob(["tests/**/*.cpp"]),
),
headers = glob(
["**/*.h"],
exclude = glob(["tests/**/*.h"]),
),
header_namespace = "",
exported_headers = subdir_glob(
[
("", "*.h"),
],
prefix = "react/uimanager",
),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
fbobjc_labels = ["supermodule:ios/default/public.react_native.infra"],
fbobjc_preprocessor_flags = get_preprocessor_flags_for_build_mode() + get_apple_inspector_flags(),
force_static = True,
macosx_tests_override = [],
platforms = (ANDROID, APPLE, CXX),
preprocessor_flags = [
"-DLOG_TAG=\"ReactNative\"",
"-DWITH_FBSYSTRACE=1",
],
tests = [":tests"],
visibility = ["PUBLIC"],
deps = [
"//xplat/fbsystrace:fbsystrace",
"//xplat/folly:headers_only",
"//xplat/folly:memory",
"//xplat/folly:molly",
"//xplat/jsi:JSIDynamic",
"//xplat/jsi:jsi",
"//xplat/third-party/glog:glog",
react_native_xplat_target("config:config"),
react_native_xplat_target("fabric/components/view:view"),
react_native_xplat_target("fabric/mounting:mounting"),
react_native_xplat_target("fabric/core:core"),
react_native_xplat_target("fabric/debug:debug"),
react_native_xplat_target("utils:utils"),
],
)
fb_xplat_cxx_test(
name = "tests",
srcs = glob(["tests/**/*.cpp"]),
headers = glob(["tests/**/*.h"]),
compiler_flags = [
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
],
contacts = ["oncall+react_native@xmail.facebook.com"],
platforms = (ANDROID, APPLE, CXX),
deps = [
":uimanager",
"//xplat/folly:molly",
"//xplat/third-party/gmock:gtest",
react_native_xplat_target("config:config"),
react_native_xplat_target("fabric/components/activityindicator:activityindicator"),
react_native_xplat_target("fabric/components/image:image"),
react_native_xplat_target("fabric/components/root:root"),
react_native_xplat_target("fabric/components/scrollview:scrollview"),
react_native_xplat_target("fabric/components/view:view"),
"//xplat/js/react-native-github:generated_components-rncore",
],
)

View File

@ -0,0 +1,34 @@
/*
* 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 <memory>
#include <react/core/ComponentDescriptor.h>
#include <react/core/EventDispatcher.h>
#include <react/utils/ContextContainer.h>
#include "ComponentDescriptorRegistry.h"
namespace facebook {
namespace react {
/**
* A factory to provide hosting app specific set of ComponentDescriptor's.
* Each app must provide an implementation of the static class method which
* should register its specific set of supported components.
*/
using ComponentRegistryFactory =
std::function<SharedComponentDescriptorRegistry(
EventDispatcher::Weak const &eventDispatcher,
ContextContainer::Shared const &contextContainer)>;
ComponentRegistryFactory getDefaultComponentRegistryFactory();
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,73 @@
/*
* 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 <react/core/ComponentDescriptor.h>
#include <react/core/EventDispatcher.h>
#include <react/utils/ContextContainer.h>
namespace facebook {
namespace react {
/*
* Callable signature that represents the signature of `ComponentDescriptor`
* constructor. The callable returns a unique pointer conveniently represents an
* abstract type and ownership of the newly created object.
*/
using ComponentDescriptorConstructor = ComponentDescriptor::Unique(
ComponentDescriptorParameters const &parameters);
/*
* Represents a unified way to construct an instance of a particular stored
* `ComponentDescriptor` class. C++ does not allow to create pointers to
* constructors, so we have to have such data structure to manipulate a
* collection of classes.
*
* Note: The actual values of `handle` and `name` for some components depend on
* `flavor`. The provider is valid if instantiated by `constructor` object with
* given `flavor` exposes the same values of `handle` and `name`.
*/
class ComponentDescriptorProvider final {
public:
ComponentHandle handle;
ComponentName name;
ComponentDescriptor::Flavor flavor;
ComponentDescriptorConstructor *constructor;
};
/*
* Creates a `ComponentDescriptor` for given `ComponentDescriptorParameters`.
*/
template <typename ComponentDescriptorT>
ComponentDescriptor::Unique concreteComponentDescriptorConstructor(
ComponentDescriptorParameters const &parameters) {
static_assert(
std::is_base_of<ComponentDescriptor, ComponentDescriptorT>::value,
"ComponentDescriptorT must be a descendant of ComponentDescriptor");
return std::make_unique<ComponentDescriptorT const>(parameters);
}
/*
* Creates a `ComponentDescriptorProvider` for given `ComponentDescriptor`
* class.
*/
template <typename ComponentDescriptorT>
ComponentDescriptorProvider concreteComponentDescriptorProvider() {
static_assert(
std::is_base_of<ComponentDescriptor, ComponentDescriptorT>::value,
"ComponentDescriptorT must be a descendant of ComponentDescriptor");
return {ComponentDescriptorT::ConcreteShadowNode::Handle(),
ComponentDescriptorT::ConcreteShadowNode::Name(),
nullptr,
&concreteComponentDescriptorConstructor<ComponentDescriptorT>};
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,84 @@
/*
* 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 "ComponentDescriptorProviderRegistry.h"
namespace facebook {
namespace react {
void ComponentDescriptorProviderRegistry::add(
ComponentDescriptorProvider provider) const {
std::unique_lock<better::shared_mutex> lock(mutex_);
/*
// TODO: T57583139
The assert is temporarily disabled to reduce the volume of the signal.
assert(
componentDescriptorProviders_.find(provider.handle) ==
componentDescriptorProviders_.end() &&
"Attempt to register an already registered ComponentDescriptorProvider.");
*/
if (componentDescriptorProviders_.find(provider.handle) !=
componentDescriptorProviders_.end()) {
// Re-registering a provider makes no sense because it's copyable: already
// registered one is as good as any new can be.
return;
}
componentDescriptorProviders_.insert({provider.handle, provider});
for (auto const &weakRegistry : componentDescriptorRegistries_) {
auto registry = weakRegistry.lock();
if (!registry) {
continue;
}
registry->add(provider);
}
}
void ComponentDescriptorProviderRegistry::setComponentDescriptorProviderRequest(
ComponentDescriptorProviderRequest componentDescriptorProviderRequest)
const {
std::shared_lock<better::shared_mutex> lock(mutex_);
componentDescriptorProviderRequest_ = componentDescriptorProviderRequest;
}
void ComponentDescriptorProviderRegistry::request(
ComponentName componentName) const {
ComponentDescriptorProviderRequest componentDescriptorProviderRequest;
{
std::shared_lock<better::shared_mutex> lock(mutex_);
componentDescriptorProviderRequest = componentDescriptorProviderRequest_;
}
if (componentDescriptorProviderRequest) {
componentDescriptorProviderRequest(componentName);
}
}
ComponentDescriptorRegistry::Shared
ComponentDescriptorProviderRegistry::createComponentDescriptorRegistry(
ComponentDescriptorParameters const &parameters) const {
std::shared_lock<better::shared_mutex> lock(mutex_);
auto registry =
std::make_shared<ComponentDescriptorRegistry const>(parameters, *this);
for (auto const &pair : componentDescriptorProviders_) {
registry->add(pair.second);
}
componentDescriptorRegistries_.push_back(registry);
return registry;
}
} // namespace react
} // namespace facebook

View File

@ -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 <better/mutex.h>
#include <react/core/ComponentDescriptor.h>
#include <react/uimanager/ComponentDescriptorProvider.h>
#include <react/uimanager/ComponentDescriptorRegistry.h>
namespace facebook {
namespace react {
using ComponentDescriptorProviderRequest =
std::function<void(ComponentName componentName)>;
/*
* Registry of `ComponentDescriptorProvider`s (and managed
* `ComponentDescriptorRegistry`s). The class maintains a list of
* `ComponentDescriptorRegistry`s (retaining pointers weakly) and update them
* accordingly to changes in the provider registry.
*/
class ComponentDescriptorProviderRegistry final {
public:
/*
* Adds a `ComponentDescriptorProvider`s and update the managed
* `ComponentDescriptorRegistry`s accordingly.
* The methods can be called on any thread.
*/
void add(ComponentDescriptorProvider provider) const;
/*
* ComponenDescriptorRegistry will call the `request` in case if a component
* with given name wasn't registered yet.
* The request handler must register a ComponentDescripor with requested name
* synchronously during handling the request.
* The request can be called on any thread.
* The methods can be called on any thread.
*/
void setComponentDescriptorProviderRequest(
ComponentDescriptorProviderRequest request) const;
/*
* Creates managed `ComponentDescriptorRegistry` based on a stored list of
* `ComponentDescriptorProvider`s and given `ComponentDescriptorParameters`.
* The methods can be called on any thread.
*/
ComponentDescriptorRegistry::Shared createComponentDescriptorRegistry(
ComponentDescriptorParameters const &parameters) const;
private:
friend class ComponentDescriptorRegistry;
void request(ComponentName componentName) const;
mutable better::shared_mutex mutex_;
mutable std::vector<std::weak_ptr<ComponentDescriptorRegistry const>>
componentDescriptorRegistries_;
mutable better::map<ComponentHandle, ComponentDescriptorProvider const>
componentDescriptorProviders_;
mutable ComponentDescriptorProviderRequest
componentDescriptorProviderRequest_;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,210 @@
/*
* 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 "ComponentDescriptorRegistry.h"
#include <react/core/ShadowNodeFragment.h>
#include <react/uimanager/ComponentDescriptorProviderRegistry.h>
#include <react/uimanager/primitives.h>
namespace facebook {
namespace react {
ComponentDescriptorRegistry::ComponentDescriptorRegistry(
ComponentDescriptorParameters const &parameters,
ComponentDescriptorProviderRegistry const &providerRegistry)
: parameters_(parameters), providerRegistry_(providerRegistry) {}
void ComponentDescriptorRegistry::add(
ComponentDescriptorProvider componentDescriptorProvider) const {
std::unique_lock<better::shared_mutex> lock(mutex_);
auto componentDescriptor = componentDescriptorProvider.constructor(
{parameters_.eventDispatcher,
parameters_.contextContainer,
componentDescriptorProvider.flavor});
assert(
componentDescriptor->getComponentHandle() ==
componentDescriptorProvider.handle);
assert(
componentDescriptor->getComponentName() ==
componentDescriptorProvider.name);
auto sharedComponentDescriptor = std::shared_ptr<ComponentDescriptor const>(
std::move(componentDescriptor));
_registryByHandle[componentDescriptorProvider.handle] =
sharedComponentDescriptor;
_registryByName[componentDescriptorProvider.name] = sharedComponentDescriptor;
}
void ComponentDescriptorRegistry::registerComponentDescriptor(
SharedComponentDescriptor componentDescriptor) const {
ComponentHandle componentHandle = componentDescriptor->getComponentHandle();
_registryByHandle[componentHandle] = componentDescriptor;
ComponentName componentName = componentDescriptor->getComponentName();
_registryByName[componentName] = componentDescriptor;
}
static std::string componentNameByReactViewName(std::string viewName) {
// We need this function only for the transition period;
// eventually, all names will be unified.
std::string rctPrefix("RCT");
if (std::mismatch(rctPrefix.begin(), rctPrefix.end(), viewName.begin())
.first == rctPrefix.end()) {
// If `viewName` has "RCT" prefix, remove it.
viewName.erase(0, rctPrefix.length());
}
// Fabric uses slightly new names for Text components because of differences
// in semantic.
if (viewName == "Text") {
return "Paragraph";
}
// TODO T63839307: remove this condition after deleting TextInlineImage from
// Paper
if (viewName == "TextInlineImage") {
return "Image";
}
if (viewName == "VirtualText") {
return "Text";
}
if (viewName == "ImageView") {
return "Image";
}
if (viewName == "AndroidHorizontalScrollView") {
return "ScrollView";
}
if (viewName == "RKShimmeringView") {
return "ShimmeringView";
}
if (viewName == "RefreshControl") {
return "PullToRefreshView";
}
if (viewName == "AndroidProgressBar") {
return "ActivityIndicatorView";
}
// We need this temporarily for testing purposes until we have proper
// implementation of core components.
if (viewName == "ScrollContentView" ||
viewName == "AndroidHorizontalScrollContentView" // Android
) {
return "View";
}
// iOS-only
if (viewName == "MultilineTextInputView" ||
viewName == "SinglelineTextInputView") {
return "TextInput";
}
return viewName;
}
ComponentDescriptor const &ComponentDescriptorRegistry::at(
std::string const &componentName) const {
std::shared_lock<better::shared_mutex> lock(mutex_);
auto unifiedComponentName = componentNameByReactViewName(componentName);
auto it = _registryByName.find(unifiedComponentName);
if (it == _registryByName.end()) {
mutex_.unlock_shared();
providerRegistry_.request(unifiedComponentName.c_str());
mutex_.lock_shared();
it = _registryByName.find(unifiedComponentName);
/*
* TODO: T54849676
* Uncomment the `assert` after the following block that checks
* `_fallbackComponentDescriptor` is no longer needed. The assert assumes
* that `componentDescriptorProviderRequest` is always not null and register
* some component on every single request.
*/
// assert(it != _registryByName.end());
}
if (it == _registryByName.end()) {
if (_fallbackComponentDescriptor == nullptr) {
throw std::invalid_argument(
("Unable to find componentDescriptor for " + unifiedComponentName)
.c_str());
}
return *_fallbackComponentDescriptor.get();
}
return *it->second;
}
ComponentDescriptor const *ComponentDescriptorRegistry::
findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(
ComponentHandle componentHandle) const {
std::shared_lock<better::shared_mutex> lock(mutex_);
auto iterator = _registryByHandle.find(componentHandle);
if (iterator == _registryByHandle.end()) {
return nullptr;
}
return iterator->second.get();
}
ComponentDescriptor const &ComponentDescriptorRegistry::at(
ComponentHandle componentHandle) const {
std::shared_lock<better::shared_mutex> lock(mutex_);
return *_registryByHandle.at(componentHandle);
}
SharedShadowNode ComponentDescriptorRegistry::createNode(
Tag tag,
std::string const &viewName,
SurfaceId surfaceId,
folly::dynamic const &propsDynamic,
SharedEventTarget const &eventTarget) const {
auto unifiedComponentName = componentNameByReactViewName(viewName);
auto const &componentDescriptor = this->at(unifiedComponentName);
auto family = componentDescriptor.createFamily(
ShadowNodeFamilyFragment{tag, surfaceId, nullptr},
std::move(eventTarget));
auto const props =
componentDescriptor.cloneProps(nullptr, RawProps(propsDynamic));
auto const state =
componentDescriptor.createInitialState(ShadowNodeFragment{props}, family);
return componentDescriptor.createShadowNode(
{
/* .props = */ props,
/* .children = */ ShadowNodeFragment::childrenPlaceholder(),
/* .state = */ state,
},
family);
}
void ComponentDescriptorRegistry::setFallbackComponentDescriptor(
SharedComponentDescriptor descriptor) {
_fallbackComponentDescriptor = descriptor;
registerComponentDescriptor(descriptor);
}
ComponentDescriptor::Shared
ComponentDescriptorRegistry::getFallbackComponentDescriptor() const {
return _fallbackComponentDescriptor;
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,89 @@
/*
* 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 <memory>
#include <better/map.h>
#include <better/mutex.h>
#include <react/core/ComponentDescriptor.h>
#include <react/uimanager/ComponentDescriptorProvider.h>
namespace facebook {
namespace react {
class ComponentDescriptorProviderRegistry;
class ComponentDescriptorRegistry;
using SharedComponentDescriptorRegistry =
std::shared_ptr<const ComponentDescriptorRegistry>;
/*
* Registry of particular `ComponentDescriptor`s.
*/
class ComponentDescriptorRegistry {
public:
using Shared = std::shared_ptr<const ComponentDescriptorRegistry>;
/*
* Creates an object with stored `ComponentDescriptorParameters` which will
* be used later to create `ComponentDescriptor`s.
*/
ComponentDescriptorRegistry(
ComponentDescriptorParameters const &parameters,
ComponentDescriptorProviderRegistry const &providerRegistry);
/*
* This is broken. Please do not use.
* If you requesting a ComponentDescriptor and unsure that it's there, you are
* doing something wrong.
*/
ComponentDescriptor const *
findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(
ComponentHandle componentHandle) const;
ComponentDescriptor const &at(std::string const &componentName) const;
ComponentDescriptor const &at(ComponentHandle componentHandle) const;
ShadowNode::Shared createNode(
Tag tag,
std::string const &viewName,
SurfaceId surfaceId,
folly::dynamic const &props,
SharedEventTarget const &eventTarget) const;
void setFallbackComponentDescriptor(SharedComponentDescriptor descriptor);
ComponentDescriptor::Shared getFallbackComponentDescriptor() const;
private:
friend class ComponentDescriptorProviderRegistry;
void registerComponentDescriptor(
SharedComponentDescriptor componentDescriptor) const;
/*
* Creates a `ComponentDescriptor` using specified
* `ComponentDescriptorProvider` and stored `ComponentDescriptorParameters`,
* and then adds that to the registry.
* To be used by `ComponentDescriptorProviderRegistry` only.
* Thread safe.
*/
void add(ComponentDescriptorProvider componentDescriptorProvider) const;
mutable better::shared_mutex mutex_;
mutable better::map<ComponentHandle, SharedComponentDescriptor>
_registryByHandle;
mutable better::map<std::string, SharedComponentDescriptor> _registryByName;
ComponentDescriptor::Shared _fallbackComponentDescriptor;
ComponentDescriptorParameters parameters_{};
ComponentDescriptorProviderRegistry const &providerRegistry_;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,344 @@
/*
* 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 "Scheduler.h"
#include <glog/logging.h>
#include <jsi/jsi.h>
#include <react/core/LayoutContext.h>
#include <react/debug/SystraceSection.h>
#include <react/uimanager/ComponentDescriptorRegistry.h>
#include <react/uimanager/UIManager.h>
#include <react/uimanager/UIManagerBinding.h>
#include <react/uimanager/UITemplateProcessor.h>
namespace facebook {
namespace react {
Scheduler::Scheduler(
SchedulerToolbox schedulerToolbox,
SchedulerDelegate *delegate) {
runtimeExecutor_ = schedulerToolbox.runtimeExecutor;
reactNativeConfig_ =
schedulerToolbox.contextContainer
->at<std::shared_ptr<const ReactNativeConfig>>("ReactNativeConfig");
auto uiManager = std::make_shared<UIManager>();
auto eventOwnerBox = std::make_shared<EventBeat::OwnerBox>();
auto eventPipe = [uiManager](
jsi::Runtime &runtime,
const EventTarget *eventTarget,
const std::string &type,
const ValueFactory &payloadFactory) {
uiManager->visitBinding([&](UIManagerBinding const &uiManagerBinding) {
uiManagerBinding.dispatchEvent(
runtime, eventTarget, type, payloadFactory);
});
};
auto statePipe = [uiManager](StateUpdate const &stateUpdate) {
uiManager->updateState(stateUpdate);
};
eventDispatcher_ = std::make_shared<EventDispatcher>(
eventPipe,
statePipe,
schedulerToolbox.synchronousEventBeatFactory,
schedulerToolbox.asynchronousEventBeatFactory,
eventOwnerBox);
eventOwnerBox->owner = eventDispatcher_;
componentDescriptorRegistry_ = schedulerToolbox.componentRegistryFactory(
eventDispatcher_, schedulerToolbox.contextContainer);
rootComponentDescriptor_ = std::make_unique<const RootComponentDescriptor>(
ComponentDescriptorParameters{eventDispatcher_, nullptr, nullptr});
uiManager->setDelegate(this);
uiManager->setComponentDescriptorRegistry(componentDescriptorRegistry_);
runtimeExecutor_([=](jsi::Runtime &runtime) {
auto uiManagerBinding = UIManagerBinding::createAndInstallIfNeeded(runtime);
uiManagerBinding->attach(uiManager);
});
auto componentDescriptorRegistryKey =
"ComponentDescriptorRegistry_DO_NOT_USE_PRETTY_PLEASE";
schedulerToolbox.contextContainer->erase(componentDescriptorRegistryKey);
schedulerToolbox.contextContainer->insert(
componentDescriptorRegistryKey,
std::weak_ptr<ComponentDescriptorRegistry const>(
componentDescriptorRegistry_));
delegate_ = delegate;
uiManager_ = uiManager;
}
Scheduler::~Scheduler() {
LOG(WARNING) << "Scheduler::~Scheduler() was called (address: " << this
<< ").";
// All Surfaces must be explicitly stopped before destroying `Scheduler`.
// The idea is that `UIManager` is allowed to call `Scheduler` only if the
// corresponding `ShadowTree` instance exists.
// The thread-safety of this operation is guaranteed by this requirement.
uiManager_->setDelegate(nullptr);
// Then, let's verify that the requirement was satisfied.
auto surfaceIds = std::vector<SurfaceId>{};
uiManager_->getShadowTreeRegistry().enumerate(
[&](ShadowTree const &shadowTree, bool &stop) {
surfaceIds.push_back(shadowTree.getSurfaceId());
});
assert(
surfaceIds.size() == 0 &&
"Scheduler was destroyed with outstanding Surfaces.");
if (surfaceIds.size() == 0) {
return;
}
LOG(ERROR) << "Scheduler was destroyed with outstanding Surfaces.";
// If we are here, that means assert didn't fire which indicates that we in
// production.
// Now we have still-running surfaces, which is no good, no good.
// That's indeed a sign of a severe issue on the application layer.
// At this point, we don't have much to lose, so we are trying to unmount all
// outstanding `ShadowTree`s to prevent all stored JSI entities from
// overliving the `Scheduler`. (Unmounting `ShadowNode`s disables
// `EventEmitter`s which destroys JSI objects.)
for (auto surfaceId : surfaceIds) {
uiManager_->getShadowTreeRegistry().visit(
surfaceId,
[](ShadowTree const &shadowTree) { shadowTree.commitEmptyTree(); });
}
}
void Scheduler::startSurface(
SurfaceId surfaceId,
const std::string &moduleName,
const folly::dynamic &initialProps,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext) const {
SystraceSection s("Scheduler::startSurface");
auto shadowTree = std::make_unique<ShadowTree>(
surfaceId,
layoutConstraints,
layoutContext,
*rootComponentDescriptor_,
*uiManager_);
auto uiManager = uiManager_;
uiManager->getShadowTreeRegistry().add(std::move(shadowTree));
runtimeExecutor_([=](jsi::Runtime &runtime) {
uiManager->visitBinding([&](UIManagerBinding const &uiManagerBinding) {
uiManagerBinding.startSurface(
runtime, surfaceId, moduleName, initialProps);
});
});
}
void Scheduler::renderTemplateToSurface(
SurfaceId surfaceId,
const std::string &uiTemplate) {
SystraceSection s("Scheduler::renderTemplateToSurface");
try {
if (uiTemplate.size() == 0) {
return;
}
NativeModuleRegistry nMR;
auto tree = UITemplateProcessor::buildShadowTree(
uiTemplate,
surfaceId,
folly::dynamic::object(),
*componentDescriptorRegistry_,
nMR,
reactNativeConfig_);
uiManager_->getShadowTreeRegistry().visit(
surfaceId, [=](const ShadowTree &shadowTree) {
return shadowTree.tryCommit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
return std::make_shared<RootShadowNode>(
*oldRootShadowNode,
ShadowNodeFragment{
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
/* .children = */
std::make_shared<SharedShadowNodeList>(
SharedShadowNodeList{tree}),
});
});
});
} catch (const std::exception &e) {
LOG(ERROR) << " >>>> EXCEPTION <<< rendering uiTemplate in "
<< "Scheduler::renderTemplateToSurface: " << e.what();
}
}
void Scheduler::stopSurface(SurfaceId surfaceId) const {
SystraceSection s("Scheduler::stopSurface");
// Note, we have to do in inside `visit` function while the Shadow Tree
// is still being registered.
uiManager_->getShadowTreeRegistry().visit(
surfaceId, [](ShadowTree const &shadowTree) {
// As part of stopping a Surface, we need to properly destroy all
// mounted views, so we need to commit an empty tree to trigger all
// side-effects that will perform that.
shadowTree.commitEmptyTree();
});
// Waiting for all concurrent commits to be finished and unregistering the
// `ShadowTree`.
uiManager_->getShadowTreeRegistry().remove(surfaceId);
// We execute JavaScript/React part of the process at the very end to minimize
// any visible side-effects of stopping the Surface. Any possible commits from
// the JavaScript side will not be able to reference a `ShadowTree` and will
// fail silently.
auto uiManager = uiManager_;
runtimeExecutor_([=](jsi::Runtime &runtime) {
uiManager->visitBinding([&](UIManagerBinding const &uiManagerBinding) {
uiManagerBinding.stopSurface(runtime, surfaceId);
});
});
}
Size Scheduler::measureSurface(
SurfaceId surfaceId,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext) const {
SystraceSection s("Scheduler::measureSurface");
Size size;
uiManager_->getShadowTreeRegistry().visit(
surfaceId, [&](const ShadowTree &shadowTree) {
shadowTree.tryCommit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
auto rootShadowNode =
oldRootShadowNode->clone(layoutConstraints, layoutContext);
rootShadowNode->layoutIfNeeded();
size = rootShadowNode->getLayoutMetrics().frame.size;
return nullptr;
});
});
return size;
}
MountingCoordinator::Shared Scheduler::findMountingCoordinator(
SurfaceId surfaceId) const {
MountingCoordinator::Shared mountingCoordinator = nullptr;
uiManager_->getShadowTreeRegistry().visit(
surfaceId, [&](const ShadowTree &shadowTree) {
mountingCoordinator = shadowTree.getMountingCoordinator();
});
return mountingCoordinator;
}
void Scheduler::constraintSurfaceLayout(
SurfaceId surfaceId,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext) const {
SystraceSection s("Scheduler::constraintSurfaceLayout");
uiManager_->getShadowTreeRegistry().visit(
surfaceId, [&](ShadowTree const &shadowTree) {
shadowTree.commit([&](RootShadowNode::Shared const &oldRootShadowNode) {
return oldRootShadowNode->clone(layoutConstraints, layoutContext);
});
});
}
ComponentDescriptor const *
Scheduler::findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(
ComponentHandle handle) const {
return componentDescriptorRegistry_
->findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(handle);
}
#pragma mark - Delegate
void Scheduler::setDelegate(SchedulerDelegate *delegate) {
delegate_ = delegate;
}
SchedulerDelegate *Scheduler::getDelegate() const {
return delegate_;
}
#pragma mark - UIManagerDelegate
void Scheduler::uiManagerDidFinishTransaction(
MountingCoordinator::Shared const &mountingCoordinator) {
SystraceSection s("Scheduler::uiManagerDidFinishTransaction");
if (delegate_) {
delegate_->schedulerDidFinishTransaction(mountingCoordinator);
}
}
void Scheduler::uiManagerDidCreateShadowNode(
const ShadowNode::Shared &shadowNode) {
SystraceSection s("Scheduler::uiManagerDidCreateShadowNode");
if (delegate_) {
auto shadowView = ShadowView(*shadowNode);
delegate_->schedulerDidRequestPreliminaryViewAllocation(
shadowNode->getSurfaceId(), shadowView);
}
}
void Scheduler::uiManagerDidDispatchCommand(
const ShadowNode::Shared &shadowNode,
std::string const &commandName,
folly::dynamic const args) {
SystraceSection s("Scheduler::uiManagerDispatchCommand");
if (delegate_) {
auto shadowView = ShadowView(*shadowNode);
delegate_->schedulerDidDispatchCommand(shadowView, commandName, args);
}
}
/*
* Set JS responder for a view
*/
void Scheduler::uiManagerDidSetJSResponder(
SurfaceId surfaceId,
const ShadowNode::Shared &shadowNode,
bool blockNativeResponder) {
if (delegate_) {
// TODO: the first shadowView paramenter, should be the first parent that
// is non virtual.
auto shadowView = ShadowView(*shadowNode);
delegate_->schedulerDidSetJSResponder(
surfaceId, shadowView, shadowView, blockNativeResponder);
}
}
/*
* Clear the JSResponder for a view
*/
void Scheduler::uiManagerDidClearJSResponder() {
if (delegate_) {
delegate_->schedulerDidClearJSResponder();
}
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,118 @@
/*
* 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 <memory>
#include <mutex>
#include <react/components/root/RootComponentDescriptor.h>
#include <react/config/ReactNativeConfig.h>
#include <react/core/ComponentDescriptor.h>
#include <react/core/LayoutConstraints.h>
#include <react/uimanager/ComponentDescriptorFactory.h>
#include <react/uimanager/ComponentDescriptorRegistry.h>
#include <react/uimanager/SchedulerDelegate.h>
#include <react/uimanager/SchedulerToolbox.h>
#include <react/uimanager/UIManagerBinding.h>
#include <react/uimanager/UIManagerDelegate.h>
#include <react/utils/ContextContainer.h>
#include <react/utils/RuntimeExecutor.h>
namespace facebook {
namespace react {
/*
* Scheduler coordinates Shadow Tree updates and event flows.
*/
class Scheduler final : public UIManagerDelegate {
public:
Scheduler(SchedulerToolbox schedulerToolbox, SchedulerDelegate *delegate);
~Scheduler();
#pragma mark - Surface Management
void startSurface(
SurfaceId surfaceId,
const std::string &moduleName,
const folly::dynamic &initialProps,
const LayoutConstraints &layoutConstraints = {},
const LayoutContext &layoutContext = {}) const;
void renderTemplateToSurface(
SurfaceId surfaceId,
const std::string &uiTemplate);
void stopSurface(SurfaceId surfaceId) const;
Size measureSurface(
SurfaceId surfaceId,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext) const;
/*
* Applies given `layoutConstraints` and `layoutContext` to a Surface.
* The user interface will be relaid out as a result. The operation will be
* performed synchronously (including mounting) if the method is called
* on the main thread.
* Can be called from any thread.
*/
void constraintSurfaceLayout(
SurfaceId surfaceId,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext) const;
/*
* This is broken. Please do not use.
* `ComponentDescriptor`s are not designed to be used outside of `UIManager`,
* there is no any garantees about their lifetime.
*/
ComponentDescriptor const *
findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(
ComponentHandle handle) const;
MountingCoordinator::Shared findMountingCoordinator(
SurfaceId surfaceId) const;
#pragma mark - Delegate
/*
* Sets and gets the Scheduler's delegate.
* If you requesting a ComponentDescriptor and unsure that it's there, you are
* doing something wrong.
*/
void setDelegate(SchedulerDelegate *delegate);
SchedulerDelegate *getDelegate() const;
#pragma mark - UIManagerDelegate
void uiManagerDidFinishTransaction(
MountingCoordinator::Shared const &mountingCoordinator) override;
void uiManagerDidCreateShadowNode(
const ShadowNode::Shared &shadowNode) override;
void uiManagerDidDispatchCommand(
const ShadowNode::Shared &shadowNode,
std::string const &commandName,
folly::dynamic const args) override;
void uiManagerDidSetJSResponder(
SurfaceId surfaceId,
const ShadowNode::Shared &shadowView,
bool blockNativeResponder) override;
void uiManagerDidClearJSResponder() override;
private:
SchedulerDelegate *delegate_;
SharedComponentDescriptorRegistry componentDescriptorRegistry_;
std::unique_ptr<const RootComponentDescriptor> rootComponentDescriptor_;
RuntimeExecutor runtimeExecutor_;
std::shared_ptr<UIManager> uiManager_;
std::shared_ptr<const ReactNativeConfig> reactNativeConfig_;
EventDispatcher::Shared eventDispatcher_;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,62 @@
/*
* 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 <memory>
#include <react/core/ReactPrimitives.h>
#include <react/mounting/MountingCoordinator.h>
#include <react/mounting/ShadowView.h>
namespace facebook {
namespace react {
/*
* Abstract class for Scheduler's delegate.
*/
class SchedulerDelegate {
public:
/*
* Called right after Scheduler computed (and laid out) a new updated version
* of the tree and calculated a set of mutations which are sufficient
* to construct a new one.
*/
virtual void schedulerDidFinishTransaction(
MountingCoordinator::Shared const &mountingCoordinator) = 0;
/*
* Called right after a new ShadowNode was created.
*/
virtual void schedulerDidRequestPreliminaryViewAllocation(
SurfaceId surfaceId,
const ShadowView &shadowView) = 0;
virtual void schedulerDidDispatchCommand(
const ShadowView &shadowView,
std::string const &commandName,
folly::dynamic const args) = 0;
/*
* Set JS responder for a view
*/
virtual void schedulerDidSetJSResponder(
SurfaceId surfaceId,
const ShadowView &shadowView,
const ShadowView &initialShadowView,
bool blockNativeResponder) = 0;
/*
* Clear the JSResponder for a view
*/
virtual void schedulerDidClearJSResponder() = 0;
virtual ~SchedulerDelegate() noexcept = default;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,8 @@
/*
* 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 "SchedulerToolbox.h"

View File

@ -0,0 +1,49 @@
/*
* 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 <react/core/EventBeat.h>
#include <react/uimanager/ComponentDescriptorFactory.h>
#include <react/utils/ContextContainer.h>
#include <react/utils/RuntimeExecutor.h>
namespace facebook {
namespace react {
/*
* Contains all external dependencies of Scheduler.
* Copyable.
*/
struct SchedulerToolbox final {
/*
* Represents general purpose DI container for product components/needs.
* Must not be `nullptr`.
*/
ContextContainer::Shared contextContainer;
/*
* Represents externally managed, lazily available collection of components.
*/
ComponentRegistryFactory componentRegistryFactory;
/*
* Represents running JavaScript VM and associated execution queue.
*/
RuntimeExecutor runtimeExecutor;
/*
* Asynchronous & synchronous event beats.
* Represent connections with the platform-specific run loops and general
* purpose background queue.
*/
EventBeat::Factory asynchronousEventBeatFactory;
EventBeat::Factory synchronousEventBeatFactory;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,294 @@
/*
* 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 "UIManager.h"
#include <react/core/ShadowNodeFragment.h>
#include <react/debug/SystraceSection.h>
#include <react/graphics/Geometry.h>
#include <glog/logging.h>
namespace facebook {
namespace react {
UIManager::~UIManager() {
LOG(WARNING) << "UIManager::~UIManager() was called (address: " << this
<< ").";
}
SharedShadowNode UIManager::createNode(
Tag tag,
std::string const &name,
SurfaceId surfaceId,
const RawProps &rawProps,
SharedEventTarget eventTarget) const {
SystraceSection s("UIManager::createNode");
auto &componentDescriptor = componentDescriptorRegistry_->at(name);
auto fallbackDescriptor =
componentDescriptorRegistry_->getFallbackComponentDescriptor();
auto family = componentDescriptor.createFamily(
ShadowNodeFamilyFragment{tag, surfaceId, nullptr},
std::move(eventTarget));
auto const props = componentDescriptor.cloneProps(nullptr, rawProps);
auto const state =
componentDescriptor.createInitialState(ShadowNodeFragment{props}, family);
auto shadowNode = componentDescriptor.createShadowNode(
ShadowNodeFragment{
/* .props = */
fallbackDescriptor != nullptr &&
fallbackDescriptor->getComponentHandle() ==
componentDescriptor.getComponentHandle()
? componentDescriptor.cloneProps(
props, RawProps(folly::dynamic::object("name", name)))
: props,
/* .children = */ ShadowNodeFragment::childrenPlaceholder(),
/* .state = */ state,
},
family);
if (delegate_) {
delegate_->uiManagerDidCreateShadowNode(shadowNode);
}
return shadowNode;
}
SharedShadowNode UIManager::cloneNode(
const ShadowNode::Shared &shadowNode,
const SharedShadowNodeSharedList &children,
const RawProps *rawProps) const {
SystraceSection s("UIManager::cloneNode");
auto &componentDescriptor = shadowNode->getComponentDescriptor();
auto clonedShadowNode = componentDescriptor.cloneShadowNode(
*shadowNode,
{
/* .props = */
rawProps ? componentDescriptor.cloneProps(
shadowNode->getProps(), *rawProps)
: ShadowNodeFragment::propsPlaceholder(),
/* .children = */ children,
});
return clonedShadowNode;
}
void UIManager::appendChild(
const ShadowNode::Shared &parentShadowNode,
const ShadowNode::Shared &childShadowNode) const {
SystraceSection s("UIManager::appendChild");
auto &componentDescriptor = parentShadowNode->getComponentDescriptor();
componentDescriptor.appendChild(parentShadowNode, childShadowNode);
}
void UIManager::completeSurface(
SurfaceId surfaceId,
const SharedShadowNodeUnsharedList &rootChildren) const {
SystraceSection s("UIManager::completeSurface");
shadowTreeRegistry_.visit(surfaceId, [&](ShadowTree const &shadowTree) {
shadowTree.commit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
return std::make_shared<RootShadowNode>(
*oldRootShadowNode,
ShadowNodeFragment{
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
/* .children = */ rootChildren,
});
},
true);
});
}
void UIManager::setJSResponder(
const ShadowNode::Shared &shadowNode,
const bool blockNativeResponder) const {
if (delegate_) {
delegate_->uiManagerDidSetJSResponder(
shadowNode->getSurfaceId(), shadowNode, blockNativeResponder);
}
}
void UIManager::clearJSResponder() const {
if (delegate_) {
delegate_->uiManagerDidClearJSResponder();
}
}
ShadowNode::Shared const *UIManager::getNewestCloneOfShadowNode(
ShadowNode::Shared const &shadowNode) const {
auto findNewestChildInParent =
[&](auto const &parentNode) -> ShadowNode::Shared const * {
for (auto const &child : parentNode.getChildren()) {
if (ShadowNode::sameFamily(*child, *shadowNode)) {
return &child;
}
}
return nullptr;
};
ShadowNode const *ancestorShadowNode;
shadowTreeRegistry_.visit(
shadowNode->getSurfaceId(), [&](ShadowTree const &shadowTree) {
shadowTree.tryCommit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
ancestorShadowNode = oldRootShadowNode.get();
return nullptr;
},
true);
});
auto ancestors = shadowNode->getFamily().getAncestors(*ancestorShadowNode);
return findNewestChildInParent(ancestors.rbegin()->first.get());
}
ShadowNode::Shared UIManager::findNodeAtPoint(
ShadowNode::Shared const &node,
Point point) const {
return LayoutableShadowNode::findNodeAtPoint(
*getNewestCloneOfShadowNode(node), point);
}
void UIManager::setNativeProps(
ShadowNode const &shadowNode,
RawProps const &rawProps) const {
SystraceSection s("UIManager::setNativeProps");
auto &componentDescriptor = shadowNode.getComponentDescriptor();
auto props = componentDescriptor.cloneProps(shadowNode.getProps(), rawProps);
shadowTreeRegistry_.visit(
shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) {
shadowTree.tryCommit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
return std::static_pointer_cast<RootShadowNode>(
oldRootShadowNode->cloneTree(
shadowNode.getFamily(),
[&](ShadowNode const &oldShadowNode) {
return oldShadowNode.clone({
/* .props = */ props,
});
}));
},
true);
});
}
LayoutMetrics UIManager::getRelativeLayoutMetrics(
ShadowNode const &shadowNode,
ShadowNode const *ancestorShadowNode,
LayoutableShadowNode::LayoutInspectingPolicy policy) const {
SystraceSection s("UIManager::getRelativeLayoutMetrics");
if (!ancestorShadowNode) {
shadowTreeRegistry_.visit(
shadowNode.getSurfaceId(), [&](ShadowTree const &shadowTree) {
shadowTree.tryCommit(
[&](RootShadowNode::Shared const &oldRootShadowNode) {
ancestorShadowNode = oldRootShadowNode.get();
return nullptr;
},
true);
});
}
auto layoutableShadowNode =
traitCast<LayoutableShadowNode const *>(&shadowNode);
auto layoutableAncestorShadowNode =
traitCast<LayoutableShadowNode const *>(ancestorShadowNode);
if (!layoutableShadowNode || !layoutableAncestorShadowNode) {
return EmptyLayoutMetrics;
}
return layoutableShadowNode->getRelativeLayoutMetrics(
*layoutableAncestorShadowNode, policy);
}
void UIManager::updateState(StateUpdate const &stateUpdate) const {
auto &callback = stateUpdate.callback;
auto &family = stateUpdate.family;
auto &componentDescriptor = family->getComponentDescriptor();
shadowTreeRegistry_.visit(
family->getSurfaceId(), [&](ShadowTree const &shadowTree) {
shadowTree.tryCommit([&](RootShadowNode::Shared const
&oldRootShadowNode) {
return std::static_pointer_cast<
RootShadowNode>(oldRootShadowNode->cloneTree(
*family, [&](ShadowNode const &oldShadowNode) {
auto newData =
callback(oldShadowNode.getState()->getDataPointer());
auto newState =
componentDescriptor.createState(*family, newData);
return oldShadowNode.clone({
/* .props = */ ShadowNodeFragment::propsPlaceholder(),
/* .children = */ ShadowNodeFragment::childrenPlaceholder(),
/* .state = */ newState,
});
}));
});
});
}
void UIManager::dispatchCommand(
const ShadowNode::Shared &shadowNode,
std::string const &commandName,
folly::dynamic const args) const {
if (delegate_) {
delegate_->uiManagerDidDispatchCommand(shadowNode, commandName, args);
}
}
void UIManager::setComponentDescriptorRegistry(
const SharedComponentDescriptorRegistry &componentDescriptorRegistry) {
componentDescriptorRegistry_ = componentDescriptorRegistry;
}
void UIManager::setDelegate(UIManagerDelegate *delegate) {
delegate_ = delegate;
}
UIManagerDelegate *UIManager::getDelegate() {
return delegate_;
}
void UIManager::visitBinding(
std::function<void(UIManagerBinding const &uiManagerBinding)> callback)
const {
if (!uiManagerBinding_) {
return;
}
callback(*uiManagerBinding_);
}
ShadowTreeRegistry const &UIManager::getShadowTreeRegistry() const {
return shadowTreeRegistry_;
}
#pragma mark - ShadowTreeDelegate
void UIManager::shadowTreeDidFinishTransaction(
ShadowTree const &shadowTree,
MountingCoordinator::Shared const &mountingCoordinator) const {
SystraceSection s("UIManager::shadowTreeDidFinishTransaction");
if (delegate_) {
delegate_->uiManagerDidFinishTransaction(mountingCoordinator);
}
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,128 @@
/*
* 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 <folly/Optional.h>
#include <folly/dynamic.h>
#include <jsi/jsi.h>
#include <react/core/ShadowNode.h>
#include <react/core/StateData.h>
#include <react/mounting/ShadowTree.h>
#include <react/mounting/ShadowTreeDelegate.h>
#include <react/mounting/ShadowTreeRegistry.h>
#include <react/uimanager/ComponentDescriptorRegistry.h>
#include <react/uimanager/UIManagerDelegate.h>
namespace facebook {
namespace react {
class UIManagerBinding;
class UIManager final : public ShadowTreeDelegate {
public:
~UIManager();
void setComponentDescriptorRegistry(
const SharedComponentDescriptorRegistry &componentDescriptorRegistry);
/*
* Sets and gets the UIManager's delegate.
* The delegate is stored as a raw pointer, so the owner must null
* the pointer before being destroyed.
*/
void setDelegate(UIManagerDelegate *delegate);
UIManagerDelegate *getDelegate();
/*
* Provides access to a UIManagerBindging.
* The `callback` methods will not be called if the internal pointer to
* `UIManagerBindging` is `nullptr`.
* The callback is called synchronously on the same thread.
*/
void visitBinding(
std::function<void(UIManagerBinding const &uiManagerBinding)> callback)
const;
#pragma mark - ShadowTreeDelegate
void shadowTreeDidFinishTransaction(
ShadowTree const &shadowTree,
MountingCoordinator::Shared const &mountingCoordinator) const override;
private:
friend class UIManagerBinding;
friend class Scheduler;
ShadowNode::Shared createNode(
Tag tag,
std::string const &componentName,
SurfaceId surfaceId,
const RawProps &props,
SharedEventTarget eventTarget) const;
ShadowNode::Shared cloneNode(
const ShadowNode::Shared &shadowNode,
const SharedShadowNodeSharedList &children = nullptr,
const RawProps *rawProps = nullptr) const;
void appendChild(
const ShadowNode::Shared &parentShadowNode,
const ShadowNode::Shared &childShadowNode) const;
void completeSurface(
SurfaceId surfaceId,
const SharedShadowNodeUnsharedList &rootChildren) const;
void setNativeProps(ShadowNode const &shadowNode, RawProps const &rawProps)
const;
void setJSResponder(
const ShadowNode::Shared &shadowNode,
const bool blockNativeResponder) const;
void clearJSResponder() const;
ShadowNode::Shared findNodeAtPoint(
ShadowNode::Shared const &shadowNode,
Point point) const;
ShadowNode::Shared const *getNewestCloneOfShadowNode(
ShadowNode::Shared const &shadowNode) const;
/*
* Returns layout metrics of given `shadowNode` relative to
* `ancestorShadowNode` (relative to the root node in case if provided
* `ancestorShadowNode` is nullptr).
*/
LayoutMetrics getRelativeLayoutMetrics(
ShadowNode const &shadowNode,
ShadowNode const *ancestorShadowNode,
LayoutableShadowNode::LayoutInspectingPolicy policy) const;
/*
* Creates a new shadow node with given state data, clones what's necessary
* and performs a commit.
*/
void updateState(StateUpdate const &stateUpdate) const;
void dispatchCommand(
const ShadowNode::Shared &shadowNode,
std::string const &commandName,
folly::dynamic const args) const;
ShadowTreeRegistry const &getShadowTreeRegistry() const;
SharedComponentDescriptorRegistry componentDescriptorRegistry_;
UIManagerDelegate *delegate_;
UIManagerBinding *uiManagerBinding_;
ShadowTreeRegistry shadowTreeRegistry_{};
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,605 @@
/*
* 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 "UIManagerBinding.h"
#include <react/debug/SystraceSection.h>
#include <glog/logging.h>
#include <jsi/JSIDynamic.h>
namespace facebook {
namespace react {
static jsi::Object getModule(
jsi::Runtime &runtime,
const std::string &moduleName) {
auto batchedBridge =
runtime.global().getPropertyAsObject(runtime, "__fbBatchedBridge");
auto getCallableModule =
batchedBridge.getPropertyAsFunction(runtime, "getCallableModule");
auto module = getCallableModule
.callWithThis(
runtime,
batchedBridge,
{jsi::String::createFromUtf8(runtime, moduleName)})
.asObject(runtime);
return module;
}
std::shared_ptr<UIManagerBinding> UIManagerBinding::createAndInstallIfNeeded(
jsi::Runtime &runtime) {
auto uiManagerModuleName = "nativeFabricUIManager";
auto uiManagerValue =
runtime.global().getProperty(runtime, uiManagerModuleName);
if (uiManagerValue.isUndefined()) {
// The global namespace does not have an instance of the binding;
// we need to create, install and return it.
auto uiManagerBinding = std::make_shared<UIManagerBinding>();
auto object = jsi::Object::createFromHostObject(runtime, uiManagerBinding);
runtime.global().setProperty(
runtime, uiManagerModuleName, std::move(object));
return uiManagerBinding;
}
// The global namespace already has an instance of the binding;
// we need to return that.
auto uiManagerObject = uiManagerValue.asObject(runtime);
return uiManagerObject.getHostObject<UIManagerBinding>(runtime);
}
UIManagerBinding::~UIManagerBinding() {
LOG(WARNING) << "UIManager::~UIManager() was called (address: " << this
<< ").";
// We must detach the `UIBinding` on deallocation to prevent accessing
// deallocated `UIManagerBinding`.
// Since `UIManagerBinding` retains `UIManager`, `UIManager` always overlive
// `UIManagerBinding`, therefore we don't need similar logic in `UIManager`'s
// destructor.
attach(nullptr);
}
void UIManagerBinding::attach(std::shared_ptr<UIManager> const &uiManager) {
if (uiManager_) {
uiManager_->uiManagerBinding_ = nullptr;
}
uiManager_ = uiManager;
if (uiManager_) {
uiManager_->uiManagerBinding_ = this;
}
}
void UIManagerBinding::startSurface(
jsi::Runtime &runtime,
SurfaceId surfaceId,
const std::string &moduleName,
const folly::dynamic &initalProps) const {
folly::dynamic parameters = folly::dynamic::object();
parameters["rootTag"] = surfaceId;
parameters["initialProps"] = initalProps;
parameters["fabric"] = true;
if (runtime.global().hasProperty(runtime, "RN$SurfaceRegistry")) {
auto registry =
runtime.global().getPropertyAsObject(runtime, "RN$SurfaceRegistry");
auto method = registry.getPropertyAsFunction(runtime, "renderSurface");
method.call(
runtime,
{jsi::String::createFromUtf8(runtime, moduleName),
jsi::valueFromDynamic(runtime, parameters)});
} else {
auto module = getModule(runtime, "AppRegistry");
auto method = module.getPropertyAsFunction(runtime, "runApplication");
method.callWithThis(
runtime,
module,
{jsi::String::createFromUtf8(runtime, moduleName),
jsi::valueFromDynamic(runtime, parameters)});
}
}
void UIManagerBinding::stopSurface(jsi::Runtime &runtime, SurfaceId surfaceId)
const {
if (runtime.global().hasProperty(runtime, "RN$stopSurface")) {
auto method =
runtime.global().getPropertyAsFunction(runtime, "RN$stopSurface");
method.call(runtime, {jsi::Value{surfaceId}});
} else {
auto module = getModule(runtime, "ReactFabric");
auto method =
module.getPropertyAsFunction(runtime, "unmountComponentAtNode");
method.callWithThis(runtime, module, {jsi::Value{surfaceId}});
}
}
void UIManagerBinding::dispatchEvent(
jsi::Runtime &runtime,
const EventTarget *eventTarget,
const std::string &type,
const ValueFactory &payloadFactory) const {
SystraceSection s("UIManagerBinding::dispatchEvent");
auto payload = payloadFactory(runtime);
auto instanceHandle = eventTarget
? [&]() {
auto instanceHandle = eventTarget->getInstanceHandle(runtime);
if (instanceHandle.isUndefined()) {
return jsi::Value::null();
}
// Mixing `target` into `payload`.
assert(payload.isObject());
payload.asObject(runtime).setProperty(runtime, "target", eventTarget->getTag());
return instanceHandle;
}()
: jsi::Value::null();
auto &eventHandlerWrapper =
static_cast<const EventHandlerWrapper &>(*eventHandler_);
eventHandlerWrapper.callback.call(
runtime,
{std::move(instanceHandle),
jsi::String::createFromUtf8(runtime, type),
std::move(payload)});
}
void UIManagerBinding::invalidate() const {
uiManager_->setDelegate(nullptr);
}
jsi::Value UIManagerBinding::get(
jsi::Runtime &runtime,
const jsi::PropNameID &name) {
auto methodName = name.utf8(runtime);
// Convert shared_ptr<UIManager> to a raw ptr
// Why? Because:
// 1) UIManagerBinding strongly retains UIManager. The JS VM
// strongly retains UIManagerBinding (through the JSI).
// These functions are JSI functions and are only called via
// the JS VM; if the JS VM is torn down, those functions can't
// execute and these lambdas won't execute.
// 2) The UIManager is only deallocated when all references to it
// are deallocated, including the UIManagerBinding. That only
// happens when the JS VM is deallocated. So, the raw pointer
// is safe.
//
// Even if it's safe, why not just use shared_ptr anyway as
// extra insurance?
// 1) Using shared_ptr or weak_ptr when they're not needed is
// a pessimisation. It's more instructions executed without
// any additional value in this case.
// 2) How and when exactly these lambdas is deallocated is
// complex. Adding shared_ptr to them which causes the UIManager
// to potentially live longer is unnecessary, complicated cognitive
// overhead.
// 3) There is a strong suspicion that retaining UIManager from
// these C++ lambdas, which are retained by an object that is held onto
// by the JSI, caused some crashes upon deallocation of the
// Scheduler and JS VM. This could happen if, for instance, C++
// semantics cause these lambda to not be deallocated until
// a CPU tick (or more) after the JS VM is deallocated.
UIManager *uiManager = uiManager_.get();
// Semantic: Creates a new node with given pieces.
if (methodName == "createNode") {
return jsi::Function::createFromHostFunction(
runtime,
name,
5,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
return valueFromShadowNode(
runtime,
uiManager->createNode(
tagFromValue(runtime, arguments[0]),
stringFromValue(runtime, arguments[1]),
surfaceIdFromValue(runtime, arguments[2]),
RawProps(runtime, arguments[3]),
eventTargetFromValue(runtime, arguments[4], arguments[0])));
});
}
// Semantic: Clones the node with *same* props and *same* children.
if (methodName == "cloneNode") {
return jsi::Function::createFromHostFunction(
runtime,
name,
1,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
return valueFromShadowNode(
runtime,
uiManager->cloneNode(shadowNodeFromValue(runtime, arguments[0])));
});
}
if (methodName == "setJSResponder") {
return jsi::Function::createFromHostFunction(
runtime,
name,
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
uiManager->setJSResponder(
shadowNodeFromValue(runtime, arguments[0]),
arguments[1].getBool());
return jsi::Value::undefined();
});
}
if (methodName == "findNodeAtPoint") {
return jsi::Function::createFromHostFunction(
runtime,
name,
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
auto node = shadowNodeFromValue(runtime, arguments[0]);
auto locationX = (Float)arguments[1].getNumber();
auto locationY = (Float)arguments[2].getNumber();
auto onSuccessFunction =
arguments[3].getObject(runtime).getFunction(runtime);
auto targetNode =
uiManager->findNodeAtPoint(node, Point{locationX, locationY});
auto &eventTarget = targetNode->getEventEmitter()->eventTarget_;
EventEmitter::DispatchMutex().lock();
eventTarget->retain(runtime);
auto instanceHandle = eventTarget->getInstanceHandle(runtime);
eventTarget->release(runtime);
EventEmitter::DispatchMutex().unlock();
onSuccessFunction.call(runtime, std::move(instanceHandle));
return jsi::Value::undefined();
});
}
if (methodName == "clearJSResponder") {
return jsi::Function::createFromHostFunction(
runtime,
name,
0,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
uiManager->clearJSResponder();
return jsi::Value::undefined();
});
}
// Semantic: Clones the node with *same* props and *empty* children.
if (methodName == "cloneNodeWithNewChildren") {
return jsi::Function::createFromHostFunction(
runtime,
name,
1,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
return valueFromShadowNode(
runtime,
uiManager->cloneNode(
shadowNodeFromValue(runtime, arguments[0]),
ShadowNode::emptySharedShadowNodeSharedList()));
});
}
// Semantic: Clones the node with *given* props and *same* children.
if (methodName == "cloneNodeWithNewProps") {
return jsi::Function::createFromHostFunction(
runtime,
name,
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
const auto &rawProps = RawProps(runtime, arguments[1]);
return valueFromShadowNode(
runtime,
uiManager->cloneNode(
shadowNodeFromValue(runtime, arguments[0]),
nullptr,
&rawProps));
});
}
// Semantic: Clones the node with *given* props and *empty* children.
if (methodName == "cloneNodeWithNewChildrenAndProps") {
return jsi::Function::createFromHostFunction(
runtime,
name,
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
const auto &rawProps = RawProps(runtime, arguments[1]);
return valueFromShadowNode(
runtime,
uiManager->cloneNode(
shadowNodeFromValue(runtime, arguments[0]),
ShadowNode::emptySharedShadowNodeSharedList(),
&rawProps));
});
}
if (methodName == "appendChild") {
return jsi::Function::createFromHostFunction(
runtime,
name,
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
uiManager->appendChild(
shadowNodeFromValue(runtime, arguments[0]),
shadowNodeFromValue(runtime, arguments[1]));
return jsi::Value::undefined();
});
}
if (methodName == "createChildSet") {
return jsi::Function::createFromHostFunction(
runtime,
name,
1,
[](jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
auto shadowNodeList =
std::make_shared<SharedShadowNodeList>(SharedShadowNodeList({}));
return valueFromShadowNodeList(runtime, shadowNodeList);
});
}
if (methodName == "appendChildToSet") {
return jsi::Function::createFromHostFunction(
runtime,
name,
2,
[](jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
auto shadowNodeList = shadowNodeListFromValue(runtime, arguments[0]);
auto shadowNode = shadowNodeFromValue(runtime, arguments[1]);
shadowNodeList->push_back(shadowNode);
return jsi::Value::undefined();
});
}
if (methodName == "completeRoot") {
return jsi::Function::createFromHostFunction(
runtime,
name,
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
uiManager->completeSurface(
surfaceIdFromValue(runtime, arguments[0]),
shadowNodeListFromValue(runtime, arguments[1]));
return jsi::Value::undefined();
});
}
if (methodName == "registerEventHandler") {
return jsi::Function::createFromHostFunction(
runtime,
name,
1,
[this](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
auto eventHandler =
arguments[0].getObject(runtime).getFunction(runtime);
eventHandler_ =
std::make_unique<EventHandlerWrapper>(std::move(eventHandler));
return jsi::Value::undefined();
});
}
if (methodName == "getRelativeLayoutMetrics") {
return jsi::Function::createFromHostFunction(
runtime,
name,
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
*shadowNodeFromValue(runtime, arguments[0]),
shadowNodeFromValue(runtime, arguments[1]).get(),
{/* .includeTransform = */ true});
auto frame = layoutMetrics.frame;
auto result = jsi::Object(runtime);
result.setProperty(runtime, "left", frame.origin.x);
result.setProperty(runtime, "top", frame.origin.y);
result.setProperty(runtime, "width", frame.size.width);
result.setProperty(runtime, "height", frame.size.height);
return result;
});
}
if (methodName == "dispatchCommand") {
return jsi::Function::createFromHostFunction(
runtime,
name,
3,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
uiManager->dispatchCommand(
shadowNodeFromValue(runtime, arguments[0]),
stringFromValue(runtime, arguments[1]),
commandArgsFromValue(runtime, arguments[2]));
return jsi::Value::undefined();
});
}
// Legacy API
if (methodName == "measureLayout") {
return jsi::Function::createFromHostFunction(
runtime,
name,
4,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
*shadowNodeFromValue(runtime, arguments[0]),
shadowNodeFromValue(runtime, arguments[1]).get(),
{/* .includeTransform = */ false});
if (layoutMetrics == EmptyLayoutMetrics) {
auto onFailFunction =
arguments[2].getObject(runtime).getFunction(runtime);
onFailFunction.call(runtime);
return jsi::Value::undefined();
}
auto onSuccessFunction =
arguments[3].getObject(runtime).getFunction(runtime);
auto frame = layoutMetrics.frame;
onSuccessFunction.call(
runtime,
{jsi::Value{runtime, (double)frame.origin.x},
jsi::Value{runtime, (double)frame.origin.y},
jsi::Value{runtime, (double)frame.size.width},
jsi::Value{runtime, (double)frame.size.height}});
return jsi::Value::undefined();
});
}
if (methodName == "measure") {
return jsi::Function::createFromHostFunction(
runtime,
name,
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
*shadowNodeFromValue(runtime, arguments[0]),
nullptr,
{/* .includeTransform = */ true});
auto frame = layoutMetrics.frame;
auto onSuccessFunction =
arguments[1].getObject(runtime).getFunction(runtime);
onSuccessFunction.call(
runtime,
{0,
0,
jsi::Value{runtime, (double)frame.size.width},
jsi::Value{runtime, (double)frame.size.height},
jsi::Value{runtime, (double)frame.origin.x},
jsi::Value{runtime, (double)frame.origin.y}});
return jsi::Value::undefined();
});
}
if (methodName == "measureInWindow") {
return jsi::Function::createFromHostFunction(
runtime,
name,
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
*shadowNodeFromValue(runtime, arguments[0]),
nullptr,
{/* .includeTransform = */ true});
auto onSuccessFunction =
arguments[1].getObject(runtime).getFunction(runtime);
auto frame = layoutMetrics.frame;
onSuccessFunction.call(
runtime,
{jsi::Value{runtime, (double)frame.origin.x},
jsi::Value{runtime, (double)frame.origin.y},
jsi::Value{runtime, (double)frame.size.width},
jsi::Value{runtime, (double)frame.size.height}});
return jsi::Value::undefined();
});
}
if (methodName == "setNativeProps") {
return jsi::Function::createFromHostFunction(
runtime,
name,
2,
[uiManager](
jsi::Runtime &runtime,
const jsi::Value &thisValue,
const jsi::Value *arguments,
size_t count) -> jsi::Value {
uiManager->setNativeProps(
*shadowNodeFromValue(runtime, arguments[0]),
RawProps(runtime, arguments[1]));
return jsi::Value::undefined();
});
}
return jsi::Value::undefined();
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,88 @@
/*
* 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 <folly/dynamic.h>
#include <jsi/jsi.h>
#include <react/uimanager/UIManager.h>
#include <react/uimanager/primitives.h>
namespace facebook {
namespace react {
/*
* Exposes UIManager to JavaScript realm.
*/
class UIManagerBinding : public jsi::HostObject {
public:
/*
* Installs UIManagerBinding into JavaScript runtime if needed.
* Creates and sets `UIManagerBinding` into the global namespace.
* In case if the global namespace already has a `UIManagerBinding` installed,
* returns that.
* Thread synchronization must be enforced externally.
*/
static std::shared_ptr<UIManagerBinding> createAndInstallIfNeeded(
jsi::Runtime &runtime);
~UIManagerBinding();
/*
* Establish a relationship between `UIManager` and `UIManagerBinding` by
* setting internal pointers to each other.
* Must be called on JavaScript thread or during VM destruction.
*/
void attach(std::shared_ptr<UIManager> const &uiManager);
/*
* Starts React Native Surface with given id, moduleName, and props.
* Thread synchronization must be enforced externally.
*/
void startSurface(
jsi::Runtime &runtime,
SurfaceId surfaceId,
const std::string &moduleName,
const folly::dynamic &initalProps) const;
/*
* Stops React Native Surface with given id.
* Thread synchronization must be enforced externally.
*/
void stopSurface(jsi::Runtime &runtime, SurfaceId surfaceId) const;
/*
* Delivers raw event data to JavaScript.
* Thread synchronization must be enforced externally.
*/
void dispatchEvent(
jsi::Runtime &runtime,
const EventTarget *eventTarget,
const std::string &type,
const ValueFactory &payloadFactory) const;
/*
* Invalidates the binding and underlying UIManager.
* Allows to save some resources and prevents UIManager's delegate to be
* called.
* Calling public methods of this class after calling this method is UB.
* Can be called on any thread.
*/
void invalidate() const;
/*
* `jsi::HostObject` specific overloads.
*/
jsi::Value get(jsi::Runtime &runtime, const jsi::PropNameID &name) override;
private:
std::shared_ptr<UIManager> uiManager_;
std::unique_ptr<const EventHandler> eventHandler_;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,62 @@
/*
* 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 <react/core/ReactPrimitives.h>
#include <react/core/ShadowNode.h>
#include <react/mounting/MountingCoordinator.h>
namespace facebook {
namespace react {
/*
* Abstract class for UIManager's delegate.
*/
class UIManagerDelegate {
public:
/*
* Called right after a new/updated Shadow Node tree is constructed.
* For this moment the tree is already laid out and sealed.
*/
virtual void uiManagerDidFinishTransaction(
MountingCoordinator::Shared const &mountingCoordinator) = 0;
/*
* Called each time when UIManager constructs a new Shadow Node. Receiver
* might use this to optimistically allocate a new native view
* instances.
*/
virtual void uiManagerDidCreateShadowNode(
const ShadowNode::Shared &shadowNode) = 0;
/*
* Called when UIManager wants to dispatch a command to the mounting layer.
*/
virtual void uiManagerDidDispatchCommand(
const ShadowNode::Shared &shadowNode,
std::string const &commandName,
folly::dynamic const args) = 0;
/*
* Set JS responder for a view
*/
virtual void uiManagerDidSetJSResponder(
SurfaceId surfaceId,
ShadowNode::Shared const &shadowView,
bool blockNativeResponder) = 0;
/*
* Clear the JSResponder for a view
*/
virtual void uiManagerDidClearJSResponder() = 0;
virtual ~UIManagerDelegate() noexcept = default;
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,155 @@
/*
* 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 "UITemplateProcessor.h"
#include <folly/json.h>
#include <glog/logging.h>
#include <react/components/view/ViewComponentDescriptor.h>
#include <react/components/view/ViewProps.h>
#include <react/components/view/ViewShadowNode.h>
#include <react/core/ComponentDescriptor.h>
#include <react/core/LayoutContext.h>
#include <react/core/ShadowNodeFragment.h>
#include <react/debug/DebugStringConvertible.h>
#include <react/debug/DebugStringConvertibleItem.h>
namespace facebook {
namespace react {
bool constexpr DEBUG_FLY = false;
struct RBCContext {
const Tag rootTag;
const std::vector<SharedShadowNode> &nodes;
const std::vector<folly::dynamic> &registers;
const ComponentDescriptorRegistry &componentDescriptorRegistry;
const NativeModuleRegistry &nativeModuleRegistry;
};
// TODO: use RBCContext instead of all the separate arguments.
SharedShadowNode UITemplateProcessor::runCommand(
const folly::dynamic &command,
SurfaceId surfaceId,
std::vector<SharedShadowNode> &nodes,
std::vector<folly::dynamic> &registers,
const ComponentDescriptorRegistry &componentDescriptorRegistry,
const NativeModuleRegistry &nativeModuleRegistry,
const std::shared_ptr<const ReactNativeConfig> reactNativeConfig) {
const std::string &opcode = command[0].asString();
const int tagOffset = 420000;
// TODO: change to integer codes and a switch statement
if (opcode == "createNode") {
int tag = command[1].asInt();
const auto &type = command[2].asString();
const auto parentTag = command[3].asInt();
const auto &props = command[4];
nodes[tag] = componentDescriptorRegistry.createNode(
tag + tagOffset, type, surfaceId, props, nullptr);
if (parentTag > -1) { // parentTag == -1 indicates root node
auto parentShadowNode = nodes[parentTag];
auto const &componentDescriptor = componentDescriptorRegistry.at(
parentShadowNode->getComponentHandle());
componentDescriptor.appendChild(parentShadowNode, nodes[tag]);
}
} else if (opcode == "returnRoot") {
if (DEBUG_FLY) {
LOG(INFO)
<< "(stop) UITemplateProcessor inject serialized 'server rendered' view tree";
}
return nodes[command[1].asInt()];
} else if (opcode == "loadNativeBool") {
int registerNumber = command[1].asInt();
std::string param = command[4][0].asString();
registers[registerNumber] = reactNativeConfig->getBool(param);
} else if (opcode == "conditional") {
int registerNumber = command[1].asInt();
auto conditionDynamic = registers[registerNumber];
if (conditionDynamic.isNull()) {
// TODO: provide original command or command line?
auto err = std::runtime_error(
"register " + command[1].asString() + " wasn't loaded before access");
throw err;
} else if (conditionDynamic.type() != folly::dynamic::BOOL) {
// TODO: provide original command or command line?
auto err = std::runtime_error(
"register " + command[1].asString() + " had type '" +
conditionDynamic.typeName() +
"' but needs to be 'boolean' for conditionals");
throw err;
}
const auto &nextCommands =
conditionDynamic.asBool() ? command[2] : command[3];
for (const auto &nextCommand : nextCommands) {
runCommand(
nextCommand,
surfaceId,
nodes,
registers,
componentDescriptorRegistry,
nativeModuleRegistry,
reactNativeConfig);
}
} else {
throw std::runtime_error("Unsupported opcode: " + command[0].asString());
}
return nullptr;
}
SharedShadowNode UITemplateProcessor::buildShadowTree(
const std::string &jsonStr,
SurfaceId surfaceId,
const folly::dynamic &params,
const ComponentDescriptorRegistry &componentDescriptorRegistry,
const NativeModuleRegistry &nativeModuleRegistry,
const std::shared_ptr<const ReactNativeConfig> reactNativeConfig) {
if (DEBUG_FLY) {
LOG(INFO)
<< "(strt) UITemplateProcessor inject hardcoded 'server rendered' view tree";
}
std::string content = jsonStr;
for (const auto &param : params.items()) {
const auto &key = param.first.asString();
size_t start_pos = content.find(key);
if (start_pos != std::string::npos) {
content.replace(start_pos, key.length(), param.second.asString());
}
}
auto parsed = folly::parseJson(content);
auto commands = parsed["commands"];
std::vector<SharedShadowNode> nodes(commands.size() * 2);
std::vector<folly::dynamic> registers(32);
for (const auto &command : commands) {
try {
if (DEBUG_FLY) {
LOG(INFO) << "try to run command " << folly::toJson(command);
}
auto ret = runCommand(
command,
surfaceId,
nodes,
registers,
componentDescriptorRegistry,
nativeModuleRegistry,
reactNativeConfig);
if (ret != nullptr) {
return ret;
}
} catch (const std::exception &e) {
LOG(ERROR) << " >>> Exception <<< running previous command '"
<< folly::toJson(command) << "': '" << e.what() << "'";
}
}
LOG(ERROR) << "react ui template missing returnRoot command :(";
throw std::runtime_error(
"Missing returnRoot command in template content:\n" + content);
return SharedShadowNode{};
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,66 @@
/*
* 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 <memory>
#include <folly/dynamic.h>
#include <react/config/ReactNativeConfig.h>
#include <react/core/ShadowNode.h>
#include <react/uimanager/ComponentDescriptorRegistry.h>
#include <react/uimanager/UIManagerDelegate.h>
namespace facebook {
namespace react {
// Temporary NativeModuleRegistry definition
using NativeModuleCallFn =
std::function<folly::dynamic(const std::string &, const folly::dynamic &)>;
class NativeModuleRegistry {
public:
void registerModule(
const std::string &moduleName,
NativeModuleCallFn callFn) {
modules_.emplace(moduleName, callFn);
}
folly::dynamic call(
const std::string &moduleName,
const std::string &methodName,
const folly::dynamic &args) const {
return modules_.at(moduleName)(methodName, args);
}
private:
std::unordered_map<std::string, NativeModuleCallFn> modules_;
};
class UITemplateProcessor {
public:
static ShadowNode::Shared buildShadowTree(
const std::string &jsonStr,
int rootTag,
const folly::dynamic &params,
const ComponentDescriptorRegistry &componentDescriptorRegistry,
const NativeModuleRegistry &nativeModuleRegistry,
const std::shared_ptr<const ReactNativeConfig> reactNativeConfig);
private:
static ShadowNode::Shared runCommand(
const folly::dynamic &command,
Tag rootTag,
std::vector<SharedShadowNode> &nodes,
std::vector<folly::dynamic> &registers,
const ComponentDescriptorRegistry &componentDescriptorRegistry,
const NativeModuleRegistry &nativeModuleRegistry,
const std::shared_ptr<const ReactNativeConfig> reactNativeConfig);
};
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,101 @@
/*
* 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 <folly/dynamic.h>
#include <jsi/JSIDynamic.h>
#include <jsi/jsi.h>
#include <react/core/EventHandler.h>
#include <react/core/ShadowNode.h>
namespace facebook {
namespace react {
struct EventHandlerWrapper : public EventHandler {
EventHandlerWrapper(jsi::Function eventHandler)
: callback(std::move(eventHandler)) {}
jsi::Function callback;
};
struct ShadowNodeWrapper : public jsi::HostObject {
ShadowNodeWrapper(SharedShadowNode shadowNode)
: shadowNode(std::move(shadowNode)) {}
ShadowNode::Shared shadowNode;
};
struct ShadowNodeListWrapper : public jsi::HostObject {
ShadowNodeListWrapper(SharedShadowNodeUnsharedList shadowNodeList)
: shadowNodeList(shadowNodeList) {}
SharedShadowNodeUnsharedList shadowNodeList;
};
inline static ShadowNode::Shared shadowNodeFromValue(
jsi::Runtime &runtime,
const jsi::Value &value) {
return value.getObject(runtime)
.getHostObject<ShadowNodeWrapper>(runtime)
->shadowNode;
}
inline static jsi::Value valueFromShadowNode(
jsi::Runtime &runtime,
const ShadowNode::Shared &shadowNode) {
return jsi::Object::createFromHostObject(
runtime, std::make_shared<ShadowNodeWrapper>(shadowNode));
}
inline static SharedShadowNodeUnsharedList shadowNodeListFromValue(
jsi::Runtime &runtime,
const jsi::Value &value) {
return value.getObject(runtime)
.getHostObject<ShadowNodeListWrapper>(runtime)
->shadowNodeList;
}
inline static jsi::Value valueFromShadowNodeList(
jsi::Runtime &runtime,
const SharedShadowNodeUnsharedList &shadowNodeList) {
return jsi::Object::createFromHostObject(
runtime, std::make_unique<ShadowNodeListWrapper>(shadowNodeList));
}
inline static SharedEventTarget eventTargetFromValue(
jsi::Runtime &runtime,
const jsi::Value &eventTargetValue,
const jsi::Value &tagValue) {
return std::make_shared<EventTarget>(
runtime, eventTargetValue, tagValue.getNumber());
}
inline static Tag tagFromValue(jsi::Runtime &runtime, const jsi::Value &value) {
return (Tag)value.getNumber();
}
inline static SurfaceId surfaceIdFromValue(
jsi::Runtime &runtime,
const jsi::Value &value) {
return (SurfaceId)value.getNumber();
}
inline static std::string stringFromValue(
jsi::Runtime &runtime,
const jsi::Value &value) {
return value.getString(runtime).utf8(runtime);
}
inline static folly::dynamic commandArgsFromValue(
jsi::Runtime &runtime,
const jsi::Value &value) {
return jsi::dynamicFromValue(runtime, value);
}
} // namespace react
} // namespace facebook

View File

@ -0,0 +1,17 @@
/*
* 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 <memory>
#include <gtest/gtest.h>
#include <react/uimanager/UIManager.h>
using namespace facebook::react;
TEST(UIManagerTest, testSomething) {
// TODO
}

View File

@ -0,0 +1,168 @@
/*
* 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 <exception>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include <react/uimanager/ComponentDescriptorFactory.h>
#include <react/uimanager/UITemplateProcessor.h>
using namespace facebook::react;
#include <react/components/rncore/ComponentDescriptors.h>
#include <react/components/scrollview/ScrollViewComponentDescriptor.h>
#include <react/components/view/ViewComponentDescriptor.h>
#include <react/config/ReactNativeConfig.h>
#include <react/uimanager/ComponentDescriptorFactory.h>
#include <react/uimanager/ComponentDescriptorProviderRegistry.h>
#include <react/uimanager/ComponentDescriptorRegistry.h>
#include <react/utils/ContextContainer.h>
namespace facebook {
namespace react {
static ComponentRegistryFactory getComponentRegistryFactory() {
return [](const EventDispatcher::Weak &eventDispatcher,
const ContextContainer::Shared &contextContainer) {
ComponentDescriptorProviderRegistry providerRegistry{};
providerRegistry.add(
concreteComponentDescriptorProvider<ViewComponentDescriptor>());
providerRegistry.add(
concreteComponentDescriptorProvider<ScrollViewComponentDescriptor>());
providerRegistry.add(concreteComponentDescriptorProvider<
ActivityIndicatorViewComponentDescriptor>());
return providerRegistry.createComponentDescriptorRegistry(
{eventDispatcher, contextContainer});
};
}
bool mockSimpleTestValue_;
NativeModuleRegistry buildNativeModuleRegistry();
NativeModuleRegistry buildNativeModuleRegistry() {
NativeModuleRegistry nMR;
nMR.registerModule(
"MobileConfig",
[&](const std::string &methodName, const folly::dynamic &args) {
return mockSimpleTestValue_;
});
return nMR;
}
class MockReactNativeConfig : public ReactNativeConfig {
public:
MockReactNativeConfig() {}
bool getBool(const std::string &param) const override {
return mockSimpleTestValue_;
}
std::string getString(const std::string &param) const override {
return "";
}
int64_t getInt64(const std::string &param) const override {
return 0;
}
double getDouble(const std::string &param) const override {
return 0.0;
}
};
std::shared_ptr<const ReactNativeConfig> mockReactNativeConfig_ =
std::make_shared<const MockReactNativeConfig>();
} // namespace react
} // namespace facebook
TEST(UITemplateProcessorTest, testSimpleBytecode) {
auto surfaceId = 11;
auto eventDispatcher = std::shared_ptr<EventDispatcher const>();
auto componentDescriptorRegistry =
getComponentRegistryFactory()(eventDispatcher, nullptr);
auto nativeModuleRegistry = buildNativeModuleRegistry();
auto bytecode = R"delim({"version":0.1,"commands":[
["createNode",2,"RCTView",-1,{"opacity": 0.5, "testId": "root"}],
["createNode",4,"RCTView",2,{"testId": "child"}],
["returnRoot",2]
]})delim";
mockSimpleTestValue_ = true;
auto root1 = UITemplateProcessor::buildShadowTree(
bytecode,
surfaceId,
folly::dynamic::object(),
*componentDescriptorRegistry,
nativeModuleRegistry,
mockReactNativeConfig_);
#ifndef NDEBUG
LOG(INFO) << std::endl << root1->getDebugDescription();
#endif
auto props1 = std::dynamic_pointer_cast<const ViewProps>(root1->getProps());
EXPECT_NEAR(props1->opacity, 0.5, 0.001);
ASSERT_STREQ(props1->testId.c_str(), "root");
auto children1 = root1->getChildren();
EXPECT_EQ(children1.size(), 1);
auto child_props1 =
std::dynamic_pointer_cast<const ViewProps>(children1.at(0)->getProps());
ASSERT_STREQ(child_props1->testId.c_str(), "child");
}
TEST(UITemplateProcessorTest, testConditionalBytecode) {
auto surfaceId = 11;
auto eventDispatcher = std::shared_ptr<EventDispatcher const>();
auto componentDescriptorRegistry =
getComponentRegistryFactory()(eventDispatcher, nullptr);
auto nativeModuleRegistry = buildNativeModuleRegistry();
auto bytecode = R"delim({"version":0.1,"commands":[
["createNode",2,"RCTView",-1,{"testId": "root"}],
["loadNativeBool",1,"MobileConfig","getBool",["qe:simple_test"]],
["conditional",1,
[["createNode",4,"RCTView",2,{"testId": "cond_true"}]],
[["createNode",4,"RCTView",2,{"testId": "cond_false"}]]
],
["returnRoot",2]
]})delim";
mockSimpleTestValue_ = true;
auto root1 = UITemplateProcessor::buildShadowTree(
bytecode,
surfaceId,
folly::dynamic::object(),
*componentDescriptorRegistry,
nativeModuleRegistry,
mockReactNativeConfig_);
#ifndef NDEBUG
LOG(INFO) << std::endl << root1->getDebugDescription();
#endif
auto props1 = std::dynamic_pointer_cast<const ViewProps>(root1->getProps());
ASSERT_STREQ(props1->testId.c_str(), "root");
auto children1 = root1->getChildren();
EXPECT_EQ(children1.size(), 1);
auto child_props1 =
std::dynamic_pointer_cast<const ViewProps>(children1.at(0)->getProps());
ASSERT_STREQ(child_props1->testId.c_str(), "cond_true");
mockSimpleTestValue_ = false;
auto root2 = UITemplateProcessor::buildShadowTree(
bytecode,
surfaceId,
folly::dynamic::object(),
*componentDescriptorRegistry,
nativeModuleRegistry,
mockReactNativeConfig_);
auto child_props2 = std::dynamic_pointer_cast<const ViewProps>(
root2->getChildren().at(0)->getProps());
ASSERT_STREQ(child_props2->testId.c_str(), "cond_false");
}