diff --git a/packages/react-native/React/CoreModules/RCTInspectorWebSocketReporter.h b/packages/react-native/React/CoreModules/RCTInspectorWebSocketReporter.h new file mode 100644 index 000000000000..474085f859bb --- /dev/null +++ b/packages/react-native/React/CoreModules/RCTInspectorWebSocketReporter.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * [Experimental] An interface for reporting WebSocket events to the modern + * debugger server. + * + * In a production (non dev or profiling) build, CDP reporting is disabled + * and all methods are a no-op. + * + * This is a helper class wrapping `facebook::react::NetworkReporter`. + */ +@interface RCTInspectorWebSocketReporter : NSObject + +/** + * Report that a WebSocket connection is about to be created. + * + * Corresponds to `Network.webSocketCreated` in CDP. + */ ++ (void)reportWebSocketCreated:(nullable NSString *)requestId url:(NSURL *)url; + +/** + * Report that a WebSocket handshake (HTTP upgrade) request is about to be + * sent, along with its final request headers. + * + * Corresponds to `Network.webSocketWillSendHandshakeRequest` in CDP. + */ ++ (void)reportWillSendHandshakeRequest:(nullable NSString *)requestId request:(NSURLRequest *)request; + +/** + * Report that the WebSocket handshake response was received and the + * connection is established. `httpMessage` is the raw handshake response, + * e.g. `SRWebSocket.receivedHTTPHeaders`. If NULL or incomplete, a minimal + * `101 Switching Protocols` response is reported instead. + * + * Corresponds to `Network.webSocketHandshakeResponseReceived` in CDP. + */ ++ (void)reportHandshakeResponseReceived:(nullable NSString *)requestId + httpMessage:(nullable CFHTTPMessageRef)httpMessage; + +/** + * Report a WebSocket message sent over an open connection. `message` must be + * an `NSString` (text message) or `NSData` (binary message). + * + * Corresponds to `Network.webSocketFrameSent` in CDP. + */ ++ (void)reportMessageSent:(nullable NSString *)requestId message:(nullable id)message; + +/** + * Report a WebSocket message received over an open connection. `message` + * must be an `NSString` (text message) or `NSData` (binary message). + * + * Corresponds to `Network.webSocketFrameReceived` in CDP. + */ ++ (void)reportMessageReceived:(nullable NSString *)requestId message:(nullable id)message; + +/** + * Report that a WebSocket connection was closed, whether cleanly or due to + * an error. + * + * Corresponds to `Network.webSocketClosed` in CDP. + */ ++ (void)reportWebSocketClosed:(nullable NSString *)requestId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/react-native/React/CoreModules/RCTInspectorWebSocketReporter.mm b/packages/react-native/React/CoreModules/RCTInspectorWebSocketReporter.mm new file mode 100644 index 000000000000..511032ab25fb --- /dev/null +++ b/packages/react-native/React/CoreModules/RCTInspectorWebSocketReporter.mm @@ -0,0 +1,156 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTInspectorWebSocketReporter.h" + +#import +#import + +using facebook::react::Headers; +using facebook::react::NetworkReporter; +using facebook::react::ReactNativeFeatureFlags; + +namespace { + +/** + * Convert an `NSString` to a `std::string`, mapping `nil` (and any string whose + * UTF-8 representation is unavailable) to an empty string. + */ +std::string toStdString(NSString *string) +{ + const char *utf8 = string.UTF8String; + return utf8 != nullptr ? std::string(utf8) : std::string(); +} + +/** + * Returns whether WebSocket events should be reported for the given request + * ID. Reporting requires the `enableNetworkEventReporting` and + * `fuseboxWebSocketEventsEnabled` feature flags, and a connected CDP debugger + * with the Network domain enabled (dev or profiling builds only). + */ +BOOL isReportingEnabled(NSString *requestId) +{ + return requestId != nil && ReactNativeFeatureFlags::enableNetworkEventReporting() && + ReactNativeFeatureFlags::fuseboxWebSocketEventsEnabled() && NetworkReporter::getInstance().isDebuggingEnabled(); +} + +Headers convertNSDictionaryToHeaders(const NSDictionary *headers) +{ + Headers result; + for (NSString *key in headers) { + result[toStdString(key)] = toStdString(headers[key]); + } + return result; +} + +/** + * Convert an `NSString` (text) or `NSData` (binary) WebSocket message to a + * CDP `payloadData` string — UTF-8 for text messages, base64 for binary. + * \returns Whether the message was a supported type, writing to the out + * params on success. + */ +bool convertMessageToPayloadData(id message, std::string &payloadData, bool &isBinary) +{ + if ([message isKindOfClass:[NSString class]]) { + payloadData = toStdString((NSString *)message); + isBinary = false; + return true; + } + + if ([message isKindOfClass:[NSData class]]) { + payloadData = toStdString([(NSData *)message base64EncodedStringWithOptions:0]); + isBinary = true; + return true; + } + + return false; +} + +} // namespace + +@implementation RCTInspectorWebSocketReporter + ++ (void)reportWebSocketCreated:(NSString *)requestId url:(NSURL *)url +{ + if (!isReportingEnabled(requestId)) { + return; + } + + NetworkReporter::getInstance().reportWebSocketCreated(toStdString(requestId), toStdString(url.absoluteString)); +} + ++ (void)reportWillSendHandshakeRequest:(NSString *)requestId request:(NSURLRequest *)request +{ + if (!isReportingEnabled(requestId)) { + return; + } + + NetworkReporter::getInstance().reportWebSocketWillSendHandshakeRequest( + toStdString(requestId), convertNSDictionaryToHeaders(request.allHTTPHeaderFields)); +} + ++ (void)reportHandshakeResponseReceived:(NSString *)requestId httpMessage:(nullable CFHTTPMessageRef)httpMessage +{ + if (!isReportingEnabled(requestId)) { + return; + } + + // Fall back to a minimal `101 Switching Protocols` response (guaranteed by + // RFC 6455 for an open connection) if the handshake response is unavailable. + uint16_t statusCode = 101; + Headers headers; + + if (httpMessage != NULL && CFHTTPMessageIsHeaderComplete(httpMessage) != 0) { + statusCode = (uint16_t)CFHTTPMessageGetResponseStatusCode(httpMessage); + NSDictionary *responseHeaders = + (NSDictionary *)CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(httpMessage)); + headers = convertNSDictionaryToHeaders(responseHeaders); + } + + NetworkReporter::getInstance().reportWebSocketHandshakeResponseReceived(toStdString(requestId), statusCode, headers); +} + ++ (void)reportMessageSent:(NSString *)requestId message:(id)message +{ + if (!isReportingEnabled(requestId)) { + return; + } + + std::string payloadData; + bool isBinary = false; + if (!convertMessageToPayloadData(message, payloadData, isBinary)) { + return; + } + + NetworkReporter::getInstance().reportWebSocketMessageSent(toStdString(requestId), payloadData, isBinary); +} + ++ (void)reportMessageReceived:(NSString *)requestId message:(id)message +{ + if (!isReportingEnabled(requestId)) { + return; + } + + std::string payloadData; + bool isBinary = false; + if (!convertMessageToPayloadData(message, payloadData, isBinary)) { + return; + } + + NetworkReporter::getInstance().reportWebSocketMessageReceived(toStdString(requestId), payloadData, isBinary); +} + ++ (void)reportWebSocketClosed:(NSString *)requestId +{ + if (!isReportingEnabled(requestId)) { + return; + } + + NetworkReporter::getInstance().reportWebSocketClosed(toStdString(requestId)); +} + +@end diff --git a/packages/react-native/React/CoreModules/RCTWebSocketModule.mm b/packages/react-native/React/CoreModules/RCTWebSocketModule.mm index bd95c32accfc..175a5b2eb2b8 100644 --- a/packages/react-native/React/CoreModules/RCTWebSocketModule.mm +++ b/packages/react-native/React/CoreModules/RCTWebSocketModule.mm @@ -16,6 +16,17 @@ #import #import "CoreModulesPlugins.h" +#import "RCTInspectorWebSocketReporter.h" + +@interface SRWebSocket (React) + +/** + * The CDP request ID used to report this connection's events to the modern + * debugger server, via `RCTInspectorWebSocketReporter`. + */ +@property (nonatomic, copy) NSString *inspectorRequestId; + +@end @implementation SRWebSocket (React) @@ -29,6 +40,16 @@ - (void)setReactTag:(NSNumber *)reactTag objc_setAssociatedObject(self, @selector(reactTag), reactTag, OBJC_ASSOCIATION_COPY_NONATOMIC); } +- (NSString *)inspectorRequestId +{ + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setInspectorRequestId:(NSString *)inspectorRequestId +{ + objc_setAssociatedObject(self, @selector(inspectorRequestId), inspectorRequestId, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + @end @interface RCTWebSocketModule () @@ -131,16 +152,23 @@ - (void)invalidate [webSocket setDelegateDispatchQueue:[self methodQueue]]; webSocket.delegate = self; webSocket.reactTag = @(socketID); + webSocket.inspectorRequestId = [[NSUUID UUID] UUIDString]; if (!_sockets) { _sockets = [NSMutableDictionary new]; } _sockets[@(socketID)] = webSocket; + + [RCTInspectorWebSocketReporter reportWebSocketCreated:webSocket.inspectorRequestId url:URL]; + [RCTInspectorWebSocketReporter reportWillSendHandshakeRequest:webSocket.inspectorRequestId request:request]; + [webSocket open]; } RCT_EXPORT_METHOD(send : (NSString *)message forSocketID : (double)socketID) { - [_sockets[@(socketID)] sendString:message error:nil]; + SRWebSocket *webSocket = _sockets[@(socketID)]; + [RCTInspectorWebSocketReporter reportMessageSent:webSocket.inspectorRequestId message:message]; + [webSocket sendString:message error:nil]; } RCT_EXPORT_METHOD(sendBinary : (NSString *)base64String forSocketID : (double)socketID) @@ -150,7 +178,9 @@ - (void)invalidate - (void)sendData:(NSData *)data forSocketID:(NSNumber *__nonnull)socketID { - [_sockets[socketID] sendData:data error:nil]; + SRWebSocket *webSocket = _sockets[socketID]; + [RCTInspectorWebSocketReporter reportMessageSent:webSocket.inspectorRequestId message:data]; + [webSocket sendData:data error:nil]; } RCT_EXPORT_METHOD(ping : (double)socketID) @@ -182,6 +212,7 @@ - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message if (!socketID) { return; } + [RCTInspectorWebSocketReporter reportMessageReceived:webSocket.inspectorRequestId message:message]; id contentHandler = _contentHandlers[socketID]; if (contentHandler) { message = [contentHandler processWebsocketMessage:message forSocketID:socketID withType:&type]; @@ -203,6 +234,8 @@ - (void)webSocketDidOpen:(SRWebSocket *)webSocket if (!socketID) { return; } + [RCTInspectorWebSocketReporter reportHandshakeResponseReceived:webSocket.inspectorRequestId + httpMessage:webSocket.receivedHTTPHeaders]; [self sendEventWithName:@"websocketOpen" body:@{@"id" : socketID, @"protocol" : webSocket.protocol ? webSocket.protocol : @""}]; } @@ -213,6 +246,7 @@ - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error if (!socketID) { return; } + [RCTInspectorWebSocketReporter reportWebSocketClosed:webSocket.inspectorRequestId]; _contentHandlers[socketID] = nil; _sockets[socketID] = nil; NSDictionary *body = @@ -229,6 +263,7 @@ - (void)webSocket:(SRWebSocket *)webSocket if (!socketID) { return; } + [RCTInspectorWebSocketReporter reportWebSocketClosed:webSocket.inspectorRequestId]; _contentHandlers[socketID] = nil; _sockets[socketID] = nil; [self sendEventWithName:@"websocketClosed" diff --git a/packages/react-native/React/CoreModules/React-CoreModules.podspec b/packages/react-native/React/CoreModules/React-CoreModules.podspec index f98c195770c6..2d8b9aa21d12 100644 --- a/packages/react-native/React/CoreModules/React-CoreModules.podspec +++ b/packages/react-native/React/CoreModules/React-CoreModules.podspec @@ -58,6 +58,7 @@ Pod::Spec.new do |s| add_dependency(s, "React-jsinspector", :framework_name => 'jsinspector_modern') add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp') add_dependency(s, "React-jsinspectortracing", :framework_name => 'jsinspector_moderntracing') + add_dependency(s, "React-networking", :framework_name => 'React_networking') add_dependency(s, "React-RCTFBReactNativeSpec") add_dependency(s, "ReactCommon", :subspec => "turbomodule/core", :additional_framework_paths => ["react/nativemodule/core"]) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index 04d0b80b3c64..f028b7e4f7da 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<854bb2a573f2c69d495aa1721f903ab0>> + * @generated SignedSource<> */ /** @@ -390,6 +390,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun fuseboxScreenshotCaptureEnabled(): Boolean = accessor.fuseboxScreenshotCaptureEnabled() + /** + * Enable reporting of WebSocket network events (`Network.webSocket*` CDP events) to the React Native DevTools CDP backend. Requires `fuseboxNetworkInspectionEnabled`. + */ + @JvmStatic + public fun fuseboxWebSocketEventsEnabled(): Boolean = accessor.fuseboxWebSocketEventsEnabled() + /** * When enabled, uses optimized platform-specific paths to apply animated props synchronously. On Android, this uses a batched int/double buffer protocol with a single JNI call. On iOS, this passes AnimatedProps directly through the delegate chain and applies them via cloneProps, avoiding the folly::dynamic round-trip. */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index 624e509c5034..0e359509c73c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<5ae6b1a0bfa9d680aa61685dbacd69d2>> + * @generated SignedSource<> */ /** @@ -80,6 +80,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var fuseboxFrameRecordingEnabledCache: Boolean? = null private var fuseboxNetworkInspectionEnabledCache: Boolean? = null private var fuseboxScreenshotCaptureEnabledCache: Boolean? = null + private var fuseboxWebSocketEventsEnabledCache: Boolean? = null private var optimizedAnimatedPropUpdatesCache: Boolean? = null private var overrideBySynchronousMountPropsAtMountingAndroidCache: Boolean? = null private var perfIssuesEnabledCache: Boolean? = null @@ -647,6 +648,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun fuseboxWebSocketEventsEnabled(): Boolean { + var cached = fuseboxWebSocketEventsEnabledCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.fuseboxWebSocketEventsEnabled() + fuseboxWebSocketEventsEnabledCache = cached + } + return cached + } + override fun optimizedAnimatedPropUpdates(): Boolean { var cached = optimizedAnimatedPropUpdatesCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index 2f1a0cfc504d..dd081be14a65 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<8b1c3206f7e63790e411f57c2dd64076>> + * @generated SignedSource<<87b76f5c2af82462dc66c7721cd66ac4>> */ /** @@ -148,6 +148,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun fuseboxScreenshotCaptureEnabled(): Boolean + @DoNotStrip @JvmStatic public external fun fuseboxWebSocketEventsEnabled(): Boolean + @DoNotStrip @JvmStatic public external fun optimizedAnimatedPropUpdates(): Boolean @DoNotStrip @JvmStatic public external fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index 91f9ac37c7ec..7b9062eb3ca9 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<9b295106ef57d7fa85846c57bb74a090>> + * @generated SignedSource<> */ /** @@ -143,6 +143,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun fuseboxScreenshotCaptureEnabled(): Boolean = false + override fun fuseboxWebSocketEventsEnabled(): Boolean = false + override fun optimizedAnimatedPropUpdates(): Boolean = false override fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean = true diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index b274311cd9d2..c5786d397193 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<98a6085638c86aaa012c8bd972627ad4>> + * @generated SignedSource<<9a2806b3934d130010954a92a7b48e0f>> */ /** @@ -84,6 +84,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var fuseboxFrameRecordingEnabledCache: Boolean? = null private var fuseboxNetworkInspectionEnabledCache: Boolean? = null private var fuseboxScreenshotCaptureEnabledCache: Boolean? = null + private var fuseboxWebSocketEventsEnabledCache: Boolean? = null private var optimizedAnimatedPropUpdatesCache: Boolean? = null private var overrideBySynchronousMountPropsAtMountingAndroidCache: Boolean? = null private var perfIssuesEnabledCache: Boolean? = null @@ -711,6 +712,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun fuseboxWebSocketEventsEnabled(): Boolean { + var cached = fuseboxWebSocketEventsEnabledCache + if (cached == null) { + cached = currentProvider.fuseboxWebSocketEventsEnabled() + accessedFeatureFlags.add("fuseboxWebSocketEventsEnabled") + fuseboxWebSocketEventsEnabledCache = cached + } + return cached + } + override fun optimizedAnimatedPropUpdates(): Boolean { var cached = optimizedAnimatedPropUpdatesCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index c611198dac44..2a6da8129f50 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<8b5a9f8966d9f096b7fa4f7020db1f34>> + * @generated SignedSource<<49c76b8072c1bd29135086c9e0a5da2f>> */ /** @@ -143,6 +143,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun fuseboxScreenshotCaptureEnabled(): Boolean + @DoNotStrip public fun fuseboxWebSocketEventsEnabled(): Boolean + @DoNotStrip public fun optimizedAnimatedPropUpdates(): Boolean @DoNotStrip public fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/InspectorNetworkReporter.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/InspectorNetworkReporter.kt index 061774eafeec..653da920f6ac 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/InspectorNetworkReporter.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/InspectorNetworkReporter.kt @@ -5,10 +5,13 @@ * LICENSE file in the root directory of this source tree. */ +@file:Suppress("DEPRECATION_ERROR") // Conflicting okhttp versions + package com.facebook.react.modules.network import com.facebook.proguard.annotations.DoNotStripAny import com.facebook.soloader.SoLoader +import okio.ByteString /** * [Experimental] An interface for reporting network events to the modern debugger server and Web @@ -125,4 +128,106 @@ internal object InspectorNetworkReporter { } @JvmStatic external fun maybeStoreResponseBodyIncrementalImpl(requestId: String, data: String) + + /** + * Report that a WebSocket connection is about to be created. + * + * Corresponds to `Network.webSocketCreated` in CDP. + */ + @JvmStatic external fun reportWebSocketCreated(requestId: String, url: String) + + /** + * Report that a WebSocket handshake (HTTP upgrade) request is about to be sent, along with its + * final request headers. + * + * Corresponds to `Network.webSocketWillSendHandshakeRequest` in CDP. + */ + @JvmStatic + external fun reportWebSocketWillSendHandshakeRequest( + requestId: String, + headers: Map, + ) + + /** + * Report that a WebSocket handshake response was received and the connection is established. + * + * Corresponds to `Network.webSocketHandshakeResponseReceived` in CDP. + */ + @JvmStatic + external fun reportWebSocketHandshakeResponseReceived( + requestId: String, + statusCode: Int, + headers: Map, + ) + + /** + * Report a text WebSocket message sent over an open connection. + * + * Corresponds to `Network.webSocketFrameSent` in CDP. + */ + @JvmStatic + fun reportWebSocketMessageSent(requestId: String, message: String) { + if (isDebuggingEnabled()) { + reportWebSocketMessageSentImpl(requestId, message, false) + } + } + + /** + * Report a binary WebSocket message sent over an open connection. + * + * Corresponds to `Network.webSocketFrameSent` in CDP. + */ + @JvmStatic + fun reportWebSocketMessageSent(requestId: String, message: ByteString) { + // Guard call to CDP-only reporting method (avoid base64 encoding) + if (isDebuggingEnabled()) { + reportWebSocketMessageSentImpl(requestId, message.base64(), true) + } + } + + @JvmStatic + external fun reportWebSocketMessageSentImpl( + requestId: String, + payloadData: String, + isBinary: Boolean, + ) + + /** + * Report a text WebSocket message received over an open connection. + * + * Corresponds to `Network.webSocketFrameReceived` in CDP. + */ + @JvmStatic + fun reportWebSocketMessageReceived(requestId: String, message: String) { + if (isDebuggingEnabled()) { + reportWebSocketMessageReceivedImpl(requestId, message, false) + } + } + + /** + * Report a binary WebSocket message received over an open connection. + * + * Corresponds to `Network.webSocketFrameReceived` in CDP. + */ + @JvmStatic + fun reportWebSocketMessageReceived(requestId: String, message: ByteString) { + // Guard call to CDP-only reporting method (avoid base64 encoding) + if (isDebuggingEnabled()) { + reportWebSocketMessageReceivedImpl(requestId, message.base64(), true) + } + } + + @JvmStatic + external fun reportWebSocketMessageReceivedImpl( + requestId: String, + payloadData: String, + isBinary: Boolean, + ) + + /** + * Report that a WebSocket connection was closed, whether cleanly or due to an error. + * + * Corresponds to `Network.webSocketClosed` in CDP. + */ + @JvmStatic external fun reportWebSocketClosed(requestId: String) } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.kt index ab9a036d59ec..025501bec436 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.kt @@ -19,14 +19,18 @@ import com.facebook.react.bridge.ReadableType import com.facebook.react.bridge.WritableMap import com.facebook.react.bridge.buildReadableMap import com.facebook.react.common.ReactConstants +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags import com.facebook.react.module.annotations.ReactModule import com.facebook.react.modules.network.CustomClientBuilder import com.facebook.react.modules.network.ForwardingCookieHandler +import com.facebook.react.modules.network.InspectorNetworkReporter +import com.facebook.react.modules.network.NetworkEventUtil import com.facebook.react.modules.network.OkHttpClientProvider import java.io.IOException import java.net.URI import java.net.URISyntaxException import java.util.HashMap +import java.util.UUID import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.TimeUnit import okhttp3.CookieJar @@ -51,12 +55,31 @@ public class WebSocketModule(context: ReactApplicationContext) : private val contentHandlers: MutableMap = ConcurrentHashMap() private val cookieHandler: ForwardingCookieHandler = ForwardingCookieHandler() + /** CDP request IDs used to report each connection's events to the modern debugger server. */ + private val inspectorRequestIds: MutableMap = ConcurrentHashMap() + override fun invalidate() { for (socket in webSocketConnections.values) { socket.close(1_001 /* endpoint is going away */, null) } webSocketConnections.clear() contentHandlers.clear() + inspectorRequestIds.clear() + } + + /** Whether WebSocket events should be reported to the modern debugger server. */ + private fun isInspectorNetworkReportingEnabled(): Boolean = + ReactNativeFeatureFlags.enableNetworkEventReporting() && + ReactNativeFeatureFlags.fuseboxWebSocketEventsEnabled() + + /** + * Run [block] with the connection's CDP request ID, when WebSocket event reporting to the modern + * debugger server is enabled. + */ + private inline fun reportToInspector(id: Int, block: (requestId: String) -> Unit) { + if (isInspectorNetworkReportingEnabled()) { + inspectorRequestIds[id]?.let(block) + } } private fun sendEvent(eventName: String, params: ReadableMap) { @@ -142,11 +165,30 @@ public class WebSocketModule(context: ReactApplicationContext) : } } + val request = builder.build() + + if (isInspectorNetworkReportingEnabled()) { + val requestId = UUID.randomUUID().toString() + inspectorRequestIds[id] = requestId + InspectorNetworkReporter.reportWebSocketCreated(requestId, url) + InspectorNetworkReporter.reportWebSocketWillSendHandshakeRequest( + requestId, + NetworkEventUtil.okHttpHeadersToMap(request.headers()), + ) + } + client.newWebSocket( - builder.build(), + request, object : WebSocketListener() { override fun onOpen(webSocket: WebSocket, response: Response) { webSocketConnections[id] = webSocket + reportToInspector(id) { requestId -> + InspectorNetworkReporter.reportWebSocketHandshakeResponseReceived( + requestId, + response.code(), + NetworkEventUtil.okHttpHeadersToMap(response.headers()), + ) + } val params = buildReadableMap { put("id", id) put("protocol", response.header("Sec-WebSocket-Protocol", "")) @@ -159,6 +201,10 @@ public class WebSocketModule(context: ReactApplicationContext) : } override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { + reportToInspector(id) { requestId -> + InspectorNetworkReporter.reportWebSocketClosed(requestId) + } + inspectorRequestIds.remove(id) val params = buildReadableMap { put("id", id) put("code", code) @@ -168,10 +214,17 @@ public class WebSocketModule(context: ReactApplicationContext) : } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { + reportToInspector(id) { requestId -> + InspectorNetworkReporter.reportWebSocketClosed(requestId) + } + inspectorRequestIds.remove(id) notifyWebSocketFailed(id, t.message) } override fun onMessage(webSocket: WebSocket, text: String) { + reportToInspector(id) { requestId -> + InspectorNetworkReporter.reportWebSocketMessageReceived(requestId, text) + } val params = Arguments.createMap() params.putInt("id", id) params.putString("type", "text") @@ -186,6 +239,9 @@ public class WebSocketModule(context: ReactApplicationContext) : } override fun onMessage(webSocket: WebSocket, bytes: ByteString) { + reportToInspector(id) { requestId -> + InspectorNetworkReporter.reportWebSocketMessageReceived(requestId, bytes) + } val params = Arguments.createMap() params.putInt("id", id) params.putString("type", "binary") @@ -240,9 +296,13 @@ public class WebSocketModule(context: ReactApplicationContext) : sendEvent("websocketClosed", params) webSocketConnections.remove(id) contentHandlers.remove(id) + inspectorRequestIds.remove(id) return } try { + reportToInspector(id) { requestId -> + InspectorNetworkReporter.reportWebSocketMessageSent(requestId, message) + } client.send(message) } catch (e: Exception) { notifyWebSocketFailed(id, e.message) @@ -267,10 +327,14 @@ public class WebSocketModule(context: ReactApplicationContext) : sendEvent("websocketClosed", params) webSocketConnections.remove(id) contentHandlers.remove(id) + inspectorRequestIds.remove(id) return } try { val decodedString = checkNotNull(ByteString.decodeBase64(base64String)) { "bytes == null" } + reportToInspector(id) { requestId -> + InspectorNetworkReporter.reportWebSocketMessageSent(requestId, decodedString) + } client.send(decodedString) } catch (e: Exception) { notifyWebSocketFailed(id, e.message) @@ -294,9 +358,13 @@ public class WebSocketModule(context: ReactApplicationContext) : sendEvent("websocketClosed", params) webSocketConnections.remove(id) contentHandlers.remove(id) + inspectorRequestIds.remove(id) return } try { + reportToInspector(id) { requestId -> + InspectorNetworkReporter.reportWebSocketMessageSent(requestId, byteString) + } client.send(byteString) } catch (e: Exception) { notifyWebSocketFailed(id, e.message) @@ -321,6 +389,7 @@ public class WebSocketModule(context: ReactApplicationContext) : sendEvent("websocketClosed", params) webSocketConnections.remove(id) contentHandlers.remove(id) + inspectorRequestIds.remove(id) return } try { diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JInspectorNetworkReporter.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JInspectorNetworkReporter.cpp index 512eaf3811e3..d9277b5caf79 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JInspectorNetworkReporter.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JInspectorNetworkReporter.cpp @@ -178,6 +178,60 @@ JInspectorNetworkReporter::maybeStoreResponseBodyIncrementalImpl( #endif } +/* static */ void JInspectorNetworkReporter::reportWebSocketCreated( + jni::alias_ref /*unused*/, + jni::alias_ref requestId, + jni::alias_ref url) { + NetworkReporter::getInstance().reportWebSocketCreated( + requestId->toStdString(), url->toStdString()); +} + +/* static */ void +JInspectorNetworkReporter::reportWebSocketWillSendHandshakeRequest( + jni::alias_ref /*unused*/, + jni::alias_ref requestId, + jni::alias_ref> headers) { + NetworkReporter::getInstance().reportWebSocketWillSendHandshakeRequest( + requestId->toStdString(), convertJavaMapToHeaders(headers)); +} + +/* static */ void +JInspectorNetworkReporter::reportWebSocketHandshakeResponseReceived( + jni::alias_ref /*unused*/, + jni::alias_ref requestId, + jint statusCode, + jni::alias_ref> headers) { + NetworkReporter::getInstance().reportWebSocketHandshakeResponseReceived( + requestId->toStdString(), + static_cast(statusCode), + convertJavaMapToHeaders(headers)); +} + +/* static */ void JInspectorNetworkReporter::reportWebSocketMessageSentImpl( + jni::alias_ref /*unused*/, + jni::alias_ref requestId, + jni::alias_ref payloadData, + jboolean isBinary) { + NetworkReporter::getInstance().reportWebSocketMessageSent( + requestId->toStdString(), payloadData->toStdString(), isBinary != 0u); +} + +/* static */ void JInspectorNetworkReporter::reportWebSocketMessageReceivedImpl( + jni::alias_ref /*unused*/, + jni::alias_ref requestId, + jni::alias_ref payloadData, + jboolean isBinary) { + NetworkReporter::getInstance().reportWebSocketMessageReceived( + requestId->toStdString(), payloadData->toStdString(), isBinary != 0u); +} + +/* static */ void JInspectorNetworkReporter::reportWebSocketClosed( + jni::alias_ref /*unused*/, + jni::alias_ref requestId) { + NetworkReporter::getInstance().reportWebSocketClosed( + requestId->toStdString()); +} + /* static */ void JInspectorNetworkReporter::registerNatives() { javaClassLocal()->registerNatives({ makeNativeMethod( @@ -204,6 +258,24 @@ JInspectorNetworkReporter::maybeStoreResponseBodyIncrementalImpl( makeNativeMethod( "maybeStoreResponseBodyIncrementalImpl", JInspectorNetworkReporter::maybeStoreResponseBodyIncrementalImpl), + makeNativeMethod( + "reportWebSocketCreated", + JInspectorNetworkReporter::reportWebSocketCreated), + makeNativeMethod( + "reportWebSocketWillSendHandshakeRequest", + JInspectorNetworkReporter::reportWebSocketWillSendHandshakeRequest), + makeNativeMethod( + "reportWebSocketHandshakeResponseReceived", + JInspectorNetworkReporter::reportWebSocketHandshakeResponseReceived), + makeNativeMethod( + "reportWebSocketMessageSentImpl", + JInspectorNetworkReporter::reportWebSocketMessageSentImpl), + makeNativeMethod( + "reportWebSocketMessageReceivedImpl", + JInspectorNetworkReporter::reportWebSocketMessageReceivedImpl), + makeNativeMethod( + "reportWebSocketClosed", + JInspectorNetworkReporter::reportWebSocketClosed), }); } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JInspectorNetworkReporter.h b/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JInspectorNetworkReporter.h index 3d50c5b044fa..f751dda40117 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JInspectorNetworkReporter.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/devsupport/JInspectorNetworkReporter.h @@ -59,6 +59,36 @@ class JInspectorNetworkReporter : public jni::HybridClass requestId, jni::alias_ref data); + static void reportWebSocketCreated( + jni::alias_ref /*unused*/, + jni::alias_ref requestId, + jni::alias_ref url); + + static void reportWebSocketWillSendHandshakeRequest( + jni::alias_ref /*unused*/, + jni::alias_ref requestId, + jni::alias_ref> headers); + + static void reportWebSocketHandshakeResponseReceived( + jni::alias_ref /*unused*/, + jni::alias_ref requestId, + jint statusCode, + jni::alias_ref> headers); + + static void reportWebSocketMessageSentImpl( + jni::alias_ref /*unused*/, + jni::alias_ref requestId, + jni::alias_ref payloadData, + jboolean isBinary); + + static void reportWebSocketMessageReceivedImpl( + jni::alias_ref /*unused*/, + jni::alias_ref requestId, + jni::alias_ref payloadData, + jboolean isBinary); + + static void reportWebSocketClosed(jni::alias_ref /*unused*/, jni::alias_ref requestId); + static void registerNatives(); private: diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index 09be85cdc306..349c5ad6a541 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<42f1901c8b7cb568cc761a791991a333>> */ /** @@ -399,6 +399,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool fuseboxWebSocketEventsEnabled() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("fuseboxWebSocketEventsEnabled"); + return method(javaProvider_); + } + bool optimizedAnimatedPropUpdates() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("optimizedAnimatedPropUpdates"); @@ -859,6 +865,11 @@ bool JReactNativeFeatureFlagsCxxInterop::fuseboxScreenshotCaptureEnabled( return ReactNativeFeatureFlags::fuseboxScreenshotCaptureEnabled(); } +bool JReactNativeFeatureFlagsCxxInterop::fuseboxWebSocketEventsEnabled( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::fuseboxWebSocketEventsEnabled(); +} + bool JReactNativeFeatureFlagsCxxInterop::optimizedAnimatedPropUpdates( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::optimizedAnimatedPropUpdates(); @@ -1200,6 +1211,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "fuseboxScreenshotCaptureEnabled", JReactNativeFeatureFlagsCxxInterop::fuseboxScreenshotCaptureEnabled), + makeNativeMethod( + "fuseboxWebSocketEventsEnabled", + JReactNativeFeatureFlagsCxxInterop::fuseboxWebSocketEventsEnabled), makeNativeMethod( "optimizedAnimatedPropUpdates", JReactNativeFeatureFlagsCxxInterop::optimizedAnimatedPropUpdates), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index 2de55550ee3a..6ffc3146e658 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<9967d87b794ba46797ed78a845683dc7>> */ /** @@ -210,6 +210,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool fuseboxScreenshotCaptureEnabled( facebook::jni::alias_ref); + static bool fuseboxWebSocketEventsEnabled( + facebook::jni::alias_ref); + static bool optimizedAnimatedPropUpdates( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/jsinspector-modern/network/CdpNetwork.cpp b/packages/react-native/ReactCommon/jsinspector-modern/network/CdpNetwork.cpp index 916103327b3a..77dbe081848c 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/network/CdpNetwork.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/network/CdpNetwork.cpp @@ -141,6 +141,87 @@ folly::dynamic LoadingFinishedParams::toDynamic() const { return params; } +/* static */ WebSocketResponse WebSocketResponse::fromInputParams( + uint16_t status, + const Headers& headers) { + return { + .status = status, + .statusText = httpReasonPhrase(status), + .headers = headers, + }; +} + +folly::dynamic WebSocketResponse::toDynamic() const { + folly::dynamic result = folly::dynamic::object; + + result["status"] = status; + result["statusText"] = statusText; + result["headers"] = headersToDynamic(headers); + + return result; +} + +folly::dynamic WebSocketFrame::toDynamic() const { + folly::dynamic result = folly::dynamic::object; + + result["opcode"] = opcode; + result["mask"] = mask; + result["payloadData"] = payloadData; + + return result; +} + +folly::dynamic WebSocketCreatedParams::toDynamic() const { + folly::dynamic params = folly::dynamic::object; + + params["requestId"] = requestId; + params["url"] = url; + params["initiator"] = initiator; + + return params; +} + +folly::dynamic WebSocketWillSendHandshakeRequestParams::toDynamic() const { + folly::dynamic params = folly::dynamic::object; + + params["requestId"] = requestId; + params["timestamp"] = timestamp; + params["wallTime"] = wallTime; + params["request"] = + folly::dynamic::object("headers", headersToDynamic(headers)); + + return params; +} + +folly::dynamic WebSocketHandshakeResponseReceivedParams::toDynamic() const { + folly::dynamic params = folly::dynamic::object; + + params["requestId"] = requestId; + params["timestamp"] = timestamp; + params["response"] = response.toDynamic(); + + return params; +} + +folly::dynamic WebSocketFrameParams::toDynamic() const { + folly::dynamic params = folly::dynamic::object; + + params["requestId"] = requestId; + params["timestamp"] = timestamp; + params["response"] = response.toDynamic(); + + return params; +} + +folly::dynamic WebSocketClosedParams::toDynamic() const { + folly::dynamic params = folly::dynamic::object; + + params["requestId"] = requestId; + params["timestamp"] = timestamp; + + return params; +} + std::string resourceTypeFromMimeType(const std::string& mimeType) { if (mimeType.find("image/") == 0) { return "Image"; diff --git a/packages/react-native/ReactCommon/jsinspector-modern/network/CdpNetwork.h b/packages/react-native/ReactCommon/jsinspector-modern/network/CdpNetwork.h index 2246057ac46a..e017d3a10d6f 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/network/CdpNetwork.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/network/CdpNetwork.h @@ -136,6 +136,99 @@ struct LoadingFinishedParams { folly::dynamic toDynamic() const; }; +/** + * HTTP response data for a WebSocket handshake (upgrade) request. + * + * https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-WebSocketResponse + */ +struct WebSocketResponse { + uint16_t status; + std::string statusText; + Headers headers; + + /** + * Convenience function to construct a `WebSocketResponse` from generic + * input params, deriving the status text. + */ + static WebSocketResponse fromInputParams(uint16_t status, const Headers &headers); + + folly::dynamic toDynamic() const; +}; + +/** + * A WebSocket message payload. NOTE: Despite the CDP type name, this + * represents a complete WebSocket message, not a wire-level frame. + * + * https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-WebSocketFrame + */ +struct WebSocketFrame { + int opcode; + bool mask; + std::string payloadData; + + folly::dynamic toDynamic() const; +}; + +/** + * https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-webSocketCreated + */ +struct WebSocketCreatedParams { + std::string requestId; + std::string url; + folly::dynamic initiator; + + folly::dynamic toDynamic() const; +}; + +/** + * https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-webSocketWillSendHandshakeRequest + */ +struct WebSocketWillSendHandshakeRequestParams { + std::string requestId; + double timestamp; + double wallTime; + /** Serialized as the `headers` field of the CDP `WebSocketRequest` type. */ + Headers headers; + + folly::dynamic toDynamic() const; +}; + +/** + * https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-webSocketHandshakeResponseReceived + */ +struct WebSocketHandshakeResponseReceivedParams { + std::string requestId; + double timestamp; + WebSocketResponse response; + + folly::dynamic toDynamic() const; +}; + +/** + * Shared params type for the `Network.webSocketFrameSent` and + * `Network.webSocketFrameReceived` events. + * + * https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-webSocketFrameSent + * https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-webSocketFrameReceived + */ +struct WebSocketFrameParams { + std::string requestId; + double timestamp; + WebSocketFrame response; + + folly::dynamic toDynamic() const; +}; + +/** + * https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-webSocketClosed + */ +struct WebSocketClosedParams { + std::string requestId; + double timestamp; + + folly::dynamic toDynamic() const; +}; + /** * Get the CDP `ResourceType` for a given MIME type. * diff --git a/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkHandler.cpp b/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkHandler.cpp index 9dbe9bfbaa8a..632a8ad25c01 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkHandler.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkHandler.cpp @@ -16,6 +16,12 @@ namespace facebook::react::jsinspector_modern { namespace { +/** + * WebSocket frame opcodes for data messages (RFC 6455). + */ +constexpr int kWebSocketOpcodeText = 1; +constexpr int kWebSocketOpcodeBinary = 2; + /** * Get the current Unix timestamp in seconds (µs precision, CDP format). */ @@ -200,6 +206,135 @@ void NetworkHandler::onLoadingFailed( } } +void NetworkHandler::onWebSocketCreated( + const std::string& requestId, + const std::string& url) { + if (!isEnabledNoSync()) { + return; + } + + std::optional initiator = + consumeStoredRequestInitiator(requestId); + auto params = cdp::network::WebSocketCreatedParams{ + .requestId = requestId, + .url = url, + .initiator = initiator.has_value() + ? std::move(initiator.value()) + : folly::dynamic::object("type", "script"), + }; + + sendToAllAgents( + cdp::jsonNotification("Network.webSocketCreated", params.toDynamic())); +} + +void NetworkHandler::onWebSocketWillSendHandshakeRequest( + const std::string& requestId, + const Headers& headers) { + if (!isEnabledNoSync()) { + return; + } + + double timestamp = getCurrentUnixTimestampSeconds(); + auto params = cdp::network::WebSocketWillSendHandshakeRequestParams{ + .requestId = requestId, + // NOTE: Both timestamp and wallTime use the same unit, however wallTime + // is relative to an "arbitrary epoch". In our implementation, use the + // Unix epoch for both. + .timestamp = timestamp, + .wallTime = timestamp, + .headers = headers, + }; + + sendToAllAgents( + cdp::jsonNotification( + "Network.webSocketWillSendHandshakeRequest", params.toDynamic())); +} + +void NetworkHandler::onWebSocketHandshakeResponseReceived( + const std::string& requestId, + uint16_t status, + const Headers& headers) { + if (!isEnabledNoSync()) { + return; + } + + auto params = cdp::network::WebSocketHandshakeResponseReceivedParams{ + .requestId = requestId, + .timestamp = getCurrentUnixTimestampSeconds(), + .response = + cdp::network::WebSocketResponse::fromInputParams(status, headers), + }; + + sendToAllAgents( + cdp::jsonNotification( + "Network.webSocketHandshakeResponseReceived", params.toDynamic())); +} + +void NetworkHandler::onWebSocketFrameSent( + const std::string& requestId, + const std::string& payloadData, + bool isBinary) { + if (!isEnabledNoSync()) { + return; + } + + auto params = cdp::network::WebSocketFrameParams{ + .requestId = requestId, + .timestamp = getCurrentUnixTimestampSeconds(), + .response = + { + .opcode = + isBinary ? kWebSocketOpcodeBinary : kWebSocketOpcodeText, + // Client-to-server messages are always masked (RFC 6455). + .mask = true, + .payloadData = payloadData, + }, + }; + + sendToAllAgents( + cdp::jsonNotification("Network.webSocketFrameSent", params.toDynamic())); +} + +void NetworkHandler::onWebSocketFrameReceived( + const std::string& requestId, + const std::string& payloadData, + bool isBinary) { + if (!isEnabledNoSync()) { + return; + } + + auto params = cdp::network::WebSocketFrameParams{ + .requestId = requestId, + .timestamp = getCurrentUnixTimestampSeconds(), + .response = + { + .opcode = + isBinary ? kWebSocketOpcodeBinary : kWebSocketOpcodeText, + // Server-to-client messages are never masked (RFC 6455). + .mask = false, + .payloadData = payloadData, + }, + }; + + sendToAllAgents( + cdp::jsonNotification( + "Network.webSocketFrameReceived", params.toDynamic())); +} + +void NetworkHandler::onWebSocketClosed(const std::string& requestId) { + if (!isEnabledNoSync()) { + return; + } + + auto params = cdp::network::WebSocketClosedParams{ + .requestId = requestId, + .timestamp = getCurrentUnixTimestampSeconds(), + }; + + sendToAllAgents( + cdp::jsonNotification("Network.webSocketClosed", params.toDynamic())); +} + void NetworkHandler::storeResponseBody( const std::string& requestId, std::string_view body, diff --git a/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkHandler.h b/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkHandler.h index aaa43aad2b47..b71890f495fc 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkHandler.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkHandler.h @@ -90,6 +90,44 @@ class NetworkHandler { */ void onLoadingFailed(const std::string &requestId, bool cancelled); + /** + * @cdp Network.webSocketCreated + */ + void onWebSocketCreated(const std::string &requestId, const std::string &url); + + /** + * @cdp Network.webSocketWillSendHandshakeRequest + */ + void onWebSocketWillSendHandshakeRequest(const std::string &requestId, const Headers &headers); + + /** + * @cdp Network.webSocketHandshakeResponseReceived + */ + void onWebSocketHandshakeResponseReceived(const std::string &requestId, uint16_t status, const Headers &headers); + + /** + * @cdp Network.webSocketFrameSent + * + * \param payloadData The message payload. A UTF-8 string for text messages, + * or a base64-encoded string for binary messages. + * \param isBinary Whether this is a binary message. + */ + void onWebSocketFrameSent(const std::string &requestId, const std::string &payloadData, bool isBinary); + + /** + * @cdp Network.webSocketFrameReceived + * + * \param payloadData The message payload. A UTF-8 string for text messages, + * or a base64-encoded string for binary messages. + * \param isBinary Whether this is a binary message. + */ + void onWebSocketFrameReceived(const std::string &requestId, const std::string &payloadData, bool isBinary); + + /** + * @cdp Network.webSocketClosed + */ + void onWebSocketClosed(const std::string &requestId); + /** * Store the fetched response body for a text or image network response. * diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tests/NetworkReporterTest.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tests/NetworkReporterTest.cpp index 678a8c164533..f18935dba169 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tests/NetworkReporterTest.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tests/NetworkReporterTest.cpp @@ -464,6 +464,114 @@ TEST_P(NetworkReporterTest, testGetResponseBodyWithBase64) { })"); } +TEST_P(NetworkReporterTest, testCompleteWebSocketFlow) { + InSequence s; + this->expectMessageFromPage(JsonEq(R"({ + "id": 1, + "result": {} + })")); + this->toPage_->sendMessage(R"({ + "id": 1, + "method": "Network.enable" + })"); + + const std::string requestId = "websocket-flow-request"; + + // Step 1: WebSocket created + this->expectMessageFromPage(JsonParsed(AllOf( + AtJsonPtr("/method", "Network.webSocketCreated"), + AtJsonPtr("/params/requestId", requestId), + AtJsonPtr("/params/url", "wss://echo.example.com/socket"), + AtJsonPtr("/params/initiator/type", "script")))); + + NetworkReporter::getInstance().reportWebSocketCreated( + requestId, "wss://echo.example.com/socket"); + + // Step 2: Handshake request will be sent + this->expectMessageFromPage(JsonParsed(AllOf( + AtJsonPtr("/method", "Network.webSocketWillSendHandshakeRequest"), + AtJsonPtr("/params/requestId", requestId), + AtJsonPtr("/params/timestamp", Gt(0)), + AtJsonPtr("/params/wallTime", Gt(0)), + AtJsonPtr("/params/request/headers/Origin", "https://example.com"), + AtJsonPtr( + "/params/request/headers/Sec-WebSocket-Protocol", "graphql-ws")))); + + NetworkReporter::getInstance().reportWebSocketWillSendHandshakeRequest( + requestId, + Headers{ + {"Origin", "https://example.com"}, + {"Sec-WebSocket-Protocol", "graphql-ws"}}); + + // Step 3: Handshake response received + this->expectMessageFromPage(JsonParsed(AllOf( + AtJsonPtr("/method", "Network.webSocketHandshakeResponseReceived"), + AtJsonPtr("/params/requestId", requestId), + AtJsonPtr("/params/timestamp", Gt(0)), + AtJsonPtr("/params/response/status", 101), + AtJsonPtr("/params/response/statusText", "Switching Protocols"), + AtJsonPtr("/params/response/headers/Upgrade", "websocket"), + AtJsonPtr("/params/response/headers/Connection", "Upgrade")))); + + NetworkReporter::getInstance().reportWebSocketHandshakeResponseReceived( + requestId, + 101, + Headers{{"Upgrade", "websocket"}, {"Connection", "Upgrade"}}); + + // Step 4: Text message sent + this->expectMessageFromPage(JsonParsed(AllOf( + AtJsonPtr("/method", "Network.webSocketFrameSent"), + AtJsonPtr("/params/requestId", requestId), + AtJsonPtr("/params/timestamp", Gt(0)), + AtJsonPtr("/params/response/opcode", 1), + AtJsonPtr("/params/response/mask", true), + AtJsonPtr("/params/response/payloadData", R"({"type": "ping"})")))); + + NetworkReporter::getInstance().reportWebSocketMessageSent( + requestId, R"({"type": "ping"})", false); + + // Step 5: Binary message sent (payload is base64-encoded) + this->expectMessageFromPage(JsonParsed(AllOf( + AtJsonPtr("/method", "Network.webSocketFrameSent"), + AtJsonPtr("/params/requestId", requestId), + AtJsonPtr("/params/timestamp", Gt(0)), + AtJsonPtr("/params/response/opcode", 2), + AtJsonPtr("/params/response/mask", true), + AtJsonPtr("/params/response/payloadData", "aGVsbG8=")))); + + NetworkReporter::getInstance().reportWebSocketMessageSent( + requestId, "aGVsbG8=", true); + + // Step 6: Text message received + this->expectMessageFromPage(JsonParsed(AllOf( + AtJsonPtr("/method", "Network.webSocketFrameReceived"), + AtJsonPtr("/params/requestId", requestId), + AtJsonPtr("/params/timestamp", Gt(0)), + AtJsonPtr("/params/response/opcode", 1), + AtJsonPtr("/params/response/mask", false), + AtJsonPtr("/params/response/payloadData", R"({"type": "pong"})")))); + + NetworkReporter::getInstance().reportWebSocketMessageReceived( + requestId, R"({"type": "pong"})", false); + + // Step 7: WebSocket closed + this->expectMessageFromPage(JsonParsed(AllOf( + AtJsonPtr("/method", "Network.webSocketClosed"), + AtJsonPtr("/params/requestId", requestId), + AtJsonPtr("/params/timestamp", Gt(0))))); + + NetworkReporter::getInstance().reportWebSocketClosed(requestId); + + this->expectMessageFromPage(JsonEq(R"({ + "id": 2, + "result": {} + })")); + this->toPage_->sendMessage(R"({ + "id": 2, + "method": "Network.disable" + })"); +} + TEST_P(NetworkReporterTest, testNetworkEventsWhenDisabled) { EXPECT_FALSE(NetworkReporter::getInstance().isDebuggingEnabled()); @@ -488,6 +596,18 @@ TEST_P(NetworkReporterTest, testNetworkEventsWhenDisabled) { "disabled-request", 512, 512); NetworkReporter::getInstance().reportResponseEnd("disabled-request", 1024); NetworkReporter::getInstance().reportRequestFailed("disabled-request", false); + + NetworkReporter::getInstance().reportWebSocketCreated( + "disabled-websocket", "wss://example.com/disabled"); + NetworkReporter::getInstance().reportWebSocketWillSendHandshakeRequest( + "disabled-websocket", {}); + NetworkReporter::getInstance().reportWebSocketHandshakeResponseReceived( + "disabled-websocket", 101, {}); + NetworkReporter::getInstance().reportWebSocketMessageSent( + "disabled-websocket", "hello", false); + NetworkReporter::getInstance().reportWebSocketMessageReceived( + "disabled-websocket", "world", false); + NetworkReporter::getInstance().reportWebSocketClosed("disabled-websocket"); } TEST_P(NetworkReporterTest, testRequestWillBeSentWithInitiator) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 8e65a9654b44..31d61b5cb247 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<6f2362e9844cab731ca01e4af563fde9>> */ /** @@ -266,6 +266,10 @@ bool ReactNativeFeatureFlags::fuseboxScreenshotCaptureEnabled() { return getAccessor().fuseboxScreenshotCaptureEnabled(); } +bool ReactNativeFeatureFlags::fuseboxWebSocketEventsEnabled() { + return getAccessor().fuseboxWebSocketEventsEnabled(); +} + bool ReactNativeFeatureFlags::optimizedAnimatedPropUpdates() { return getAccessor().optimizedAnimatedPropUpdates(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index a2ca86c275a7..344997745dac 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<4276ae19d55acb679c9e635122e813b3>> + * @generated SignedSource<> */ /** @@ -339,6 +339,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool fuseboxScreenshotCaptureEnabled(); + /** + * Enable reporting of WebSocket network events (`Network.webSocket*` CDP events) to the React Native DevTools CDP backend. Requires `fuseboxNetworkInspectionEnabled`. + */ + RN_EXPORT static bool fuseboxWebSocketEventsEnabled(); + /** * When enabled, uses optimized platform-specific paths to apply animated props synchronously. On Android, this uses a batched int/double buffer protocol with a single JNI call. On iOS, this passes AnimatedProps directly through the delegate chain and applies them via cloneProps, avoiding the folly::dynamic round-trip. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 041bc351eac9..eef175da896e 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2aee1d5b6266189201bb6f5f6c0ceb4c>> + * @generated SignedSource<<4c6a965958d151f22574bc85a6714c11>> */ /** @@ -1109,6 +1109,24 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxScreenshotCaptureEnabled() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::fuseboxWebSocketEventsEnabled() { + auto flagValue = fuseboxWebSocketEventsEnabled_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(60, "fuseboxWebSocketEventsEnabled"); + + flagValue = currentProvider_->fuseboxWebSocketEventsEnabled(); + fuseboxWebSocketEventsEnabled_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::optimizedAnimatedPropUpdates() { auto flagValue = optimizedAnimatedPropUpdates_.load(); @@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::optimizedAnimatedPropUpdates() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(60, "optimizedAnimatedPropUpdates"); + markFlagAsAccessed(61, "optimizedAnimatedPropUpdates"); flagValue = currentProvider_->optimizedAnimatedPropUpdates(); optimizedAnimatedPropUpdates_ = flagValue; @@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(62, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1154,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::perfIssuesEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "perfIssuesEnabled"); + markFlagAsAccessed(63, "perfIssuesEnabled"); flagValue = currentProvider_->perfIssuesEnabled(); perfIssuesEnabled_ = flagValue; @@ -1172,7 +1190,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(63, "perfMonitorV2Enabled"); + markFlagAsAccessed(64, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1190,7 +1208,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(64, "preparedTextCacheSize"); + markFlagAsAccessed(65, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1208,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(66, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1226,7 +1244,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2Android() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "redBoxV2Android"); + markFlagAsAccessed(67, "redBoxV2Android"); flagValue = currentProvider_->redBoxV2Android(); redBoxV2Android_ = flagValue; @@ -1244,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2IOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "redBoxV2IOS"); + markFlagAsAccessed(68, "redBoxV2IOS"); flagValue = currentProvider_->redBoxV2IOS(); redBoxV2IOS_ = flagValue; @@ -1262,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(69, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1280,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldTriggerResponderTransferOnScrollAndr // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(69, "shouldTriggerResponderTransferOnScrollAndroid"); + markFlagAsAccessed(70, "shouldTriggerResponderTransferOnScrollAndroid"); flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid(); shouldTriggerResponderTransferOnScrollAndroid_ = flagValue; @@ -1298,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(70, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(71, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -1316,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::syncAndroidClipBoundsWithOverflow() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(71, "syncAndroidClipBoundsWithOverflow"); + markFlagAsAccessed(72, "syncAndroidClipBoundsWithOverflow"); flagValue = currentProvider_->syncAndroidClipBoundsWithOverflow(); syncAndroidClipBoundsWithOverflow_ = flagValue; @@ -1334,7 +1352,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(72, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(73, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1352,7 +1370,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(73, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(74, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1370,7 +1388,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommitT // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(74, "updateRuntimeShadowNodeReferencesOnCommitThread"); + markFlagAsAccessed(75, "updateRuntimeShadowNodeReferencesOnCommitThread"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommitThread(); updateRuntimeShadowNodeReferencesOnCommitThread_ = flagValue; @@ -1388,7 +1406,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(75, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(76, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1406,7 +1424,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(76, "useFabricInterop"); + markFlagAsAccessed(77, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1424,7 +1442,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(77, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(78, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1442,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::useNestedScrollViewAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "useNestedScrollViewAndroid"); + markFlagAsAccessed(79, "useNestedScrollViewAndroid"); flagValue = currentProvider_->useNestedScrollViewAndroid(); useNestedScrollViewAndroid_ = flagValue; @@ -1460,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(79, "useSharedAnimatedBackend"); + markFlagAsAccessed(80, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1478,7 +1496,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(80, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(81, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1496,7 +1514,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(81, "useTurboModuleInterop"); + markFlagAsAccessed(82, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1514,7 +1532,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(82, "viewCullingOutsetRatio"); + markFlagAsAccessed(83, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1532,7 +1550,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(83, "viewTransitionEnabled"); + markFlagAsAccessed(84, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1550,7 +1568,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionUseHardwareBitmapAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(84, "viewTransitionUseHardwareBitmapAndroid"); + markFlagAsAccessed(85, "viewTransitionUseHardwareBitmapAndroid"); flagValue = currentProvider_->viewTransitionUseHardwareBitmapAndroid(); viewTransitionUseHardwareBitmapAndroid_ = flagValue; @@ -1568,7 +1586,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(85, "virtualViewPrerenderRatio"); + markFlagAsAccessed(86, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index cf3369c9aa0f..6a67566e46cc 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -92,6 +92,7 @@ class ReactNativeFeatureFlagsAccessor { bool fuseboxFrameRecordingEnabled(); bool fuseboxNetworkInspectionEnabled(); bool fuseboxScreenshotCaptureEnabled(); + bool fuseboxWebSocketEventsEnabled(); bool optimizedAnimatedPropUpdates(); bool overrideBySynchronousMountPropsAtMountingAndroid(); bool perfIssuesEnabled(); @@ -129,7 +130,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 86> accessedFeatureFlags_; + std::array, 87> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -191,6 +192,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> fuseboxFrameRecordingEnabled_; std::atomic> fuseboxNetworkInspectionEnabled_; std::atomic> fuseboxScreenshotCaptureEnabled_; + std::atomic> fuseboxWebSocketEventsEnabled_; std::atomic> optimizedAnimatedPropUpdates_; std::atomic> overrideBySynchronousMountPropsAtMountingAndroid_; std::atomic> perfIssuesEnabled_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index ca9ab82ce1ec..aa36d5cfa7d5 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2c09412cdf8edaf4f7d6d90a446ce6fa>> + * @generated SignedSource<<2e19fd4b007d0ae7397babcc605b5783>> */ /** @@ -267,6 +267,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool fuseboxWebSocketEventsEnabled() override { + return false; + } + bool optimizedAnimatedPropUpdates() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 3fb33d362dac..940811f0174c 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -585,6 +585,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::fuseboxScreenshotCaptureEnabled(); } + bool fuseboxWebSocketEventsEnabled() override { + auto value = values_["fuseboxWebSocketEventsEnabled"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::fuseboxWebSocketEventsEnabled(); + } + bool optimizedAnimatedPropUpdates() override { auto value = values_["optimizedAnimatedPropUpdates"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 93d5f232ac49..d80700290554 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<14cafde581fa0fb72ffebb5e192040e8>> + * @generated SignedSource<<2d4757d38664169286dacd779b0e267a>> */ /** @@ -85,6 +85,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool fuseboxFrameRecordingEnabled() = 0; virtual bool fuseboxNetworkInspectionEnabled() = 0; virtual bool fuseboxScreenshotCaptureEnabled() = 0; + virtual bool fuseboxWebSocketEventsEnabled() = 0; virtual bool optimizedAnimatedPropUpdates() = 0; virtual bool overrideBySynchronousMountPropsAtMountingAndroid() = 0; virtual bool perfIssuesEnabled() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 8e627b8ace38..2490fdbd5be5 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<1e26f8dc1f474475c0a4d6be79aedb24>> + * @generated SignedSource<<2a0a030bafa14c6f20691f029703a642>> */ /** @@ -344,6 +344,11 @@ bool NativeReactNativeFeatureFlags::fuseboxScreenshotCaptureEnabled( return ReactNativeFeatureFlags::fuseboxScreenshotCaptureEnabled(); } +bool NativeReactNativeFeatureFlags::fuseboxWebSocketEventsEnabled( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::fuseboxWebSocketEventsEnabled(); +} + bool NativeReactNativeFeatureFlags::optimizedAnimatedPropUpdates( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::optimizedAnimatedPropUpdates(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index d0acb696863d..7d07e49ad1ba 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<86a4b76371a4005f7083c3806d53cc87>> + * @generated SignedSource<<206f5586c0ea22387a48a484d6d96f54>> */ /** @@ -156,6 +156,8 @@ class NativeReactNativeFeatureFlags bool fuseboxScreenshotCaptureEnabled(jsi::Runtime& runtime); + bool fuseboxWebSocketEventsEnabled(jsi::Runtime& runtime); + bool optimizedAnimatedPropUpdates(jsi::Runtime& runtime); bool overrideBySynchronousMountPropsAtMountingAndroid(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/networking/NetworkReporter.cpp b/packages/react-native/ReactCommon/react/networking/NetworkReporter.cpp index 458afb1fa6f7..d74c2e1e291a 100644 --- a/packages/react-native/ReactCommon/react/networking/NetworkReporter.cpp +++ b/packages/react-native/ReactCommon/react/networking/NetworkReporter.cpp @@ -241,6 +241,68 @@ void NetworkReporter::reportRequestFailed( #endif } +void NetworkReporter::reportWebSocketCreated( + const std::string& requestId, + const std::string& url) const { +#ifdef REACT_NATIVE_DEBUGGER_ENABLED + // Debugger enabled: CDP event handling + jsinspector_modern::NetworkHandler::getInstance().onWebSocketCreated( + requestId, url); +#endif +} + +void NetworkReporter::reportWebSocketWillSendHandshakeRequest( + const std::string& requestId, + const Headers& headers) const { +#ifdef REACT_NATIVE_DEBUGGER_ENABLED + // Debugger enabled: CDP event handling + jsinspector_modern::NetworkHandler::getInstance() + .onWebSocketWillSendHandshakeRequest(requestId, headers); +#endif +} + +void NetworkReporter::reportWebSocketHandshakeResponseReceived( + const std::string& requestId, + uint16_t statusCode, + const Headers& headers) const { +#ifdef REACT_NATIVE_DEBUGGER_ENABLED + // Debugger enabled: CDP event handling + jsinspector_modern::NetworkHandler::getInstance() + .onWebSocketHandshakeResponseReceived(requestId, statusCode, headers); +#endif +} + +void NetworkReporter::reportWebSocketMessageSent( + const std::string& requestId, + const std::string& payloadData, + bool isBinary) const { +#ifdef REACT_NATIVE_DEBUGGER_ENABLED + // Debugger enabled: CDP event handling + jsinspector_modern::NetworkHandler::getInstance().onWebSocketFrameSent( + requestId, payloadData, isBinary); +#endif +} + +void NetworkReporter::reportWebSocketMessageReceived( + const std::string& requestId, + const std::string& payloadData, + bool isBinary) const { +#ifdef REACT_NATIVE_DEBUGGER_ENABLED + // Debugger enabled: CDP event handling + jsinspector_modern::NetworkHandler::getInstance().onWebSocketFrameReceived( + requestId, payloadData, isBinary); +#endif +} + +void NetworkReporter::reportWebSocketClosed( + const std::string& requestId) const { +#ifdef REACT_NATIVE_DEBUGGER_ENABLED + // Debugger enabled: CDP event handling + jsinspector_modern::NetworkHandler::getInstance().onWebSocketClosed( + requestId); +#endif +} + void NetworkReporter::storeResponseBody( const std::string& requestId, std::string_view body, diff --git a/packages/react-native/ReactCommon/react/networking/NetworkReporter.h b/packages/react-native/ReactCommon/react/networking/NetworkReporter.h index e76adbf6afe6..841a592c1c6d 100644 --- a/packages/react-native/ReactCommon/react/networking/NetworkReporter.h +++ b/packages/react-native/ReactCommon/react/networking/NetworkReporter.h @@ -122,6 +122,66 @@ class NetworkReporter { */ void reportRequestFailed(const std::string &requestId, bool cancelled) const; + /** + * Report that a WebSocket connection is about to be created. + * + * Corresponds to `Network.webSocketCreated` in CDP. WebSocket events are + * reported to CDP only and have no Web Performance counterpart — in a + * production (non dev or profiling) build, this method is a no-op. + */ + void reportWebSocketCreated(const std::string &requestId, const std::string &url) const; + + /** + * Report that a WebSocket handshake (HTTP upgrade) request is about to be + * sent, along with its final request headers. + * + * Corresponds to `Network.webSocketWillSendHandshakeRequest` in CDP. In a + * production (non dev or profiling) build, this method is a no-op. + */ + void reportWebSocketWillSendHandshakeRequest(const std::string &requestId, const Headers &headers) const; + + /** + * Report that a WebSocket handshake response was received and the + * connection is established. + * + * Corresponds to `Network.webSocketHandshakeResponseReceived` in CDP. In a + * production (non dev or profiling) build, this method is a no-op. + */ + void reportWebSocketHandshakeResponseReceived( + const std::string &requestId, + uint16_t statusCode, + const Headers &headers) const; + + /** + * Report a WebSocket message sent over an open connection. `payloadData` + * must be a UTF-8 string for text messages, or a base64-encoded string for + * binary messages (`isBinary`). + * + * Corresponds to `Network.webSocketFrameSent` in CDP. In a production + * (non dev or profiling) build, this method is a no-op. + */ + void reportWebSocketMessageSent(const std::string &requestId, const std::string &payloadData, bool isBinary) const; + + /** + * Report a WebSocket message received over an open connection. + * `payloadData` must be a UTF-8 string for text messages, or a + * base64-encoded string for binary messages (`isBinary`). + * + * Corresponds to `Network.webSocketFrameReceived` in CDP. In a production + * (non dev or profiling) build, this method is a no-op. + */ + void reportWebSocketMessageReceived(const std::string &requestId, const std::string &payloadData, bool isBinary) + const; + + /** + * Report that a WebSocket connection was closed, whether cleanly or due to + * an error. + * + * Corresponds to `Network.webSocketClosed` in CDP. In a production (non dev + * or profiling) build, this method is a no-op. + */ + void reportWebSocketClosed(const std::string &requestId) const; + /** * Store the fetched response body for a text or image network response. * These may be retrieved by CDP clients to to render a response preview via diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 54ffc48eefc9..1f903b9c290d 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -690,6 +690,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'experimental', }, + fuseboxWebSocketEventsEnabled: { + defaultValue: false, + metadata: { + dateAdded: '2026-07-11', + description: + 'Enable reporting of WebSocket network events (`Network.webSocket*` CDP events) to the React Native DevTools CDP backend. Requires `fuseboxNetworkInspectionEnabled`.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, optimizedAnimatedPropUpdates: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index a9c690414348..b9d7e539d61b 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<71e307d568421d49e3943f58c4fcb163>> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -109,6 +109,7 @@ export type ReactNativeFeatureFlags = Readonly<{ fuseboxFrameRecordingEnabled: Getter, fuseboxNetworkInspectionEnabled: Getter, fuseboxScreenshotCaptureEnabled: Getter, + fuseboxWebSocketEventsEnabled: Getter, optimizedAnimatedPropUpdates: Getter, overrideBySynchronousMountPropsAtMountingAndroid: Getter, perfIssuesEnabled: Getter, @@ -451,6 +452,10 @@ export const fuseboxNetworkInspectionEnabled: Getter = createNativeFlag * Enable Page.captureScreenshot CDP method support in the React Native DevTools CDP backend. This flag is global and should not be changed across React Host lifetimes. */ export const fuseboxScreenshotCaptureEnabled: Getter = createNativeFlagGetter('fuseboxScreenshotCaptureEnabled', false); +/** + * Enable reporting of WebSocket network events (`Network.webSocket*` CDP events) to the React Native DevTools CDP backend. Requires `fuseboxNetworkInspectionEnabled`. + */ +export const fuseboxWebSocketEventsEnabled: Getter = createNativeFlagGetter('fuseboxWebSocketEventsEnabled', false); /** * When enabled, uses optimized platform-specific paths to apply animated props synchronously. On Android, this uses a batched int/double buffer protocol with a single JNI call. On iOS, this passes AnimatedProps directly through the delegate chain and applies them via cloneProps, avoiding the folly::dynamic round-trip. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index a4145d59c8c2..67ae1cb7d8a1 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<68ccb8d73a8f29fbf88f15a183bd910b>> + * @generated SignedSource<<4dd4bbc51723d64c94a8e7ba1bf67bf1>> * @flow strict * @noformat */ @@ -85,6 +85,7 @@ export interface Spec extends TurboModule { readonly fuseboxFrameRecordingEnabled?: () => boolean; readonly fuseboxNetworkInspectionEnabled?: () => boolean; readonly fuseboxScreenshotCaptureEnabled?: () => boolean; + readonly fuseboxWebSocketEventsEnabled?: () => boolean; readonly optimizedAnimatedPropUpdates?: () => boolean; readonly overrideBySynchronousMountPropsAtMountingAndroid?: () => boolean; readonly perfIssuesEnabled?: () => boolean; diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api index 0aaf2efc5caa..dcf221dac974 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api @@ -3936,6 +3936,12 @@ class facebook::react::NetworkReporter { public void reportRequestStart(const std::string& requestId, const facebook::react::RequestInfo& requestInfo, int encodedDataLength, const std::optional& redirectResponse); public void reportResponseEnd(const std::string& requestId, int encodedDataLength); public void reportResponseStart(const std::string& requestId, const facebook::react::ResponseInfo& responseInfo, int encodedDataLength); + public void reportWebSocketClosed(const std::string& requestId) const; + public void reportWebSocketCreated(const std::string& requestId, const std::string& url) const; + public void reportWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t statusCode, const facebook::react::Headers& headers) const; + public void reportWebSocketMessageReceived(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketMessageSent(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::Headers& headers) const; public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -10795,6 +10801,12 @@ class facebook::react::jsinspector_modern::JInspectorNetworkReporter : public jn public static void reportRequestStart(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref requestUrl, jni::alias_ref requestMethod, jni::alias_ref> requestHeaders, jni::alias_ref requestBody, jlong encodedDataLength); public static void reportResponseEnd(jni::alias_ref, jni::alias_ref requestId, jlong encodedDataLength); public static void reportResponseStart(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref requestUrl, jint responseStatus, jni::alias_ref> responseHeaders, jlong encodedDataLength); + public static void reportWebSocketClosed(jni::alias_ref, jni::alias_ref requestId); + public static void reportWebSocketCreated(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref url); + public static void reportWebSocketHandshakeResponseReceived(jni::alias_ref, jni::alias_ref requestId, jint statusCode, jni::alias_ref> headers); + public static void reportWebSocketMessageReceivedImpl(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref payloadData, jboolean isBinary); + public static void reportWebSocketMessageSentImpl(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref payloadData, jboolean isBinary); + public static void reportWebSocketWillSendHandshakeRequest(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref> headers); } class facebook::react::jsinspector_modern::LoadNetworkResourceDelegate { @@ -10819,6 +10831,12 @@ class facebook::react::jsinspector_modern::NetworkHandler { public void onRequestWillBeSent(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Request& request, const std::optional& redirectResponse); public void onRequestWillBeSentExtraInfo(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void onResponseReceived(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Response& response); + public void onWebSocketClosed(const std::string& requestId); + public void onWebSocketCreated(const std::string& requestId, const std::string& url); + public void onWebSocketFrameReceived(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketFrameSent(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t status, const facebook::react::jsinspector_modern::Headers& headers); + public void onWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void recordRequestInitiatorStack(const std::string& requestId, folly::dynamic stackTrace); public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -11538,6 +11556,56 @@ struct facebook::react::jsinspector_modern::cdp::network::ResponseReceivedParams public std::string type; } +struct facebook::react::jsinspector_modern::cdp::network::WebSocketClosedParams { + public double timestamp; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketCreatedParams { + public folly::dynamic initiator; + public folly::dynamic toDynamic() const; + public std::string requestId; + public std::string url; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrame { + public bool mask; + public folly::dynamic toDynamic() const; + public int opcode; + public std::string payloadData; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrameParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketFrame response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketHandshakeResponseReceivedParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketResponse response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketResponse { + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public static facebook::react::jsinspector_modern::cdp::network::WebSocketResponse fromInputParams(uint16_t status, const facebook::react::jsinspector_modern::cdp::network::Headers& headers); + public std::string statusText; + public uint16_t status; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketWillSendHandshakeRequestParams { + public double timestamp; + public double wallTime; + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINED_BY; constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINS; diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api index be2958293dad..5c35c3b0f39f 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api @@ -3790,6 +3790,12 @@ class facebook::react::NetworkReporter { public void reportRequestStart(const std::string& requestId, const facebook::react::RequestInfo& requestInfo, int encodedDataLength, const std::optional& redirectResponse); public void reportResponseEnd(const std::string& requestId, int encodedDataLength); public void reportResponseStart(const std::string& requestId, const facebook::react::ResponseInfo& responseInfo, int encodedDataLength); + public void reportWebSocketClosed(const std::string& requestId) const; + public void reportWebSocketCreated(const std::string& requestId, const std::string& url) const; + public void reportWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t statusCode, const facebook::react::Headers& headers) const; + public void reportWebSocketMessageReceived(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketMessageSent(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::Headers& headers) const; public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -10418,6 +10424,12 @@ class facebook::react::jsinspector_modern::JInspectorNetworkReporter : public jn public static void reportRequestStart(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref requestUrl, jni::alias_ref requestMethod, jni::alias_ref> requestHeaders, jni::alias_ref requestBody, jlong encodedDataLength); public static void reportResponseEnd(jni::alias_ref, jni::alias_ref requestId, jlong encodedDataLength); public static void reportResponseStart(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref requestUrl, jint responseStatus, jni::alias_ref> responseHeaders, jlong encodedDataLength); + public static void reportWebSocketClosed(jni::alias_ref, jni::alias_ref requestId); + public static void reportWebSocketCreated(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref url); + public static void reportWebSocketHandshakeResponseReceived(jni::alias_ref, jni::alias_ref requestId, jint statusCode, jni::alias_ref> headers); + public static void reportWebSocketMessageReceivedImpl(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref payloadData, jboolean isBinary); + public static void reportWebSocketMessageSentImpl(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref payloadData, jboolean isBinary); + public static void reportWebSocketWillSendHandshakeRequest(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref> headers); } class facebook::react::jsinspector_modern::LoadNetworkResourceDelegate { @@ -10442,6 +10454,12 @@ class facebook::react::jsinspector_modern::NetworkHandler { public void onRequestWillBeSent(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Request& request, const std::optional& redirectResponse); public void onRequestWillBeSentExtraInfo(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void onResponseReceived(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Response& response); + public void onWebSocketClosed(const std::string& requestId); + public void onWebSocketCreated(const std::string& requestId, const std::string& url); + public void onWebSocketFrameReceived(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketFrameSent(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t status, const facebook::react::jsinspector_modern::Headers& headers); + public void onWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void recordRequestInitiatorStack(const std::string& requestId, folly::dynamic stackTrace); public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -11161,6 +11179,56 @@ struct facebook::react::jsinspector_modern::cdp::network::ResponseReceivedParams public std::string type; } +struct facebook::react::jsinspector_modern::cdp::network::WebSocketClosedParams { + public double timestamp; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketCreatedParams { + public folly::dynamic initiator; + public folly::dynamic toDynamic() const; + public std::string requestId; + public std::string url; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrame { + public bool mask; + public folly::dynamic toDynamic() const; + public int opcode; + public std::string payloadData; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrameParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketFrame response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketHandshakeResponseReceivedParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketResponse response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketResponse { + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public static facebook::react::jsinspector_modern::cdp::network::WebSocketResponse fromInputParams(uint16_t status, const facebook::react::jsinspector_modern::cdp::network::Headers& headers); + public std::string statusText; + public uint16_t status; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketWillSendHandshakeRequestParams { + public double timestamp; + public double wallTime; + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINED_BY; constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINS; diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api index faf7b0b6db37..f0acc551bb18 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api @@ -3933,6 +3933,12 @@ class facebook::react::NetworkReporter { public void reportRequestStart(const std::string& requestId, const facebook::react::RequestInfo& requestInfo, int encodedDataLength, const std::optional& redirectResponse); public void reportResponseEnd(const std::string& requestId, int encodedDataLength); public void reportResponseStart(const std::string& requestId, const facebook::react::ResponseInfo& responseInfo, int encodedDataLength); + public void reportWebSocketClosed(const std::string& requestId) const; + public void reportWebSocketCreated(const std::string& requestId, const std::string& url) const; + public void reportWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t statusCode, const facebook::react::Headers& headers) const; + public void reportWebSocketMessageReceived(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketMessageSent(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::Headers& headers) const; public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -10648,6 +10654,12 @@ class facebook::react::jsinspector_modern::JInspectorNetworkReporter : public jn public static void reportRequestStart(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref requestUrl, jni::alias_ref requestMethod, jni::alias_ref> requestHeaders, jni::alias_ref requestBody, jlong encodedDataLength); public static void reportResponseEnd(jni::alias_ref, jni::alias_ref requestId, jlong encodedDataLength); public static void reportResponseStart(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref requestUrl, jint responseStatus, jni::alias_ref> responseHeaders, jlong encodedDataLength); + public static void reportWebSocketClosed(jni::alias_ref, jni::alias_ref requestId); + public static void reportWebSocketCreated(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref url); + public static void reportWebSocketHandshakeResponseReceived(jni::alias_ref, jni::alias_ref requestId, jint statusCode, jni::alias_ref> headers); + public static void reportWebSocketMessageReceivedImpl(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref payloadData, jboolean isBinary); + public static void reportWebSocketMessageSentImpl(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref payloadData, jboolean isBinary); + public static void reportWebSocketWillSendHandshakeRequest(jni::alias_ref, jni::alias_ref requestId, jni::alias_ref> headers); } class facebook::react::jsinspector_modern::LoadNetworkResourceDelegate { @@ -10672,6 +10684,12 @@ class facebook::react::jsinspector_modern::NetworkHandler { public void onRequestWillBeSent(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Request& request, const std::optional& redirectResponse); public void onRequestWillBeSentExtraInfo(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void onResponseReceived(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Response& response); + public void onWebSocketClosed(const std::string& requestId); + public void onWebSocketCreated(const std::string& requestId, const std::string& url); + public void onWebSocketFrameReceived(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketFrameSent(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t status, const facebook::react::jsinspector_modern::Headers& headers); + public void onWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void recordRequestInitiatorStack(const std::string& requestId, folly::dynamic stackTrace); public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -11391,6 +11409,56 @@ struct facebook::react::jsinspector_modern::cdp::network::ResponseReceivedParams public std::string type; } +struct facebook::react::jsinspector_modern::cdp::network::WebSocketClosedParams { + public double timestamp; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketCreatedParams { + public folly::dynamic initiator; + public folly::dynamic toDynamic() const; + public std::string requestId; + public std::string url; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrame { + public bool mask; + public folly::dynamic toDynamic() const; + public int opcode; + public std::string payloadData; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrameParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketFrame response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketHandshakeResponseReceivedParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketResponse response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketResponse { + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public static facebook::react::jsinspector_modern::cdp::network::WebSocketResponse fromInputParams(uint16_t status, const facebook::react::jsinspector_modern::cdp::network::Headers& headers); + public std::string statusText; + public uint16_t status; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketWillSendHandshakeRequestParams { + public double timestamp; + public double wallTime; + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINED_BY; constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINS; diff --git a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api index 2ec052291919..de18e199e774 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api @@ -1254,6 +1254,15 @@ interface RCTInspectorUtils : public NSObject { public virtual static CommonHostMetadata* getHostMetadata(); } +interface RCTInspectorWebSocketReporter : public NSObject { + public virtual static void reportHandshakeResponseReceived:httpMessage:(_Nullable NSString* requestId, _Nullable CFHTTPMessageRef httpMessage); + public virtual static void reportMessageReceived:message:(_Nullable NSString* requestId, _Nullable id message); + public virtual static void reportMessageSent:message:(_Nullable NSString* requestId, _Nullable id message); + public virtual static void reportWebSocketClosed:(_Nullable NSString* requestId); + public virtual static void reportWebSocketCreated:url:(_Nullable NSString* requestId, NSURL* url); + public virtual static void reportWillSendHandshakeRequest:request:(_Nullable NSString* requestId, NSURLRequest* request); +} + interface RCTInstance : public NSObject { public @property (strong, readonly) RCTSurfacePresenter* surfacePresenter; public virtual instancetype initWithDelegate:jsRuntimeFactory:bundleManager:turboModuleManagerDelegate:moduleRegistry:parentInspectorTarget:launchOptions:(id delegate, std::shared_ptr jsRuntimeFactory, RCTBundleManager* bundleManager, id turboModuleManagerDelegate, RCTModuleRegistry* moduleRegistry, facebook::react::jsinspector_modern::HostTarget* parentInspectorTarget, _Nullable NSDictionary* launchOptions); @@ -6111,6 +6120,12 @@ class facebook::react::NetworkReporter { public void reportRequestStart(const std::string& requestId, const facebook::react::RequestInfo& requestInfo, int encodedDataLength, const std::optional& redirectResponse); public void reportResponseEnd(const std::string& requestId, int encodedDataLength); public void reportResponseStart(const std::string& requestId, const facebook::react::ResponseInfo& responseInfo, int encodedDataLength); + public void reportWebSocketClosed(const std::string& requestId) const; + public void reportWebSocketCreated(const std::string& requestId, const std::string& url) const; + public void reportWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t statusCode, const facebook::react::Headers& headers) const; + public void reportWebSocketMessageReceived(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketMessageSent(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::Headers& headers) const; public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -12666,6 +12681,12 @@ class facebook::react::jsinspector_modern::NetworkHandler { public void onRequestWillBeSent(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Request& request, const std::optional& redirectResponse); public void onRequestWillBeSentExtraInfo(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void onResponseReceived(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Response& response); + public void onWebSocketClosed(const std::string& requestId); + public void onWebSocketCreated(const std::string& requestId, const std::string& url); + public void onWebSocketFrameReceived(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketFrameSent(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t status, const facebook::react::jsinspector_modern::Headers& headers); + public void onWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void recordRequestInitiatorStack(const std::string& requestId, folly::dynamic stackTrace); public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -13372,6 +13393,56 @@ struct facebook::react::jsinspector_modern::cdp::network::ResponseReceivedParams public std::string type; } +struct facebook::react::jsinspector_modern::cdp::network::WebSocketClosedParams { + public double timestamp; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketCreatedParams { + public folly::dynamic initiator; + public folly::dynamic toDynamic() const; + public std::string requestId; + public std::string url; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrame { + public bool mask; + public folly::dynamic toDynamic() const; + public int opcode; + public std::string payloadData; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrameParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketFrame response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketHandshakeResponseReceivedParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketResponse response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketResponse { + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public static facebook::react::jsinspector_modern::cdp::network::WebSocketResponse fromInputParams(uint16_t status, const facebook::react::jsinspector_modern::cdp::network::Headers& headers); + public std::string statusText; + public uint16_t status; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketWillSendHandshakeRequestParams { + public double timestamp; + public double wallTime; + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINED_BY; constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINS; diff --git a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api index 319fba1e0d52..3d2428683ae3 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api @@ -1252,6 +1252,15 @@ interface RCTInspectorUtils : public NSObject { public virtual static CommonHostMetadata* getHostMetadata(); } +interface RCTInspectorWebSocketReporter : public NSObject { + public virtual static void reportHandshakeResponseReceived:httpMessage:(_Nullable NSString* requestId, _Nullable CFHTTPMessageRef httpMessage); + public virtual static void reportMessageReceived:message:(_Nullable NSString* requestId, _Nullable id message); + public virtual static void reportMessageSent:message:(_Nullable NSString* requestId, _Nullable id message); + public virtual static void reportWebSocketClosed:(_Nullable NSString* requestId); + public virtual static void reportWebSocketCreated:url:(_Nullable NSString* requestId, NSURL* url); + public virtual static void reportWillSendHandshakeRequest:request:(_Nullable NSString* requestId, NSURLRequest* request); +} + interface RCTInstance : public NSObject { public @property (strong, readonly) RCTSurfacePresenter* surfacePresenter; public virtual instancetype initWithDelegate:jsRuntimeFactory:bundleManager:turboModuleManagerDelegate:moduleRegistry:parentInspectorTarget:launchOptions:(id delegate, std::shared_ptr jsRuntimeFactory, RCTBundleManager* bundleManager, id turboModuleManagerDelegate, RCTModuleRegistry* moduleRegistry, facebook::react::jsinspector_modern::HostTarget* parentInspectorTarget, _Nullable NSDictionary* launchOptions); @@ -5993,6 +6002,12 @@ class facebook::react::NetworkReporter { public void reportRequestStart(const std::string& requestId, const facebook::react::RequestInfo& requestInfo, int encodedDataLength, const std::optional& redirectResponse); public void reportResponseEnd(const std::string& requestId, int encodedDataLength); public void reportResponseStart(const std::string& requestId, const facebook::react::ResponseInfo& responseInfo, int encodedDataLength); + public void reportWebSocketClosed(const std::string& requestId) const; + public void reportWebSocketCreated(const std::string& requestId, const std::string& url) const; + public void reportWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t statusCode, const facebook::react::Headers& headers) const; + public void reportWebSocketMessageReceived(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketMessageSent(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::Headers& headers) const; public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -12351,6 +12366,12 @@ class facebook::react::jsinspector_modern::NetworkHandler { public void onRequestWillBeSent(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Request& request, const std::optional& redirectResponse); public void onRequestWillBeSentExtraInfo(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void onResponseReceived(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Response& response); + public void onWebSocketClosed(const std::string& requestId); + public void onWebSocketCreated(const std::string& requestId, const std::string& url); + public void onWebSocketFrameReceived(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketFrameSent(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t status, const facebook::react::jsinspector_modern::Headers& headers); + public void onWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void recordRequestInitiatorStack(const std::string& requestId, folly::dynamic stackTrace); public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -13057,6 +13078,56 @@ struct facebook::react::jsinspector_modern::cdp::network::ResponseReceivedParams public std::string type; } +struct facebook::react::jsinspector_modern::cdp::network::WebSocketClosedParams { + public double timestamp; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketCreatedParams { + public folly::dynamic initiator; + public folly::dynamic toDynamic() const; + public std::string requestId; + public std::string url; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrame { + public bool mask; + public folly::dynamic toDynamic() const; + public int opcode; + public std::string payloadData; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrameParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketFrame response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketHandshakeResponseReceivedParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketResponse response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketResponse { + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public static facebook::react::jsinspector_modern::cdp::network::WebSocketResponse fromInputParams(uint16_t status, const facebook::react::jsinspector_modern::cdp::network::Headers& headers); + public std::string statusText; + public uint16_t status; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketWillSendHandshakeRequestParams { + public double timestamp; + public double wallTime; + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINED_BY; constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINS; diff --git a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api index 3ec823148f22..a24cb26ab17a 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api @@ -1254,6 +1254,15 @@ interface RCTInspectorUtils : public NSObject { public virtual static CommonHostMetadata* getHostMetadata(); } +interface RCTInspectorWebSocketReporter : public NSObject { + public virtual static void reportHandshakeResponseReceived:httpMessage:(_Nullable NSString* requestId, _Nullable CFHTTPMessageRef httpMessage); + public virtual static void reportMessageReceived:message:(_Nullable NSString* requestId, _Nullable id message); + public virtual static void reportMessageSent:message:(_Nullable NSString* requestId, _Nullable id message); + public virtual static void reportWebSocketClosed:(_Nullable NSString* requestId); + public virtual static void reportWebSocketCreated:url:(_Nullable NSString* requestId, NSURL* url); + public virtual static void reportWillSendHandshakeRequest:request:(_Nullable NSString* requestId, NSURLRequest* request); +} + interface RCTInstance : public NSObject { public @property (strong, readonly) RCTSurfacePresenter* surfacePresenter; public virtual instancetype initWithDelegate:jsRuntimeFactory:bundleManager:turboModuleManagerDelegate:moduleRegistry:parentInspectorTarget:launchOptions:(id delegate, std::shared_ptr jsRuntimeFactory, RCTBundleManager* bundleManager, id turboModuleManagerDelegate, RCTModuleRegistry* moduleRegistry, facebook::react::jsinspector_modern::HostTarget* parentInspectorTarget, _Nullable NSDictionary* launchOptions); @@ -6108,6 +6117,12 @@ class facebook::react::NetworkReporter { public void reportRequestStart(const std::string& requestId, const facebook::react::RequestInfo& requestInfo, int encodedDataLength, const std::optional& redirectResponse); public void reportResponseEnd(const std::string& requestId, int encodedDataLength); public void reportResponseStart(const std::string& requestId, const facebook::react::ResponseInfo& responseInfo, int encodedDataLength); + public void reportWebSocketClosed(const std::string& requestId) const; + public void reportWebSocketCreated(const std::string& requestId, const std::string& url) const; + public void reportWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t statusCode, const facebook::react::Headers& headers) const; + public void reportWebSocketMessageReceived(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketMessageSent(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::Headers& headers) const; public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -12529,6 +12544,12 @@ class facebook::react::jsinspector_modern::NetworkHandler { public void onRequestWillBeSent(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Request& request, const std::optional& redirectResponse); public void onRequestWillBeSentExtraInfo(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void onResponseReceived(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Response& response); + public void onWebSocketClosed(const std::string& requestId); + public void onWebSocketCreated(const std::string& requestId, const std::string& url); + public void onWebSocketFrameReceived(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketFrameSent(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t status, const facebook::react::jsinspector_modern::Headers& headers); + public void onWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void recordRequestInitiatorStack(const std::string& requestId, folly::dynamic stackTrace); public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -13235,6 +13256,56 @@ struct facebook::react::jsinspector_modern::cdp::network::ResponseReceivedParams public std::string type; } +struct facebook::react::jsinspector_modern::cdp::network::WebSocketClosedParams { + public double timestamp; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketCreatedParams { + public folly::dynamic initiator; + public folly::dynamic toDynamic() const; + public std::string requestId; + public std::string url; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrame { + public bool mask; + public folly::dynamic toDynamic() const; + public int opcode; + public std::string payloadData; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrameParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketFrame response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketHandshakeResponseReceivedParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketResponse response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketResponse { + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public static facebook::react::jsinspector_modern::cdp::network::WebSocketResponse fromInputParams(uint16_t status, const facebook::react::jsinspector_modern::cdp::network::Headers& headers); + public std::string statusText; + public uint16_t status; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketWillSendHandshakeRequestParams { + public double timestamp; + public double wallTime; + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINED_BY; constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINS; diff --git a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api index 64301436eeda..56d20f0b002e 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api @@ -2573,6 +2573,12 @@ class facebook::react::NetworkReporter { public void reportRequestStart(const std::string& requestId, const facebook::react::RequestInfo& requestInfo, int encodedDataLength, const std::optional& redirectResponse); public void reportResponseEnd(const std::string& requestId, int encodedDataLength); public void reportResponseStart(const std::string& requestId, const facebook::react::ResponseInfo& responseInfo, int encodedDataLength); + public void reportWebSocketClosed(const std::string& requestId) const; + public void reportWebSocketCreated(const std::string& requestId, const std::string& url) const; + public void reportWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t statusCode, const facebook::react::Headers& headers) const; + public void reportWebSocketMessageReceived(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketMessageSent(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::Headers& headers) const; public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -7830,6 +7836,12 @@ class facebook::react::jsinspector_modern::NetworkHandler { public void onRequestWillBeSent(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Request& request, const std::optional& redirectResponse); public void onRequestWillBeSentExtraInfo(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void onResponseReceived(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Response& response); + public void onWebSocketClosed(const std::string& requestId); + public void onWebSocketCreated(const std::string& requestId, const std::string& url); + public void onWebSocketFrameReceived(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketFrameSent(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t status, const facebook::react::jsinspector_modern::Headers& headers); + public void onWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void recordRequestInitiatorStack(const std::string& requestId, folly::dynamic stackTrace); public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -8536,6 +8548,56 @@ struct facebook::react::jsinspector_modern::cdp::network::ResponseReceivedParams public std::string type; } +struct facebook::react::jsinspector_modern::cdp::network::WebSocketClosedParams { + public double timestamp; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketCreatedParams { + public folly::dynamic initiator; + public folly::dynamic toDynamic() const; + public std::string requestId; + public std::string url; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrame { + public bool mask; + public folly::dynamic toDynamic() const; + public int opcode; + public std::string payloadData; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrameParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketFrame response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketHandshakeResponseReceivedParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketResponse response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketResponse { + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public static facebook::react::jsinspector_modern::cdp::network::WebSocketResponse fromInputParams(uint16_t status, const facebook::react::jsinspector_modern::cdp::network::Headers& headers); + public std::string statusText; + public uint16_t status; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketWillSendHandshakeRequestParams { + public double timestamp; + public double wallTime; + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINED_BY; constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINS; diff --git a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api index eda51abd3696..d898e370e8fa 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api @@ -2467,6 +2467,12 @@ class facebook::react::NetworkReporter { public void reportRequestStart(const std::string& requestId, const facebook::react::RequestInfo& requestInfo, int encodedDataLength, const std::optional& redirectResponse); public void reportResponseEnd(const std::string& requestId, int encodedDataLength); public void reportResponseStart(const std::string& requestId, const facebook::react::ResponseInfo& responseInfo, int encodedDataLength); + public void reportWebSocketClosed(const std::string& requestId) const; + public void reportWebSocketCreated(const std::string& requestId, const std::string& url) const; + public void reportWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t statusCode, const facebook::react::Headers& headers) const; + public void reportWebSocketMessageReceived(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketMessageSent(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::Headers& headers) const; public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -7655,6 +7661,12 @@ class facebook::react::jsinspector_modern::NetworkHandler { public void onRequestWillBeSent(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Request& request, const std::optional& redirectResponse); public void onRequestWillBeSentExtraInfo(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void onResponseReceived(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Response& response); + public void onWebSocketClosed(const std::string& requestId); + public void onWebSocketCreated(const std::string& requestId, const std::string& url); + public void onWebSocketFrameReceived(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketFrameSent(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t status, const facebook::react::jsinspector_modern::Headers& headers); + public void onWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void recordRequestInitiatorStack(const std::string& requestId, folly::dynamic stackTrace); public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -8361,6 +8373,56 @@ struct facebook::react::jsinspector_modern::cdp::network::ResponseReceivedParams public std::string type; } +struct facebook::react::jsinspector_modern::cdp::network::WebSocketClosedParams { + public double timestamp; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketCreatedParams { + public folly::dynamic initiator; + public folly::dynamic toDynamic() const; + public std::string requestId; + public std::string url; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrame { + public bool mask; + public folly::dynamic toDynamic() const; + public int opcode; + public std::string payloadData; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrameParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketFrame response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketHandshakeResponseReceivedParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketResponse response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketResponse { + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public static facebook::react::jsinspector_modern::cdp::network::WebSocketResponse fromInputParams(uint16_t status, const facebook::react::jsinspector_modern::cdp::network::Headers& headers); + public std::string statusText; + public uint16_t status; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketWillSendHandshakeRequestParams { + public double timestamp; + public double wallTime; + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINED_BY; constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINS; diff --git a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api index 3c9c5e8ab249..d2eb086c932c 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api @@ -2570,6 +2570,12 @@ class facebook::react::NetworkReporter { public void reportRequestStart(const std::string& requestId, const facebook::react::RequestInfo& requestInfo, int encodedDataLength, const std::optional& redirectResponse); public void reportResponseEnd(const std::string& requestId, int encodedDataLength); public void reportResponseStart(const std::string& requestId, const facebook::react::ResponseInfo& responseInfo, int encodedDataLength); + public void reportWebSocketClosed(const std::string& requestId) const; + public void reportWebSocketCreated(const std::string& requestId, const std::string& url) const; + public void reportWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t statusCode, const facebook::react::Headers& headers) const; + public void reportWebSocketMessageReceived(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketMessageSent(const std::string& requestId, const std::string& payloadData, bool isBinary) const; + public void reportWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::Headers& headers) const; public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -7821,6 +7827,12 @@ class facebook::react::jsinspector_modern::NetworkHandler { public void onRequestWillBeSent(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Request& request, const std::optional& redirectResponse); public void onRequestWillBeSentExtraInfo(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void onResponseReceived(const std::string& requestId, const facebook::react::jsinspector_modern::cdp::network::Response& response); + public void onWebSocketClosed(const std::string& requestId); + public void onWebSocketCreated(const std::string& requestId, const std::string& url); + public void onWebSocketFrameReceived(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketFrameSent(const std::string& requestId, const std::string& payloadData, bool isBinary); + public void onWebSocketHandshakeResponseReceived(const std::string& requestId, uint16_t status, const facebook::react::jsinspector_modern::Headers& headers); + public void onWebSocketWillSendHandshakeRequest(const std::string& requestId, const facebook::react::jsinspector_modern::Headers& headers); public void recordRequestInitiatorStack(const std::string& requestId, folly::dynamic stackTrace); public void storeResponseBody(const std::string& requestId, std::string_view body, bool base64Encoded); } @@ -8527,6 +8539,56 @@ struct facebook::react::jsinspector_modern::cdp::network::ResponseReceivedParams public std::string type; } +struct facebook::react::jsinspector_modern::cdp::network::WebSocketClosedParams { + public double timestamp; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketCreatedParams { + public folly::dynamic initiator; + public folly::dynamic toDynamic() const; + public std::string requestId; + public std::string url; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrame { + public bool mask; + public folly::dynamic toDynamic() const; + public int opcode; + public std::string payloadData; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketFrameParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketFrame response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketHandshakeResponseReceivedParams { + public double timestamp; + public facebook::react::jsinspector_modern::cdp::network::WebSocketResponse response; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketResponse { + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public static facebook::react::jsinspector_modern::cdp::network::WebSocketResponse fromInputParams(uint16_t status, const facebook::react::jsinspector_modern::cdp::network::Headers& headers); + public std::string statusText; + public uint16_t status; +} + +struct facebook::react::jsinspector_modern::cdp::network::WebSocketWillSendHandshakeRequestParams { + public double timestamp; + public double wallTime; + public facebook::react::jsinspector_modern::cdp::network::Headers headers; + public folly::dynamic toDynamic() const; + public std::string requestId; +} + constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINED_BY; constexpr uint_fast16_t facebook::react::dom::DOCUMENT_POSITION_CONTAINS;