UIManagerBinding.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. /*
  2. * Copyright (c) Facebook, Inc. and its 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. #include "UIManagerBinding.h"
  8. #include <react/debug/SystraceSection.h>
  9. #include <glog/logging.h>
  10. #include <jsi/JSIDynamic.h>
  11. namespace facebook {
  12. namespace react {
  13. static jsi::Object getModule(
  14. jsi::Runtime &runtime,
  15. const std::string &moduleName) {
  16. auto batchedBridge =
  17. runtime.global().getPropertyAsObject(runtime, "__fbBatchedBridge");
  18. auto getCallableModule =
  19. batchedBridge.getPropertyAsFunction(runtime, "getCallableModule");
  20. auto module = getCallableModule
  21. .callWithThis(
  22. runtime,
  23. batchedBridge,
  24. {jsi::String::createFromUtf8(runtime, moduleName)})
  25. .asObject(runtime);
  26. return module;
  27. }
  28. std::shared_ptr<UIManagerBinding> UIManagerBinding::createAndInstallIfNeeded(
  29. jsi::Runtime &runtime) {
  30. auto uiManagerModuleName = "nativeFabricUIManager";
  31. auto uiManagerValue =
  32. runtime.global().getProperty(runtime, uiManagerModuleName);
  33. if (uiManagerValue.isUndefined()) {
  34. // The global namespace does not have an instance of the binding;
  35. // we need to create, install and return it.
  36. auto uiManagerBinding = std::make_shared<UIManagerBinding>();
  37. auto object = jsi::Object::createFromHostObject(runtime, uiManagerBinding);
  38. runtime.global().setProperty(
  39. runtime, uiManagerModuleName, std::move(object));
  40. return uiManagerBinding;
  41. }
  42. // The global namespace already has an instance of the binding;
  43. // we need to return that.
  44. auto uiManagerObject = uiManagerValue.asObject(runtime);
  45. return uiManagerObject.getHostObject<UIManagerBinding>(runtime);
  46. }
  47. UIManagerBinding::~UIManagerBinding() {
  48. LOG(WARNING) << "UIManager::~UIManager() was called (address: " << this
  49. << ").";
  50. // We must detach the `UIBinding` on deallocation to prevent accessing
  51. // deallocated `UIManagerBinding`.
  52. // Since `UIManagerBinding` retains `UIManager`, `UIManager` always overlive
  53. // `UIManagerBinding`, therefore we don't need similar logic in `UIManager`'s
  54. // destructor.
  55. attach(nullptr);
  56. }
  57. void UIManagerBinding::attach(std::shared_ptr<UIManager> const &uiManager) {
  58. if (uiManager_) {
  59. uiManager_->uiManagerBinding_ = nullptr;
  60. }
  61. uiManager_ = uiManager;
  62. if (uiManager_) {
  63. uiManager_->uiManagerBinding_ = this;
  64. }
  65. }
  66. void UIManagerBinding::startSurface(
  67. jsi::Runtime &runtime,
  68. SurfaceId surfaceId,
  69. const std::string &moduleName,
  70. const folly::dynamic &initalProps) const {
  71. folly::dynamic parameters = folly::dynamic::object();
  72. parameters["rootTag"] = surfaceId;
  73. parameters["initialProps"] = initalProps;
  74. parameters["fabric"] = true;
  75. if (runtime.global().hasProperty(runtime, "RN$SurfaceRegistry")) {
  76. auto registry =
  77. runtime.global().getPropertyAsObject(runtime, "RN$SurfaceRegistry");
  78. auto method = registry.getPropertyAsFunction(runtime, "renderSurface");
  79. method.call(
  80. runtime,
  81. {jsi::String::createFromUtf8(runtime, moduleName),
  82. jsi::valueFromDynamic(runtime, parameters)});
  83. } else {
  84. auto module = getModule(runtime, "AppRegistry");
  85. auto method = module.getPropertyAsFunction(runtime, "runApplication");
  86. method.callWithThis(
  87. runtime,
  88. module,
  89. {jsi::String::createFromUtf8(runtime, moduleName),
  90. jsi::valueFromDynamic(runtime, parameters)});
  91. }
  92. }
  93. void UIManagerBinding::stopSurface(jsi::Runtime &runtime, SurfaceId surfaceId)
  94. const {
  95. if (runtime.global().hasProperty(runtime, "RN$stopSurface")) {
  96. auto method =
  97. runtime.global().getPropertyAsFunction(runtime, "RN$stopSurface");
  98. method.call(runtime, {jsi::Value{surfaceId}});
  99. } else {
  100. auto module = getModule(runtime, "ReactFabric");
  101. auto method =
  102. module.getPropertyAsFunction(runtime, "unmountComponentAtNode");
  103. method.callWithThis(runtime, module, {jsi::Value{surfaceId}});
  104. }
  105. }
  106. void UIManagerBinding::dispatchEvent(
  107. jsi::Runtime &runtime,
  108. const EventTarget *eventTarget,
  109. const std::string &type,
  110. const ValueFactory &payloadFactory) const {
  111. SystraceSection s("UIManagerBinding::dispatchEvent");
  112. auto payload = payloadFactory(runtime);
  113. auto instanceHandle = eventTarget
  114. ? [&]() {
  115. auto instanceHandle = eventTarget->getInstanceHandle(runtime);
  116. if (instanceHandle.isUndefined()) {
  117. return jsi::Value::null();
  118. }
  119. // Mixing `target` into `payload`.
  120. assert(payload.isObject());
  121. payload.asObject(runtime).setProperty(runtime, "target", eventTarget->getTag());
  122. return instanceHandle;
  123. }()
  124. : jsi::Value::null();
  125. auto &eventHandlerWrapper =
  126. static_cast<const EventHandlerWrapper &>(*eventHandler_);
  127. eventHandlerWrapper.callback.call(
  128. runtime,
  129. {std::move(instanceHandle),
  130. jsi::String::createFromUtf8(runtime, type),
  131. std::move(payload)});
  132. }
  133. void UIManagerBinding::invalidate() const {
  134. uiManager_->setDelegate(nullptr);
  135. }
  136. jsi::Value UIManagerBinding::get(
  137. jsi::Runtime &runtime,
  138. const jsi::PropNameID &name) {
  139. auto methodName = name.utf8(runtime);
  140. // Convert shared_ptr<UIManager> to a raw ptr
  141. // Why? Because:
  142. // 1) UIManagerBinding strongly retains UIManager. The JS VM
  143. // strongly retains UIManagerBinding (through the JSI).
  144. // These functions are JSI functions and are only called via
  145. // the JS VM; if the JS VM is torn down, those functions can't
  146. // execute and these lambdas won't execute.
  147. // 2) The UIManager is only deallocated when all references to it
  148. // are deallocated, including the UIManagerBinding. That only
  149. // happens when the JS VM is deallocated. So, the raw pointer
  150. // is safe.
  151. //
  152. // Even if it's safe, why not just use shared_ptr anyway as
  153. // extra insurance?
  154. // 1) Using shared_ptr or weak_ptr when they're not needed is
  155. // a pessimisation. It's more instructions executed without
  156. // any additional value in this case.
  157. // 2) How and when exactly these lambdas is deallocated is
  158. // complex. Adding shared_ptr to them which causes the UIManager
  159. // to potentially live longer is unnecessary, complicated cognitive
  160. // overhead.
  161. // 3) There is a strong suspicion that retaining UIManager from
  162. // these C++ lambdas, which are retained by an object that is held onto
  163. // by the JSI, caused some crashes upon deallocation of the
  164. // Scheduler and JS VM. This could happen if, for instance, C++
  165. // semantics cause these lambda to not be deallocated until
  166. // a CPU tick (or more) after the JS VM is deallocated.
  167. UIManager *uiManager = uiManager_.get();
  168. // Semantic: Creates a new node with given pieces.
  169. if (methodName == "createNode") {
  170. return jsi::Function::createFromHostFunction(
  171. runtime,
  172. name,
  173. 5,
  174. [uiManager](
  175. jsi::Runtime &runtime,
  176. const jsi::Value &thisValue,
  177. const jsi::Value *arguments,
  178. size_t count) -> jsi::Value {
  179. return valueFromShadowNode(
  180. runtime,
  181. uiManager->createNode(
  182. tagFromValue(runtime, arguments[0]),
  183. stringFromValue(runtime, arguments[1]),
  184. surfaceIdFromValue(runtime, arguments[2]),
  185. RawProps(runtime, arguments[3]),
  186. eventTargetFromValue(runtime, arguments[4], arguments[0])));
  187. });
  188. }
  189. // Semantic: Clones the node with *same* props and *same* children.
  190. if (methodName == "cloneNode") {
  191. return jsi::Function::createFromHostFunction(
  192. runtime,
  193. name,
  194. 1,
  195. [uiManager](
  196. jsi::Runtime &runtime,
  197. const jsi::Value &thisValue,
  198. const jsi::Value *arguments,
  199. size_t count) -> jsi::Value {
  200. return valueFromShadowNode(
  201. runtime,
  202. uiManager->cloneNode(shadowNodeFromValue(runtime, arguments[0])));
  203. });
  204. }
  205. if (methodName == "setJSResponder") {
  206. return jsi::Function::createFromHostFunction(
  207. runtime,
  208. name,
  209. 2,
  210. [uiManager](
  211. jsi::Runtime &runtime,
  212. const jsi::Value &thisValue,
  213. const jsi::Value *arguments,
  214. size_t count) -> jsi::Value {
  215. uiManager->setJSResponder(
  216. shadowNodeFromValue(runtime, arguments[0]),
  217. arguments[1].getBool());
  218. return jsi::Value::undefined();
  219. });
  220. }
  221. if (methodName == "findNodeAtPoint") {
  222. return jsi::Function::createFromHostFunction(
  223. runtime,
  224. name,
  225. 2,
  226. [uiManager](
  227. jsi::Runtime &runtime,
  228. const jsi::Value &thisValue,
  229. const jsi::Value *arguments,
  230. size_t count) -> jsi::Value {
  231. auto node = shadowNodeFromValue(runtime, arguments[0]);
  232. auto locationX = (Float)arguments[1].getNumber();
  233. auto locationY = (Float)arguments[2].getNumber();
  234. auto onSuccessFunction =
  235. arguments[3].getObject(runtime).getFunction(runtime);
  236. auto targetNode =
  237. uiManager->findNodeAtPoint(node, Point{locationX, locationY});
  238. auto &eventTarget = targetNode->getEventEmitter()->eventTarget_;
  239. EventEmitter::DispatchMutex().lock();
  240. eventTarget->retain(runtime);
  241. auto instanceHandle = eventTarget->getInstanceHandle(runtime);
  242. eventTarget->release(runtime);
  243. EventEmitter::DispatchMutex().unlock();
  244. onSuccessFunction.call(runtime, std::move(instanceHandle));
  245. return jsi::Value::undefined();
  246. });
  247. }
  248. if (methodName == "clearJSResponder") {
  249. return jsi::Function::createFromHostFunction(
  250. runtime,
  251. name,
  252. 0,
  253. [uiManager](
  254. jsi::Runtime &runtime,
  255. const jsi::Value &thisValue,
  256. const jsi::Value *arguments,
  257. size_t count) -> jsi::Value {
  258. uiManager->clearJSResponder();
  259. return jsi::Value::undefined();
  260. });
  261. }
  262. // Semantic: Clones the node with *same* props and *empty* children.
  263. if (methodName == "cloneNodeWithNewChildren") {
  264. return jsi::Function::createFromHostFunction(
  265. runtime,
  266. name,
  267. 1,
  268. [uiManager](
  269. jsi::Runtime &runtime,
  270. const jsi::Value &thisValue,
  271. const jsi::Value *arguments,
  272. size_t count) -> jsi::Value {
  273. return valueFromShadowNode(
  274. runtime,
  275. uiManager->cloneNode(
  276. shadowNodeFromValue(runtime, arguments[0]),
  277. ShadowNode::emptySharedShadowNodeSharedList()));
  278. });
  279. }
  280. // Semantic: Clones the node with *given* props and *same* children.
  281. if (methodName == "cloneNodeWithNewProps") {
  282. return jsi::Function::createFromHostFunction(
  283. runtime,
  284. name,
  285. 2,
  286. [uiManager](
  287. jsi::Runtime &runtime,
  288. const jsi::Value &thisValue,
  289. const jsi::Value *arguments,
  290. size_t count) -> jsi::Value {
  291. const auto &rawProps = RawProps(runtime, arguments[1]);
  292. return valueFromShadowNode(
  293. runtime,
  294. uiManager->cloneNode(
  295. shadowNodeFromValue(runtime, arguments[0]),
  296. nullptr,
  297. &rawProps));
  298. });
  299. }
  300. // Semantic: Clones the node with *given* props and *empty* children.
  301. if (methodName == "cloneNodeWithNewChildrenAndProps") {
  302. return jsi::Function::createFromHostFunction(
  303. runtime,
  304. name,
  305. 2,
  306. [uiManager](
  307. jsi::Runtime &runtime,
  308. const jsi::Value &thisValue,
  309. const jsi::Value *arguments,
  310. size_t count) -> jsi::Value {
  311. const auto &rawProps = RawProps(runtime, arguments[1]);
  312. return valueFromShadowNode(
  313. runtime,
  314. uiManager->cloneNode(
  315. shadowNodeFromValue(runtime, arguments[0]),
  316. ShadowNode::emptySharedShadowNodeSharedList(),
  317. &rawProps));
  318. });
  319. }
  320. if (methodName == "appendChild") {
  321. return jsi::Function::createFromHostFunction(
  322. runtime,
  323. name,
  324. 2,
  325. [uiManager](
  326. jsi::Runtime &runtime,
  327. const jsi::Value &thisValue,
  328. const jsi::Value *arguments,
  329. size_t count) -> jsi::Value {
  330. uiManager->appendChild(
  331. shadowNodeFromValue(runtime, arguments[0]),
  332. shadowNodeFromValue(runtime, arguments[1]));
  333. return jsi::Value::undefined();
  334. });
  335. }
  336. if (methodName == "createChildSet") {
  337. return jsi::Function::createFromHostFunction(
  338. runtime,
  339. name,
  340. 1,
  341. [](jsi::Runtime &runtime,
  342. const jsi::Value &thisValue,
  343. const jsi::Value *arguments,
  344. size_t count) -> jsi::Value {
  345. auto shadowNodeList =
  346. std::make_shared<SharedShadowNodeList>(SharedShadowNodeList({}));
  347. return valueFromShadowNodeList(runtime, shadowNodeList);
  348. });
  349. }
  350. if (methodName == "appendChildToSet") {
  351. return jsi::Function::createFromHostFunction(
  352. runtime,
  353. name,
  354. 2,
  355. [](jsi::Runtime &runtime,
  356. const jsi::Value &thisValue,
  357. const jsi::Value *arguments,
  358. size_t count) -> jsi::Value {
  359. auto shadowNodeList = shadowNodeListFromValue(runtime, arguments[0]);
  360. auto shadowNode = shadowNodeFromValue(runtime, arguments[1]);
  361. shadowNodeList->push_back(shadowNode);
  362. return jsi::Value::undefined();
  363. });
  364. }
  365. if (methodName == "completeRoot") {
  366. return jsi::Function::createFromHostFunction(
  367. runtime,
  368. name,
  369. 2,
  370. [uiManager](
  371. jsi::Runtime &runtime,
  372. const jsi::Value &thisValue,
  373. const jsi::Value *arguments,
  374. size_t count) -> jsi::Value {
  375. uiManager->completeSurface(
  376. surfaceIdFromValue(runtime, arguments[0]),
  377. shadowNodeListFromValue(runtime, arguments[1]));
  378. return jsi::Value::undefined();
  379. });
  380. }
  381. if (methodName == "registerEventHandler") {
  382. return jsi::Function::createFromHostFunction(
  383. runtime,
  384. name,
  385. 1,
  386. [this](
  387. jsi::Runtime &runtime,
  388. const jsi::Value &thisValue,
  389. const jsi::Value *arguments,
  390. size_t count) -> jsi::Value {
  391. auto eventHandler =
  392. arguments[0].getObject(runtime).getFunction(runtime);
  393. eventHandler_ =
  394. std::make_unique<EventHandlerWrapper>(std::move(eventHandler));
  395. return jsi::Value::undefined();
  396. });
  397. }
  398. if (methodName == "getRelativeLayoutMetrics") {
  399. return jsi::Function::createFromHostFunction(
  400. runtime,
  401. name,
  402. 2,
  403. [uiManager](
  404. jsi::Runtime &runtime,
  405. const jsi::Value &thisValue,
  406. const jsi::Value *arguments,
  407. size_t count) -> jsi::Value {
  408. auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
  409. *shadowNodeFromValue(runtime, arguments[0]),
  410. shadowNodeFromValue(runtime, arguments[1]).get(),
  411. {/* .includeTransform = */ true});
  412. auto frame = layoutMetrics.frame;
  413. auto result = jsi::Object(runtime);
  414. result.setProperty(runtime, "left", frame.origin.x);
  415. result.setProperty(runtime, "top", frame.origin.y);
  416. result.setProperty(runtime, "width", frame.size.width);
  417. result.setProperty(runtime, "height", frame.size.height);
  418. return result;
  419. });
  420. }
  421. if (methodName == "dispatchCommand") {
  422. return jsi::Function::createFromHostFunction(
  423. runtime,
  424. name,
  425. 3,
  426. [uiManager](
  427. jsi::Runtime &runtime,
  428. const jsi::Value &thisValue,
  429. const jsi::Value *arguments,
  430. size_t count) -> jsi::Value {
  431. uiManager->dispatchCommand(
  432. shadowNodeFromValue(runtime, arguments[0]),
  433. stringFromValue(runtime, arguments[1]),
  434. commandArgsFromValue(runtime, arguments[2]));
  435. return jsi::Value::undefined();
  436. });
  437. }
  438. // Legacy API
  439. if (methodName == "measureLayout") {
  440. return jsi::Function::createFromHostFunction(
  441. runtime,
  442. name,
  443. 4,
  444. [uiManager](
  445. jsi::Runtime &runtime,
  446. const jsi::Value &thisValue,
  447. const jsi::Value *arguments,
  448. size_t count) -> jsi::Value {
  449. auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
  450. *shadowNodeFromValue(runtime, arguments[0]),
  451. shadowNodeFromValue(runtime, arguments[1]).get(),
  452. {/* .includeTransform = */ false});
  453. if (layoutMetrics == EmptyLayoutMetrics) {
  454. auto onFailFunction =
  455. arguments[2].getObject(runtime).getFunction(runtime);
  456. onFailFunction.call(runtime);
  457. return jsi::Value::undefined();
  458. }
  459. auto onSuccessFunction =
  460. arguments[3].getObject(runtime).getFunction(runtime);
  461. auto frame = layoutMetrics.frame;
  462. onSuccessFunction.call(
  463. runtime,
  464. {jsi::Value{runtime, (double)frame.origin.x},
  465. jsi::Value{runtime, (double)frame.origin.y},
  466. jsi::Value{runtime, (double)frame.size.width},
  467. jsi::Value{runtime, (double)frame.size.height}});
  468. return jsi::Value::undefined();
  469. });
  470. }
  471. if (methodName == "measure") {
  472. return jsi::Function::createFromHostFunction(
  473. runtime,
  474. name,
  475. 2,
  476. [uiManager](
  477. jsi::Runtime &runtime,
  478. const jsi::Value &thisValue,
  479. const jsi::Value *arguments,
  480. size_t count) -> jsi::Value {
  481. auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
  482. *shadowNodeFromValue(runtime, arguments[0]),
  483. nullptr,
  484. {/* .includeTransform = */ true});
  485. auto frame = layoutMetrics.frame;
  486. auto onSuccessFunction =
  487. arguments[1].getObject(runtime).getFunction(runtime);
  488. onSuccessFunction.call(
  489. runtime,
  490. {0,
  491. 0,
  492. jsi::Value{runtime, (double)frame.size.width},
  493. jsi::Value{runtime, (double)frame.size.height},
  494. jsi::Value{runtime, (double)frame.origin.x},
  495. jsi::Value{runtime, (double)frame.origin.y}});
  496. return jsi::Value::undefined();
  497. });
  498. }
  499. if (methodName == "measureInWindow") {
  500. return jsi::Function::createFromHostFunction(
  501. runtime,
  502. name,
  503. 2,
  504. [uiManager](
  505. jsi::Runtime &runtime,
  506. const jsi::Value &thisValue,
  507. const jsi::Value *arguments,
  508. size_t count) -> jsi::Value {
  509. auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
  510. *shadowNodeFromValue(runtime, arguments[0]),
  511. nullptr,
  512. {/* .includeTransform = */ true});
  513. auto onSuccessFunction =
  514. arguments[1].getObject(runtime).getFunction(runtime);
  515. auto frame = layoutMetrics.frame;
  516. onSuccessFunction.call(
  517. runtime,
  518. {jsi::Value{runtime, (double)frame.origin.x},
  519. jsi::Value{runtime, (double)frame.origin.y},
  520. jsi::Value{runtime, (double)frame.size.width},
  521. jsi::Value{runtime, (double)frame.size.height}});
  522. return jsi::Value::undefined();
  523. });
  524. }
  525. if (methodName == "setNativeProps") {
  526. return jsi::Function::createFromHostFunction(
  527. runtime,
  528. name,
  529. 2,
  530. [uiManager](
  531. jsi::Runtime &runtime,
  532. const jsi::Value &thisValue,
  533. const jsi::Value *arguments,
  534. size_t count) -> jsi::Value {
  535. uiManager->setNativeProps(
  536. *shadowNodeFromValue(runtime, arguments[0]),
  537. RawProps(runtime, arguments[1]));
  538. return jsi::Value::undefined();
  539. });
  540. }
  541. return jsi::Value::undefined();
  542. }
  543. } // namespace react
  544. } // namespace facebook