Refactor uimanager stuff a bit

Summary: Simplies UIManager a bit and some other tweaks

Reviewed By: shergin

Differential Revision: D10211883

fbshipit-source-id: 93ab23dd2baab2fdc6d9c54e976b001a19efab7f
This commit is contained in:
Spencer Ahrens 2018-10-10 19:51:29 -07:00 committed by Facebook Github Bot
parent 8258b6a280
commit 83da74b556
9 changed files with 205 additions and 79 deletions

View File

@ -33,6 +33,9 @@ inline void fromDynamic(const folly::dynamic &value, double &result) {
inline void fromDynamic(const folly::dynamic &value, std::string &result) {
result = value.getString();
}
inline void fromDynamic(const folly::dynamic &value, folly::dynamic &result) {
result = value;
}
template <typename T>
inline void fromDynamic(const folly::dynamic &value, std::vector<T> &result) {

View File

@ -27,7 +27,7 @@ class ShadowNode;
using SharedShadowNode = std::shared_ptr<const ShadowNode>;
using UnsharedShadowNode = std::shared_ptr<ShadowNode>;
using SharedShadowNodeList = std::vector<std::shared_ptr<const ShadowNode>>;
using SharedShadowNodeList = std::vector<SharedShadowNode>;
using SharedShadowNodeSharedList = std::shared_ptr<const SharedShadowNodeList>;
using SharedShadowNodeUnsharedList = std::shared_ptr<SharedShadowNodeList>;

View File

@ -5,6 +5,8 @@
#include "ComponentDescriptorRegistry.h"
#include <fabric/core/ShadowNodeFragment.h>
namespace facebook {
namespace react {
@ -33,5 +35,83 @@ const SharedComponentDescriptor ComponentDescriptorRegistry::operator[](
return it->second;
}
static const 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";
}
if (viewName == "VirtualText") {
return "Text";
}
if (viewName == "ImageView") {
return "Image";
}
if (viewName == "AndroidHorizontalScrollView") {
return "ScrollView";
}
if (viewName == "AndroidProgressBar") {
return "ActivityIndicatorView";
}
// We need this temporarly for testing purposes until we have proper
// implementation of core components.
if (viewName == "SinglelineTextInputView" ||
viewName == "MultilineTextInputView" || viewName == "RefreshControl" ||
viewName == "SafeAreaView" || viewName == "ScrollContentView" ||
viewName == "AndroidHorizontalScrollContentView" // Android
) {
return "View";
}
return viewName;
}
static const RawProps rawPropsFromDynamic(const folly::dynamic object) {
// TODO: Convert this to something smarter, probably returning `std::iterator`.
RawProps result;
if (object.isNull()) {
return result;
}
assert(object.isObject());
for (const auto &pair : object.items()) {
assert(pair.first.isString());
result[pair.first.asString()] = pair.second;
}
return result;
}
SharedShadowNode ComponentDescriptorRegistry::createNode(Tag tag, const std::string &viewName, Tag rootTag, const folly::dynamic &props, const SharedEventTarget &eventTarget) const {
ComponentName componentName = componentNameByReactViewName(viewName);
const SharedComponentDescriptor &componentDescriptor = (*this)[componentName];
RawProps rawProps = rawPropsFromDynamic(props);
SharedShadowNode shadowNode =
componentDescriptor->createShadowNode({
.tag = tag,
.rootTag = rootTag,
.eventEmitter = componentDescriptor->createEventEmitter(std::move(eventTarget), tag),
.props = componentDescriptor->cloneProps(nullptr, rawProps)
});
return shadowNode;
}
} // namespace react
} // namespace facebook

View File

@ -29,6 +29,8 @@ class ComponentDescriptorRegistry {
const SharedShadowNode &shadowNode) const;
const SharedComponentDescriptor operator[](
const ComponentName &componentName) const;
SharedShadowNode createNode(
Tag tag, const std::string &viewName, Tag rootTag, const folly::dynamic &props, const SharedEventTarget &eventTarget) const;
private:
std::unordered_map<ComponentHandle, SharedComponentDescriptor>

View File

@ -40,51 +40,6 @@ static const RawProps rawPropsFromDynamic(const folly::dynamic object) {
return result;
}
static const 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";
}
if (viewName == "VirtualText") {
return "Text";
}
if (viewName == "ImageView") {
return "Image";
}
if (viewName == "AndroidHorizontalScrollView") {
return "ScrollView";
}
if (viewName == "AndroidProgressBar") {
return "ActivityIndicatorView";
}
// We need this temporarly for testing purposes until we have proper
// implementation of core components.
if (viewName == "SinglelineTextInputView" ||
viewName == "MultilineTextInputView" || viewName == "RefreshControl" ||
viewName == "SafeAreaView" || viewName == "ScrollContentView" ||
viewName == "AndroidHorizontalScrollContentView" // Android
) {
return "View";
}
return viewName;
}
FabricUIManager::FabricUIManager(
std::unique_ptr<EventBeatBasedExecutor> executor,
std::function<UIManagerInstaller> installer,
@ -168,28 +123,11 @@ void FabricUIManager::stopSurface(SurfaceId surfaceId) const {
(*executor_)([this, surfaceId] { stopSurfaceFunction_(surfaceId); });
}
SharedShadowNode FabricUIManager::createNode(
int tag,
std::string viewName,
int rootTag,
folly::dynamic props,
SharedEventTarget eventTarget) const {
ComponentName componentName = componentNameByReactViewName(viewName);
const SharedComponentDescriptor &componentDescriptor =
(*componentDescriptorRegistry_)[componentName];
RawProps rawProps = rawPropsFromDynamic(props);
SharedShadowNode shadowNode = componentDescriptor->createShadowNode(
{.tag = tag,
.rootTag = rootTag,
.eventEmitter =
componentDescriptor->createEventEmitter(std::move(eventTarget), tag),
.props = componentDescriptor->cloneProps(nullptr, rawProps)});
SharedShadowNode FabricUIManager::createNode(int tag, std::string viewName, int rootTag, folly::dynamic props, SharedEventTarget eventTarget) const {
SharedShadowNode shadowNode = componentDescriptorRegistry_->createNode(tag, viewName, rootTag, props, eventTarget);
if (delegate_) {
delegate_->uiManagerDidCreateShadowNode(shadowNode);
}
return shadowNode;
}

View File

@ -8,6 +8,7 @@
#include <fabric/core/LayoutContext.h>
#include <fabric/uimanager/ComponentDescriptorRegistry.h>
#include <fabric/uimanager/FabricUIManager.h>
#include <fabric/uimanager/TemplateRenderer.h>
#include "ComponentDescriptorFactory.h"
#include "Differentiator.h"
@ -39,9 +40,11 @@ Scheduler::Scheduler(const SharedContextContainer &contextContainer)
synchronousEventBeatFactory,
asynchronousEventBeatFactory);
componentDescriptorRegistry_ = ComponentDescriptorFactory::buildRegistry(
eventDispatcher, contextContainer);
uiManager_->setComponentDescriptorRegistry(
ComponentDescriptorFactory::buildRegistry(
eventDispatcher, contextContainer));
componentDescriptorRegistry_
);
uiManager_->setDelegate(this);
}
@ -55,7 +58,7 @@ void Scheduler::startSurface(
const std::string &moduleName,
const folly::dynamic &initialProps,
const LayoutConstraints &layoutConstraints,
const LayoutContext &layoutContext) const {
const LayoutContext &layoutContext) {
std::lock_guard<std::mutex> lock(mutex_);
auto shadowTree =
@ -64,7 +67,18 @@ void Scheduler::startSurface(
shadowTreeRegistry_.emplace(surfaceId, std::move(shadowTree));
#ifndef ANDROID
uiManager_->startSurface(surfaceId, moduleName, initialProps);
// TODO: Is this an ok place to do this?
auto serializedCommands = initialProps.find("serializedCommands");
if (serializedCommands != initialProps.items().end()) {
auto tree = TemplateRenderer::buildShadowTree(serializedCommands->second.asString(), surfaceId, folly::dynamic::object(), *componentDescriptorRegistry_);
uiManagerDidFinishTransactionWithoutLock(surfaceId, std::make_shared<SharedShadowNodeList>(SharedShadowNodeList {tree}));
// TODO: hydrate rather than replace
uiManager_->startSurface(surfaceId, moduleName, initialProps);
} else {
uiManager_->startSurface(surfaceId, moduleName, initialProps);
}
#endif
}
@ -107,6 +121,16 @@ void Scheduler::constraintSurfaceLayout(
});
}
void Scheduler::uiManagerDidFinishTransactionWithoutLock(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes) {
const auto iterator = shadowTreeRegistry_.find(rootTag);
if (iterator == shadowTreeRegistry_.end()) {
// This might happen during surface unmounting/deallocation process
// due to the asynchronous nature of JS calls.
return;
}
iterator->second->complete(rootChildNodes);
}
#pragma mark - Delegate
void Scheduler::setDelegate(SchedulerDelegate *delegate) {
@ -134,15 +158,7 @@ void Scheduler::uiManagerDidFinishTransaction(
Tag rootTag,
const SharedShadowNodeUnsharedList &rootChildNodes) {
std::lock_guard<std::mutex> lock(mutex_);
const auto iterator = shadowTreeRegistry_.find(rootTag);
if (iterator == shadowTreeRegistry_.end()) {
// This might happen during surface unmounting/deallocation process
// due to the asynchronous nature of JS calls.
return;
}
iterator->second->complete(rootChildNodes);
uiManagerDidFinishTransactionWithoutLock(rootTag, rootChildNodes);
}
void Scheduler::uiManagerDidCreateShadowNode(

View File

@ -10,6 +10,7 @@
#include <fabric/core/ComponentDescriptor.h>
#include <fabric/core/LayoutConstraints.h>
#include <fabric/uimanager/ComponentDescriptorRegistry.h>
#include <fabric/uimanager/ContextContainer.h>
#include <fabric/uimanager/SchedulerDelegate.h>
#include <fabric/uimanager/ShadowTree.h>
@ -36,7 +37,7 @@ class Scheduler final : public UIManagerDelegate, public ShadowTreeDelegate {
const std::string &moduleName,
const folly::dynamic &initialProps,
const LayoutConstraints &layoutConstraints = {},
const LayoutContext &layoutContext = {}) const;
const LayoutContext &layoutContext = {});
void stopSurface(SurfaceId surfaceId) const;
@ -91,11 +92,15 @@ class Scheduler final : public UIManagerDelegate, public ShadowTreeDelegate {
private:
SchedulerDelegate *delegate_;
std::shared_ptr<FabricUIManager> uiManager_;
SharedComponentDescriptorRegistry componentDescriptorRegistry_;
mutable std::mutex mutex_;
mutable std::unordered_map<SurfaceId, std::unique_ptr<ShadowTree>>
shadowTreeRegistry_; // Protected by `mutex_`.
SharedEventDispatcher eventDispatcher_;
SharedContextContainer contextContainer_;
void uiManagerDidFinishTransactionWithoutLock(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes);
};
} // namespace react

View File

@ -0,0 +1,57 @@
/**
* 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 "TemplateRenderer.h"
#include <glog/logging.h>
#include <fabric/components/view/ViewComponentDescriptor.h>
#include <fabric/components/view/ViewProps.h>
#include <fabric/components/view/ViewShadowNode.h>
#include <fabric/core/componentDescriptor.h>
#include <fabric/core/LayoutContext.h>
#include <fabric/core/ShadowNodeFragment.h>
#include <fabric/debug/DebugStringConvertible.h>
#include <fabric/debug/DebugStringConvertibleItem.h>
#include <folly/json.h>
namespace facebook {
namespace react {
SharedShadowNode TemplateRenderer::buildShadowTree(const std::string &jsonStr, int rootTag, const folly::dynamic &params, const ComponentDescriptorRegistry &componentDescriptorRegistry) {
LOG(INFO) << "(strt) TemplateRenderer 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 json = folly::parseJson(content);
std::vector<SharedShadowNode> nodes;
nodes.resize(json.size() * 2);
int tagOffset = 4560; // MAYBE TODO: use number of existing tags so they don't collide rather than random value
for (const auto& command : json) {
if (command[0] == "createNode") {
int tag = command[1].asInt();
const auto& type = command[2].asString();
const auto& props = command[3];
nodes[tag] = componentDescriptorRegistry.createNode(tag + tagOffset, type, rootTag, props, nullptr);
} else if (command[0] == "appendChild") {
auto parentShadowNode = nodes[command[1].asInt()];
const SharedComponentDescriptor &componentDescriptor = componentDescriptorRegistry[parentShadowNode];
componentDescriptor->appendChild(parentShadowNode, nodes[command[2].asInt()]);
} else if (command[0] == "childSetNode") {
LOG(INFO) << "(stop) TemplateView inject serialized 'server rendered' view tree";
return nodes[command[1].asInt()];
}
}
throw std::runtime_error("Missing childSetNode command in template content:\n" + content);
return SharedShadowNode {};
}
}
}

View File

@ -0,0 +1,25 @@
/**
* 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 <fabric/core/ShadowNode.h>
#include <fabric/uimanager/ComponentDescriptorRegistry.h>
#include <fabric/uimanager/UIManagerDelegate.h>
namespace facebook {
namespace react {
class TemplateRenderer {
public:
static SharedShadowNode buildShadowTree(const std::string &jsonStr, int rootTag, const folly::dynamic &params, const ComponentDescriptorRegistry &componentDescriptorRegistry);
};
} // react
} // facebook