From 6df19ee1fc3a75dbc8d153509b2aa960e5bd6424 Mon Sep 17 00:00:00 2001 From: Alex Hunt Date: Thu, 30 Oct 2025 07:07:45 -0700 Subject: [PATCH] Support parsing of detail arg from console.timeStamp (#54262) Summary: Similar to `performance.measure()`, Chrome is adding an optional `detail` arg to `console.timeStamp`. Here we implement this for React Native, by direct passing to the `"TimeStamp"` trace event args. [`console.timeStamp()`](https://developer.mozilla.org/en-US/docs/Web/API/console/timeStamp_static) remains an experimental, non-standard API. Changelog: [Internal] Reviewed By: hoxyq Differential Revision: D85437162 --- .../__tests__/consoleTimeStamp-itest.js | 4 +++- .../RuntimeTargetConsole.cpp | 11 +++++++++- .../tracing/ConsoleTimeStamp.cpp | 22 +++++++++++++++++++ .../tracing/ConsoleTimeStamp.h | 4 ++++ .../tracing/PerformanceTracer.cpp | 11 +++++++++- .../tracing/PerformanceTracer.h | 4 +++- packages/react-native/flow/bom.js.flow | 1 + 7 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 packages/react-native/ReactCommon/jsinspector-modern/tracing/ConsoleTimeStamp.cpp diff --git a/packages/polyfills/__tests__/consoleTimeStamp-itest.js b/packages/polyfills/__tests__/consoleTimeStamp-itest.js index 5610348a89da82..713ee72eb5e8bb 100644 --- a/packages/polyfills/__tests__/consoleTimeStamp-itest.js +++ b/packages/polyfills/__tests__/consoleTimeStamp-itest.js @@ -26,7 +26,9 @@ describe('console.timeStamp()', () => { it("doesn't throw when additional arguments are specified", () => { expect(() => - console.timeStamp('label', 100, 500, 'Track', 'Group', 'error'), + console.timeStamp('label', 100, 500, 'Track', 'Group', 'error', { + tooltipText: 'Image processing failed', + }), ).not.toThrow(); }); diff --git a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetConsole.cpp b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetConsole.cpp index 5d89b6b9f53c62..839869e013f310 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetConsole.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetConsole.cpp @@ -467,9 +467,18 @@ void consoleTimeStamp( } } + std::optional detail; + if (performanceTracer.isTracing() && argumentsCount >= 7) { + const jsi::Value& detailArgument = arguments[6]; + if (detailArgument.isObject()) { + detail = + tracing::getConsoleTimeStampDetailFromObject(runtime, detailArgument); + } + } + if (performanceTracer.isTracing()) { performanceTracer.reportTimeStamp( - label, start, end, trackName, trackGroup, color); + label, start, end, trackName, trackGroup, color, std::move(detail)); } if (ReactPerfettoLogger::isTracing()) { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/ConsoleTimeStamp.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tracing/ConsoleTimeStamp.cpp new file mode 100644 index 00000000000000..33e41bf05a59cb --- /dev/null +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/ConsoleTimeStamp.cpp @@ -0,0 +1,22 @@ +/* + * 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 "ConsoleTimeStamp.h" + +namespace facebook::react::jsinspector_modern::tracing { + +std::optional getConsoleTimeStampDetailFromObject( + jsi::Runtime& runtime, + const jsi::Value& detailValue) { + try { + return jsi::dynamicFromValue(runtime, detailValue); + } catch (jsi::JSIException&) { + return std::nullopt; + } +} + +} // namespace facebook::react::jsinspector_modern::tracing diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/ConsoleTimeStamp.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/ConsoleTimeStamp.h index 79f5575d851360..d73702fa7fda18 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/ConsoleTimeStamp.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/ConsoleTimeStamp.h @@ -7,6 +7,8 @@ #pragma once +#include +#include #include #include @@ -96,4 +98,6 @@ inline std::optional getConsoleTimeStampColorFromString(c } }; +std::optional getConsoleTimeStampDetailFromObject(jsi::Runtime &runtime, const jsi::Value &detailValue); + }; // namespace facebook::react::jsinspector_modern::tracing diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp index 490387400439ce..66f1b4844479fb 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp @@ -213,7 +213,8 @@ void PerformanceTracer::reportTimeStamp( std::optional end, std::optional trackName, std::optional trackGroup, - std::optional color) { + std::optional color, + std::optional detail) { if (!tracingAtomic_) { return; } @@ -231,6 +232,7 @@ void PerformanceTracer::reportTimeStamp( .trackName = std::move(trackName), .trackGroup = std::move(trackGroup), .color = std::move(color), + .detail = std::move(detail), .threadId = getCurrentThreadId(), }); } @@ -651,6 +653,13 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent( if (event.color) { data["color"] = consoleTimeStampColorToString(*event.color); } + if (event.detail) { + folly::dynamic devtoolsDetail = folly::dynamic::object(); + for (const auto& [key, value] : event.detail->items()) { + devtoolsDetail[key] = value; + } + data["devtools"] = folly::toJson(devtoolsDetail); + } events.emplace_back( TraceEvent{ diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h index a85a4af8846564..462c3c4d51a4f3 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h @@ -96,7 +96,8 @@ class PerformanceTracer { std::optional end = std::nullopt, std::optional trackName = std::nullopt, std::optional trackGroup = std::nullopt, - std::optional color = std::nullopt); + std::optional color = std::nullopt, + std::optional detail = std::nullopt); /** * Record an Event Loop tick, which will be represented as an Event Loop task @@ -252,6 +253,7 @@ class PerformanceTracer { std::optional trackName; std::optional trackGroup; std::optional color; + std::optional detail; ThreadId threadId; HighResTimeStamp createdAt = HighResTimeStamp::now(); }; diff --git a/packages/react-native/flow/bom.js.flow b/packages/react-native/flow/bom.js.flow index 59f402968d71ba..896253932a4777 100644 --- a/packages/react-native/flow/bom.js.flow +++ b/packages/react-native/flow/bom.js.flow @@ -72,6 +72,7 @@ declare var console: { trackName?: string, trackGroup?: string, color?: DevToolsColor, + detail?: {[string]: mixed}, ): void, ...