diff --git a/packages/react-native/React/Inspector/RCTCxxInspectorPackagerConnection.mm b/packages/react-native/React/Inspector/RCTCxxInspectorPackagerConnection.mm index 19d53df5ff8512..e1eba0bc222efe 100644 --- a/packages/react-native/React/Inspector/RCTCxxInspectorPackagerConnection.mm +++ b/packages/react-native/React/Inspector/RCTCxxInspectorPackagerConnection.mm @@ -52,6 +52,11 @@ - (void)sendEventToAllConnections:(NSString *)event _cxxImpl->sendEventToAllConnections(event.UTF8String); } +- (void)sendWrappedEventToPackager:(NSString *)event pageId:(NSString *)pageId +{ + _cxxImpl->sendWrappedEventToPackager(event.UTF8String, pageId.UTF8String); +} + - (bool)isConnected { return _cxxImpl->isConnected(); diff --git a/packages/react-native/React/Inspector/RCTInspectorPackagerConnection.h b/packages/react-native/React/Inspector/RCTInspectorPackagerConnection.h index 72e279dce30b3a..531d2f42428e2e 100644 --- a/packages/react-native/React/Inspector/RCTInspectorPackagerConnection.h +++ b/packages/react-native/React/Inspector/RCTInspectorPackagerConnection.h @@ -17,6 +17,7 @@ - (void)connect; - (void)closeQuietly; - (void)sendEventToAllConnections:(NSString *)event; +- (void)sendWrappedEventToPackager:(NSString *)event pageId:(NSString *)pageId @end @interface RCTInspectorPackagerConnection : NSObject diff --git a/packages/react-native/React/Inspector/RCTInspectorPackagerConnection.m b/packages/react-native/React/Inspector/RCTInspectorPackagerConnection.m index 7d934b43343261..f1c7bf15b3fa31 100644 --- a/packages/react-native/React/Inspector/RCTInspectorPackagerConnection.m +++ b/packages/react-native/React/Inspector/RCTInspectorPackagerConnection.m @@ -80,6 +80,11 @@ - (void)sendEventToAllConnections:(NSString *)event } } +- (void)sendWrappedEventToPackager:(NSString *)event pageId:(NSString *)pageId +{ + [self sendWrappedEvent:pageId message:event]; +} + - (void)closeAllConnections { for (NSString *pageId in _inspectorConnections) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/CxxInspectorPackagerConnection.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/CxxInspectorPackagerConnection.java index e70c6c65117c0c..cbad4d9f86978f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/CxxInspectorPackagerConnection.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/CxxInspectorPackagerConnection.java @@ -41,6 +41,8 @@ private static native HybridData initHybrid( public native void sendEventToAllConnections(String event); + public native void sendWrappedEventToPackager(String event, String pageId); + /** Java wrapper around a C++ IWebSocketDelegate, allowing us to call the interface from Java. */ @DoNotStrip private static class WebSocketDelegate implements Closeable { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/IInspectorPackagerConnection.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/IInspectorPackagerConnection.java index 59afe06a6f4795..e704757537d108 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/IInspectorPackagerConnection.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/IInspectorPackagerConnection.java @@ -13,4 +13,6 @@ public void closeQuietly(); public void sendEventToAllConnections(String event); + + public void sendWrappedEventToPackager(String event, String pageId); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java index 84107fb5e4c8a6..b231f55e14e78a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorPackagerConnection.java @@ -56,6 +56,10 @@ public void sendEventToAllConnections(String event) { } } + public void sendWrappedEventToPackager(String event, String pageId) { + sendWrappedEvent(pageId, event); + } + void handleProxyMessage(JSONObject message) throws JSONException, IOException { String event = message.getString("event"); switch (event) { diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JCxxInspectorPackagerConnection.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JCxxInspectorPackagerConnection.cpp index 8b6a20d2aa9338..f6a70d25367b05 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JCxxInspectorPackagerConnection.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JCxxInspectorPackagerConnection.cpp @@ -43,6 +43,12 @@ void JCxxInspectorPackagerConnection::sendEventToAllConnections( cxxImpl_.sendEventToAllConnections(event); } +void JCxxInspectorPackagerConnection::sendWrappedEventToPackager( + const std::string& event, + const std::string& pageId) { + cxxImpl_.sendWrappedEventToPackager(event, pageId); +} + void JCxxInspectorPackagerConnection::registerNatives() { registerHybrid( {makeNativeMethod( diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JCxxInspectorPackagerConnection.h b/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JCxxInspectorPackagerConnection.h index 7fc555cf2942e6..1c4ff140f6f9fc 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JCxxInspectorPackagerConnection.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JCxxInspectorPackagerConnection.h @@ -33,6 +33,9 @@ class JCxxInspectorPackagerConnection void connect(); void closeQuietly(); void sendEventToAllConnections(const std::string& event); + void sendWrappedEventToPackager( + const std::string& event, + const std::string& pageId); private: friend HybridBase; diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnection.cpp b/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnection.cpp index 6c0d2875d801bc..229db586ca2b41 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnection.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnection.cpp @@ -73,6 +73,25 @@ void InspectorPackagerConnection::Impl::sendEventToAllConnections( } } +void InspectorPackagerConnection::Impl::sendWrappedEventToPackager( + const std::string& event, + const std::string& pageId) { + delegate_->scheduleCallback( + [weakSelf = weak_from_this(), + event = std::move(event), + pageId = std::move(pageId)]() { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return; + } + strongSelf->sendToPackager(folly::dynamic::object( + "event", "wrappedEvent")( + "payload", + folly::dynamic::object("pageId", pageId)("wrappedEvent", event))); + }, + 0ms); +} + void InspectorPackagerConnection::Impl::closeAllConnections() { for (auto& connection : inspectorSessions_) { connection.second.localConnection->disconnect(); @@ -367,4 +386,10 @@ void InspectorPackagerConnection::sendEventToAllConnections(std::string event) { impl_->sendEventToAllConnections(event); } +void InspectorPackagerConnection::sendWrappedEventToPackager( + const std::string& event, + const std::string& pageId) { + impl_->sendWrappedEventToPackager(event, pageId); +} + } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnection.h b/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnection.h index a6d2bca845ac9f..c53e0614126534 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnection.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnection.h @@ -47,6 +47,9 @@ class InspectorPackagerConnection { void connect(); void closeQuietly(); void sendEventToAllConnections(std::string event); + void sendWrappedEventToPackager( + const std::string& event, + const std::string& pageId); private: class Impl; diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnectionImpl.h b/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnectionImpl.h index 7bf5727bf3c97f..f8437320bc8852 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnectionImpl.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/InspectorPackagerConnectionImpl.h @@ -40,6 +40,9 @@ class InspectorPackagerConnection::Impl void connect(); void closeQuietly(); void sendEventToAllConnections(std::string event); + void sendWrappedEventToPackager( + const std::string& event, + const std::string& pageId); std::unique_ptr removeConnectionForPage(std::string pageId); /** diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tests/InspectorPackagerConnectionTest.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tests/InspectorPackagerConnectionTest.cpp index a4c59d9735db36..65690509b8147a 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tests/InspectorPackagerConnectionTest.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tests/InspectorPackagerConnectionTest.cpp @@ -471,6 +471,54 @@ TEST_F(InspectorPackagerConnectionTest, TestSendEventToAllConnections) { getInspectorInstance().removePage(pageId); } +TEST_F(InspectorPackagerConnectionTest, TestSendWrappedEventToPackager) { + // Configure gmock to expect calls in a specific order. + InSequence mockCallsMustBeInSequence; + + packagerConnection_->connect(); + auto pageId = getInspectorInstance().addPage( + "mock-description", + "mock-vm", + localConnections_ + .lazily_make_unique>()); + + // Connect to the page. + webSockets_[0]->getDelegate().didReceiveMessage(sformat( + R"({{ + "event": "connect", + "payload": {{ + "pageId": {0} + }} + }})", + toJson(std::to_string(pageId)))); + ASSERT_TRUE(localConnections_[0]); + + // Send an event using sendWrappedEventToPackager and + // observe it being sent via the socket. + EXPECT_CALL( + *webSockets_[0], + send(JsonParsed(AllOf( + AtJsonPtr("/event", Eq("wrappedEvent")), + AtJsonPtr("/payload/pageId", Eq(std::to_string(pageId))), + AtJsonPtr( + "/payload/wrappedEvent", + JsonEq( + R"({{ + "method": "FakeDomain.eventTriggered", + "params": ["arg1", "arg2"] + }})")))))) + .RetiresOnSaturation(); + packagerConnection_->sendWrappedEventToPackager( + R"({{ + "method": "FakeDomain.eventTriggered", + "params": ["arg1", "arg2"] + }})", + pageId); + + EXPECT_CALL(*localConnections_[0], disconnect()).RetiresOnSaturation(); + getInspectorInstance().removePage(pageId); +} + TEST_F(InspectorPackagerConnectionTest, TestConnectThenDisconnect) { // Configure gmock to expect calls in a specific order. InSequence mockCallsMustBeInSequence;