From 973409fc0bbc53fdc2de516ddefffbc9c6884270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Fri, 3 Jan 2025 01:54:08 -0800 Subject: [PATCH 1/3] Follow naming convention for NativeFantom module Differential Revision: D67549203 --- .../src/getFantomRenderedOutput.js | 4 ++-- packages/react-native-fantom/src/index.js | 12 ++++++------ .../specs/{NativeFantomModule.js => NativeFantom.js} | 4 +++- 3 files changed, 11 insertions(+), 9 deletions(-) rename packages/react-native-fantom/src/specs/{NativeFantomModule.js => NativeFantom.js} (90%) diff --git a/packages/react-native-fantom/src/getFantomRenderedOutput.js b/packages/react-native-fantom/src/getFantomRenderedOutput.js index 496a462a3d32..45500fde1742 100644 --- a/packages/react-native-fantom/src/getFantomRenderedOutput.js +++ b/packages/react-native-fantom/src/getFantomRenderedOutput.js @@ -9,7 +9,7 @@ * @oncall react_native */ -import FantomModule from './specs/NativeFantomModule'; +import NativeFantom from './specs/NativeFantom'; // $FlowExpectedError[untyped-import] import micromatch from 'micromatch'; import * as React from 'react'; @@ -114,7 +114,7 @@ export default function getFantomRenderedOutput( } = config; return new FantomRenderedOutput( JSON.parse( - FantomModule.getRenderedOutput(surfaceId, { + NativeFantom.getRenderedOutput(surfaceId, { includeRoot, includeLayoutMetrics, }), diff --git a/packages/react-native-fantom/src/index.js b/packages/react-native-fantom/src/index.js index b765810f66a0..7b06e4e9a655 100644 --- a/packages/react-native-fantom/src/index.js +++ b/packages/react-native-fantom/src/index.js @@ -15,7 +15,7 @@ import type { import type {MixedElement} from 'react'; import getFantomRenderedOutput from './getFantomRenderedOutput'; -import FantomModule from './specs/NativeFantomModule'; +import NativeFantom from './specs/NativeFantom'; import ReactFabric from 'react-native/Libraries/Renderer/shims/ReactFabric'; let globalSurfaceIdCounter = 1; @@ -35,7 +35,7 @@ class Root { render(element: MixedElement) { if (!this.#hasRendered) { - FantomModule.startSurface(this.#surfaceId); + NativeFantom.startSurface(this.#surfaceId); this.#hasRendered = true; } @@ -43,13 +43,13 @@ class Root { } getMountingLogs(): Array { - return FantomModule.getMountingManagerLogs(this.#surfaceId); + return NativeFantom.getMountingManagerLogs(this.#surfaceId); } destroy() { // TODO: check for leaks. - FantomModule.stopSurface(this.#surfaceId); - FantomModule.flushMessageQueue(); + NativeFantom.stopSurface(this.#surfaceId); + NativeFantom.flushMessageQueue(); } getRenderedOutput(config: RenderOutputConfig = {}): FantomRenderedOutput { @@ -100,7 +100,7 @@ export function runWorkLoop(): void { try { flushingQueue = true; - FantomModule.flushMessageQueue(); + NativeFantom.flushMessageQueue(); } finally { flushingQueue = false; } diff --git a/packages/react-native-fantom/src/specs/NativeFantomModule.js b/packages/react-native-fantom/src/specs/NativeFantom.js similarity index 90% rename from packages/react-native-fantom/src/specs/NativeFantomModule.js rename to packages/react-native-fantom/src/specs/NativeFantom.js index da67f38a1e75..a2d2db9fc31c 100644 --- a/packages/react-native-fantom/src/specs/NativeFantomModule.js +++ b/packages/react-native-fantom/src/specs/NativeFantom.js @@ -26,4 +26,6 @@ interface Spec extends TurboModule { getRenderedOutput: (surfaceId: number, config: RenderFormatOptions) => string; } -export default TurboModuleRegistry.getEnforcing('Fantom') as Spec; +export default TurboModuleRegistry.getEnforcing( + 'NativeFantomCxx', +) as Spec; From 381291938956cb0fe264bc1adf2bdbf374bc904c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Fri, 3 Jan 2025 01:54:08 -0800 Subject: [PATCH 2/3] Use codegen for Fantom native module Differential Revision: D67759729 --- packages/react-native-fantom/src/getFantomRenderedOutput.js | 2 +- packages/react-native-fantom/src/index.js | 2 +- .../src/private/specs/modules}/NativeFantom.js | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) rename packages/{react-native-fantom/src/specs => react-native/src/private/specs/modules}/NativeFantom.js (79%) diff --git a/packages/react-native-fantom/src/getFantomRenderedOutput.js b/packages/react-native-fantom/src/getFantomRenderedOutput.js index 45500fde1742..2e7f36cc9a3d 100644 --- a/packages/react-native-fantom/src/getFantomRenderedOutput.js +++ b/packages/react-native-fantom/src/getFantomRenderedOutput.js @@ -9,10 +9,10 @@ * @oncall react_native */ -import NativeFantom from './specs/NativeFantom'; // $FlowExpectedError[untyped-import] import micromatch from 'micromatch'; import * as React from 'react'; +import NativeFantom from 'react-native/src/private/specs/modules/NativeFantom'; export type RenderOutputConfig = { ...FantomRenderedOutputConfig, diff --git a/packages/react-native-fantom/src/index.js b/packages/react-native-fantom/src/index.js index 7b06e4e9a655..67795cc5c67f 100644 --- a/packages/react-native-fantom/src/index.js +++ b/packages/react-native-fantom/src/index.js @@ -15,8 +15,8 @@ import type { import type {MixedElement} from 'react'; import getFantomRenderedOutput from './getFantomRenderedOutput'; -import NativeFantom from './specs/NativeFantom'; import ReactFabric from 'react-native/Libraries/Renderer/shims/ReactFabric'; +import NativeFantom from 'react-native/src/private/specs/modules/NativeFantom'; let globalSurfaceIdCounter = 1; diff --git a/packages/react-native-fantom/src/specs/NativeFantom.js b/packages/react-native/src/private/specs/modules/NativeFantom.js similarity index 79% rename from packages/react-native-fantom/src/specs/NativeFantom.js rename to packages/react-native/src/private/specs/modules/NativeFantom.js index a2d2db9fc31c..34c04f2eaebf 100644 --- a/packages/react-native-fantom/src/specs/NativeFantom.js +++ b/packages/react-native/src/private/specs/modules/NativeFantom.js @@ -4,13 +4,13 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ -import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport'; +import type {TurboModule} from '../../../../Libraries/TurboModule/RCTExport'; -import {TurboModuleRegistry} from 'react-native'; +import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboModuleRegistry'; // match RenderFormatOptions.h export type RenderFormatOptions = { From b45dafdcb2d965d3915cec2caf67dcc44e3ca9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Fri, 3 Jan 2025 01:59:30 -0800 Subject: [PATCH 3/3] Allow configuring viewport width, height and device pixel ratio (#48433) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/48433 Changelog: [internal] Allows customizing the root dimensions. Reviewed By: christophpurrer Differential Revision: D67693031 --- .../src/__tests__/Fantom-itest.js | 57 +++++++++++++++++++ packages/react-native-fantom/src/index.js | 33 +++++++++-- .../src/private/specs/modules/NativeFantom.js | 7 ++- 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/packages/react-native-fantom/src/__tests__/Fantom-itest.js b/packages/react-native-fantom/src/__tests__/Fantom-itest.js index f16d30bd10fb..97c65c8eaa66 100644 --- a/packages/react-native-fantom/src/__tests__/Fantom-itest.js +++ b/packages/react-native-fantom/src/__tests__/Fantom-itest.js @@ -7,13 +7,47 @@ * @flow strict-local * @format * @oncall react_native + * @fantom_flags enableAccessToHostTreeInFabric:true */ import 'react-native/Libraries/Core/InitializeCore'; +import type {Root} from '..'; + import {createRoot, runTask} from '..'; import * as React from 'react'; import {Text, View} from 'react-native'; +import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement'; + +function getActualViewportDimensions(root: Root): { + viewportWidth: number, + viewportHeight: number, +} { + let maybeNode; + + runTask(() => { + root.render( + { + maybeNode = node; + }} + />, + ); + }); + + if (!(maybeNode instanceof ReactNativeElement)) { + throw new Error( + `Expected instance of ReactNativeElement but got ${String(maybeNode)}`, + ); + } + + const rect = maybeNode.getBoundingClientRect(); + return { + viewportWidth: rect.width, + viewportHeight: rect.height, + }; +} describe('Fantom', () => { describe('runTask', () => { @@ -101,6 +135,29 @@ describe('Fantom', () => { }); }); + describe('createRoot', () => { + it('allows creating a root with specific dimensions', () => { + const rootWithDefaults = createRoot(); + + expect(getActualViewportDimensions(rootWithDefaults)).toEqual({ + viewportWidth: 1000, + viewportHeight: 1000, + }); + + const rootWithCustomWidthAndHeight = createRoot({ + viewportWidth: 200, + viewportHeight: 600, + }); + + expect(getActualViewportDimensions(rootWithCustomWidthAndHeight)).toEqual( + { + viewportWidth: 200, + viewportHeight: 600, + }, + ); + }); + }); + describe('getRenderedOutput', () => { describe('toJSX', () => { it('default config', () => { diff --git a/packages/react-native-fantom/src/index.js b/packages/react-native-fantom/src/index.js index 67795cc5c67f..92e0bf0558ff 100644 --- a/packages/react-native-fantom/src/index.js +++ b/packages/react-native-fantom/src/index.js @@ -24,18 +24,41 @@ const nativeRuntimeScheduler = global.nativeRuntimeScheduler; const schedulerPriorityImmediate = nativeRuntimeScheduler.unstable_ImmediatePriority; +export type RootConfig = { + viewportWidth?: number, + viewportHeight?: number, + devicePixelRatio?: number, +}; + +const DEFAULT_VIEWPORT_WIDTH = 1000; +const DEFAULT_VIEWPORT_HEIGHT = 1000; +const DEFAULT_DEVICE_PIXEL_RATIO = 1; + class Root { #surfaceId: number; + #viewportWidth: number; + #viewportHeight: number; + #devicePixelRatio: number; + #hasRendered: boolean = false; - constructor() { + constructor(config?: RootConfig) { this.#surfaceId = globalSurfaceIdCounter; + this.#viewportWidth = config?.viewportWidth ?? DEFAULT_VIEWPORT_WIDTH; + this.#viewportHeight = config?.viewportHeight ?? DEFAULT_VIEWPORT_HEIGHT; + this.#devicePixelRatio = + config?.devicePixelRatio ?? DEFAULT_DEVICE_PIXEL_RATIO; globalSurfaceIdCounter += 10; } render(element: MixedElement) { if (!this.#hasRendered) { - NativeFantom.startSurface(this.#surfaceId); + NativeFantom.startSurface( + this.#surfaceId, + this.#viewportWidth, + this.#viewportHeight, + this.#devicePixelRatio, + ); this.#hasRendered = true; } @@ -59,6 +82,8 @@ class Root { // TODO: add an API to check if all surfaces were deallocated when tests are finished. } +export type {Root}; + const DEFAULT_TASK_PRIORITY = schedulerPriorityImmediate; /** @@ -108,6 +133,6 @@ export function runWorkLoop(): void { // TODO: Add option to define surface props and pass it to startSurface // Surfacep rops: concurrentRoot, surfaceWidth, surfaceHeight, layoutDirection, pointScaleFactor. -export function createRoot(): Root { - return new Root(); +export function createRoot(rootConfig?: RootConfig): Root { + return new Root(rootConfig); } diff --git a/packages/react-native/src/private/specs/modules/NativeFantom.js b/packages/react-native/src/private/specs/modules/NativeFantom.js index 34c04f2eaebf..f4b5ce24d49b 100644 --- a/packages/react-native/src/private/specs/modules/NativeFantom.js +++ b/packages/react-native/src/private/specs/modules/NativeFantom.js @@ -19,7 +19,12 @@ export type RenderFormatOptions = { }; interface Spec extends TurboModule { - startSurface: (surfaceId: number) => void; + startSurface: ( + surfaceId: number, + viewportWidth: number, + viewportHeight: number, + devicePixelRatio: number, + ) => void; stopSurface: (surfaceId: number) => void; getMountingManagerLogs: (surfaceId: number) => Array; flushMessageQueue: () => void;