From 43cdd84a22a760e32e99e0f3c122c6b4efca7bee Mon Sep 17 00:00:00 2001 From: David Vacca Date: Sat, 24 Jul 2021 09:11:19 -0700 Subject: [PATCH] Expose new API FabricUIManager.getInspectorDataForInstance Summary: This diff exposes a new API in FabricUIManager called getInspectorDataForInstance. The goal of this method is to return React metadata for a Native view. This data will be used from partner teams to build tools that uses React metadata in RN Data returned from React: P429701924 changelog: [internal] internal allow-large-files Reviewed By: JoshuaGross Differential Revision: D29747864 fbshipit-source-id: 8cb55573be08cb530f7e3c83eed8b4fcf43e7781 --- .../com/facebook/react/fabric/Binding.java | 5 ++++ .../react/fabric/FabricUIManager.java | 10 +++++++ .../com/facebook/react/fabric/jni/Binding.cpp | 28 +++++++++++++++++++ .../com/facebook/react/fabric/jni/Binding.h | 5 ++++ .../react/renderer/scheduler/Scheduler.cpp | 25 +++++++++++++++++ .../react/renderer/scheduler/Scheduler.h | 5 ++++ .../renderer/uimanager/UIManagerBinding.cpp | 27 ++++++++++++++++++ .../renderer/uimanager/UIManagerBinding.h | 4 +++ 8 files changed, 109 insertions(+) diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java index b7af81b34de..6449ef07579 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java @@ -12,9 +12,11 @@ import androidx.annotation.NonNull; import com.facebook.jni.HybridData; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.NativeMap; +import com.facebook.react.bridge.ReadableNativeMap; import com.facebook.react.bridge.RuntimeExecutor; import com.facebook.react.bridge.queue.MessageQueueThread; import com.facebook.react.fabric.events.EventBeatManager; +import com.facebook.react.fabric.events.EventEmitterWrapper; import com.facebook.react.uimanager.PixelUtil; @DoNotStrip @@ -76,6 +78,9 @@ public class Binding { public native void driveCxxAnimations(); + public native ReadableNativeMap getInspectorDataForInstance( + EventEmitterWrapper eventEmitterWrapper); + public void register( @NonNull RuntimeExecutor runtimeExecutor, @NonNull FabricUIManager fabricUIManager, diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index d4fbb1c177b..80dec6b13b2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -223,6 +223,16 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { return rootTag; } + @UiThread + @ThreadConfined(UI) + public ReadableMap getInspectorDataForInstance(final int surfaceId, final View view) { + UiThreadUtil.assertOnUiThread(); + int reactTag = view.getId(); + + EventEmitterWrapper eventEmitter = mMountingManager.getEventEmitter(surfaceId, reactTag); + return mBinding.getInspectorDataForInstance(eventEmitter); + } + @Override public void preInitializeViewManagers(List viewManagerNames) { for (String viewManagerName : viewManagerNames) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp index 454168c0fba..f8419c8dacf 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp @@ -233,6 +233,32 @@ std::shared_ptr Binding::getScheduler() { return scheduler_; } +jni::local_ref +Binding::getInspectorDataForInstance( + jni::alias_ref eventEmitterWrapper) { + std::shared_ptr scheduler = getScheduler(); + if (!scheduler) { + LOG(ERROR) << "Binding::startSurface: scheduler disappeared"; + return ReadableNativeMap::newObjectCxxArgs(folly::dynamic::object()); + } + + EventEmitterWrapper *cEventEmitter = cthis(eventEmitterWrapper); + InspectorData data = + scheduler->getInspectorDataForInstance(cEventEmitter->eventEmitter); + + folly::dynamic result = folly::dynamic::object; + result["fileName"] = data.fileName; + result["lineNumber"] = data.lineNumber; + result["columnNumber"] = data.columnNumber; + result["selectedIndex"] = data.selectedIndex; + auto hierarchy = folly::dynamic::array(); + for (auto hierarchyItem : data.hierarchy) { + hierarchy.push_back(hierarchyItem); + } + result["hierarchy"] = hierarchy; + return ReadableNativeMap::newObjectCxxArgs(result); +} + void Binding::startSurface( jint surfaceId, jni::alias_ref moduleName, @@ -1314,6 +1340,8 @@ void Binding::registerNatives() { makeNativeMethod( "installFabricUIManager", Binding::installFabricUIManager), makeNativeMethod("startSurface", Binding::startSurface), + makeNativeMethod( + "getInspectorDataForInstance", Binding::getInspectorDataForInstance), makeNativeMethod( "startSurfaceWithConstraints", Binding::startSurfaceWithConstraints), makeNativeMethod( diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h index 033e57700a3..defc80a3d23 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.h @@ -15,10 +15,12 @@ #include #include #include + #include #include #include "ComponentFactory.h" #include "EventBeatManager.h" +#include "EventEmitterWrapper.h" #include "JBackgroundExecutor.h" #include "SurfaceHandlerBinding.h" @@ -96,6 +98,9 @@ class Binding : public jni::HybridClass, jboolean isRTL, jboolean doLeftAndRightSwapInRTL); + jni::local_ref getInspectorDataForInstance( + jni::alias_ref eventEmitterWrapper); + static jni::local_ref initHybrid(jni::alias_ref); void installFabricUIManager( diff --git a/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/ReactCommon/react/renderer/scheduler/Scheduler.cpp index 55a31c4a92f..361d4f970e1 100644 --- a/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -206,6 +206,31 @@ void Scheduler::registerSurface( surfaceHandler.setUIManager(uiManager_.get()); } +InspectorData Scheduler::getInspectorDataForInstance( + SharedEventEmitter eventEmitter) const noexcept { + return executeSynchronouslyOnSameThread_CAN_DEADLOCK( + runtimeExecutor_, [=](jsi::Runtime &runtime) -> InspectorData { + auto uiManagerBinding = UIManagerBinding::getBinding(runtime); + auto value = uiManagerBinding->getInspectorDataForInstance( + runtime, eventEmitter); + + // TODO: avoid transforming jsi + auto dynamic = jsi::dynamicFromValue(runtime, value); + auto source = dynamic["source"]; + + InspectorData result = {}; + result.fileName = source["fileName"].c_str(); + result.lineNumber = (int)source["lineNumber"].getDouble(); + result.columnNumber = (int)source["columnNumber"].getDouble(); + result.selectedIndex = (int)dynamic["selectedIndex"].getDouble(); + auto hierarchy = dynamic["hierarchy"]; + for (size_t i = 0; i < hierarchy.size(); i++) { + result.hierarchy.push_back(hierarchy[i]["name"].c_str()); + } + return result; + }); +} + void Scheduler::unregisterSurface( SurfaceHandler const &surfaceHandler) const noexcept { surfaceHandler.setUIManager(nullptr); diff --git a/ReactCommon/react/renderer/scheduler/Scheduler.h b/ReactCommon/react/renderer/scheduler/Scheduler.h index f0f72ff53d2..470890137b9 100644 --- a/ReactCommon/react/renderer/scheduler/Scheduler.h +++ b/ReactCommon/react/renderer/scheduler/Scheduler.h @@ -15,8 +15,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -49,6 +51,9 @@ class Scheduler final : public UIManagerDelegate { void registerSurface(SurfaceHandler const &surfaceHandler) const noexcept; void unregisterSurface(SurfaceHandler const &surfaceHandler) const noexcept; + InspectorData getInspectorDataForInstance( + SharedEventEmitter eventEmitter) const noexcept; + void renderTemplateToSurface( SurfaceId surfaceId, const std::string &uiTemplate); diff --git a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp index 392c8dd60a9..8d15f2549a1 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp +++ b/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp @@ -133,6 +133,33 @@ static jsi::Value callMethodOfModule( return jsi::Value::undefined(); } +jsi::Value UIManagerBinding::getInspectorDataForInstance( + jsi::Runtime &runtime, + SharedEventEmitter eventEmitter) const { + auto eventTarget = eventEmitter->eventTarget_; + EventEmitter::DispatchMutex().lock(); + + if (!runtime.global().hasProperty(runtime, "__fbBatchedBridge") || + !eventTarget) { + return jsi::Value::undefined(); + } + + eventTarget->retain(runtime); + auto instanceHandle = eventTarget->getInstanceHandle(runtime); + eventTarget->release(runtime); + EventEmitter::DispatchMutex().unlock(); + + if (instanceHandle.isUndefined()) { + return jsi::Value::undefined(); + } + + return callMethodOfModule( + runtime, + "ReactFabric", + "getInspectorDataForInstance", + {std::move(instanceHandle)}); +} + void UIManagerBinding::startSurface( jsi::Runtime &runtime, SurfaceId surfaceId, diff --git a/ReactCommon/react/renderer/uimanager/UIManagerBinding.h b/ReactCommon/react/renderer/uimanager/UIManagerBinding.h index 3b3671612bd..8e96f231dc9 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerBinding.h +++ b/ReactCommon/react/renderer/uimanager/UIManagerBinding.h @@ -74,6 +74,10 @@ class UIManagerBinding : public jsi::HostObject { folly::dynamic const &props, DisplayMode displayMode) const; + jsi::Value getInspectorDataForInstance( + jsi::Runtime &runtime, + SharedEventEmitter eventEmitter) const; + /* * Stops React Native Surface with given id. * Thread synchronization must be enforced externally.