react-native 0.85.2 → 0.85.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/Libraries/Core/ReactNativeVersion.js +1 -1
  2. package/Libraries/Utilities/HMRClient.js +28 -1
  3. package/React/Base/RCTVersion.m +1 -1
  4. package/React/CoreModules/RCTJscSafeUrl+Internal.h +23 -0
  5. package/React/CoreModules/RCTJscSafeUrl.mm +38 -0
  6. package/React/CoreModules/RCTRedBox+Internal.h +42 -0
  7. package/React/CoreModules/RCTRedBox.mm +30 -454
  8. package/React/CoreModules/RCTRedBox2AnsiParser+Internal.h +22 -0
  9. package/React/CoreModules/RCTRedBox2AnsiParser.mm +55 -0
  10. package/React/CoreModules/RCTRedBox2Controller+Internal.h +34 -0
  11. package/React/CoreModules/RCTRedBox2Controller.mm +764 -0
  12. package/React/CoreModules/RCTRedBox2ErrorParser+Internal.h +46 -0
  13. package/React/CoreModules/RCTRedBox2ErrorParser.mm +57 -0
  14. package/React/CoreModules/RCTRedBoxController+Internal.h +31 -0
  15. package/React/CoreModules/RCTRedBoxController.mm +447 -0
  16. package/React/CoreModules/RCTRedBoxHMRClient+Internal.h +26 -0
  17. package/React/CoreModules/RCTRedBoxHMRClient.mm +125 -0
  18. package/React/CoreModules/React-CoreModules.podspec +1 -0
  19. package/React/DevSupport/RCTFrameTimingsObserver.h +24 -0
  20. package/React/DevSupport/RCTFrameTimingsObserver.mm +298 -0
  21. package/React/FBReactNativeSpec/FBReactNativeSpecJSI.h +40 -0
  22. package/ReactAndroid/gradle.properties +1 -1
  23. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorFlags.kt +4 -0
  24. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/FrameTimingSequence.kt +1 -1
  25. package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/FrameTimingsObserver.kt +127 -26
  26. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +31 -1
  27. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +51 -1
  28. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +11 -1
  29. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +11 -1
  30. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +56 -1
  31. package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +11 -1
  32. package/ReactAndroid/src/main/java/com/facebook/react/internal/tracing/PerformanceTracer.kt +39 -0
  33. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
  34. package/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt +50 -10
  35. package/ReactAndroid/src/main/jni/CMakeLists.txt +7 -0
  36. package/ReactAndroid/src/main/jni/react/devsupport/JInspectorFlags.cpp +22 -0
  37. package/ReactAndroid/src/main/jni/react/devsupport/JInspectorFlags.h +2 -0
  38. package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +71 -1
  39. package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +16 -1
  40. package/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.cpp +14 -0
  41. package/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.h +18 -4
  42. package/ReactCommon/React-Fabric.podspec +6 -0
  43. package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
  44. package/ReactCommon/jsinspector-modern/HostAgent.cpp +36 -0
  45. package/ReactCommon/jsinspector-modern/HostTarget.cpp +7 -1
  46. package/ReactCommon/jsinspector-modern/HostTarget.h +25 -0
  47. package/ReactCommon/jsinspector-modern/HostTargetTracing.cpp +1 -1
  48. package/ReactCommon/jsinspector-modern/HostTargetTracing.h +4 -4
  49. package/ReactCommon/jsinspector-modern/InspectorFlags.cpp +12 -0
  50. package/ReactCommon/jsinspector-modern/InspectorFlags.h +12 -0
  51. package/ReactCommon/jsinspector-modern/NetworkIOAgent.cpp +1 -1
  52. package/ReactCommon/jsinspector-modern/RuntimeAgent.cpp +19 -0
  53. package/ReactCommon/jsinspector-modern/RuntimeAgent.h +7 -0
  54. package/ReactCommon/jsinspector-modern/RuntimeTarget.cpp +33 -0
  55. package/ReactCommon/jsinspector-modern/RuntimeTarget.h +6 -0
  56. package/ReactCommon/jsinspector-modern/tests/HostTargetTest.cpp +12 -0
  57. package/ReactCommon/jsinspector-modern/tests/InspectorMocks.h +3 -2
  58. package/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp +1 -0
  59. package/ReactCommon/jsinspector-modern/tests/NetworkReporterTest.cpp +1 -1
  60. package/ReactCommon/jsinspector-modern/tests/TracingTest.cpp +1 -1
  61. package/ReactCommon/jsinspector-modern/tests/utils/InspectorFlagOverridesGuard.cpp +10 -0
  62. package/ReactCommon/jsinspector-modern/tests/utils/InspectorFlagOverridesGuard.h +3 -1
  63. package/ReactCommon/jsinspector-modern/tracing/CMakeLists.txt +1 -0
  64. package/ReactCommon/jsinspector-modern/tracing/FrameTimingSequence.h +7 -3
  65. package/ReactCommon/jsinspector-modern/tracing/HostTracingProfileSerializer.cpp +52 -29
  66. package/ReactCommon/jsinspector-modern/tracing/HostTracingProfileSerializer.h +6 -6
  67. package/ReactCommon/jsinspector-modern/tracing/PerformanceTracerSection.h +113 -0
  68. package/ReactCommon/jsinspector-modern/tracing/React-jsinspectortracing.podspec +1 -0
  69. package/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.cpp +12 -5
  70. package/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.h +3 -1
  71. package/ReactCommon/jsinspector-modern/tracing/TraceEventSerializer.cpp +42 -0
  72. package/ReactCommon/jsinspector-modern/tracing/TraceEventSerializer.h +7 -0
  73. package/ReactCommon/react/debug/CMakeLists.txt +2 -1
  74. package/ReactCommon/react/debug/React-debug.podspec +7 -1
  75. package/ReactCommon/react/debug/redbox/AnsiParser.cpp +139 -0
  76. package/ReactCommon/react/debug/redbox/AnsiParser.h +35 -0
  77. package/ReactCommon/react/debug/redbox/JscSafeUrl.cpp +179 -0
  78. package/ReactCommon/react/debug/redbox/JscSafeUrl.h +27 -0
  79. package/ReactCommon/react/debug/redbox/RedBoxErrorParser.cpp +171 -0
  80. package/ReactCommon/react/debug/redbox/RedBoxErrorParser.h +40 -0
  81. package/ReactCommon/react/debug/redbox/tests/AnsiParserTest.cpp +97 -0
  82. package/ReactCommon/react/debug/redbox/tests/JscSafeUrlTest.cpp +173 -0
  83. package/ReactCommon/react/debug/redbox/tests/RedBoxErrorParserTest.cpp +107 -0
  84. package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +21 -1
  85. package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +26 -1
  86. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +135 -45
  87. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +12 -2
  88. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +21 -1
  89. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +46 -1
  90. package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +6 -1
  91. package/ReactCommon/react/nativemodule/defaults/CMakeLists.txt +1 -0
  92. package/ReactCommon/react/nativemodule/defaults/DefaultTurboModules.cpp +7 -0
  93. package/ReactCommon/react/nativemodule/defaults/React-defaultsnativemodule.podspec +1 -0
  94. package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +26 -1
  95. package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +11 -1
  96. package/ReactCommon/react/nativemodule/mutationobserver/NativeMutationObserver.h +4 -0
  97. package/ReactCommon/react/nativemodule/mutationobserver/React-mutationobservernativemodule.podspec +66 -0
  98. package/ReactCommon/react/performance/timeline/PerformanceObserver.cpp +18 -6
  99. package/ReactCommon/react/performance/timeline/PerformanceObserver.h +2 -0
  100. package/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm +115 -0
  101. package/ReactCommon/{jsinspector-modern → react/utils}/Base64.h +2 -2
  102. package/package.json +11 -11
  103. package/scripts/cocoapods/utils.rb +1 -0
  104. package/scripts/react_native_pods.rb +1 -0
  105. package/scripts/replace-rncore-version.js +72 -15
  106. package/src/private/featureflags/ReactNativeFeatureFlags.js +26 -1
  107. package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +6 -1
  108. package/src/private/setup/setUpDefaultReactNativeEnvironment.js +6 -0
@@ -0,0 +1,125 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ #import "RCTRedBoxHMRClient+Internal.h"
9
+
10
+ #if RCT_DEV_MENU
11
+
12
+ @implementation RCTRedBoxHMRClient {
13
+ NSURL *_bundleURL;
14
+ NSURLSessionWebSocketTask *_webSocketTask;
15
+ NSURLSession *_session;
16
+ void (^_onFileChange)(void);
17
+ BOOL _stopped;
18
+ }
19
+
20
+ - (instancetype)initWithBundleURL:(NSURL *)bundleURL onFileChange:(void (^)(void))onFileChange
21
+ {
22
+ if (self = [super init]) {
23
+ _bundleURL = bundleURL;
24
+ _onFileChange = [onFileChange copy];
25
+ }
26
+ return self;
27
+ }
28
+
29
+ - (void)start
30
+ {
31
+ if (![_bundleURL.scheme hasPrefix:@"http"]) {
32
+ return;
33
+ }
34
+
35
+ NSURLComponents *components = [[NSURLComponents alloc] initWithURL:_bundleURL resolvingAgainstBaseURL:NO];
36
+ components.scheme = [_bundleURL.scheme isEqualToString:@"https"] ? @"wss" : @"ws";
37
+ components.path = @"/hot";
38
+ components.query = nil;
39
+ components.fragment = nil;
40
+ NSURL *wsURL = components.URL;
41
+ if (!wsURL) {
42
+ return;
43
+ }
44
+
45
+ _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
46
+ delegate:self
47
+ delegateQueue:nil];
48
+ _webSocketTask = [_session webSocketTaskWithURL:wsURL];
49
+ [_webSocketTask resume];
50
+ }
51
+
52
+ - (void)stop
53
+ {
54
+ _stopped = YES;
55
+ _onFileChange = nil;
56
+ [_webSocketTask cancel];
57
+ _webSocketTask = nil;
58
+ [_session invalidateAndCancel];
59
+ _session = nil;
60
+ }
61
+
62
+ - (void)URLSession:(__unused NSURLSession *)session
63
+ webSocketTask:(__unused NSURLSessionWebSocketTask *)webSocketTask
64
+ didOpenWithProtocol:(__unused NSString *)protocol
65
+ {
66
+ NSDictionary *registration = @{
67
+ @"type" : @"register-entrypoints",
68
+ @"entryPoints" : @[ _bundleURL.absoluteString ],
69
+ };
70
+ NSData *json = [NSJSONSerialization dataWithJSONObject:registration options:0 error:nil];
71
+ NSURLSessionWebSocketMessage *msg = [[NSURLSessionWebSocketMessage alloc]
72
+ initWithString:[[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding]];
73
+ [_webSocketTask sendMessage:msg
74
+ completionHandler:^(__unused NSError *error){
75
+ }];
76
+ [self _listenForNextMessage];
77
+ }
78
+
79
+ - (void)_listenForNextMessage
80
+ {
81
+ if (_stopped) {
82
+ return;
83
+ }
84
+ __weak __typeof(self) weakSelf = self;
85
+ [_webSocketTask receiveMessageWithCompletionHandler:^(NSURLSessionWebSocketMessage *message, NSError *error) {
86
+ if (error || !message) {
87
+ return;
88
+ }
89
+ [weakSelf _handleMessage:message];
90
+ [weakSelf _listenForNextMessage];
91
+ }];
92
+ }
93
+
94
+ - (void)_handleMessage:(NSURLSessionWebSocketMessage *)message
95
+ {
96
+ if (message.type != NSURLSessionWebSocketMessageTypeString || _stopped) {
97
+ return;
98
+ }
99
+ NSData *data = [message.string dataUsingEncoding:NSUTF8StringEncoding];
100
+ NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
101
+ if ([json[@"type"] isEqualToString:@"update-start"]) {
102
+ // Ignore the initial update that fires when the client first registers.
103
+ // Only react to subsequent file changes.
104
+ NSDictionary *body = json[@"body"];
105
+ if ([body isKindOfClass:[NSDictionary class]] && [body[@"isInitialUpdate"] boolValue]) {
106
+ return;
107
+ }
108
+ dispatch_async(dispatch_get_main_queue(), ^{
109
+ if (self->_onFileChange) {
110
+ self->_onFileChange();
111
+ }
112
+ });
113
+ }
114
+ }
115
+
116
+ - (void)URLSession:(__unused NSURLSession *)session
117
+ webSocketTask:(__unused NSURLSessionWebSocketTask *)task
118
+ didCloseWithCode:(__unused NSURLSessionWebSocketCloseCode)closeCode
119
+ reason:(__unused NSData *)reason
120
+ {
121
+ }
122
+
123
+ @end
124
+
125
+ #endif
@@ -51,6 +51,7 @@ Pod::Spec.new do |s|
51
51
  s.dependency "React-Core/CoreModulesHeaders", version
52
52
  s.dependency "React-RCTImage", version
53
53
  s.dependency "React-jsi", version
54
+ add_dependency(s, "React-featureflags")
54
55
  s.dependency 'React-RCTBlob'
55
56
  add_dependency(s, "React-debug")
56
57
  add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"])
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ #import <Foundation/Foundation.h>
9
+
10
+ #ifdef __cplusplus
11
+ #import <jsinspector-modern/tracing/FrameTimingSequence.h>
12
+
13
+ using RCTFrameTimingCallback = void (^)(facebook::react::jsinspector_modern::tracing::FrameTimingSequence);
14
+ #endif
15
+
16
+ @interface RCTFrameTimingsObserver : NSObject
17
+
18
+ #ifdef __cplusplus
19
+ - (instancetype)initWithScreenshotsEnabled:(BOOL)screenshotsEnabled callback:(RCTFrameTimingCallback)callback;
20
+ #endif
21
+ - (void)start;
22
+ - (void)stop;
23
+
24
+ @end
@@ -0,0 +1,298 @@
1
+ /*
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ #import "RCTFrameTimingsObserver.h"
9
+
10
+ #import <UIKit/UIKit.h>
11
+
12
+ #import <mach/thread_act.h>
13
+ #import <pthread.h>
14
+
15
+ #import <atomic>
16
+ #import <chrono>
17
+ #import <mutex>
18
+ #import <optional>
19
+ #import <vector>
20
+
21
+ #import <react/timing/primitives.h>
22
+
23
+ using namespace facebook::react;
24
+
25
+ static constexpr CGFloat kScreenshotScaleFactor = 1.0;
26
+ static constexpr CGFloat kScreenshotJPEGQuality = 0.8;
27
+
28
+ namespace {
29
+
30
+ // Stores a captured frame screenshot and its associated metadata, used for
31
+ // buffering frames during dynamic sampling.
32
+ struct FrameData {
33
+ UIImage *image;
34
+ uint64_t frameId;
35
+ jsinspector_modern::tracing::ThreadId threadId;
36
+ HighResTimeStamp beginTimestamp;
37
+ HighResTimeStamp endTimestamp;
38
+ };
39
+
40
+ } // namespace
41
+
42
+ @implementation RCTFrameTimingsObserver {
43
+ BOOL _screenshotsEnabled;
44
+ RCTFrameTimingCallback _callback;
45
+ CADisplayLink *_displayLink;
46
+ uint64_t _frameCounter;
47
+ // Serial queue for encoding work (single background thread). We limit to 1
48
+ // thread to minimize the performance impact of screenshot recording.
49
+ dispatch_queue_t _encodingQueue;
50
+ std::atomic<bool> _running;
51
+ uint64_t _lastScreenshotHash;
52
+
53
+ // Stores the most recently captured frame to opportunistically encode after
54
+ // the current frame. Replaced frames are emitted as timings without
55
+ // screenshots.
56
+ std::mutex _lastFrameMutex;
57
+ std::optional<FrameData> _lastFrameData;
58
+
59
+ std::atomic<bool> _encodingInProgress;
60
+ }
61
+
62
+ - (instancetype)initWithScreenshotsEnabled:(BOOL)screenshotsEnabled callback:(RCTFrameTimingCallback)callback
63
+ {
64
+ if (self = [super init]) {
65
+ _screenshotsEnabled = screenshotsEnabled;
66
+ _callback = [callback copy];
67
+ _frameCounter = 0;
68
+ _encodingQueue = dispatch_queue_create("com.facebook.react.frame-timings-observer", DISPATCH_QUEUE_SERIAL);
69
+ _running.store(false);
70
+ _lastScreenshotHash = 0;
71
+ _encodingInProgress.store(false);
72
+ }
73
+ return self;
74
+ }
75
+
76
+ - (void)start
77
+ {
78
+ _running.store(true, std::memory_order_relaxed);
79
+ _frameCounter = 0;
80
+ _lastScreenshotHash = 0;
81
+ _encodingInProgress.store(false, std::memory_order_relaxed);
82
+ {
83
+ std::lock_guard<std::mutex> lock(_lastFrameMutex);
84
+ _lastFrameData.reset();
85
+ }
86
+
87
+ // Emit initial frame event
88
+ auto now = HighResTimeStamp::now();
89
+ [self _emitFrameTimingWithBeginTimestamp:now endTimestamp:now];
90
+
91
+ _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_displayLinkTick:)];
92
+ [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
93
+ }
94
+
95
+ - (void)stop
96
+ {
97
+ _running.store(false, std::memory_order_relaxed);
98
+ [_displayLink invalidate];
99
+ _displayLink = nil;
100
+ {
101
+ std::lock_guard<std::mutex> lock(_lastFrameMutex);
102
+ _lastFrameData.reset();
103
+ }
104
+ }
105
+
106
+ - (void)_displayLinkTick:(CADisplayLink *)sender
107
+ {
108
+ // CADisplayLink.timestamp and targetTimestamp are in the same timebase as
109
+ // CACurrentMediaTime() / mach_absolute_time(), which on Apple platforms maps
110
+ // to CLOCK_UPTIME_RAW — the same clock backing std::chrono::steady_clock.
111
+ auto beginNanos = static_cast<int64_t>(sender.timestamp * 1e9);
112
+ auto endNanos = static_cast<int64_t>(sender.targetTimestamp * 1e9);
113
+
114
+ auto beginTimestamp = HighResTimeStamp::fromChronoSteadyClockTimePoint(
115
+ std::chrono::steady_clock::time_point(std::chrono::nanoseconds(beginNanos)));
116
+ auto endTimestamp = HighResTimeStamp::fromChronoSteadyClockTimePoint(
117
+ std::chrono::steady_clock::time_point(std::chrono::nanoseconds(endNanos)));
118
+
119
+ [self _emitFrameTimingWithBeginTimestamp:beginTimestamp endTimestamp:endTimestamp];
120
+ }
121
+
122
+ - (void)_emitFrameTimingWithBeginTimestamp:(HighResTimeStamp)beginTimestamp endTimestamp:(HighResTimeStamp)endTimestamp
123
+ {
124
+ uint64_t frameId = _frameCounter++;
125
+ auto threadId = static_cast<jsinspector_modern::tracing::ThreadId>(pthread_mach_thread_np(pthread_self()));
126
+
127
+ if (!_screenshotsEnabled) {
128
+ // Screenshots disabled - emit without screenshot
129
+ [self _emitFrameEventWithFrameId:frameId
130
+ threadId:threadId
131
+ beginTimestamp:beginTimestamp
132
+ endTimestamp:endTimestamp
133
+ screenshot:std::nullopt];
134
+ return;
135
+ }
136
+
137
+ UIImage *image = [self _captureScreenshot];
138
+ if (image == nil) {
139
+ // Failed to capture (e.g. no window, duplicate hash) - emit without screenshot
140
+ [self _emitFrameEventWithFrameId:frameId
141
+ threadId:threadId
142
+ beginTimestamp:beginTimestamp
143
+ endTimestamp:endTimestamp
144
+ screenshot:std::nullopt];
145
+ return;
146
+ }
147
+
148
+ FrameData frameData{image, frameId, threadId, beginTimestamp, endTimestamp};
149
+
150
+ bool expected = false;
151
+ if (_encodingInProgress.compare_exchange_strong(expected, true)) {
152
+ // Not encoding - encode this frame immediately
153
+ [self _encodeFrame:std::move(frameData)];
154
+ } else {
155
+ // Encoding thread busy - store current screenshot in buffer for tail-capture
156
+ std::optional<FrameData> oldFrame;
157
+ {
158
+ std::lock_guard<std::mutex> lock(_lastFrameMutex);
159
+ oldFrame = std::move(_lastFrameData);
160
+ _lastFrameData = std::move(frameData);
161
+ }
162
+ if (oldFrame.has_value()) {
163
+ // Skipped frame - emit event without screenshot
164
+ [self _emitFrameEventWithFrameId:oldFrame->frameId
165
+ threadId:oldFrame->threadId
166
+ beginTimestamp:oldFrame->beginTimestamp
167
+ endTimestamp:oldFrame->endTimestamp
168
+ screenshot:std::nullopt];
169
+ }
170
+ }
171
+ }
172
+
173
+ - (void)_emitFrameEventWithFrameId:(uint64_t)frameId
174
+ threadId:(jsinspector_modern::tracing::ThreadId)threadId
175
+ beginTimestamp:(HighResTimeStamp)beginTimestamp
176
+ endTimestamp:(HighResTimeStamp)endTimestamp
177
+ screenshot:(std::optional<std::vector<uint8_t>>)screenshot
178
+ {
179
+ dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
180
+ if (!self->_running.load(std::memory_order_relaxed)) {
181
+ return;
182
+ }
183
+ jsinspector_modern::tracing::FrameTimingSequence sequence{
184
+ frameId, threadId, beginTimestamp, endTimestamp, std::move(screenshot)};
185
+ self->_callback(std::move(sequence));
186
+ });
187
+ }
188
+
189
+ - (void)_encodeFrame:(FrameData)frameData
190
+ {
191
+ dispatch_async(_encodingQueue, ^{
192
+ if (!self->_running.load(std::memory_order_relaxed)) {
193
+ return;
194
+ }
195
+
196
+ auto screenshot = [self _encodeScreenshot:frameData.image];
197
+ [self _emitFrameEventWithFrameId:frameData.frameId
198
+ threadId:frameData.threadId
199
+ beginTimestamp:frameData.beginTimestamp
200
+ endTimestamp:frameData.endTimestamp
201
+ screenshot:std::move(screenshot)];
202
+
203
+ // Clear encoding flag early, allowing new frames to start fresh encoding
204
+ // sessions
205
+ self->_encodingInProgress.store(false, std::memory_order_release);
206
+
207
+ // Opportunistically encode tail frame (if present) without blocking new
208
+ // frames
209
+ std::optional<FrameData> tailFrame;
210
+ {
211
+ std::lock_guard<std::mutex> lock(self->_lastFrameMutex);
212
+ tailFrame = std::move(self->_lastFrameData);
213
+ self->_lastFrameData.reset();
214
+ }
215
+ if (tailFrame.has_value()) {
216
+ if (!self->_running.load(std::memory_order_relaxed)) {
217
+ return;
218
+ }
219
+ auto tailScreenshot = [self _encodeScreenshot:tailFrame->image];
220
+ [self _emitFrameEventWithFrameId:tailFrame->frameId
221
+ threadId:tailFrame->threadId
222
+ beginTimestamp:tailFrame->beginTimestamp
223
+ endTimestamp:tailFrame->endTimestamp
224
+ screenshot:std::move(tailScreenshot)];
225
+ }
226
+ });
227
+ }
228
+
229
+ // Captures a screenshot of the current window. Must be called on the main
230
+ // thread. Returns nil if capture fails or if the frame content is unchanged.
231
+ - (UIImage *)_captureScreenshot
232
+ {
233
+ UIWindow *keyWindow = [self _getKeyWindow];
234
+ if (keyWindow == nil) {
235
+ return nil;
236
+ }
237
+
238
+ UIView *rootView = keyWindow.rootViewController.view ?: keyWindow;
239
+ CGSize viewSize = rootView.bounds.size;
240
+ CGSize scaledSize = CGSizeMake(viewSize.width * kScreenshotScaleFactor, viewSize.height * kScreenshotScaleFactor);
241
+
242
+ UIGraphicsImageRendererFormat *format = [UIGraphicsImageRendererFormat defaultFormat];
243
+ format.scale = 1.0;
244
+ UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:scaledSize format:format];
245
+
246
+ UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext *context) {
247
+ [rootView drawViewHierarchyInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height) afterScreenUpdates:NO];
248
+ }];
249
+
250
+ // Skip duplicate frames via sampled FNV-1a pixel hash
251
+ CGImageRef cgImage = image.CGImage;
252
+ CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
253
+ uint64_t hash = 0xcbf29ce484222325ULL;
254
+ const uint8_t *ptr = CFDataGetBytePtr(pixelData);
255
+ CFIndex length = CFDataGetLength(pixelData);
256
+ // Use prime stride to prevent row alignment on power-of-2 pixel widths
257
+ for (CFIndex i = 0; i < length; i += 67) {
258
+ hash ^= ptr[i];
259
+ hash *= 0x100000001b3ULL;
260
+ }
261
+ CFRelease(pixelData);
262
+
263
+ if (hash == _lastScreenshotHash) {
264
+ return nil;
265
+ }
266
+ _lastScreenshotHash = hash;
267
+
268
+ return image;
269
+ }
270
+
271
+ - (std::optional<std::vector<uint8_t>>)_encodeScreenshot:(UIImage *)image
272
+ {
273
+ NSData *jpegData = UIImageJPEGRepresentation(image, kScreenshotJPEGQuality);
274
+ if (jpegData == nil) {
275
+ return std::nullopt;
276
+ }
277
+
278
+ const auto *bytes = static_cast<const uint8_t *>(jpegData.bytes);
279
+ return std::vector<uint8_t>(bytes, bytes + jpegData.length);
280
+ }
281
+
282
+ - (UIWindow *)_getKeyWindow
283
+ {
284
+ for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
285
+ if (scene.activationState == UISceneActivationStateForegroundActive &&
286
+ [scene isKindOfClass:[UIWindowScene class]]) {
287
+ auto windowScene = (UIWindowScene *)scene;
288
+ for (UIWindow *window = nullptr in windowScene.windows) {
289
+ if (window.isKeyWindow) {
290
+ return window;
291
+ }
292
+ }
293
+ }
294
+ }
295
+ return nil;
296
+ }
297
+
298
+ @end
@@ -265,6 +265,7 @@ protected:
265
265
  methodMap_["enableLayoutAnimationsOnIOS"] = MethodMetadata {.argCount = 0, .invoker = __enableLayoutAnimationsOnIOS};
266
266
  methodMap_["enableMainQueueCoordinatorOnIOS"] = MethodMetadata {.argCount = 0, .invoker = __enableMainQueueCoordinatorOnIOS};
267
267
  methodMap_["enableModuleArgumentNSNullConversionIOS"] = MethodMetadata {.argCount = 0, .invoker = __enableModuleArgumentNSNullConversionIOS};
268
+ methodMap_["enableMutationObserverByDefault"] = MethodMetadata {.argCount = 0, .invoker = __enableMutationObserverByDefault};
268
269
  methodMap_["enableNativeCSSParsing"] = MethodMetadata {.argCount = 0, .invoker = __enableNativeCSSParsing};
269
270
  methodMap_["enableNetworkEventReporting"] = MethodMetadata {.argCount = 0, .invoker = __enableNetworkEventReporting};
270
271
  methodMap_["enablePreparedTextLayout"] = MethodMetadata {.argCount = 0, .invoker = __enablePreparedTextLayout};
@@ -283,13 +284,17 @@ protected:
283
284
  methodMap_["fixTextClippingAndroid15useBoundsForWidth"] = MethodMetadata {.argCount = 0, .invoker = __fixTextClippingAndroid15useBoundsForWidth};
284
285
  methodMap_["fuseboxAssertSingleHostState"] = MethodMetadata {.argCount = 0, .invoker = __fuseboxAssertSingleHostState};
285
286
  methodMap_["fuseboxEnabledRelease"] = MethodMetadata {.argCount = 0, .invoker = __fuseboxEnabledRelease};
287
+ methodMap_["fuseboxFrameRecordingEnabled"] = MethodMetadata {.argCount = 0, .invoker = __fuseboxFrameRecordingEnabled};
286
288
  methodMap_["fuseboxNetworkInspectionEnabled"] = MethodMetadata {.argCount = 0, .invoker = __fuseboxNetworkInspectionEnabled};
289
+ methodMap_["fuseboxScreenshotCaptureEnabled"] = MethodMetadata {.argCount = 0, .invoker = __fuseboxScreenshotCaptureEnabled};
287
290
  methodMap_["hideOffscreenVirtualViewsOnIOS"] = MethodMetadata {.argCount = 0, .invoker = __hideOffscreenVirtualViewsOnIOS};
288
291
  methodMap_["overrideBySynchronousMountPropsAtMountingAndroid"] = MethodMetadata {.argCount = 0, .invoker = __overrideBySynchronousMountPropsAtMountingAndroid};
289
292
  methodMap_["perfIssuesEnabled"] = MethodMetadata {.argCount = 0, .invoker = __perfIssuesEnabled};
290
293
  methodMap_["perfMonitorV2Enabled"] = MethodMetadata {.argCount = 0, .invoker = __perfMonitorV2Enabled};
291
294
  methodMap_["preparedTextCacheSize"] = MethodMetadata {.argCount = 0, .invoker = __preparedTextCacheSize};
292
295
  methodMap_["preventShadowTreeCommitExhaustion"] = MethodMetadata {.argCount = 0, .invoker = __preventShadowTreeCommitExhaustion};
296
+ methodMap_["redBoxV2Android"] = MethodMetadata {.argCount = 0, .invoker = __redBoxV2Android};
297
+ methodMap_["redBoxV2IOS"] = MethodMetadata {.argCount = 0, .invoker = __redBoxV2IOS};
293
298
  methodMap_["shouldPressibilityUseW3CPointerEventsForHover"] = MethodMetadata {.argCount = 0, .invoker = __shouldPressibilityUseW3CPointerEventsForHover};
294
299
  methodMap_["shouldTriggerResponderTransferOnScrollAndroid"] = MethodMetadata {.argCount = 0, .invoker = __shouldTriggerResponderTransferOnScrollAndroid};
295
300
  methodMap_["skipActivityIdentityAssertionOnHostPause"] = MethodMetadata {.argCount = 0, .invoker = __skipActivityIdentityAssertionOnHostPause};
@@ -606,6 +611,13 @@ private:
606
611
  return bridging::callFromJs<bool>(rt, &T::enableModuleArgumentNSNullConversionIOS, static_cast<NativeReactNativeFeatureFlagsCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
607
612
  }
608
613
 
614
+ static jsi::Value __enableMutationObserverByDefault(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
615
+ static_assert(
616
+ bridging::getParameterCount(&T::enableMutationObserverByDefault) == 1,
617
+ "Expected enableMutationObserverByDefault(...) to have 1 parameters");
618
+ return bridging::callFromJs<bool>(rt, &T::enableMutationObserverByDefault, static_cast<NativeReactNativeFeatureFlagsCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
619
+ }
620
+
609
621
  static jsi::Value __enableNativeCSSParsing(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
610
622
  static_assert(
611
623
  bridging::getParameterCount(&T::enableNativeCSSParsing) == 1,
@@ -732,6 +744,13 @@ private:
732
744
  return bridging::callFromJs<bool>(rt, &T::fuseboxEnabledRelease, static_cast<NativeReactNativeFeatureFlagsCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
733
745
  }
734
746
 
747
+ static jsi::Value __fuseboxFrameRecordingEnabled(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
748
+ static_assert(
749
+ bridging::getParameterCount(&T::fuseboxFrameRecordingEnabled) == 1,
750
+ "Expected fuseboxFrameRecordingEnabled(...) to have 1 parameters");
751
+ return bridging::callFromJs<bool>(rt, &T::fuseboxFrameRecordingEnabled, static_cast<NativeReactNativeFeatureFlagsCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
752
+ }
753
+
735
754
  static jsi::Value __fuseboxNetworkInspectionEnabled(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
736
755
  static_assert(
737
756
  bridging::getParameterCount(&T::fuseboxNetworkInspectionEnabled) == 1,
@@ -739,6 +758,13 @@ private:
739
758
  return bridging::callFromJs<bool>(rt, &T::fuseboxNetworkInspectionEnabled, static_cast<NativeReactNativeFeatureFlagsCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
740
759
  }
741
760
 
761
+ static jsi::Value __fuseboxScreenshotCaptureEnabled(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
762
+ static_assert(
763
+ bridging::getParameterCount(&T::fuseboxScreenshotCaptureEnabled) == 1,
764
+ "Expected fuseboxScreenshotCaptureEnabled(...) to have 1 parameters");
765
+ return bridging::callFromJs<bool>(rt, &T::fuseboxScreenshotCaptureEnabled, static_cast<NativeReactNativeFeatureFlagsCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
766
+ }
767
+
742
768
  static jsi::Value __hideOffscreenVirtualViewsOnIOS(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
743
769
  static_assert(
744
770
  bridging::getParameterCount(&T::hideOffscreenVirtualViewsOnIOS) == 1,
@@ -781,6 +807,20 @@ private:
781
807
  return bridging::callFromJs<bool>(rt, &T::preventShadowTreeCommitExhaustion, static_cast<NativeReactNativeFeatureFlagsCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
782
808
  }
783
809
 
810
+ static jsi::Value __redBoxV2Android(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
811
+ static_assert(
812
+ bridging::getParameterCount(&T::redBoxV2Android) == 1,
813
+ "Expected redBoxV2Android(...) to have 1 parameters");
814
+ return bridging::callFromJs<bool>(rt, &T::redBoxV2Android, static_cast<NativeReactNativeFeatureFlagsCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
815
+ }
816
+
817
+ static jsi::Value __redBoxV2IOS(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
818
+ static_assert(
819
+ bridging::getParameterCount(&T::redBoxV2IOS) == 1,
820
+ "Expected redBoxV2IOS(...) to have 1 parameters");
821
+ return bridging::callFromJs<bool>(rt, &T::redBoxV2IOS, static_cast<NativeReactNativeFeatureFlagsCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
822
+ }
823
+
784
824
  static jsi::Value __shouldPressibilityUseW3CPointerEventsForHover(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
785
825
  static_assert(
786
826
  bridging::getParameterCount(&T::shouldPressibilityUseW3CPointerEventsForHover) == 1,
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.85.2
1
+ VERSION_NAME=0.85.3
2
2
  react.internal.publishingGroup=com.facebook.react
3
3
  react.internal.hermesPublishingGroup=com.facebook.hermes
4
4
 
@@ -17,7 +17,11 @@ internal object InspectorFlags {
17
17
  SoLoader.loadLibrary("react_devsupportjni")
18
18
  }
19
19
 
20
+ @DoNotStrip @JvmStatic external fun getScreenshotCaptureEnabled(): Boolean
21
+
20
22
  @DoNotStrip @JvmStatic external fun getFuseboxEnabled(): Boolean
21
23
 
22
24
  @DoNotStrip @JvmStatic external fun getIsProfilingBuild(): Boolean
25
+
26
+ @DoNotStrip @JvmStatic external fun getFrameRecordingEnabled(): Boolean
23
27
  }
@@ -12,5 +12,5 @@ internal data class FrameTimingSequence(
12
12
  val threadId: Int,
13
13
  val beginTimestamp: Long,
14
14
  val endTimestamp: Long,
15
- val screenshot: String? = null,
15
+ val screenshot: ByteArray? = null,
16
16
  )