diff --git a/ReactCommon/fabric/core/propsConversions.h b/ReactCommon/fabric/core/propsConversions.h index c0e9b85c99c71f..a1a8db5b00f8a1 100644 --- a/ReactCommon/fabric/core/propsConversions.h +++ b/ReactCommon/fabric/core/propsConversions.h @@ -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 inline void fromDynamic(const folly::dynamic &value, std::vector &result) { diff --git a/ReactCommon/fabric/core/shadownode/ShadowNode.h b/ReactCommon/fabric/core/shadownode/ShadowNode.h index cb50219d70f09e..a58c3aeb86d4fb 100644 --- a/ReactCommon/fabric/core/shadownode/ShadowNode.h +++ b/ReactCommon/fabric/core/shadownode/ShadowNode.h @@ -27,7 +27,7 @@ class ShadowNode; using SharedShadowNode = std::shared_ptr; using UnsharedShadowNode = std::shared_ptr; -using SharedShadowNodeList = std::vector>; +using SharedShadowNodeList = std::vector; using SharedShadowNodeSharedList = std::shared_ptr; using SharedShadowNodeUnsharedList = std::shared_ptr; diff --git a/ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.cpp b/ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.cpp index b057f71a803c23..967397367fb268 100644 --- a/ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.cpp +++ b/ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.cpp @@ -5,6 +5,8 @@ #include "ComponentDescriptorRegistry.h" +#include + 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 diff --git a/ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.h b/ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.h index 1a45747f4df957..4a4100e7966303 100644 --- a/ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.h +++ b/ReactCommon/fabric/uimanager/ComponentDescriptorRegistry.h @@ -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 diff --git a/ReactCommon/fabric/uimanager/FabricUIManager.cpp b/ReactCommon/fabric/uimanager/FabricUIManager.cpp index 3d92072af7fe63..73767281f81ddd 100644 --- a/ReactCommon/fabric/uimanager/FabricUIManager.cpp +++ b/ReactCommon/fabric/uimanager/FabricUIManager.cpp @@ -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 executor, std::function 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; } diff --git a/ReactCommon/fabric/uimanager/Scheduler.cpp b/ReactCommon/fabric/uimanager/Scheduler.cpp index 9439610d59ddec..6867e3f889d627 100644 --- a/ReactCommon/fabric/uimanager/Scheduler.cpp +++ b/ReactCommon/fabric/uimanager/Scheduler.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #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 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 {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 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( diff --git a/ReactCommon/fabric/uimanager/Scheduler.h b/ReactCommon/fabric/uimanager/Scheduler.h index 3bd5a4876d842c..681b20f49f0123 100644 --- a/ReactCommon/fabric/uimanager/Scheduler.h +++ b/ReactCommon/fabric/uimanager/Scheduler.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -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 uiManager_; + SharedComponentDescriptorRegistry componentDescriptorRegistry_; mutable std::mutex mutex_; mutable std::unordered_map> shadowTreeRegistry_; // Protected by `mutex_`. SharedEventDispatcher eventDispatcher_; SharedContextContainer contextContainer_; + + void uiManagerDidFinishTransactionWithoutLock(Tag rootTag, const SharedShadowNodeUnsharedList &rootChildNodes); + }; } // namespace react diff --git a/ReactCommon/fabric/uimanager/TemplateRenderer.cpp b/ReactCommon/fabric/uimanager/TemplateRenderer.cpp new file mode 100644 index 00000000000000..4e41accd82d147 --- /dev/null +++ b/ReactCommon/fabric/uimanager/TemplateRenderer.cpp @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace facebook { + namespace react { + SharedShadowNode TemplateRenderer::buildShadowTree(const std::string &jsonStr, int rootTag, const folly::dynamic ¶ms, 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 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 {}; + } + } +} diff --git a/ReactCommon/fabric/uimanager/TemplateRenderer.h b/ReactCommon/fabric/uimanager/TemplateRenderer.h new file mode 100644 index 00000000000000..36978538e41a61 --- /dev/null +++ b/ReactCommon/fabric/uimanager/TemplateRenderer.h @@ -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 + +#include + +#include +#include +#include + +namespace facebook { + namespace react { + class TemplateRenderer { + public: + static SharedShadowNode buildShadowTree(const std::string &jsonStr, int rootTag, const folly::dynamic ¶ms, const ComponentDescriptorRegistry &componentDescriptorRegistry); + }; + } // react +} // facebook