From 74f84f7167b9aa3103307b2caa81585d3551dff1 Mon Sep 17 00:00:00 2001 From: Alex Hunt Date: Fri, 2 May 2025 06:31:45 -0700 Subject: [PATCH 1/2] Fix missing podspec dependency in RCTNetwork (#51071) Summary: Follows D73995864. Changelog: [Internal] Reviewed By: hoxyq Differential Revision: D74062263 --- .../Network/React-RCTNetwork.podspec | 1 + packages/rn-tester/Podfile.lock | 146 +++++++++--------- 2 files changed, 74 insertions(+), 73 deletions(-) diff --git a/packages/react-native/Libraries/Network/React-RCTNetwork.podspec b/packages/react-native/Libraries/Network/React-RCTNetwork.podspec index 5560831aa5ea..feda0b84f1bc 100644 --- a/packages/react-native/Libraries/Network/React-RCTNetwork.podspec +++ b/packages/react-native/Libraries/Network/React-RCTNetwork.podspec @@ -46,6 +46,7 @@ Pod::Spec.new do |s| add_dependency(s, "React-RCTFBReactNativeSpec") add_dependency(s, "ReactCommon", :subspec => "turbomodule/core", :additional_framework_paths => ["react/nativemodule/core"]) + add_dependency(s, "React-featureflags") add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp') add_dependency(s, "React-jsinspectornetwork", :framework_name => 'jsinspector_modernnetwork') add_dependency(s, "React-NativeModulesApple", :additional_framework_paths => ["build/generated/ios"]) diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 04dc96e2e44d..9bc4825c0e1f 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -1807,7 +1807,6 @@ PODS: - glog - RCT-Folly - RCT-Folly/Fabric - - React-cxxreact - React-featureflags - React-jsinspectortracing - React-perflogger @@ -1968,6 +1967,7 @@ PODS: - RCT-Folly/Fabric - RCTTypeSafety - React-Core/RCTNetworkHeaders + - React-featureflags - React-jsi - React-jsinspectorcdp - React-jsinspectornetwork @@ -2562,88 +2562,88 @@ EXTERNAL SOURCES: :path: "../react-native/ReactCommon/yoga" SPEC CHECKSUMS: - boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 + boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 + DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb fast_float: b32c788ed9c6a8c584d114d0047beda9664e7cc6 - FBLazyVector: 05133a4d3886934758fc5a9b149e1881dc333ab7 - fmt: 01b82d4ca6470831d1cc0852a1af644be019e8f6 - glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a - hermes-engine: 1d4ebea15d13af28be0dd602bf09c8a27bdf5163 + FBLazyVector: d3c2dd739a63c1a124e775df075dc7c517a719cb + fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd + glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 + hermes-engine: 5a9adf9081befbac6b81bc0c81522430a7eb7da1 MyNativeView: 649cc955b283053e5832e8d45c2a20597daa94cd NativeCxxModuleExample: d9bac8e847a427f0a40b0c9fb066ab102b3aefd0 OCMock: 589f2c84dacb1f5aaf6e4cec1f292551fe748e74 OSSLibraryExample: 9264008f6ba6a068cb2db3765ffdf519c9c0f6a4 RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 RCTDeprecation: 3808e36294137f9ee5668f4df2e73dc079cd1dcf - RCTRequired: c482db71773e4c26f5590519ee0146ff97969018 - RCTTypeSafety: 9dbb69d76b01b3eb6cfff10751b120eb4719ce90 - React: 879e9950c4e457a0b9be526798a2203056afed3f - React-callinvoker: e88f762ae931d38f3cfc01264c40c1b8f96bfec5 - React-Core: b2c9b5ddac40521fcb16f20207e0d90e699f0c4a - React-CoreModules: 959fe435b21b13b20dd7ef6704c842e5df0dfa5b - React-cxxreact: 70dba7dd6868ac16f35972ca1530adaae213847a - React-debug: 19bd15a4a86f82d088b818dcbde0f0d0cb390295 - React-defaultsnativemodule: a10c0e1ce88b78d8592f16c36baa0b3606688577 - React-domnativemodule: b3dadb6d8e294af4ac3753ef149aae65460477ac - React-Fabric: 3d970b3cb38e9caff5f23e5bd9084524682df818 - React-FabricComponents: 573612e5e9860abc659022d5cc7e1ea8f3353097 - React-FabricImage: 2d08fc8b5cd765296d1c94b8ecb309195cd92359 - React-featureflags: f1aa11419d898f0e37fcb69aafbb998ad29f401e - React-featureflagsnativemodule: ade1ba31d23165a496b79d796ae90b6e09456c7b - React-graphics: ef012d8da4de9f8918245695d94632a3574e8ea5 - React-hermes: 1c67e1a0fc49390e87cdbf94c593ea067d32ce7b - React-idlecallbacksnativemodule: 819e49f01e4ec1c734b79963c6e199456445fed0 - React-ImageManager: 491286f8492d005ddd9c9d26fc6148f718754eb0 - React-jserrorhandler: e127c151810a44eef37e217ce3ea8dc53420aa98 - React-jsi: 59b237396968e53b088eb7bca9c95234ea1450f5 - React-jsiexecutor: a4c1de07d91be19a293ec81d291f9d4ba345cf23 - React-jsinspector: a3a3c0734d8653dfaafd7935350f5f43ff59e60e - React-jsinspectorcdp: f9f23167d4badcd29964aa33d1f60e649bde6f21 - React-jsinspectornetwork: 87f3a2fe7b04888d0941c1c5153607ed4d76c729 - React-jsinspectortracing: a1746d20ea583ddd63235d152016fa2fabbedd74 - React-jsitooling: 13a6ebc6c03a50ca547f62a91035e2ecce9faa4d - React-jsitracing: 2cb26de27216895357b7210c5a3b667d40a2a528 - React-logger: 69cbfaada2b1c8afe1bc9f07321887f8b77e1cb0 - React-Mapbuffer: 0dc40f635b0bbe2bb5233157a5ca55c55f975bee - React-microtasksnativemodule: 1c80957ecd552c51a72ae1fdd7e2c5d48d6e2198 - React-NativeModulesApple: d1dc1e75a9a14f03dc2f92670c9a47b3700b909f - React-oscompat: 3901fd714c34a966ec7bae2a9174c0cbe1ac1cfb - React-perflogger: b0932efaaba483aa13641b9353617c26ea2c124d - React-performancetimeline: 63e45db09d75e583b1936650cceb5b046ee58523 - React-RCTActionSheet: de1d8b7aeb3ae93a251a63f8e8038cc93b9d6ca7 - React-RCTAnimation: 3e795e73197b73e03f5d0f304bba1064778cc2ed - React-RCTAppDelegate: 08268b740164533a5aae8f7c7c92b4699e7c4863 - React-RCTBlob: 010dc14969ca00e517f4787f8cd753a0fd4ae500 - React-RCTFabric: 714bceb61d1f756ed51e1487ba420e149bb92c00 - React-RCTFBReactNativeSpec: 63ae58457f1fac05840b1b5cc647e347d12059cd - React-RCTImage: e111fae23fcd63032a44359e2706557dc36d5179 - React-RCTLinking: 4cb3934aa86071ba4944f05d068b62e407aa30d6 - React-RCTNetwork: 675ad1254b2acf6c8ec8c458ca8b272852bbeac1 - React-RCTPushNotification: 69e20f2132e9c6ef4b1f3d259931846d6f557694 - React-RCTRuntime: 7100e6ea948233548900ca58a3165c38e7624f7b - React-RCTSettings: bde52ef71ca7bdd23b4811e3abc8d4b0ecec8856 - React-RCTTest: 9671edc72a8cc2d5ed6bcbdd7203ab5d5b992d06 - React-RCTText: 564f5a51a1dd2916d4e2711acb10d238ff4c605d - React-RCTVibration: daf99169033e893ee17aa8690593cac6626fab81 - React-rendererconsistency: afa3b7b2f1e5c890e782de21c7f99f190953a2c5 - React-renderercss: ebafade0a3898cd029adbb08fea725c56bf6231c - React-rendererdebug: 6953132835c928321f2cefb866dd0b66b5cdd1b1 - React-rncore: 06ab92ea476589c10e8f0f57a54617312a2e6c5e - React-RuntimeApple: ea3a775f149d886455320498fd25bb207b827cde - React-RuntimeCore: 562bffbcfc958d9fa5c91f50ed642713165c51fa - React-runtimeexecutor: 09cce6fc74b13861ec60fdfd91066a393812fde5 - React-RuntimeHermes: 8609744a966766484b0615955b783d3419157c6b - React-runtimescheduler: 2251e2735d02b45f01b1116e8e5bfe0c9bc755df - React-timing: 3603ea1f09bcf7dd97cf62604218f7b471b90942 - React-utils: e2117130776cd778c123a31fcc0c7270b4f87f66 - ReactAppDependencyProvider: 7fa8930678f4d59ea5c58e22ba8bf73039b257fb + RCTRequired: a00614e2da5344c2cda3d287050b6cee00e21dc6 + RCTTypeSafety: 459a16418c6b413060d35434ba3e83f5b0bd2651 + React: 170a01a19ba2525ab7f11243e2df6b19bf268093 + React-callinvoker: f08f425e4043cd1998a158b6e39a6aed1fd1d718 + React-Core: 798334ba9a8a5a97e84ab56b5565d6ed5ac09712 + React-CoreModules: 25e5ff9b077e8a3780672a9eae5197e54ce367e1 + React-cxxreact: 8a5de0fe0933c56fdf4c3548902d9525ea73322b + React-debug: 195df38487d3f48a7af04deddeb4a5c6d4440416 + React-defaultsnativemodule: 47959b704240e39c6b401d3e3fe1da114b271ac6 + React-domnativemodule: b6dd0c3be3e4514052f68d0b5a94a8d06e0f14fd + React-Fabric: 06175f348d6df210e7545ee513e38fe1709a6d82 + React-FabricComponents: ace73c8cc991f299d9b3aa9556cd5b6e86a8b8e6 + React-FabricImage: ff9dc6afafc92e0da3f3d33d96ec40317bb8ec10 + React-featureflags: 595651ea13c63a9f77f06d9a1973b665b4a28b7e + React-featureflagsnativemodule: f5f69151bc4c2945003fc502ebaecee7fda02c42 + React-graphics: 38cc9ba3336bd50960e6052648374f3591abc7a6 + React-hermes: 34666bbd8d6b585e290f000d4d31c2182ece8940 + React-idlecallbacksnativemodule: b66d99ffb2ff765e1bd952b6bc6bf4ba3d5204d3 + React-ImageManager: a6833445e17879933378b7c0ba45ee42115c14bc + React-jserrorhandler: bec134a192c50338193544404d45df24fb8a19ca + React-jsi: 4ad77650fb0ca4229569eb2532db7a87e3d12662 + React-jsiexecutor: 569425f7cd2c3e005a17e5211843e541c11d6916 + React-jsinspector: 885e8180e898f07e4d7df29e2681a89e69d736d3 + React-jsinspectorcdp: 5fb266e5f23d3a2819ba848e9d4d0b6b00f95934 + React-jsinspectornetwork: 207422b56a7918e83c94c207570849f83ab9052a + React-jsinspectortracing: 80e9418ac67630c76f15ef06534087037a822330 + React-jsitooling: 0c28fbc10441f8b63f4c6bf443cb36416500ce2b + React-jsitracing: ce443686f52538d1033ce7db1e7d643e866262f0 + React-logger: 116c3ae5a9906671d157aa00882a5ee75a5a7ebc + React-Mapbuffer: fc937cfa41140d7724c559c3d16c50dd725361c8 + React-microtasksnativemodule: dd4dfd6306d8b42f5dab9ecb3bf04124e979a3da + React-NativeModulesApple: d3aec3f4d3cb80507777e1feeba3bdc70f9504a0 + React-oscompat: 7133e0e945cda067ae36b22502df663d73002864 + React-perflogger: ada3cdf3dfc8b7cd1fabe3c91b672e23981611ab + React-performancetimeline: e7d5849d89ee39557dcd56dfb6e7b0d49003d925 + React-RCTActionSheet: 1bf8cc8086ad1c15da3407dfb7bc9dd94dc7595d + React-RCTAnimation: 263593e66c89bf810604b1ace15dfa382a1ca2df + React-RCTAppDelegate: 3d35d7226338009b22d1cf9621eaa827acb8fd1d + React-RCTBlob: 7b76230c53fe87d305eeeb250b0aae031bb6cbae + React-RCTFabric: a43fc393b6e505fd60a7fea43edbdcf609f33bf0 + React-RCTFBReactNativeSpec: 503491a0584dc29f03ef9f8ed366794604cd59ef + React-RCTImage: de404b6b0ebe53976a97e3a0dee819c83e12977b + React-RCTLinking: 06742cfad41c506091403a414370743a4ed75af3 + React-RCTNetwork: b4577eec0092c16d8996e415e4cac7a372d6d362 + React-RCTPushNotification: ea11178d499696516e0ff9ae335edbe99b06f94b + React-RCTRuntime: 07b41aed797e8d950ada851c6363ecf931335663 + React-RCTSettings: d3c2dd305ec81f7faf42762ec598d57f07fd43be + React-RCTTest: 2db46eda60bc2228cb67622a580e8e86b00088d9 + React-RCTText: e416825b80c530647040ef91d23ffd35ccc87981 + React-RCTVibration: 1837a27fc16eeffc9509779c3334fde54c012bcc + React-rendererconsistency: 777c894edc43dde01499189917ac54ee76ae6a6a + React-renderercss: a9cb6ba7f49a80dc4b4f7008bae1590d12f27049 + React-rendererdebug: fea8bde927403a198742b2d940a5f1cd8230c0b4 + React-rncore: 4a81ce7b8e47448973a6b29c765b07e01715921e + React-RuntimeApple: 97755a0b9f6adff1e1911ef4894cb0c5a9e40c77 + React-RuntimeCore: 6854e513a18b7b8980732f561679de6cab1b5b4d + React-runtimeexecutor: fb2d342a477bb13f7128cceb711ee8311edce0c0 + React-RuntimeHermes: 90f09fae56f0b1f458927beb171177a157334fe6 + React-runtimescheduler: 0b50423a4c40db7d1d0c3cc6893b407bf7fb946c + React-timing: 9d49179631e5e3c759e6e82d4c613c73da80a144 + React-utils: 3ea3fa757fec88afb26db14889fb4e7e8b5ca134 + ReactAppDependencyProvider: 68f2d2cefd6c9b9f2865246be2bfe86ebd49238d ReactCodegen: 0c8d830ce35b1b48f8b674b0d00e532abc448470 - ReactCommon: 621bc6d58e5f902e35e9f3452092aaab7fcd025f - ReactCommon-Samples: 25ccde761335cc3fca1aa2adf670625823c98f6e + ReactCommon: a53973ab35d399560ace331ec9e2b26db0592cec + ReactCommon-Samples: 3dd174c775b04ab7d59a1b3c1a832e04377c9538 ScreenshotManager: 8687f6358b007230590842b03505606e905c3ce9 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: a99949337fd8434aedb236685499ef43362c7c18 + Yoga: 59290f2ce3fc5c34797a21244288cad99b357b63 PODFILE CHECKSUM: 8591f96a513620a2a83a0b9a125ad3fa32ea1369 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 From 6fda5eaaac54010a80b6da0f8c5fbfb132888403 Mon Sep 17 00:00:00 2001 From: Alex Hunt Date: Fri, 2 May 2025 06:31:45 -0700 Subject: [PATCH 2/2] Report PerformanceResourceTiming events (#51025) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: (Sparsely) wires up reporting of Network events to the Web Performance subsystem. Our plan is to report to the Web Performance APIs (lightweight timing metadata, here) for all build flavours, and report to CDP (more costly full metadata/previews) in dev/profiling builds. **Notes** - Introduces `PerformanceEntryReporter::unstable_reportResourceTiming` — this will become "stable" when further network events/fields are fully hydrated on Android and iOS. Changelog: [Internal] Reviewed By: rubennorte Differential Revision: D73922341 --- .../jsinspector-modern/network/CMakeLists.txt | 3 +- .../network/NetworkReporter.cpp | 96 +++++++++++++++++-- .../network/NetworkReporter.h | 35 +++++-- .../network/React-jsinspectornetwork.podspec | 3 + .../react/performance/timeline/CMakeLists.txt | 1 + .../performance/timeline/PerformanceEntry.h | 4 +- .../timeline/PerformanceEntryReporter.cpp | 35 +++++++ .../timeline/PerformanceEntryReporter.h | 20 +++- .../internals/RawPerformanceEntry.js | 2 + packages/rn-tester/Podfile.lock | 5 +- 10 files changed, 183 insertions(+), 21 deletions(-) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/network/CMakeLists.txt b/packages/react-native/ReactCommon/jsinspector-modern/network/CMakeLists.txt index 1749a26b3e0d..162debbcf27f 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/network/CMakeLists.txt +++ b/packages/react-native/ReactCommon/jsinspector-modern/network/CMakeLists.txt @@ -24,4 +24,5 @@ target_include_directories(jsinspector_network PUBLIC ${REACT_COMMON_DIR}) target_link_libraries(jsinspector_network folly_runtime jsinspector_cdp -) + react_performance_timeline + react_timing) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.cpp b/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.cpp index 44e49417ecee..1eb6e19f20f6 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.cpp @@ -15,6 +15,8 @@ #include #include #endif +#include +#include #ifdef REACT_NATIVE_DEBUGGER_ENABLED #include @@ -27,7 +29,7 @@ namespace facebook::react::jsinspector_modern { namespace { /** - * Get the current Unix timestamp in seconds (µs precision). + * Get the current Unix timestamp in seconds (µs precision, CDP format). */ double getCurrentUnixTimestampSeconds() { auto now = std::chrono::system_clock::now().time_since_epoch(); @@ -74,7 +76,23 @@ void NetworkReporter::reportRequestStart( const std::string& requestId, const RequestInfo& requestInfo, int encodedDataLength, - const std::optional& redirectResponse) const { + const std::optional& redirectResponse) { + if (ReactNativeFeatureFlags::enableResourceTimingAPI()) { + double now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); + + // All builds: Annotate PerformanceResourceTiming metadata + { + std::lock_guard lock(perfTimingsMutex_); + perfTimingsBuffer_.emplace( + requestId, + ResourceTimingData{ + .url = requestInfo.url, + .fetchStart = now, + .requestStart = now, + }); + } + } + #ifdef REACT_NATIVE_DEBUGGER_ENABLED // Debug build: CDP event handling if (!isDebuggingEnabledNoSync()) { @@ -107,8 +125,20 @@ void NetworkReporter::reportRequestStart( #endif } -void NetworkReporter::reportConnectionTiming( - const std::string& /*requestId*/) const { +void NetworkReporter::reportConnectionTiming(const std::string& requestId) { + if (ReactNativeFeatureFlags::enableResourceTimingAPI()) { + double now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); + + // All builds: Annotate PerformanceResourceTiming metadata + { + std::lock_guard lock(perfTimingsMutex_); + auto it = perfTimingsBuffer_.find(requestId); + if (it != perfTimingsBuffer_.end()) { + it->second.connectStart = now; + } + } + } + #ifdef REACT_NATIVE_DEBUGGER_ENABLED // Debug build: CDP event handling if (!isDebuggingEnabledNoSync()) { @@ -136,7 +166,21 @@ void NetworkReporter::reportRequestFailed( void NetworkReporter::reportResponseStart( const std::string& requestId, const ResponseInfo& responseInfo, - int encodedDataLength) const { + int encodedDataLength) { + if (ReactNativeFeatureFlags::enableResourceTimingAPI()) { + double now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); + + // All builds: Annotate PerformanceResourceTiming metadata + { + std::lock_guard lock(perfTimingsMutex_); + auto it = perfTimingsBuffer_.find(requestId); + if (it != perfTimingsBuffer_.end()) { + it->second.responseStart = now; + it->second.responseStatus = responseInfo.statusCode; + } + } + } + #ifdef REACT_NATIVE_DEBUGGER_ENABLED // Debug build: CDP event handling if (!isDebuggingEnabledNoSync()) { @@ -159,8 +203,21 @@ void NetworkReporter::reportResponseStart( #endif } -void NetworkReporter::reportDataReceived( - const std::string& /*requestId*/) const { +void NetworkReporter::reportDataReceived(const std::string& requestId) { + if (ReactNativeFeatureFlags::enableResourceTimingAPI()) { + double now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); + + // All builds: Annotate PerformanceResourceTiming metadata + { + std::lock_guard lock(perfTimingsMutex_); + auto it = perfTimingsBuffer_.find(requestId); + if (it != perfTimingsBuffer_.end()) { + it->second.connectEnd = now; + it->second.responseStart = now; + } + } + } + #ifdef REACT_NATIVE_DEBUGGER_ENABLED // Debug build: CDP event handling if (!isDebuggingEnabledNoSync()) { @@ -174,7 +231,30 @@ void NetworkReporter::reportDataReceived( void NetworkReporter::reportResponseEnd( const std::string& requestId, - int encodedDataLength) const { + int encodedDataLength) { + if (ReactNativeFeatureFlags::enableResourceTimingAPI()) { + double now = PerformanceEntryReporter::getInstance()->getCurrentTimeStamp(); + + // All builds: Report PerformanceResourceTiming event + { + std::lock_guard lock(perfTimingsMutex_); + auto it = perfTimingsBuffer_.find(requestId); + if (it != perfTimingsBuffer_.end()) { + auto& eventData = it->second; + PerformanceEntryReporter::getInstance()->reportResourceTiming( + eventData.url, + eventData.fetchStart, + eventData.requestStart, + eventData.connectStart.value_or(now), + eventData.connectEnd.value_or(now), + eventData.responseStart.value_or(now), + now, + eventData.responseStatus); + perfTimingsBuffer_.erase(requestId); + } + } + } + #ifdef REACT_NATIVE_DEBUGGER_ENABLED // Debug build: CDP event handling if (!isDebuggingEnabledNoSync()) { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.h b/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.h index c6d281e15c75..4227deca3df0 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/network/NetworkReporter.h @@ -9,8 +9,11 @@ #include "NetworkTypes.h" +#include + #include #include +#include #include namespace facebook::react::jsinspector_modern { @@ -22,6 +25,24 @@ namespace facebook::react::jsinspector_modern { */ using FrontendChannel = std::function; +/** + * Container for static network event metadata aligning with the + * `PerformanceResourceTiming` interface. + * + * This is a lightweight type stored in `perfTimingsBuffer_` and used for + * reporting complete events to the Web Performance subsystem. Not used for CDP + * reporting. + */ +struct ResourceTimingData { + std::string url; + DOMHighResTimeStamp fetchStart; + DOMHighResTimeStamp requestStart; + std::optional connectStart; + std::optional connectEnd; + std::optional responseStart; + std::optional responseStatus; +}; + /** * [Experimental] An interface for reporting network events to the modern * debugger server and Web Performance APIs. @@ -67,7 +88,7 @@ class NetworkReporter { const std::string& requestId, const RequestInfo& requestInfo, int encodedDataLength, - const std::optional& redirectResponse) const; + const std::optional& redirectResponse); /** * Report detailed timing info, such as DNS lookup, when a request has @@ -79,7 +100,7 @@ class NetworkReporter { * * https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-connectstart */ - void reportConnectionTiming(const std::string& requestId) const; + void reportConnectionTiming(const std::string& requestId); /** * Report when a network request has failed. @@ -100,14 +121,14 @@ class NetworkReporter { void reportResponseStart( const std::string& requestId, const ResponseInfo& responseInfo, - int encodedDataLength) const; + int encodedDataLength); /** * Report when additional chunks of the response body have been received. * * Corresponds to `Network.dataReceived` in CDP. */ - void reportDataReceived(const std::string& requestId) const; + void reportDataReceived(const std::string& requestId); /** * Report when a network request is complete and we are no longer receiving @@ -118,8 +139,7 @@ class NetworkReporter { * * https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responseend */ - void reportResponseEnd(const std::string& requestId, int encodedDataLength) - const; + void reportResponseEnd(const std::string& requestId, int encodedDataLength); private: FrontendChannel frontendChannel_; @@ -134,6 +154,9 @@ class NetworkReporter { inline bool isDebuggingEnabledNoSync() const { return debuggingEnabled_.load(std::memory_order_relaxed); } + + std::unordered_map perfTimingsBuffer_{}; + std::mutex perfTimingsMutex_; }; } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/network/React-jsinspectornetwork.podspec b/packages/react-native/ReactCommon/jsinspector-modern/network/React-jsinspectornetwork.podspec index eb2b513439a7..b4c4a2b545f2 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/network/React-jsinspectornetwork.podspec +++ b/packages/react-native/ReactCommon/jsinspector-modern/network/React-jsinspectornetwork.podspec @@ -47,6 +47,9 @@ Pod::Spec.new do |s| end add_dependency(s, "React-jsinspectorcdp", :framework_name => 'jsinspector_moderncdp') + add_dependency(s, "React-featureflags") + s.dependency "React-performancetimeline" + s.dependency "React-timing" add_rn_third_party_dependencies(s) end diff --git a/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt b/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt index c507e4fd449e..4de6a14a5098 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt +++ b/packages/react-native/ReactCommon/react/performance/timeline/CMakeLists.txt @@ -18,4 +18,5 @@ target_include_directories(react_performance_timeline PUBLIC ${REACT_COMMON_DIR} target_link_libraries(react_performance_timeline jsinspector_tracing reactperflogger + react_featureflags react_timing) diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntry.h b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntry.h index 45a7044934c7..e794902f85dc 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntry.h +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntry.h @@ -57,8 +57,8 @@ struct PerformanceResourceTiming : AbstractPerformanceEntry { static constexpr PerformanceEntryType entryType = PerformanceEntryType::RESOURCE; /** Aligns with `startTime`. */ - std::optional fetchStart; - std::optional requestStart; + DOMHighResTimeStamp fetchStart; + DOMHighResTimeStamp requestStart; std::optional connectStart; std::optional connectEnd; std::optional responseStart; diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp index 8273a89d429a..2d0ba22aec3f 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.cpp @@ -30,6 +30,10 @@ std::vector getSupportedEntryTypesInternal() { PerformanceEntryType::LONGTASK, }; + if (ReactNativeFeatureFlags::enableResourceTimingAPI()) { + supportedEntryTypes.emplace_back(PerformanceEntryType::RESOURCE); + } + return supportedEntryTypes; } @@ -289,6 +293,37 @@ void PerformanceEntryReporter::reportLongTask( observerRegistry_->queuePerformanceEntry(entry); } +PerformanceResourceTiming PerformanceEntryReporter::reportResourceTiming( + const std::string& url, + DOMHighResTimeStamp fetchStart, + DOMHighResTimeStamp requestStart, + std::optional connectStart, + std::optional connectEnd, + DOMHighResTimeStamp responseStart, + DOMHighResTimeStamp responseEnd, + const std::optional& responseStatus) { + const auto entry = PerformanceResourceTiming{ + {.name = url, .startTime = fetchStart}, + fetchStart, + requestStart, + connectStart, + connectEnd, + responseStart, + responseEnd, + responseStatus, + }; + + // Add to buffers & notify observers + { + std::unique_lock lock(buffersMutex_); + resourceTimingBuffer_.add(entry); + } + + observerRegistry_->queuePerformanceEntry(entry); + + return entry; +} + void PerformanceEntryReporter::traceMark(const PerformanceMark& entry) const { auto& performanceTracer = jsinspector_modern::PerformanceTracer::getInstance(); diff --git a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h index a8a75fbfcd6b..fba99695551f 100644 --- a/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h +++ b/packages/react-native/ReactCommon/react/performance/timeline/PerformanceEntryReporter.h @@ -21,8 +21,11 @@ namespace facebook::react { +// Aligned with maxBufferSize implemented by browsers +// https://w3c.github.io/timing-entrytypes-registry/#registry constexpr size_t EVENT_BUFFER_SIZE = 150; constexpr size_t LONG_TASK_BUFFER_SIZE = 200; +constexpr size_t RESOURCE_TIMING_BUFFER_SIZE = 250; constexpr DOMHighResTimeStamp LONG_TASK_DURATION_THRESHOLD_MS = 50.0; @@ -101,15 +104,26 @@ class PerformanceEntryReporter { void reportLongTask(double startTime, double duration); + PerformanceResourceTiming reportResourceTiming( + const std::string& url, + DOMHighResTimeStamp fetchStart, + DOMHighResTimeStamp requestStart, + std::optional connectStart, + std::optional connectEnd, + DOMHighResTimeStamp responseStart, + DOMHighResTimeStamp responseEnd, + const std::optional& responseStatus); + private: std::unique_ptr observerRegistry_; mutable std::shared_mutex buffersMutex_; PerformanceEntryCircularBuffer eventBuffer_{EVENT_BUFFER_SIZE}; PerformanceEntryCircularBuffer longTaskBuffer_{LONG_TASK_BUFFER_SIZE}; + PerformanceEntryCircularBuffer resourceTimingBuffer_{ + RESOURCE_TIMING_BUFFER_SIZE}; PerformanceEntryKeyedBuffer markBuffer_; PerformanceEntryKeyedBuffer measureBuffer_; - PerformanceEntryKeyedBuffer resourceBuffer_; std::unordered_map eventCounts_; @@ -129,7 +143,7 @@ class PerformanceEntryReporter { case PerformanceEntryType::LONGTASK: return longTaskBuffer_; case PerformanceEntryType::RESOURCE: - return resourceBuffer_; + return resourceTimingBuffer_; case PerformanceEntryType::_NEXT: throw std::logic_error("Cannot get buffer for _NEXT entry type"); } @@ -147,7 +161,7 @@ class PerformanceEntryReporter { case PerformanceEntryType::LONGTASK: return longTaskBuffer_; case PerformanceEntryType::RESOURCE: - return resourceBuffer_; + return resourceTimingBuffer_; case PerformanceEntryType::_NEXT: throw std::logic_error("Cannot get buffer for _NEXT entry type"); } diff --git a/packages/react-native/src/private/webapis/performance/internals/RawPerformanceEntry.js b/packages/react-native/src/private/webapis/performance/internals/RawPerformanceEntry.js index 99a0cbb4bfb9..6a635c0a37a9 100644 --- a/packages/react-native/src/private/webapis/performance/internals/RawPerformanceEntry.js +++ b/packages/react-native/src/private/webapis/performance/internals/RawPerformanceEntry.js @@ -77,6 +77,8 @@ export function rawToPerformanceEntryType( return 'event'; case RawPerformanceEntryTypeValues.LONGTASK: return 'longtask'; + case RawPerformanceEntryTypeValues.RESOURCE: + return 'resource'; default: throw new TypeError( `rawToPerformanceEntryType: unexpected performance entry type received: ${type}`, diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 9bc4825c0e1f..03c0606ae220 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -1706,7 +1706,10 @@ PODS: - glog - RCT-Folly - RCT-Folly/Fabric + - React-featureflags - React-jsinspectorcdp + - React-performancetimeline + - React-timing - SocketRocket - React-jsinspectortracing (1000.0.0): - boost @@ -2599,7 +2602,7 @@ SPEC CHECKSUMS: React-jsiexecutor: 569425f7cd2c3e005a17e5211843e541c11d6916 React-jsinspector: 885e8180e898f07e4d7df29e2681a89e69d736d3 React-jsinspectorcdp: 5fb266e5f23d3a2819ba848e9d4d0b6b00f95934 - React-jsinspectornetwork: 207422b56a7918e83c94c207570849f83ab9052a + React-jsinspectornetwork: 1655a81f3fe14789df41e063bd56dd130cc3562a React-jsinspectortracing: 80e9418ac67630c76f15ef06534087037a822330 React-jsitooling: 0c28fbc10441f8b63f4c6bf443cb36416500ce2b React-jsitracing: ce443686f52538d1033ce7db1e7d643e866262f0