diff --git a/.changeset/rem-unit-transform.md b/.changeset/rem-unit-transform.md new file mode 100644 index 0000000000..14b56e3889 --- /dev/null +++ b/.changeset/rem-unit-transform.md @@ -0,0 +1,21 @@ +--- +"@lynx-js/web-core": patch +--- + +feat(web-core): add support for configurable rem unit transform + +- **Description**: Added a new configuration option `transformREM` (also exposed as `transform_rem` on the Rust layer) to the Web Core renderer. When enabled, it recursively converts static `rem` unit values in your styles into dynamic CSS custom properties (`calc(VALUE * var(--rem-unit))`) during template decoding and evaluation. This enables developers to implement responsive font scaling and layout sizing dynamically on the client side simply by modifying the root CSS variable `--rem-unit`. + +- **Usage**: + You can enable this feature when working with `LynxView` by setting `transformREM` to `true`, or directly as an HTML attribute `transform-rem`: + + ```html + + ``` + + ```javascript + const lynxView = document.createElement('lynx-view'); + lynxView.transformREM = true; + ``` + + With this enabled, a CSS declaration like `font-size: 1.5rem;` is transparently evaluated as `font-size: calc(1.5 * var(--rem-unit));` by the runtime engine. diff --git a/packages/web-platform/web-core/binary/client/client.d.ts b/packages/web-platform/web-core/binary/client/client.d.ts index 525ea62e99..2c21820086 100644 --- a/packages/web-platform/web-core/binary/client/client.d.ts +++ b/packages/web-platform/web-core/binary/client/client.d.ts @@ -160,17 +160,17 @@ export class StyleSheetResource { */ export function add_inline_style_raw_string_key(dom: HTMLElement, key: string, value?: string | null): void; -export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; -export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; export function get_font_face_content(buffer: Uint8Array): string; export function get_style_content(buffer: Uint8Array): string; -export function set_inline_styles_in_key_value_vec(dom: HTMLElement, k_v_vec: string[], transform_vw: boolean, transform_vh: boolean): void; +export function set_inline_styles_in_key_value_vec(dom: HTMLElement, k_v_vec: string[], transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): void; -export function set_inline_styles_in_str(dom: HTMLElement, styles: string, transform_vw: boolean, transform_vh: boolean): boolean; +export function set_inline_styles_in_str(dom: HTMLElement, styles: string, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): boolean; export function set_inline_styles_number_key(dom: HTMLElement, key: number, value?: string | null): void; @@ -192,8 +192,8 @@ export interface InitOutput { readonly __wbg_set_eventinfo_event_type: (a: number, b: number, c: number) => void; readonly __wbg_stylesheetresource_free: (a: number, b: number) => void; readonly add_inline_style_raw_string_key: (a: any, b: number, c: number, d: number, e: number) => void; - readonly decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; - readonly encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; + readonly decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; + readonly encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; readonly get_font_face_content: (a: any) => [number, number, number, number]; readonly get_style_content: (a: any) => [number, number, number, number]; readonly mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; @@ -231,8 +231,8 @@ export interface InitOutput { readonly ruleprelude_new: () => number; readonly ruleprelude_push_selector: (a: number, b: number) => void; readonly selector_push_one_selector_section: (a: number, b: number, c: number, d: number, e: number) => [number, number]; - readonly set_inline_styles_in_key_value_vec: (a: any, b: number, c: number, d: number, e: number) => void; - readonly set_inline_styles_in_str: (a: any, b: number, c: number, d: number, e: number) => number; + readonly set_inline_styles_in_key_value_vec: (a: any, b: number, c: number, d: number, e: number, f: number) => void; + readonly set_inline_styles_in_str: (a: any, b: number, c: number, d: number, e: number, f: number) => number; readonly set_inline_styles_number_key: (a: any, b: number, c: number, d: number) => void; readonly stylesheetresource_new: (a: any, b: any) => [number, number, number]; readonly selector_new: () => number; diff --git a/packages/web-platform/web-core/binary/client/client_bg.wasm.d.ts b/packages/web-platform/web-core/binary/client/client_bg.wasm.d.ts index 28ee68a718..6e315ce555 100644 --- a/packages/web-platform/web-core/binary/client/client_bg.wasm.d.ts +++ b/packages/web-platform/web-core/binary/client/client_bg.wasm.d.ts @@ -15,8 +15,8 @@ export const __wbg_set_eventinfo_event_name: (a: number, b: number, c: number) = export const __wbg_set_eventinfo_event_type: (a: number, b: number, c: number) => void; export const __wbg_stylesheetresource_free: (a: number, b: number) => void; export const add_inline_style_raw_string_key: (a: any, b: number, c: number, d: number, e: number) => void; -export const decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; -export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; +export const decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; +export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; export const get_font_face_content: (a: any) => [number, number, number, number]; export const get_style_content: (a: any) => [number, number, number, number]; export const mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; @@ -54,8 +54,8 @@ export const rule_set_prelude: (a: number, b: number) => void; export const ruleprelude_new: () => number; export const ruleprelude_push_selector: (a: number, b: number) => void; export const selector_push_one_selector_section: (a: number, b: number, c: number, d: number, e: number) => [number, number]; -export const set_inline_styles_in_key_value_vec: (a: any, b: number, c: number, d: number, e: number) => void; -export const set_inline_styles_in_str: (a: any, b: number, c: number, d: number, e: number) => number; +export const set_inline_styles_in_key_value_vec: (a: any, b: number, c: number, d: number, e: number, f: number) => void; +export const set_inline_styles_in_str: (a: any, b: number, c: number, d: number, e: number, f: number) => number; export const set_inline_styles_number_key: (a: any, b: number, c: number, d: number) => void; export const stylesheetresource_new: (a: any, b: any) => [number, number, number]; export const selector_new: () => number; diff --git a/packages/web-platform/web-core/binary/client_legacy/client.d.ts b/packages/web-platform/web-core/binary/client_legacy/client.d.ts index cb4310ddf3..c07a8fbd63 100644 --- a/packages/web-platform/web-core/binary/client_legacy/client.d.ts +++ b/packages/web-platform/web-core/binary/client_legacy/client.d.ts @@ -160,17 +160,17 @@ export class StyleSheetResource { */ export function add_inline_style_raw_string_key(dom: HTMLElement, key: string, value?: string | null): void; -export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; -export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; export function get_font_face_content(buffer: Uint8Array): string; export function get_style_content(buffer: Uint8Array): string; -export function set_inline_styles_in_key_value_vec(dom: HTMLElement, k_v_vec: string[], transform_vw: boolean, transform_vh: boolean): void; +export function set_inline_styles_in_key_value_vec(dom: HTMLElement, k_v_vec: string[], transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): void; -export function set_inline_styles_in_str(dom: HTMLElement, styles: string, transform_vw: boolean, transform_vh: boolean): boolean; +export function set_inline_styles_in_str(dom: HTMLElement, styles: string, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): boolean; export function set_inline_styles_number_key(dom: HTMLElement, key: number, value?: string | null): void; @@ -192,8 +192,8 @@ export interface InitOutput { readonly __wbg_set_eventinfo_event_type: (a: number, b: number, c: number) => void; readonly __wbg_stylesheetresource_free: (a: number, b: number) => void; readonly add_inline_style_raw_string_key: (a: number, b: number, c: number, d: number, e: number) => void; - readonly decode_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; - readonly encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; + readonly decode_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; + readonly encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; readonly get_font_face_content: (a: number, b: number) => void; readonly get_style_content: (a: number, b: number) => void; readonly mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; @@ -231,8 +231,8 @@ export interface InitOutput { readonly ruleprelude_new: () => number; readonly ruleprelude_push_selector: (a: number, b: number) => void; readonly selector_push_one_selector_section: (a: number, b: number, c: number, d: number, e: number, f: number) => void; - readonly set_inline_styles_in_key_value_vec: (a: number, b: number, c: number, d: number, e: number) => void; - readonly set_inline_styles_in_str: (a: number, b: number, c: number, d: number, e: number) => number; + readonly set_inline_styles_in_key_value_vec: (a: number, b: number, c: number, d: number, e: number, f: number) => void; + readonly set_inline_styles_in_str: (a: number, b: number, c: number, d: number, e: number, f: number) => number; readonly set_inline_styles_number_key: (a: number, b: number, c: number, d: number) => void; readonly stylesheetresource_new: (a: number, b: number, c: number) => void; readonly selector_new: () => number; diff --git a/packages/web-platform/web-core/binary/client_legacy/client_bg.wasm.d.ts b/packages/web-platform/web-core/binary/client_legacy/client_bg.wasm.d.ts index 75ae3b0b4a..51fabb5823 100644 --- a/packages/web-platform/web-core/binary/client_legacy/client_bg.wasm.d.ts +++ b/packages/web-platform/web-core/binary/client_legacy/client_bg.wasm.d.ts @@ -15,8 +15,8 @@ export const __wbg_set_eventinfo_event_name: (a: number, b: number, c: number) = export const __wbg_set_eventinfo_event_type: (a: number, b: number, c: number) => void; export const __wbg_stylesheetresource_free: (a: number, b: number) => void; export const add_inline_style_raw_string_key: (a: number, b: number, c: number, d: number, e: number) => void; -export const decode_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; -export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void; +export const decode_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; +export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; export const get_font_face_content: (a: number, b: number) => void; export const get_style_content: (a: number, b: number) => void; export const mainthreadwasmcontext_add_cross_thread_event: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => void; @@ -54,8 +54,8 @@ export const rule_set_prelude: (a: number, b: number) => void; export const ruleprelude_new: () => number; export const ruleprelude_push_selector: (a: number, b: number) => void; export const selector_push_one_selector_section: (a: number, b: number, c: number, d: number, e: number, f: number) => void; -export const set_inline_styles_in_key_value_vec: (a: number, b: number, c: number, d: number, e: number) => void; -export const set_inline_styles_in_str: (a: number, b: number, c: number, d: number, e: number) => number; +export const set_inline_styles_in_key_value_vec: (a: number, b: number, c: number, d: number, e: number, f: number) => void; +export const set_inline_styles_in_str: (a: number, b: number, c: number, d: number, e: number, f: number) => number; export const set_inline_styles_number_key: (a: number, b: number, c: number, d: number) => void; export const stylesheetresource_new: (a: number, b: number, c: number) => void; export const selector_new: () => number; diff --git a/packages/web-platform/web-core/binary/encode/encode.d.ts b/packages/web-platform/web-core/binary/encode/encode.d.ts index 6844850a6a..a4534787c6 100644 --- a/packages/web-platform/web-core/binary/encode/encode.d.ts +++ b/packages/web-platform/web-core/binary/encode/encode.d.ts @@ -109,9 +109,9 @@ export class StyleInfoDecoder { [Symbol.dispose](): void; } -export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; -export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; export function get_font_face_content(buffer: Uint8Array): string; diff --git a/packages/web-platform/web-core/binary/encode/encode_bg.wasm.d.ts b/packages/web-platform/web-core/binary/encode/encode_bg.wasm.d.ts index b6a0949cde..6d26873978 100644 --- a/packages/web-platform/web-core/binary/encode/encode_bg.wasm.d.ts +++ b/packages/web-platform/web-core/binary/encode/encode_bg.wasm.d.ts @@ -6,8 +6,8 @@ export const __wbg_rule_free: (a: number, b: number) => void; export const __wbg_ruleprelude_free: (a: number, b: number) => void; export const __wbg_selector_free: (a: number, b: number) => void; export const __wbg_styleinfodecoder_free: (a: number, b: number) => void; -export const decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; -export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; +export const decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; +export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; export const get_font_face_content: (a: any) => [number, number, number, number]; export const get_style_content: (a: any) => [number, number, number, number]; export const rawstyleinfo_append_import: (a: number, b: number, c: number) => void; diff --git a/packages/web-platform/web-core/binary/server/server.d.ts b/packages/web-platform/web-core/binary/server/server.d.ts index 7c45e78d63..cf5898de3c 100644 --- a/packages/web-platform/web-core/binary/server/server.d.ts +++ b/packages/web-platform/web-core/binary/server/server.d.ts @@ -15,7 +15,7 @@ export class MainThreadServerContext { get_parent(child_id: number): number | undefined; get_tag(element_id: number): string | undefined; insert_before(parent_id: number, child_id: number, ref_id?: number | null): void; - constructor(view_attributes: string, enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean); + constructor(view_attributes: string, enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean); push_style_sheet(resource: StyleSheetResource, entry_name?: string | null): void; remove_attribute(element_id: number, key: string): void; remove_child(parent_id: number, child_id: number): void; @@ -130,9 +130,9 @@ export class StyleSheetResource { constructor(buffer: Uint8Array, _document: any); } -export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function decode_style_info(buffer: Uint8Array, entry_name: string | null | undefined, config_enable_css_selector: boolean, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; -export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean): Uint8Array; +export function encode_legacy_json_generated_raw_style_info(raw_style_info: RawStyleInfo, config_enable_css_selector: boolean, entry_name: string | null | undefined, transform_vw: boolean, transform_vh: boolean, transform_rem: boolean): Uint8Array; export function get_font_face_content(buffer: Uint8Array): string; diff --git a/packages/web-platform/web-core/binary/server/server_bg.wasm.d.ts b/packages/web-platform/web-core/binary/server/server_bg.wasm.d.ts index e0f2ca4a39..f334a6c1af 100644 --- a/packages/web-platform/web-core/binary/server/server_bg.wasm.d.ts +++ b/packages/web-platform/web-core/binary/server/server_bg.wasm.d.ts @@ -7,8 +7,8 @@ export const __wbg_rule_free: (a: number, b: number) => void; export const __wbg_ruleprelude_free: (a: number, b: number) => void; export const __wbg_selector_free: (a: number, b: number) => void; export const __wbg_stylesheetresource_free: (a: number, b: number) => void; -export const decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; -export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number) => [number, number, number]; +export const decode_style_info: (a: any, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; +export const encode_legacy_json_generated_raw_style_info: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number]; export const get_font_face_content: (a: any) => [number, number, number, number]; export const get_style_content: (a: any) => [number, number, number, number]; export const init_server_in_shadow_css: (a: number, b: number) => void; @@ -23,7 +23,7 @@ export const mainthreadservercontext_get_page_css: (a: number) => [number, numbe export const mainthreadservercontext_get_parent: (a: number, b: number) => number; export const mainthreadservercontext_get_tag: (a: number, b: number) => [number, number]; export const mainthreadservercontext_insert_before: (a: number, b: number, c: number, d: number) => void; -export const mainthreadservercontext_new: (a: number, b: number, c: number, d: number, e: number) => number; +export const mainthreadservercontext_new: (a: number, b: number, c: number, d: number, e: number, f: number) => number; export const mainthreadservercontext_push_style_sheet: (a: number, b: number, c: number, d: number) => [number, number]; export const mainthreadservercontext_remove_attribute: (a: number, b: number, c: number, d: number) => void; export const mainthreadservercontext_remove_child: (a: number, b: number, c: number) => void; diff --git a/packages/web-platform/web-core/css/index.css b/packages/web-platform/web-core/css/index.css index ff9985279c..d73bf259ef 100644 --- a/packages/web-platform/web-core/css/index.css +++ b/packages/web-platform/web-core/css/index.css @@ -40,6 +40,11 @@ lynx-view::part(page) { --vh-unit: inherit; } +@property --rem-unit { + syntax: ""; + inherits: true; +} + @property --rpx-unit { syntax: ""; inherits: true; diff --git a/packages/web-platform/web-core/src/main_thread/client/element_apis/style_apis.rs b/packages/web-platform/web-core/src/main_thread/client/element_apis/style_apis.rs index d97e846b51..0f4246813d 100644 --- a/packages/web-platform/web-core/src/main_thread/client/element_apis/style_apis.rs +++ b/packages/web-platform/web-core/src/main_thread/client/element_apis/style_apis.rs @@ -110,12 +110,14 @@ pub fn set_inline_styles_in_str( styles: String, transform_vw: bool, transform_vh: bool, + transform_rem: bool, ) -> bool { let transformed_style_str = transform_inline_style_string( &styles, &crate::style_transformer::token_transformer::TransformerConfig { transform_vw, transform_vh, + transform_rem, }, ); // we compare the transformed style string with the original one @@ -133,12 +135,14 @@ pub fn set_inline_styles_in_key_value_vec( k_v_vec: Vec, transform_vw: bool, transform_vh: bool, + transform_rem: bool, ) { let transformed_style_str = transform_inline_style_key_value_vec( k_v_vec, &crate::style_transformer::token_transformer::TransformerConfig { transform_vw, transform_vh, + transform_rem, }, ); let _ = dom.set_attribute("style", &transformed_style_str); diff --git a/packages/web-platform/web-core/src/main_thread/server/main_thread_server_context.rs b/packages/web-platform/web-core/src/main_thread/server/main_thread_server_context.rs index d7bd8734e8..6afb126254 100644 --- a/packages/web-platform/web-core/src/main_thread/server/main_thread_server_context.rs +++ b/packages/web-platform/web-core/src/main_thread/server/main_thread_server_context.rs @@ -34,6 +34,7 @@ pub struct MainThreadServerContext { enable_css_selector: bool, transform_vw: bool, transform_vh: bool, + transform_rem: bool, } #[wasm_bindgen] @@ -44,6 +45,7 @@ impl MainThreadServerContext { enable_css_selector: bool, transform_vw: bool, transform_vh: bool, + transform_rem: bool, ) -> Self { Self { elements: Vec::new(), @@ -52,6 +54,7 @@ impl MainThreadServerContext { enable_css_selector, transform_vw, transform_vh, + transform_rem, } } @@ -272,6 +275,7 @@ impl MainThreadServerContext { &crate::style_transformer::token_transformer::TransformerConfig { transform_vw: self.transform_vw, transform_vh: self.transform_vh, + transform_rem: self.transform_rem, }, ); element.set_attribute(key, transformed); @@ -329,6 +333,7 @@ impl MainThreadServerContext { &crate::style_transformer::token_transformer::TransformerConfig { transform_vw: self.transform_vw, transform_vh: self.transform_vh, + transform_rem: self.transform_rem, }, ); if transformed_style_str == styles { @@ -346,6 +351,7 @@ impl MainThreadServerContext { &crate::style_transformer::token_transformer::TransformerConfig { transform_vw: self.transform_vw, transform_vh: self.transform_vh, + transform_rem: self.transform_rem, }, ); if let Some(Some(element)) = self.elements.get_mut(element_id) { @@ -524,7 +530,7 @@ mod tests { #[test] fn test_html_generation() { - let mut ctx = MainThreadServerContext::new("".to_string(), true, false, false); + let mut ctx = MainThreadServerContext::new("".to_string(), true, false, false, false); // Create
let div_id = ctx.create_element("div".to_string(), None, None, None); @@ -555,7 +561,7 @@ mod tests { #[test] fn test_set_style_empty_value() { - let mut ctx = MainThreadServerContext::new("".to_string(), true, false, false); + let mut ctx = MainThreadServerContext::new("".to_string(), true, false, false, false); let div_id = ctx.create_element("div".to_string(), None, None, None); // This should not panic diff --git a/packages/web-platform/web-core/src/style_transformer/inline_style.rs b/packages/web-platform/web-core/src/style_transformer/inline_style.rs index a598a79fbf..fd98cfdd9b 100644 --- a/packages/web-platform/web-core/src/style_transformer/inline_style.rs +++ b/packages/web-platform/web-core/src/style_transformer/inline_style.rs @@ -30,6 +30,7 @@ pub(crate) fn transform_inline_style_string(source: &str, config: &TransformerCo TransformerConfig { transform_vw: config.transform_vw, transform_vh: config.transform_vh, + transform_rem: config.transform_rem, }, ); transformer.parse(source); @@ -49,6 +50,7 @@ pub(crate) fn transform_inline_style_key_value_vec( TransformerConfig { transform_vw: config.transform_vw, transform_vh: config.transform_vh, + transform_rem: config.transform_rem, }, ); diff --git a/packages/web-platform/web-core/src/style_transformer/token_transformer.rs b/packages/web-platform/web-core/src/style_transformer/token_transformer.rs index b23ab5f1e9..6dc38d8f27 100644 --- a/packages/web-platform/web-core/src/style_transformer/token_transformer.rs +++ b/packages/web-platform/web-core/src/style_transformer/token_transformer.rs @@ -11,6 +11,7 @@ use std::borrow::Cow; pub struct TransformerConfig { pub transform_vw: bool, pub transform_vh: bool, + pub transform_rem: bool, } /** @@ -40,6 +41,12 @@ pub(crate) fn transform_one_token<'a>( token_type, Cow::Owned(format!("calc({value} * var(--ppx-unit))")), ); + } else if config.transform_rem && suffix.eq_ignore_ascii_case("rem") { + let value = &token_value[..len - 3]; + return ( + token_type, + Cow::Owned(format!("calc({value} * var(--rem-unit))")), + ); } } if len > 2 { @@ -142,6 +149,25 @@ mod tests { assert_eq!(tv, "100vh"); } + #[test] + fn test_transform_rem() { + let (_, tv) = transform_one_token( + DIMENSION_TOKEN, + "2rem", + &TransformerConfig { + transform_rem: true, + ..Default::default() + }, + ); + assert_eq!(tv, "calc(2 * var(--rem-unit))"); + } + + #[test] + fn test_transform_rem_disabled() { + let (_, tv) = transform_one_token(DIMENSION_TOKEN, "2rem", &TransformerConfig::default()); + assert_eq!(tv, "2rem"); + } + #[test] fn test_transform_vw_case_insensitive() { let (_, tv) = transform_one_token( diff --git a/packages/web-platform/web-core/src/template/template_sections/style_info/decoded_style_data.rs b/packages/web-platform/web-core/src/template/template_sections/style_info/decoded_style_data.rs index cbaa320d70..d28eef506f 100644 --- a/packages/web-platform/web-core/src/template/template_sections/style_info/decoded_style_data.rs +++ b/packages/web-platform/web-core/src/template/template_sections/style_info/decoded_style_data.rs @@ -48,6 +48,7 @@ pub fn decode_style_info( config_enable_css_selector: bool, transform_vw: bool, transform_vh: bool, + transform_rem: bool, ) -> Result { let buf = buffer.to_vec(); let data = unsafe { rkyv::from_bytes_unchecked::(&buf) } @@ -59,6 +60,7 @@ pub fn decode_style_info( config_enable_css_selector, transform_vw, transform_vh, + transform_rem, )? .into(); @@ -76,6 +78,7 @@ pub fn encode_legacy_json_generated_raw_style_info( entry_name: Option, transform_vw: bool, transform_vh: bool, + transform_rem: bool, ) -> Result { let decode_data: DecodedStyleData = StyleInfoDecoder::new( raw_style_info, @@ -83,6 +86,7 @@ pub fn encode_legacy_json_generated_raw_style_info( config_enable_css_selector, transform_vw, transform_vh, + transform_rem, )? .into(); let serialized = rkyv::to_bytes::<_, 1024>(&decode_data).map_err(|e| { diff --git a/packages/web-platform/web-core/src/template/template_sections/style_info/raw_style_info.rs b/packages/web-platform/web-core/src/template/template_sections/style_info/raw_style_info.rs index 4dc4d92c95..4c766271fd 100644 --- a/packages/web-platform/web-core/src/template/template_sections/style_info/raw_style_info.rs +++ b/packages/web-platform/web-core/src/template/template_sections/style_info/raw_style_info.rs @@ -141,7 +141,7 @@ impl RawStyleInfo { */ #[cfg(feature = "encode")] pub fn encode(&mut self) -> Result { - let decoded_style_info = StyleInfoDecoder::new(self.clone(), None, true, false, false)?; + let decoded_style_info = StyleInfoDecoder::new(self.clone(), None, true, false, false, false)?; self.style_content_str_size_hint = decoded_style_info.style_content.len(); let serialized = rkyv::to_bytes::<_, 1024>(self) .map_err(|e| JsError::new(&format!("Failed to encode RawStyleInfo: {e:?}")))?; diff --git a/packages/web-platform/web-core/src/template/template_sections/style_info/style_info_decoder.rs b/packages/web-platform/web-core/src/template/template_sections/style_info/style_info_decoder.rs index eceeee5e42..418e829364 100644 --- a/packages/web-platform/web-core/src/template/template_sections/style_info/style_info_decoder.rs +++ b/packages/web-platform/web-core/src/template/template_sections/style_info/style_info_decoder.rs @@ -28,6 +28,7 @@ pub struct StyleInfoDecoder { config_enable_css_selector: bool, transform_vw: bool, transform_vh: bool, + transform_rem: bool, entry_name: Option, css_og_current_processing_css_ids: Option>, css_og_current_processing_class_selector_names: Option>, @@ -40,6 +41,7 @@ impl StyleInfoDecoder { config_enable_css_selector: bool, transform_vw: bool, transform_vh: bool, + transform_rem: bool, ) -> Result { let flattened_style_info: FlattenedStyleInfo = raw_style_info.into(); let mut decoded_style_info = StyleInfoDecoder { @@ -55,6 +57,7 @@ impl StyleInfoDecoder { config_enable_css_selector, transform_vw, transform_vh, + transform_rem, is_processing_font_face: false, css_og_current_processing_css_ids: None, css_og_current_processing_class_selector_names: None, @@ -288,6 +291,7 @@ impl StyleInfoDecoder { crate::style_transformer::token_transformer::TransformerConfig { transform_vw: self.transform_vw, transform_vh: self.transform_vh, + transform_rem: self.transform_rem, }, ); @@ -383,6 +387,7 @@ mod test { config_enable_css_selector, false, false, + false, ) .unwrap() } @@ -911,7 +916,7 @@ mod tests_roundtrip { let decoded_raw = unsafe { rkyv::from_bytes_unchecked::(&buf) } .expect("RawStyleInfo decode should succeed"); - let decoder = StyleInfoDecoder::new(decoded_raw, None, true, false, false) + let decoder = StyleInfoDecoder::new(decoded_raw, None, true, false, false, false) .expect("StyleInfoDecoder should succeed"); let decoded_string = decoder.style_content; @@ -962,7 +967,7 @@ mod tests_roundtrip { let decoded_raw = unsafe { rkyv::from_bytes_unchecked::(&buf) } .expect("RawStyleInfo decode should succeed"); - let decoder = StyleInfoDecoder::new(decoded_raw, None, true, false, false) + let decoder = StyleInfoDecoder::new(decoded_raw, None, true, false, false, false) .expect("StyleInfoDecoder should succeed"); let decoded_string = decoder.style_content; diff --git a/packages/web-platform/web-core/tests/deploy.spec.ts b/packages/web-platform/web-core/tests/deploy.spec.ts new file mode 100644 index 0000000000..8e07ebf844 --- /dev/null +++ b/packages/web-platform/web-core/tests/deploy.spec.ts @@ -0,0 +1,60 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { executeTemplate } from '../ts/server/deploy.js'; +import * as decodeModule from '../ts/server/decode.js'; +import type { DecodedTemplate } from '../ts/server/decode.js'; +import * as createElementAPIModule from '../ts/server/elementAPIs/createElementAPI.js'; +import type { MainThreadServerContext } from '../ts/server/wasm.js'; +import type { ElementPAPIs } from '../ts/types/index.js'; + +vi.mock('../ts/server/decode.js', () => ({ + decodeTemplate: vi.fn(), +})); + +vi.mock('../ts/server/elementAPIs/createElementAPI.js', () => ({ + createElementAPI: vi.fn(), +})); + +vi.mock('vm', () => ({ + createContext: vi.fn(), + runInContext: vi.fn(), +})); + +describe('executeTemplate', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should pass viewAttributes correctly when called with 7 arguments (legacy call)', () => { + const mockDecodeTemplate = vi.mocked(decodeModule.decodeTemplate); + const mockCreateElementAPI = vi.mocked( + createElementAPIModule.createElementAPI, + ); + + mockDecodeTemplate.mockReturnValue({ + config: { enableCSSSelector: 'true' }, + lepusCode: {}, + styleInfo: new Uint8Array(), + } as DecodedTemplate); + + mockCreateElementAPI.mockReturnValue({ + globalThisAPIs: {} as ElementPAPIs, + wasmContext: {} as MainThreadServerContext, + }); + + const dummyBuffer = Buffer.from('test'); + + executeTemplate( + dummyBuffer, + {}, + {}, + () => {}, + true, // transformVW + true, // transformVH + 'my-view-attr="123"', // viewAttributes + ); + + expect(mockCreateElementAPI).toHaveBeenCalled(); + // 3rd arg is viewAttributes + expect(mockCreateElementAPI.mock.calls[0][2]).toBe('my-view-attr="123"'); + }); +}); diff --git a/packages/web-platform/web-core/tests/template-manager.spec.ts b/packages/web-platform/web-core/tests/template-manager.spec.ts index b8402996ed..a45f35314b 100644 --- a/packages/web-platform/web-core/tests/template-manager.spec.ts +++ b/packages/web-platform/web-core/tests/template-manager.spec.ts @@ -87,6 +87,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, ); // Verify data using getCustomSection @@ -125,6 +126,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, ), ) .rejects.toThrow('Unsupported version: 2'); @@ -169,6 +171,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, ); // Verify data using getCustomSection @@ -225,6 +228,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, ), ).rejects.toThrow('Stream failed'); @@ -258,6 +262,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, overrideConfig as any, ); @@ -333,6 +338,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, ); // Verify config @@ -384,6 +390,7 @@ describe('Template Manager', () => { Promise.resolve(mockLynxViewInstance), false, false, + false, ); // Verify config has appType = lazy and isLazy = true @@ -432,12 +439,14 @@ describe('Template Manager', () => { Promise.resolve(instance1 as unknown as LynxViewInstance), false, false, + false, ), templateManager.fetchBundle( 'http://example.com/template_concurrent', Promise.resolve(instance2 as unknown as LynxViewInstance), false, false, + false, ), ]); diff --git a/packages/web-platform/web-core/ts/client/decodeWorker/cssLoader.ts b/packages/web-platform/web-core/ts/client/decodeWorker/cssLoader.ts index a88dafe470..9c71809cae 100644 --- a/packages/web-platform/web-core/ts/client/decodeWorker/cssLoader.ts +++ b/packages/web-platform/web-core/ts/client/decodeWorker/cssLoader.ts @@ -20,6 +20,7 @@ export function loadStyleFromJSON( configEnableCSSSelector: boolean, transformVW: boolean, transformVH: boolean, + transformREM: boolean, entryName?: string, ): Uint8Array { const rawStyleInfo = new wasmInstance.RawStyleInfo(); @@ -105,6 +106,7 @@ export function loadStyleFromJSON( entryName, transformVW, transformVH, + transformREM, ); } diff --git a/packages/web-platform/web-core/ts/client/decodeWorker/decode.worker.ts b/packages/web-platform/web-core/ts/client/decodeWorker/decode.worker.ts index 7fc4914cab..600e3fb5c9 100644 --- a/packages/web-platform/web-core/ts/client/decodeWorker/decode.worker.ts +++ b/packages/web-platform/web-core/ts/client/decodeWorker/decode.worker.ts @@ -106,7 +106,14 @@ self.onmessage = async ( wasmInstance.initSync({ module: wasmModule }); wasmModuleLoadedResolve(); } else if (data.type === 'load') { - const { url, fetchUrl, overrideConfig, transformVW, transformVH } = data; + const { + url, + fetchUrl, + overrideConfig, + transformVW, + transformVH, + transformREM, + } = data; try { const response = await fetch(fetchUrl, { headers: { @@ -117,7 +124,14 @@ self.onmessage = async ( throw new Error(`Failed to fetch template: ${response.statusText}`); } const reader = response.body.getReader(); - await handleStream(url, reader, transformVW, transformVH, overrideConfig); + await handleStream( + url, + reader, + transformVW, + transformVH, + transformREM, + overrideConfig, + ); postMessage({ type: 'done', url } as MainMessage); } catch (error) { postMessage( @@ -131,6 +145,7 @@ async function handleStream( reader: ReadableStreamDefaultReader, transformVW: boolean, transformVH: boolean, + transformREM: boolean, overrideConfig?: Partial, ) { const streamReader = new StreamReader(reader); @@ -148,7 +163,14 @@ async function handleStream( const decoder = new TextDecoder(); const jsonStr = decoder.decode(headerBytes) + decoder.decode(rest); const json = JSON.parse(jsonStr); - await handleJSON(json, url, transformVW, transformVH, overrideConfig); + await handleJSON( + json, + url, + transformVW, + transformVH, + transformREM, + overrideConfig, + ); return; } @@ -227,6 +249,7 @@ async function handleStream( config['enableCSSSelector'] === 'true', transformVW, transformVH, + transformREM, ); postMessage( { @@ -313,6 +336,7 @@ async function handleJSON( url: string, transformVW: boolean, transformVH: boolean, + transformREM: boolean, overrideConfig?: Partial, ) { // Configurations @@ -350,6 +374,7 @@ async function handleJSON( config['enableCSSSelector'] === 'true', transformVW, transformVH, + transformREM, config['isLazy'] === 'true' ? url : undefined, ); postMessage( diff --git a/packages/web-platform/web-core/ts/client/decodeWorker/types.ts b/packages/web-platform/web-core/ts/client/decodeWorker/types.ts index a442481d99..3cdef345a6 100644 --- a/packages/web-platform/web-core/ts/client/decodeWorker/types.ts +++ b/packages/web-platform/web-core/ts/client/decodeWorker/types.ts @@ -15,6 +15,7 @@ export interface LoadTemplateMessage extends DecodeWorkerMessage { fetchUrl: string; transformVW: boolean; transformVH: boolean; + transformREM: boolean; overrideConfig?: Record; } diff --git a/packages/web-platform/web-core/ts/client/mainthread/LynxView.ts b/packages/web-platform/web-core/ts/client/mainthread/LynxView.ts index d871057e08..d58c376f21 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/LynxView.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/LynxView.ts @@ -75,6 +75,7 @@ export class LynxViewElement extends HTMLElement { 'browser-config', 'transform-vw', 'transform-vh', + 'transform-rem', ]; /** * @private @@ -163,6 +164,24 @@ export class LynxViewElement extends HTMLElement { } } + #transformREM: boolean = false; + /** + * @public + * @property transformREM + * Enable evaluating rem unit to the current CSS var(--rem-unit) + */ + get transformREM(): boolean { + return this.#transformREM; + } + set transformREM(val: boolean) { + this.#transformREM = val; + if (val) { + this.setAttribute('transform-rem', ''); + } else { + this.removeAttribute('transform-rem'); + } + } + constructor() { super(); if (!this.onNativeModulesCall) { @@ -373,6 +392,9 @@ export class LynxViewElement extends HTMLElement { case 'transform-vh': this.transformVH = newValue !== 'false' && newValue !== null; break; + case 'transform-rem': + this.transformREM = newValue !== 'false' && newValue !== null; + break; } } } @@ -459,6 +481,7 @@ export class LynxViewElement extends HTMLElement { this.#initI18nResources, this.transformVW, this.transformVH, + this.transformREM, this.browserConfig, ); }); @@ -467,6 +490,7 @@ export class LynxViewElement extends HTMLElement { lynxViewInstance, this.transformVW, this.transformVH, + this.transformREM, undefined, // overrideConfig ); @@ -493,6 +517,7 @@ export class LynxViewElement extends HTMLElement { this.#upgradeProperty('browserConfig'); this.#upgradeProperty('transformVW'); this.#upgradeProperty('transformVH'); + this.#upgradeProperty('transformREM'); if (this.url) { this.#url = this.url; } diff --git a/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts b/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts index fe18f9b244..ab3a7eabaa 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/LynxViewInstance.ts @@ -86,6 +86,7 @@ export class LynxViewInstance implements AsyncDisposable { initI18nResources?: InitI18nResources, private readonly transformVW: boolean = false, private readonly transformVH: boolean = false, + private readonly transformREM: boolean = false, browserConfig?: Record, ) { this.systemInfo = createSystemInfo(browserConfig); @@ -132,6 +133,7 @@ export class LynxViewInstance implements AsyncDisposable { defaultOverflowVisible, this.transformVW, this.transformVH, + this.transformREM, ), createMainThreadGlobalAPIs( this, @@ -233,6 +235,7 @@ export class LynxViewInstance implements AsyncDisposable { Promise.resolve(this), this.transformVW, this.transformVH, + this.transformREM, { enableCSSSelector: this.#pageConfig!['enableCSSSelector'], }, diff --git a/packages/web-platform/web-core/ts/client/mainthread/TemplateManager.ts b/packages/web-platform/web-core/ts/client/mainthread/TemplateManager.ts index 0bb03322d1..2ac62db93b 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/TemplateManager.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/TemplateManager.ts @@ -49,6 +49,7 @@ export class TemplateManager { lynxViewInstancePromise: Promise, transformVW: boolean, transformVH: boolean, + transformREM: boolean, overrideConfig?: Record, ): Promise { if (this.#bundles.has(url) && !overrideConfig) { @@ -80,6 +81,7 @@ export class TemplateManager { lynxViewInstancePromise, transformVW, transformVH, + transformREM, overrideConfig, ); this.#loadingPromises.set(url, promise); @@ -92,6 +94,7 @@ export class TemplateManager { lynxViewInstancePromise: Promise, transformVW: boolean, transformVH: boolean, + transformREM: boolean, overrideConfig?: Partial, ): Promise { const currentTime = performance.now() + performance.timeOrigin; @@ -112,6 +115,7 @@ export class TemplateManager { fetchUrl: (new URL(url, location.href)).toString(), transformVW, transformVH, + transformREM, overrideConfig, }; this.#worker!.postMessage(msg); diff --git a/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/createElementAPI.ts b/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/createElementAPI.ts index a3c7d7ebc8..27ab251815 100644 --- a/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/createElementAPI.ts +++ b/packages/web-platform/web-core/ts/client/mainthread/elementAPIs/createElementAPI.ts @@ -66,6 +66,7 @@ export function createElementAPI( config_default_overflow_visible: boolean, transform_vw: boolean, transform_vh: boolean, + transform_rem: boolean, ): ElementPAPIs { const wasmContext = new MainThreadWasmContext( rootDom, @@ -294,6 +295,7 @@ export function createElementAPI( value, transform_vw, transform_vh, + transform_rem, ) ) { element.setAttribute('style', value); @@ -312,6 +314,7 @@ export function createElementAPI( vec, transform_vw, transform_vh, + transform_rem, ); } } diff --git a/packages/web-platform/web-core/ts/server/decode.ts b/packages/web-platform/web-core/ts/server/decode.ts index f929b72cc9..df64244064 100644 --- a/packages/web-platform/web-core/ts/server/decode.ts +++ b/packages/web-platform/web-core/ts/server/decode.ts @@ -18,6 +18,7 @@ export function decodeTemplate( buffer: Uint8Array, transformVW: boolean, transformVH: boolean, + transformREM: boolean, ): DecodedTemplate { if (buffer.length < 8) { throw new Error('Buffer too short for Magic Header'); @@ -89,6 +90,7 @@ export function decodeTemplate( config['enableCSSSelector'] === 'true', transformVW, transformVH, + transformREM, ); styleInfo = buffer; break; diff --git a/packages/web-platform/web-core/ts/server/deploy.ts b/packages/web-platform/web-core/ts/server/deploy.ts index 9c5e28bf78..8cf6c27f0e 100644 --- a/packages/web-platform/web-core/ts/server/deploy.ts +++ b/packages/web-platform/web-core/ts/server/deploy.ts @@ -15,8 +15,14 @@ export function executeTemplate( transformVW: boolean, transformVH: boolean, viewAttributes?: string, + transformREM?: boolean, ): string | undefined { - const result = decodeTemplate(templateBuffer, transformVW, transformVH); + const result = decodeTemplate( + templateBuffer, + transformVW, + transformVH, + !!transformREM, + ); const config = result.config; const binding: SSRBinding = { ssrResult: '' }; @@ -30,6 +36,7 @@ export function executeTemplate( defaultDisplayLinear: config['defaultDisplayLinear'] !== 'false', // Default to true if not present or 'true' transformVW: transformVW, transformVH: transformVH, + transformREM: !!transformREM, }, ); diff --git a/packages/web-platform/web-core/ts/server/elementAPIs/createElementAPI.ts b/packages/web-platform/web-core/ts/server/elementAPIs/createElementAPI.ts index a6f120d7df..7caee0ac37 100644 --- a/packages/web-platform/web-core/ts/server/elementAPIs/createElementAPI.ts +++ b/packages/web-platform/web-core/ts/server/elementAPIs/createElementAPI.ts @@ -98,6 +98,7 @@ export function createElementAPI( defaultDisplayLinear: boolean; transformVW: boolean; transformVH: boolean; + transformREM: boolean; }, ): { globalThisAPIs: ElementPAPIs; wasmContext: MainThreadServerContext } { const wasmContext = new MainThreadServerContext( @@ -105,6 +106,7 @@ export function createElementAPI( config.enableCSSSelector, config.transformVW, config.transformVH, + config.transformREM, ); if (styleInfo) { const resource = new StyleSheetResource(styleInfo, undefined);