first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
file(GLOB react_performance_cdpmetrics_SRC CONFIGURE_DEPENDS *.cpp)
add_library(react_performance_cdpmetrics OBJECT ${react_performance_cdpmetrics_SRC})
target_compile_reactnative_options(react_performance_cdpmetrics PRIVATE)
target_compile_options(react_performance_cdpmetrics PRIVATE -Wpedantic)
target_include_directories(react_performance_cdpmetrics PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(react_performance_cdpmetrics
folly_runtime
jsi
react_performance_timeline
react_timing
runtimeexecutor)

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "CdpInteractionTypes.h"
namespace facebook::react {
const InteractionTypesMap& getInteractionTypes() {
static InteractionTypesMap INTERACTION_TYPES = {
{"auxclick", "pointer"},
{"click", "pointer"},
{"contextmenu", "pointer"},
{"dblclick", "pointer"},
{"mousedown", "pointer"},
{"mouseenter", "pointer"},
{"mouseleave", "pointer"},
{"mouseout", "pointer"},
{"mouseover", "pointer"},
{"mouseup", "pointer"},
{"pointerover", "pointer"},
{"pointerenter", "pointer"},
{"pointerdown", "pointer"},
{"pointerup", "pointer"},
{"pointercancel", "pointer"},
{"pointerout", "pointer"},
{"pointerleave", "pointer"},
{"gotpointercapture", "pointer"},
{"lostpointercapture", "pointer"},
{"touchstart", "touch"},
{"touchend", "touch"},
{"touchcancel", "touch"},
{"keydown", "keyboard"},
{"keypress", "keyboard"},
{"keyup", "keyboard"},
{"beforeinput", "keyboard"},
{"input", "keyboard"},
{"compositionstart", "composition"},
{"compositionupdate", "composition"},
{"compositionend", "composition"},
{"dragstart", "drag"},
{"dragend", "drag"},
{"dragenter", "drag"},
{"dragleave", "drag"},
{"dragover", "drag"},
{"drop", "drag"},
};
return INTERACTION_TYPES;
}
std::string_view getInteractionTypeForEvent(std::string_view eventName) {
const auto& interactionTypes = getInteractionTypes();
auto it = interactionTypes.find(eventName);
if (it != interactionTypes.end()) {
return it->second;
}
return "unknown";
}
} // namespace facebook::react

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <string_view>
#include <unordered_map>
namespace facebook::react {
using InteractionTypesMap = std::unordered_map<std::string_view, std::string_view>;
const InteractionTypesMap &getInteractionTypes();
std::string_view getInteractionTypeForEvent(std::string_view eventName);
} // namespace facebook::react

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "CdpMetricsReporter.h"
#include "CdpInteractionTypes.h"
#include <folly/dynamic.h>
#include <folly/json.h>
#include <string_view>
namespace facebook::react {
namespace {
constexpr std::string_view metricsReporterName =
"__chromium_devtools_metrics_reporter";
} // namespace
CdpMetricsReporter::CdpMetricsReporter(RuntimeExecutor runtimeExecutor)
: runtimeExecutor_(std::move(runtimeExecutor)) {}
void CdpMetricsReporter::onEventTimingEntry(
const PerformanceEventTiming& entry) {
runtimeExecutor_([entry](jsi::Runtime& runtime) {
auto global = runtime.global();
if (!global.hasProperty(runtime, metricsReporterName.data())) {
return;
}
auto inputDelay = entry.processingStart - entry.startTime;
auto processingDuration = entry.processingEnd - entry.processingStart;
auto nextPaintTime = entry.startTime + entry.duration;
auto presentationDelay = nextPaintTime - entry.taskEndTime;
folly::dynamic jsonPayload = folly::dynamic::object;
if (presentationDelay.toNanoseconds() > 0) {
jsonPayload["name"] = "INP";
jsonPayload["value"] =
static_cast<int>(entry.duration.toDOMHighResTimeStamp());
} else {
jsonPayload["name"] = "InteractionEntry";
jsonPayload["duration"] =
static_cast<int>(entry.duration.toDOMHighResTimeStamp());
jsonPayload["nextPaintTime"] =
static_cast<int>(nextPaintTime.toDOMHighResTimeStamp());
jsonPayload["eventName"] = std::string(entry.name);
jsonPayload["longAnimationFrameEntries"] = folly::dynamic::array();
}
jsonPayload["phases"] = folly::dynamic::object(
"inputDelay", static_cast<int>(inputDelay.toDOMHighResTimeStamp()))(
"processingDuration",
static_cast<int>(processingDuration.toDOMHighResTimeStamp()))(
"presentationDelay",
static_cast<int>(presentationDelay.toDOMHighResTimeStamp()));
jsonPayload["startTime"] =
static_cast<int>(entry.startTime.toDOMHighResTimeStamp());
jsonPayload["interactionType"] =
std::string(getInteractionTypeForEvent(entry.name));
auto jsonString = folly::toJson(jsonPayload);
auto jsiString = jsi::String::createFromUtf8(runtime, jsonString);
auto metricsReporter =
global.getPropertyAsFunction(runtime, metricsReporterName.data());
metricsReporter.call(runtime, jsiString);
});
}
} // namespace facebook::react

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <ReactCommon/RuntimeExecutor.h>
#include <jsi/jsi.h>
#include <react/performance/timeline/PerformanceEntry.h>
#include <react/performance/timeline/PerformanceEntryReporterListeners.h>
#include <react/timing/primitives.h>
namespace facebook::react {
/**
* [Experimental] Reports CDP interaction events via the
* "__chromium_devtools_metrics_reporter" runtime binding.
*
* This populates the Interaction to Next Paint (INP) live metric in React
* Native DevTools. Events are reported immediately and do not require an
* active CDP Tracing session.
*/
class CdpMetricsReporter : public PerformanceEntryReporterEventListener {
public:
explicit CdpMetricsReporter(RuntimeExecutor runtimeExecutor);
void onEventTimingEntry(const PerformanceEventTiming &entry) override;
private:
const RuntimeExecutor runtimeExecutor_{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "CdpPerfIssuesReporter.h"
#include <folly/dynamic.h>
#include <folly/json.h>
#include <string_view>
namespace facebook::react {
namespace {
constexpr std::string_view issuesReporterName =
"__react_native_perf_issues_reporter";
} // namespace
CdpPerfIssuesReporter::CdpPerfIssuesReporter(RuntimeExecutor runtimeExecutor)
: runtimeExecutor_(std::move(runtimeExecutor)) {}
void CdpPerfIssuesReporter::onMeasureEntry(
const PerformanceMeasure& entry,
const std::optional<UserTimingDetailProvider>& detailProvider) {
std::optional<folly::dynamic> maybeDetail = nullptr;
if (detailProvider.has_value()) {
maybeDetail = (*detailProvider)();
}
if (!maybeDetail.has_value() || !maybeDetail->isObject()) {
return;
}
runtimeExecutor_([entry,
detail = std::move(*maybeDetail)](jsi::Runtime& runtime) {
auto global = runtime.global();
if (!global.hasProperty(runtime, issuesReporterName.data())) {
return;
}
if (detail.count("rnPerfIssue") != 0 && detail["rnPerfIssue"].isObject()) {
auto& perfIssue = detail["rnPerfIssue"];
if (perfIssue.count("name") != 0 && perfIssue.count("severity") != 0 &&
perfIssue.count("description") != 0) {
auto jsonString = folly::toJson(perfIssue);
auto jsiString = jsi::String::createFromUtf8(runtime, jsonString);
auto issuesReporter =
global.getPropertyAsFunction(runtime, issuesReporterName.data());
issuesReporter.call(runtime, jsiString);
}
}
});
}
} // namespace facebook::react

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <ReactCommon/RuntimeExecutor.h>
#include <folly/dynamic.h>
#include <jsi/jsi.h>
#include <react/performance/timeline/PerformanceEntry.h>
#include <react/performance/timeline/PerformanceEntryReporterListeners.h>
#include <react/timing/primitives.h>
namespace facebook::react {
/**
* [Experimental] Reports new Performance Issues via the
* "__react_native_perf_issues_reporter" runtime binding.
*
* This populates the Perf Issues indicator in the V2 Perf Monitor. Events are
* reported immediately and do not require an active CDP Tracing session.
*/
class CdpPerfIssuesReporter : public PerformanceEntryReporterEventListener {
public:
explicit CdpPerfIssuesReporter(RuntimeExecutor runtimeExecutor);
void onMeasureEntry(const PerformanceMeasure &entry, const std::optional<UserTimingDetailProvider> &detailProvider)
override;
private:
const RuntimeExecutor runtimeExecutor_{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,53 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
require "json"
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "..", "..", "package.json")))
version = package['version']
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
else
source[:tag] = "v#{version}"
end
header_search_paths = []
if ENV['USE_FRAMEWORKS']
header_search_paths << "\"$(PODS_TARGET_SRCROOT)/../../..\"" # this is needed to allow the cdpmetrics access its own files
end
Pod::Spec.new do |s|
s.name = "React-performancecdpmetrics"
s.version = version
s.summary = "Module for reporting React Native performance live metrics to the debugger"
s.homepage = "https://reactnative.dev/"
s.license = package["license"]
s.author = "Meta Platforms, Inc. and its affiliates"
s.platforms = min_supported_versions
s.source = source
s.source_files = podspec_sources("**/*.{cpp,h}", "**/*.h")
s.header_dir = "react/performance/cdpmetrics"
s.exclude_files = "tests"
s.pod_target_xcconfig = {
"CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(),
"HEADER_SEARCH_PATHS" => header_search_paths.join(' ')}
resolve_use_frameworks(s, header_mappings_dir: "../../..", module_name: "React_performancecdpmetrics")
add_dependency(s, "React-runtimeexecutor", :additional_framework_paths => ["platform/ios"])
s.dependency "React-jsi"
s.dependency "React-performancetimeline"
s.dependency "React-timing"
add_rn_third_party_dependencies(s)
add_rncore_dependency(s)
if use_hermes()
s.dependency 'hermes-engine'
end
end