Skip to content

Commit

Permalink
Refactor uimanager stuff a bit
Browse files Browse the repository at this point in the history
Summary: Simplies UIManager a bit and some other tweaks

Reviewed By: shergin

Differential Revision: D10211883

fbshipit-source-id: 93ab23dd2baab2fdc6d9c54e976b001a19efab7f
  • Loading branch information
sahrens authored and facebook-github-bot committed Oct 11, 2018
1 parent 8258b6a commit 83da74b
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 79 deletions.
3 changes: 3 additions & 0 deletions ReactCommon/fabric/core/propsConversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion ReactCommon/fabric/core/shadownode/ShadowNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>;

Expand Down
80 changes: 80 additions & 0 deletions ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

#include "ComponentDescriptorRegistry.h"

#include <fabric/core/ShadowNodeFragment.h>

namespace facebook {
namespace react {

Expand Down Expand Up @@ -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
2 changes: 2 additions & 0 deletions ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
66 changes: 2 additions & 64 deletions ReactCommon/fabric/uimanager/FabricUIManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}

Expand Down
42 changes: 29 additions & 13 deletions ReactCommon/fabric/uimanager/Scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
}
Expand All @@ -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 =
Expand All @@ -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
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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(
Expand Down
7 changes: 6 additions & 1 deletion ReactCommon/fabric/uimanager/Scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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;

Expand Down Expand Up @@ -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
Expand Down
57 changes: 57 additions & 0 deletions ReactCommon/fabric/uimanager/TemplateRenderer.cpp
Original file line number Diff line number Diff line change
@@ -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 {};
}
}
}
Loading

0 comments on commit 83da74b

Please sign in to comment.