1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #include "ABI49_0_0Scheduler.h"
9 
10 #include <glog/logging.h>
11 #include <ABI49_0_0jsi/ABI49_0_0jsi.h>
12 
13 #include <ABI49_0_0React/debug/ABI49_0_0React_native_assert.h>
14 #include <ABI49_0_0React/renderer/componentregistry/ABI49_0_0ComponentDescriptorRegistry.h>
15 #include <ABI49_0_0React/renderer/core/ABI49_0_0EventQueueProcessor.h>
16 #include <ABI49_0_0React/renderer/core/ABI49_0_0LayoutContext.h>
17 #include <ABI49_0_0React/renderer/debug/ABI49_0_0SystraceSection.h>
18 #include <ABI49_0_0React/renderer/mounting/ABI49_0_0MountingOverrideDelegate.h>
19 #include <ABI49_0_0React/renderer/mounting/ABI49_0_0ShadowViewMutation.h>
20 #include <ABI49_0_0React/renderer/runtimescheduler/ABI49_0_0RuntimeScheduler.h>
21 #include <ABI49_0_0React/renderer/templateprocessor/ABI49_0_0UITemplateProcessor.h>
22 #include <ABI49_0_0React/renderer/uimanager/ABI49_0_0UIManager.h>
23 #include <ABI49_0_0React/renderer/uimanager/ABI49_0_0UIManagerBinding.h>
24 
25 #ifdef ABI49_0_0RN_SHADOW_TREE_INTROSPECTION
26 #include <ABI49_0_0React/renderer/mounting/ABI49_0_0stubs.h>
27 #include <iostream>
28 #endif
29 
30 namespace ABI49_0_0facebook::ABI49_0_0React {
31 
Scheduler(SchedulerToolbox const & schedulerToolbox,UIManagerAnimationDelegate * animationDelegate,SchedulerDelegate * delegate)32 Scheduler::Scheduler(
33     SchedulerToolbox const &schedulerToolbox,
34     UIManagerAnimationDelegate *animationDelegate,
35     SchedulerDelegate *delegate) {
36   runtimeExecutor_ = schedulerToolbox.runtimeExecutor;
37   contextContainer_ = schedulerToolbox.contextContainer;
38 
39   ABI49_0_0ReactNativeConfig_ =
40       contextContainer_->at<std::shared_ptr<const ABI49_0_0ReactNativeConfig>>(
41           "ABI49_0_0ReactNativeConfig");
42 
43   // Creating a container for future `EventDispatcher` instance.
44   eventDispatcher_ = std::make_shared<std::optional<EventDispatcher const>>();
45 
46   auto uiManager = std::make_shared<UIManager>(
47       runtimeExecutor_, schedulerToolbox.backgroundExecutor, contextContainer_);
48   auto eventOwnerBox = std::make_shared<EventBeat::OwnerBox>();
49   eventOwnerBox->owner = eventDispatcher_;
50 
51   auto weakRuntimeScheduler =
52       contextContainer_->find<std::weak_ptr<RuntimeScheduler>>(
53           "RuntimeScheduler");
54   auto runtimeScheduler = weakRuntimeScheduler.has_value()
55       ? weakRuntimeScheduler.value().lock()
56       : nullptr;
57 
58   auto eventPipe = [uiManager, runtimeScheduler = runtimeScheduler.get()](
59                        jsi::Runtime &runtime,
60                        const EventTarget *eventTarget,
61                        const std::string &type,
62                        ABI49_0_0ReactEventPriority priority,
63                        const ValueFactory &payloadFactory) {
64     uiManager->visitBinding(
65         [&](UIManagerBinding const &uiManagerBinding) {
66           uiManagerBinding.dispatchEvent(
67               runtime, eventTarget, type, priority, payloadFactory);
68         },
69         runtime);
70     if (runtimeScheduler != nullptr) {
71       runtimeScheduler->callExpiredTasks(runtime);
72     }
73   };
74 
75   auto statePipe = [uiManager](StateUpdate const &stateUpdate) {
76     uiManager->updateState(stateUpdate);
77   };
78 
79   // Creating an `EventDispatcher` instance inside the already allocated
80   // container (inside the optional).
81   eventDispatcher_->emplace(
82       EventQueueProcessor(eventPipe, statePipe),
83       schedulerToolbox.synchronousEventBeatFactory,
84       schedulerToolbox.asynchronousEventBeatFactory,
85       eventOwnerBox);
86 
87   // Casting to `std::shared_ptr<EventDispatcher const>`.
88   auto eventDispatcher =
89       EventDispatcher::Shared{eventDispatcher_, &eventDispatcher_->value()};
90 
91   componentDescriptorRegistry_ = schedulerToolbox.componentRegistryFactory(
92       eventDispatcher, contextContainer_);
93 
94   uiManager->setDelegate(this);
95   uiManager->setComponentDescriptorRegistry(componentDescriptorRegistry_);
96 
97   auto bindingsExecutor =
98       schedulerToolbox.bridgelessBindingsExecutor.has_value()
99       ? schedulerToolbox.bridgelessBindingsExecutor.value()
100       : runtimeExecutor_;
101   bindingsExecutor([uiManager](jsi::Runtime &runtime) {
102     UIManagerBinding::createAndInstallIfNeeded(runtime, uiManager);
103   });
104 
105   auto componentDescriptorRegistryKey =
106       "ComponentDescriptorRegistry_DO_NOT_USE_PRETTY_PLEASE";
107   contextContainer_->erase(componentDescriptorRegistryKey);
108   contextContainer_->insert(
109       componentDescriptorRegistryKey,
110       std::weak_ptr<ComponentDescriptorRegistry const>(
111           componentDescriptorRegistry_));
112 
113   delegate_ = delegate;
114   commitHooks_ = schedulerToolbox.commitHooks;
115   uiManager_ = uiManager;
116 
117   for (auto const &commitHook : commitHooks_) {
118     uiManager->registerCommitHook(*commitHook);
119   }
120 
121 #ifdef ANDROID
122   removeOutstandingSurfacesOnDestruction_ = true;
123   reduceDeleteCreateMutationLayoutAnimation_ = ABI49_0_0ReactNativeConfig_->getBool(
124       "ABI49_0_0React_fabric:reduce_delete_create_mutation_layout_animation_android");
125 #else
126   removeOutstandingSurfacesOnDestruction_ = ABI49_0_0ReactNativeConfig_->getBool(
127       "ABI49_0_0React_fabric:remove_outstanding_surfaces_on_destruction_ios");
128   reduceDeleteCreateMutationLayoutAnimation_ = true;
129 #endif
130 
131   CoreFeatures::blockPaintForUseLayoutEffect = ABI49_0_0ReactNativeConfig_->getBool(
132       "ABI49_0_0React_fabric:block_paint_for_use_layout_effect");
133 
134   if (animationDelegate != nullptr) {
135     animationDelegate->setComponentDescriptorRegistry(
136         componentDescriptorRegistry_);
137     animationDelegate->setReduceDeleteCreateMutation(
138         reduceDeleteCreateMutationLayoutAnimation_);
139   }
140   uiManager_->setAnimationDelegate(animationDelegate);
141 }
142 
~Scheduler()143 Scheduler::~Scheduler() {
144   LOG(WARNING) << "Scheduler::~Scheduler() was called (address: " << this
145                << ").";
146 
147   for (auto const &commitHook : commitHooks_) {
148     uiManager_->unregisterCommitHook(*commitHook);
149   }
150 
151   // All Surfaces must be explicitly stopped before destroying `Scheduler`.
152   // The idea is that `UIManager` is allowed to call `Scheduler` only if the
153   // corresponding `ShadowTree` instance exists.
154 
155   // The thread-safety of this operation is guaranteed by this requirement.
156   uiManager_->setDelegate(nullptr);
157   uiManager_->setAnimationDelegate(nullptr);
158 
159   // Then, let's verify that the requirement was satisfied.
160   auto surfaceIds = std::vector<SurfaceId>{};
161   uiManager_->getShadowTreeRegistry().enumerate(
162       [&surfaceIds](ShadowTree const &shadowTree, bool &) {
163         surfaceIds.push_back(shadowTree.getSurfaceId());
164       });
165 
166   // TODO(T88046056): Fix Android memory leak before uncommenting changes
167   //  ABI49_0_0React_native_assert(
168   //      surfaceIds.empty() &&
169   //      "Scheduler was destroyed with outstanding Surfaces.");
170 
171   if (surfaceIds.empty()) {
172     return;
173   }
174 
175   LOG(ERROR) << "Scheduler was destroyed with outstanding Surfaces.";
176 
177   // If we are here, that means assert didn't fire which indicates that we in
178   // production.
179 
180   // Now we have still-running surfaces, which is no good, no good.
181   // That's indeed a sign of a severe issue on the application layer.
182   // At this point, we don't have much to lose, so we are trying to unmount all
183   // outstanding `ShadowTree`s to prevent all stored JSI entities from
184   // overliving the `Scheduler`. (Unmounting `ShadowNode`s disables
185   // `EventEmitter`s which destroys JSI objects.)
186   for (auto surfaceId : surfaceIds) {
187     uiManager_->getShadowTreeRegistry().visit(
188         surfaceId,
189         [](ShadowTree const &shadowTree) { shadowTree.commitEmptyTree(); });
190 
191     // Removing surfaces is gated because it acquires mutex waiting for commits
192     // in flight; in theory, it can deadlock.
193     if (removeOutstandingSurfacesOnDestruction_) {
194       uiManager_->getShadowTreeRegistry().remove(surfaceId);
195     }
196   }
197 }
198 
registerSurface(SurfaceHandler const & surfaceHandler) const199 void Scheduler::registerSurface(
200     SurfaceHandler const &surfaceHandler) const noexcept {
201   surfaceHandler.setContextContainer(getContextContainer());
202   surfaceHandler.setUIManager(uiManager_.get());
203 }
204 
getInspectorDataForInstance(EventEmitter const & eventEmitter) const205 InspectorData Scheduler::getInspectorDataForInstance(
206     EventEmitter const &eventEmitter) const noexcept {
207   return executeSynchronouslyOnSameThread_CAN_DEADLOCK<InspectorData>(
208       runtimeExecutor_, [=](jsi::Runtime &runtime) -> InspectorData {
209         auto uiManagerBinding = UIManagerBinding::getBinding(runtime);
210         auto value = uiManagerBinding->getInspectorDataForInstance(
211             runtime, eventEmitter);
212 
213         // TODO T97216348: avoid transforming jsi into folly::dynamic
214         auto dynamic = jsi::dynamicFromValue(runtime, value);
215         auto source = dynamic["source"];
216 
217         InspectorData result = {};
218         result.fileName =
219             source["fileName"].isNull() ? "" : source["fileName"].c_str();
220         result.lineNumber = (int)source["lineNumber"].getDouble();
221         result.columnNumber = (int)source["columnNumber"].getDouble();
222         result.selectedIndex = (int)dynamic["selectedIndex"].getDouble();
223         // TODO T97216348: remove folly::dynamic from InspectorData struct
224         result.props = dynamic["props"];
225         auto hierarchy = dynamic["hierarchy"];
226         for (auto &i : hierarchy) {
227           auto viewHierarchyValue = i["name"];
228           if (!viewHierarchyValue.isNull()) {
229             result.hierarchy.emplace_back(viewHierarchyValue.c_str());
230           }
231         }
232         return result;
233       });
234 }
235 
unregisterSurface(SurfaceHandler const & surfaceHandler) const236 void Scheduler::unregisterSurface(
237     SurfaceHandler const &surfaceHandler) const noexcept {
238   surfaceHandler.setUIManager(nullptr);
239 }
240 
renderTemplateToSurface(SurfaceId surfaceId,const std::string & uiTemplate)241 void Scheduler::renderTemplateToSurface(
242     SurfaceId surfaceId,
243     const std::string &uiTemplate) {
244   SystraceSection s("Scheduler::renderTemplateToSurface");
245   try {
246     if (uiTemplate.empty()) {
247       return;
248     }
249     NativeModuleRegistry nMR;
250     auto tree = UITemplateProcessor::buildShadowTree(
251         uiTemplate,
252         surfaceId,
253         folly::dynamic::object(),
254         *componentDescriptorRegistry_,
255         nMR,
256         ABI49_0_0ReactNativeConfig_);
257 
258     uiManager_->getShadowTreeRegistry().visit(
259         surfaceId, [=](const ShadowTree &shadowTree) {
260           return shadowTree.tryCommit(
261               [&](RootShadowNode const &oldRootShadowNode) {
262                 return std::make_shared<RootShadowNode>(
263                     oldRootShadowNode,
264                     ShadowNodeFragment{
265                         /* .props = */ ShadowNodeFragment::propsPlaceholder(),
266                         /* .children = */
267                         std::make_shared<ShadowNode::ListOfShared>(
268                             ShadowNode::ListOfShared{tree}),
269                     });
270               },
271               {/* default commit options */});
272         });
273   } catch (const std::exception &e) {
274     LOG(ERROR) << "    >>>> ABI49_0_0EXCEPTION <<<  rendering uiTemplate in "
275                << "Scheduler::renderTemplateToSurface: " << e.what();
276   }
277 }
278 
279 ComponentDescriptor const *
findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(ComponentHandle handle) const280 Scheduler::findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(
281     ComponentHandle handle) const {
282   return componentDescriptorRegistry_
283       ->findComponentDescriptorByHandle_DO_NOT_USE_THIS_IS_BROKEN(handle);
284 }
285 
286 #pragma mark - Delegate
287 
setDelegate(SchedulerDelegate * delegate)288 void Scheduler::setDelegate(SchedulerDelegate *delegate) {
289   delegate_ = delegate;
290 }
291 
getDelegate() const292 SchedulerDelegate *Scheduler::getDelegate() const {
293   return delegate_;
294 }
295 
296 #pragma mark - UIManagerAnimationDelegate
297 
animationTick() const298 void Scheduler::animationTick() const {
299   uiManager_->animationTick();
300 }
301 
302 #pragma mark - UIManagerDelegate
303 
uiManagerDidFinishTransaction(MountingCoordinator::Shared mountingCoordinator,bool mountSynchronously)304 void Scheduler::uiManagerDidFinishTransaction(
305     MountingCoordinator::Shared mountingCoordinator,
306     bool mountSynchronously) {
307   SystraceSection s("Scheduler::uiManagerDidFinishTransaction");
308 
309   if (delegate_ != nullptr) {
310     if (CoreFeatures::blockPaintForUseLayoutEffect) {
311       auto weakRuntimeScheduler =
312           contextContainer_->find<std::weak_ptr<RuntimeScheduler>>(
313               "RuntimeScheduler");
314       auto runtimeScheduler = weakRuntimeScheduler.has_value()
315           ? weakRuntimeScheduler.value().lock()
316           : nullptr;
317       if (runtimeScheduler && !mountSynchronously) {
318         runtimeScheduler->scheduleTask(
319             SchedulerPriority::UserBlockingPriority,
320             [delegate = delegate_,
321              mountingCoordinator =
322                  std::move(mountingCoordinator)](jsi::Runtime &) {
323               delegate->schedulerDidFinishTransaction(
324                   std::move(mountingCoordinator));
325             });
326       } else {
327         delegate_->schedulerDidFinishTransaction(
328             std::move(mountingCoordinator));
329       }
330     } else {
331       delegate_->schedulerDidFinishTransaction(std::move(mountingCoordinator));
332     }
333   }
334 }
uiManagerDidCreateShadowNode(const ShadowNode & shadowNode)335 void Scheduler::uiManagerDidCreateShadowNode(const ShadowNode &shadowNode) {
336   SystraceSection s("Scheduler::uiManagerDidCreateShadowNode");
337 
338   if (delegate_ != nullptr) {
339     delegate_->schedulerDidRequestPreliminaryViewAllocation(
340         shadowNode.getSurfaceId(), shadowNode);
341   }
342 }
343 
uiManagerDidDispatchCommand(const ShadowNode::Shared & shadowNode,std::string const & commandName,folly::dynamic const & args)344 void Scheduler::uiManagerDidDispatchCommand(
345     const ShadowNode::Shared &shadowNode,
346     std::string const &commandName,
347     folly::dynamic const &args) {
348   SystraceSection s("Scheduler::uiManagerDispatchCommand");
349 
350   if (delegate_ != nullptr) {
351     auto shadowView = ShadowView(*shadowNode);
352     delegate_->schedulerDidDispatchCommand(shadowView, commandName, args);
353   }
354 }
355 
uiManagerDidSendAccessibilityEvent(const ShadowNode::Shared & shadowNode,std::string const & eventType)356 void Scheduler::uiManagerDidSendAccessibilityEvent(
357     const ShadowNode::Shared &shadowNode,
358     std::string const &eventType) {
359   SystraceSection s("Scheduler::uiManagerDidSendAccessibilityEvent");
360 
361   if (delegate_ != nullptr) {
362     auto shadowView = ShadowView(*shadowNode);
363     delegate_->schedulerDidSendAccessibilityEvent(shadowView, eventType);
364   }
365 }
366 
367 /*
368  * Set JS responder for a view.
369  */
uiManagerDidSetIsJSResponder(ShadowNode::Shared const & shadowNode,bool isJSResponder,bool blockNativeResponder)370 void Scheduler::uiManagerDidSetIsJSResponder(
371     ShadowNode::Shared const &shadowNode,
372     bool isJSResponder,
373     bool blockNativeResponder) {
374   if (delegate_ != nullptr) {
375     delegate_->schedulerDidSetIsJSResponder(
376         ShadowView(*shadowNode), isJSResponder, blockNativeResponder);
377   }
378 }
379 
getContextContainer() const380 ContextContainer::Shared Scheduler::getContextContainer() const {
381   return contextContainer_;
382 }
383 
getUIManager() const384 std::shared_ptr<UIManager> Scheduler::getUIManager() const {
385   return uiManager_;
386 }
387 
addEventListener(const std::shared_ptr<EventListener const> & listener)388 void Scheduler::addEventListener(
389     const std::shared_ptr<EventListener const> &listener) {
390   if (eventDispatcher_->has_value()) {
391     eventDispatcher_->value().addListener(listener);
392   }
393 }
394 
removeEventListener(const std::shared_ptr<EventListener const> & listener)395 void Scheduler::removeEventListener(
396     const std::shared_ptr<EventListener const> &listener) {
397   if (eventDispatcher_->has_value()) {
398     eventDispatcher_->value().removeListener(listener);
399   }
400 }
401 
402 } // namespace ABI49_0_0facebook::ABI49_0_0React
403