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.
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Libraries/Utilities/HMRClient.js +28 -1
- package/React/Base/RCTVersion.m +1 -1
- package/React/CoreModules/RCTJscSafeUrl+Internal.h +23 -0
- package/React/CoreModules/RCTJscSafeUrl.mm +38 -0
- package/React/CoreModules/RCTRedBox+Internal.h +42 -0
- package/React/CoreModules/RCTRedBox.mm +30 -454
- package/React/CoreModules/RCTRedBox2AnsiParser+Internal.h +22 -0
- package/React/CoreModules/RCTRedBox2AnsiParser.mm +55 -0
- package/React/CoreModules/RCTRedBox2Controller+Internal.h +34 -0
- package/React/CoreModules/RCTRedBox2Controller.mm +764 -0
- package/React/CoreModules/RCTRedBox2ErrorParser+Internal.h +46 -0
- package/React/CoreModules/RCTRedBox2ErrorParser.mm +57 -0
- package/React/CoreModules/RCTRedBoxController+Internal.h +31 -0
- package/React/CoreModules/RCTRedBoxController.mm +447 -0
- package/React/CoreModules/RCTRedBoxHMRClient+Internal.h +26 -0
- package/React/CoreModules/RCTRedBoxHMRClient.mm +125 -0
- package/React/CoreModules/React-CoreModules.podspec +1 -0
- package/React/DevSupport/RCTFrameTimingsObserver.h +24 -0
- package/React/DevSupport/RCTFrameTimingsObserver.mm +298 -0
- package/React/FBReactNativeSpec/FBReactNativeSpecJSI.h +40 -0
- package/ReactAndroid/gradle.properties +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/InspectorFlags.kt +4 -0
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/FrameTimingSequence.kt +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/FrameTimingsObserver.kt +127 -26
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +31 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +51 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +11 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +11 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +56 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +11 -1
- package/ReactAndroid/src/main/java/com/facebook/react/internal/tracing/PerformanceTracer.kt +39 -0
- package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
- package/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.kt +50 -10
- package/ReactAndroid/src/main/jni/CMakeLists.txt +7 -0
- package/ReactAndroid/src/main/jni/react/devsupport/JInspectorFlags.cpp +22 -0
- package/ReactAndroid/src/main/jni/react/devsupport/JInspectorFlags.h +2 -0
- package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +71 -1
- package/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +16 -1
- package/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.cpp +14 -0
- package/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.h +18 -4
- package/ReactCommon/React-Fabric.podspec +6 -0
- package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
- package/ReactCommon/jsinspector-modern/HostAgent.cpp +36 -0
- package/ReactCommon/jsinspector-modern/HostTarget.cpp +7 -1
- package/ReactCommon/jsinspector-modern/HostTarget.h +25 -0
- package/ReactCommon/jsinspector-modern/HostTargetTracing.cpp +1 -1
- package/ReactCommon/jsinspector-modern/HostTargetTracing.h +4 -4
- package/ReactCommon/jsinspector-modern/InspectorFlags.cpp +12 -0
- package/ReactCommon/jsinspector-modern/InspectorFlags.h +12 -0
- package/ReactCommon/jsinspector-modern/NetworkIOAgent.cpp +1 -1
- package/ReactCommon/jsinspector-modern/RuntimeAgent.cpp +19 -0
- package/ReactCommon/jsinspector-modern/RuntimeAgent.h +7 -0
- package/ReactCommon/jsinspector-modern/RuntimeTarget.cpp +33 -0
- package/ReactCommon/jsinspector-modern/RuntimeTarget.h +6 -0
- package/ReactCommon/jsinspector-modern/tests/HostTargetTest.cpp +12 -0
- package/ReactCommon/jsinspector-modern/tests/InspectorMocks.h +3 -2
- package/ReactCommon/jsinspector-modern/tests/JsiIntegrationTest.cpp +1 -0
- package/ReactCommon/jsinspector-modern/tests/NetworkReporterTest.cpp +1 -1
- package/ReactCommon/jsinspector-modern/tests/TracingTest.cpp +1 -1
- package/ReactCommon/jsinspector-modern/tests/utils/InspectorFlagOverridesGuard.cpp +10 -0
- package/ReactCommon/jsinspector-modern/tests/utils/InspectorFlagOverridesGuard.h +3 -1
- package/ReactCommon/jsinspector-modern/tracing/CMakeLists.txt +1 -0
- package/ReactCommon/jsinspector-modern/tracing/FrameTimingSequence.h +7 -3
- package/ReactCommon/jsinspector-modern/tracing/HostTracingProfileSerializer.cpp +52 -29
- package/ReactCommon/jsinspector-modern/tracing/HostTracingProfileSerializer.h +6 -6
- package/ReactCommon/jsinspector-modern/tracing/PerformanceTracerSection.h +113 -0
- package/ReactCommon/jsinspector-modern/tracing/React-jsinspectortracing.podspec +1 -0
- package/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.cpp +12 -5
- package/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.h +3 -1
- package/ReactCommon/jsinspector-modern/tracing/TraceEventSerializer.cpp +42 -0
- package/ReactCommon/jsinspector-modern/tracing/TraceEventSerializer.h +7 -0
- package/ReactCommon/react/debug/CMakeLists.txt +2 -1
- package/ReactCommon/react/debug/React-debug.podspec +7 -1
- package/ReactCommon/react/debug/redbox/AnsiParser.cpp +139 -0
- package/ReactCommon/react/debug/redbox/AnsiParser.h +35 -0
- package/ReactCommon/react/debug/redbox/JscSafeUrl.cpp +179 -0
- package/ReactCommon/react/debug/redbox/JscSafeUrl.h +27 -0
- package/ReactCommon/react/debug/redbox/RedBoxErrorParser.cpp +171 -0
- package/ReactCommon/react/debug/redbox/RedBoxErrorParser.h +40 -0
- package/ReactCommon/react/debug/redbox/tests/AnsiParserTest.cpp +97 -0
- package/ReactCommon/react/debug/redbox/tests/JscSafeUrlTest.cpp +173 -0
- package/ReactCommon/react/debug/redbox/tests/RedBoxErrorParserTest.cpp +107 -0
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +21 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +26 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +135 -45
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +12 -2
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +21 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +46 -1
- package/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +6 -1
- package/ReactCommon/react/nativemodule/defaults/CMakeLists.txt +1 -0
- package/ReactCommon/react/nativemodule/defaults/DefaultTurboModules.cpp +7 -0
- package/ReactCommon/react/nativemodule/defaults/React-defaultsnativemodule.podspec +1 -0
- package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +26 -1
- package/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +11 -1
- package/ReactCommon/react/nativemodule/mutationobserver/NativeMutationObserver.h +4 -0
- package/ReactCommon/react/nativemodule/mutationobserver/React-mutationobservernativemodule.podspec +66 -0
- package/ReactCommon/react/performance/timeline/PerformanceObserver.cpp +18 -6
- package/ReactCommon/react/performance/timeline/PerformanceObserver.h +2 -0
- package/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm +115 -0
- package/ReactCommon/{jsinspector-modern → react/utils}/Base64.h +2 -2
- package/package.json +11 -11
- package/scripts/cocoapods/utils.rb +1 -0
- package/scripts/react_native_pods.rb +1 -0
- package/scripts/replace-rncore-version.js +72 -15
- package/src/private/featureflags/ReactNativeFeatureFlags.js +26 -1
- package/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +6 -1
- 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,
|
|
@@ -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
|
}
|