From c3fa234445bd621db8968e02e4028c3c73b2fc47 Mon Sep 17 00:00:00 2001
From: pupiltong <12288479+PupilTong@users.noreply.github.com>
Date: Mon, 11 May 2026 20:01:25 +0800
Subject: [PATCH] feat(web-core): support frame element
---
.changeset/frame-web-core.md | 5 ++
.../web-core-e2e/tests/reactlynx.spec.ts | 65 ++++++++++++++++
.../reactlynx/api-frame-auto-height/index.jsx | 19 +++++
.../reactlynx/api-frame-auto-width/index.jsx | 19 +++++
.../reactlynx/api-frame-bindload/index.jsx | 27 +++++++
.../reactlynx/api-frame-data-update/index.jsx | 23 ++++++
.../tests/reactlynx/api-frame-data/index.jsx | 18 +++++
.../reactlynx/api-frame-element-map/index.jsx | 18 +++++
.../api-frame-global-props/index.jsx | 18 +++++
.../tests/reactlynx/api-frame-inner/index.jsx | 19 +++++
.../tests/reactlynx/api-frame-src/index.jsx | 18 +++++
.../web-platform/web-core/src/constants.rs | 1 +
.../style_info/style_info_decoder.rs | 38 +++++++++
.../web-core/ts/client/mainthread/LynxView.ts | 77 ++++++++++++++-----
.../elementAPIs/createElementAPI.ts | 29 +++++++
.../web-platform/web-core/ts/constants.ts | 1 +
.../ts/server/elementAPIs/createElementAPI.ts | 10 +++
.../web-core/ts/types/IElementPAPI.ts | 3 +
18 files changed, 390 insertions(+), 18 deletions(-)
create mode 100644 .changeset/frame-web-core.md
create mode 100644 packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-auto-height/index.jsx
create mode 100644 packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-auto-width/index.jsx
create mode 100644 packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-bindload/index.jsx
create mode 100644 packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-data-update/index.jsx
create mode 100644 packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-data/index.jsx
create mode 100644 packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-element-map/index.jsx
create mode 100644 packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-global-props/index.jsx
create mode 100644 packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-inner/index.jsx
create mode 100644 packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-src/index.jsx
diff --git a/.changeset/frame-web-core.md b/.changeset/frame-web-core.md
new file mode 100644
index 0000000000..59945c9170
--- /dev/null
+++ b/.changeset/frame-web-core.md
@@ -0,0 +1,5 @@
+---
+"@lynx-js/web-core": patch
+---
+
+Add web support for the `` element by mapping it to ``.
diff --git a/packages/web-platform/web-core-e2e/tests/reactlynx.spec.ts b/packages/web-platform/web-core-e2e/tests/reactlynx.spec.ts
index c93d3eae21..4874396030 100644
--- a/packages/web-platform/web-core-e2e/tests/reactlynx.spec.ts
+++ b/packages/web-platform/web-core-e2e/tests/reactlynx.spec.ts
@@ -261,6 +261,71 @@ test.describe('reactlynx3 tests', () => {
await expect(height).toHaveText('5678');
});
+ test('api-frame-element-map', async ({ page }, { title }) => {
+ await goto(page, title);
+ await expect(page.locator('#target')).toHaveJSProperty(
+ 'tagName',
+ 'LYNX-VIEW',
+ );
+ });
+
+ test('api-frame-src', async ({ page }, { title }) => {
+ await goto(page, title);
+ await expect(page.locator('#frame-ready')).toHaveText('frame:ready');
+ });
+
+ test('api-frame-data', async ({ page }, { title }) => {
+ await goto(page, title);
+ await expect(page.locator('#frame-data')).toHaveText('data:from-data');
+ });
+
+ test('api-frame-data-update', async ({ page }, { title }) => {
+ await goto(page, title);
+ await expect(page.locator('#frame-data')).toHaveText('data:before');
+ await page.locator('#update-frame-data').click();
+ await expect(page.locator('#frame-data')).toHaveText('data:after');
+ });
+
+ test('api-frame-global-props', async ({ page }, { title }) => {
+ await goto(page, title);
+ await expect(page.locator('#frame-global-props')).toHaveText(
+ 'global:from-global-props',
+ );
+ });
+
+ test('api-frame-bindload', async ({ page }, { title }) => {
+ await goto(page, title);
+ await expect(page.locator('#frame-load-status')).toHaveText('0');
+ await expect(page.locator('#frame-load-message')).toHaveText('success');
+ await expect(page.locator('#frame-load-url')).toContainText(
+ '/dist/api-frame-inner.web.bundle',
+ );
+ });
+
+ test('api-frame-auto-height', async ({ page }, { title }) => {
+ await goto(page, title);
+ await expect(page.locator('#target')).toHaveAttribute(
+ 'auto-height',
+ 'true',
+ );
+ await expect(page.locator('#target')).not.toHaveAttribute(
+ 'height',
+ 'auto',
+ );
+ });
+
+ test('api-frame-auto-width', async ({ page }, { title }) => {
+ await goto(page, title);
+ await expect(page.locator('#target')).toHaveAttribute(
+ 'auto-width',
+ 'true',
+ );
+ await expect(page.locator('#target')).not.toHaveAttribute(
+ 'width',
+ 'auto',
+ );
+ });
+
test('basic-bindtap-simultaneous', async ({ page }, { title }) => {
await goto(page, title);
await wait(100);
diff --git a/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-auto-height/index.jsx b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-auto-height/index.jsx
new file mode 100644
index 0000000000..369ac24666
--- /dev/null
+++ b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-auto-height/index.jsx
@@ -0,0 +1,19 @@
+// Copyright 2026 The Lynx Authors. All rights reserved.
+// Licensed under the Apache License Version 2.0 that can be found in the
+// LICENSE file in the root directory of this source tree.
+import { root } from '@lynx-js/react';
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+root.render();
diff --git a/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-auto-width/index.jsx b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-auto-width/index.jsx
new file mode 100644
index 0000000000..c5313ea6ad
--- /dev/null
+++ b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-auto-width/index.jsx
@@ -0,0 +1,19 @@
+// Copyright 2026 The Lynx Authors. All rights reserved.
+// Licensed under the Apache License Version 2.0 that can be found in the
+// LICENSE file in the root directory of this source tree.
+import { root } from '@lynx-js/react';
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+root.render();
diff --git a/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-bindload/index.jsx b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-bindload/index.jsx
new file mode 100644
index 0000000000..098c811a80
--- /dev/null
+++ b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-bindload/index.jsx
@@ -0,0 +1,27 @@
+// Copyright 2026 The Lynx Authors. All rights reserved.
+// Licensed under the Apache License Version 2.0 that can be found in the
+// LICENSE file in the root directory of this source tree.
+import { root, useState } from '@lynx-js/react';
+
+function App() {
+ const [detail, setDetail] = useState({
+ statusCode: -1,
+ statusMessage: '',
+ url: '',
+ });
+
+ return (
+
+ setDetail(event.detail)}
+ style={{ width: '300px', height: '120px' }}
+ />
+ {detail.statusCode}
+ {detail.statusMessage}
+ {detail.url}
+
+ );
+}
+
+root.render();
diff --git a/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-data-update/index.jsx b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-data-update/index.jsx
new file mode 100644
index 0000000000..684ddee557
--- /dev/null
+++ b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-data-update/index.jsx
@@ -0,0 +1,23 @@
+// Copyright 2026 The Lynx Authors. All rights reserved.
+// Licensed under the Apache License Version 2.0 that can be found in the
+// LICENSE file in the root directory of this source tree.
+import { root, useState } from '@lynx-js/react';
+
+function App() {
+ const [label, setLabel] = useState('before');
+
+ return (
+
+
+ setLabel('after')}>
+ update
+
+
+ );
+}
+
+root.render();
diff --git a/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-data/index.jsx b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-data/index.jsx
new file mode 100644
index 0000000000..56c8eb93f6
--- /dev/null
+++ b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-data/index.jsx
@@ -0,0 +1,18 @@
+// Copyright 2026 The Lynx Authors. All rights reserved.
+// Licensed under the Apache License Version 2.0 that can be found in the
+// LICENSE file in the root directory of this source tree.
+import { root } from '@lynx-js/react';
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+root.render();
diff --git a/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-element-map/index.jsx b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-element-map/index.jsx
new file mode 100644
index 0000000000..a3c402d363
--- /dev/null
+++ b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-element-map/index.jsx
@@ -0,0 +1,18 @@
+// Copyright 2026 The Lynx Authors. All rights reserved.
+// Licensed under the Apache License Version 2.0 that can be found in the
+// LICENSE file in the root directory of this source tree.
+import { root } from '@lynx-js/react';
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+root.render();
diff --git a/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-global-props/index.jsx b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-global-props/index.jsx
new file mode 100644
index 0000000000..8895c8fe3b
--- /dev/null
+++ b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-global-props/index.jsx
@@ -0,0 +1,18 @@
+// Copyright 2026 The Lynx Authors. All rights reserved.
+// Licensed under the Apache License Version 2.0 that can be found in the
+// LICENSE file in the root directory of this source tree.
+import { root } from '@lynx-js/react';
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+root.render();
diff --git a/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-inner/index.jsx b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-inner/index.jsx
new file mode 100644
index 0000000000..1ff85d8f6d
--- /dev/null
+++ b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-inner/index.jsx
@@ -0,0 +1,19 @@
+// Copyright 2026 The Lynx Authors. All rights reserved.
+// Licensed under the Apache License Version 2.0 that can be found in the
+// LICENSE file in the root directory of this source tree.
+import { root, useInitData } from '@lynx-js/react';
+
+function App() {
+ const initData = useInitData();
+ const globalProps = lynx.__globalProps;
+
+ return (
+
+ frame:ready
+ data:{initData.label}
+ global:{globalProps.message}
+
+ );
+}
+
+root.render();
diff --git a/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-src/index.jsx b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-src/index.jsx
new file mode 100644
index 0000000000..a3c402d363
--- /dev/null
+++ b/packages/web-platform/web-core-e2e/tests/reactlynx/api-frame-src/index.jsx
@@ -0,0 +1,18 @@
+// Copyright 2026 The Lynx Authors. All rights reserved.
+// Licensed under the Apache License Version 2.0 that can be found in the
+// LICENSE file in the root directory of this source tree.
+import { root } from '@lynx-js/react';
+
+function App() {
+ return (
+
+
+
+ );
+}
+
+root.render();
diff --git a/packages/web-platform/web-core/src/constants.rs b/packages/web-platform/web-core/src/constants.rs
index 32521fc388..d058f921f1 100644
--- a/packages/web-platform/web-core/src/constants.rs
+++ b/packages/web-platform/web-core/src/constants.rs
@@ -32,6 +32,7 @@ lazy_static::lazy_static! {
("list", "x-list"),
("page", "div"),
("svg", "x-svg"),
+ ("frame", "lynx-view"),
]);
pub static ref HTML_TAG_TO_LYNX_TAG_MAP: FnvHashMap<&'static str, &'static str> = FnvHashMap::from_iter(LYNX_TAG_TO_HTML_TAG_MAP
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 418e829364..4df1840de1 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
@@ -703,6 +703,44 @@ mod test {
assert_eq!(result.style_content, expected);
}
+ #[test]
+ fn test_frame_type_selector() {
+ let raw_style_info = RawStyleInfo {
+ css_id_to_style_sheet: FnvHashMap::from_iter(vec![(
+ 0,
+ StyleSheet {
+ imports: vec![],
+ rules: vec![Rule {
+ nested_rules: vec![],
+ rule_type: RuleType::Declaration,
+ prelude: RulePrelude {
+ selector_list: vec![Selector {
+ simple_selectors: vec![OneSimpleSelector {
+ selector_type: OneSimpleSelectorType::TypeSelector,
+ value: "frame".to_string(),
+ }],
+ }],
+ },
+ declaration_block: DeclarationBlock {
+ declarations: vec![ParsedDeclaration {
+ property_id: CSSPropertyEnum::Height.into(),
+ is_important: false,
+ value_token_list: vec![ValueToken {
+ token_type: crate::css_tokenizer::token_types::DIMENSION_TOKEN,
+ value: "200px".to_string(),
+ }],
+ }],
+ },
+ }],
+ },
+ )]),
+ style_content_str_size_hint: 0,
+ };
+ let result = generate_string_buf(raw_style_info, true, None);
+ let expected = "lynx-view:not([l-e-name]){height:200px;}";
+ assert_eq!(result.style_content, expected);
+ }
+
#[test]
fn test_multiple_selectors() {
let raw_style_info = RawStyleInfo {
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 2b8270058c..ca4c4f4eab 100644
--- a/packages/web-platform/web-core/ts/client/mainthread/LynxView.ts
+++ b/packages/web-platform/web-core/ts/client/mainthread/LynxView.ts
@@ -70,8 +70,10 @@ export class LynxViewElement extends HTMLElement {
static tag = 'lynx-view' as const;
static observedAttributeAsProperties = [
'url',
+ 'src',
'global-props',
'init-data',
+ 'data',
'browser-config',
'transform-vw',
'transform-vh',
@@ -203,11 +205,21 @@ export class LynxViewElement extends HTMLElement {
get url(): string | undefined {
return this.#url;
}
- set url(val: string) {
+ set url(val: string | undefined) {
+ if (this.#url === val) {
+ return;
+ }
this.#url = val;
this.#render();
}
+ get src(): string | undefined {
+ return this.url;
+ }
+ set src(val: string | undefined) {
+ this.url = val;
+ }
+
#globalProps: Cloneable = {};
/**
* @public
@@ -218,11 +230,16 @@ export class LynxViewElement extends HTMLElement {
return this.#globalProps;
}
set globalProps(val: string | Cloneable) {
- if (typeof val === 'string') {
- this.#globalProps = JSON.parse(val);
- } else {
- this.#globalProps = val;
- }
+ const nextGlobalProps = typeof val === 'string' ? JSON.parse(val) : val;
+ this.#globalProps = nextGlobalProps;
+ this.#instance?.updateGlobalProps(nextGlobalProps);
+ }
+
+ get ['global-props'](): Cloneable {
+ return this.globalProps;
+ }
+ set ['global-props'](val: string | Cloneable) {
+ this.globalProps = val;
}
#initData: Cloneable = {};
@@ -235,11 +252,22 @@ export class LynxViewElement extends HTMLElement {
return this.#initData;
}
set initData(val: string | Cloneable) {
- if (typeof val === 'string') {
- this.#initData = JSON.parse(val);
- } else {
- this.#initData = val;
- }
+ const nextInitData = typeof val === 'string' ? JSON.parse(val) : val;
+ this.updateData(nextInitData);
+ }
+
+ get ['init-data'](): Cloneable {
+ return this.initData;
+ }
+ set ['init-data'](val: string | Cloneable) {
+ this.initData = val;
+ }
+
+ get data(): Cloneable {
+ return this.initData;
+ }
+ set data(val: string | Cloneable) {
+ this.initData = val;
}
#initI18nResources: InitI18nResources = [];
@@ -320,6 +348,7 @@ export class LynxViewElement extends HTMLElement {
processorName?: string,
callback?: () => void,
) {
+ this.#initData = data;
this.#instance?.updateData(data, processorName).then(() => {
callback?.();
});
@@ -331,7 +360,6 @@ export class LynxViewElement extends HTMLElement {
* update the `__globalProps`
*/
updateGlobalProps(data: Cloneable) {
- this.#instance?.updateGlobalProps(data);
this.globalProps = data;
}
@@ -371,20 +399,26 @@ export class LynxViewElement extends HTMLElement {
/**
* @private
*/
- attributeChangedCallback(name: string, oldValue: string, newValue: string) {
+ attributeChangedCallback(
+ name: string,
+ oldValue: string | null,
+ newValue: string | null,
+ ) {
if (oldValue !== newValue) {
switch (name) {
case 'url':
- this.#url = newValue;
+ case 'src':
+ this.url = newValue ?? undefined;
break;
case 'global-props':
- this.#globalProps = JSON.parse(newValue);
+ this.globalProps = newValue ? JSON.parse(newValue) : {};
break;
case 'browser-config':
- this.browserConfig = JSON.parse(newValue);
+ this.browserConfig = newValue ? JSON.parse(newValue) : undefined;
break;
case 'init-data':
- this.#initData = JSON.parse(newValue);
+ case 'data':
+ this.initData = newValue ? JSON.parse(newValue) : {};
break;
case 'transform-vw':
this.transformVW = newValue !== 'false' && newValue !== null;
@@ -446,7 +480,7 @@ export class LynxViewElement extends HTMLElement {
* @private
*/
async #render() {
- if (!this.#rendering && this.#connected) {
+ if (!this.#rendering && this.#connected && this.#url) {
this.#rendering = true;
if (!this.shadowRoot) {
this.attachShadow({ mode: 'open' });
@@ -524,6 +558,13 @@ export class LynxViewElement extends HTMLElement {
* @private
*/
connectedCallback() {
+ this.#upgradeProperty('url');
+ this.#upgradeProperty('src');
+ this.#upgradeProperty('globalProps');
+ this.#upgradeProperty('global-props');
+ this.#upgradeProperty('initData');
+ this.#upgradeProperty('init-data');
+ this.#upgradeProperty('data');
this.#upgradeProperty('browserConfig');
this.#upgradeProperty('transformVW');
this.#upgradeProperty('transformVH');
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 7e81b79b0e..c4be2b0d2f 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
@@ -59,6 +59,21 @@ const {
set_inline_styles_in_key_value_vec,
} = wasmInstance;
+function dispatchLynxViewLoadEvent(host: HTMLElement & { url?: string }) {
+ host.dispatchEvent(
+ new CustomEvent('load', {
+ detail: {
+ statusCode: 0,
+ statusMessage: 'success',
+ url: host.url ?? '',
+ },
+ bubbles: true,
+ cancelable: true,
+ composed: true,
+ }),
+ );
+}
+
export function createElementAPI(
rootDom: ShadowRoot,
mtsBinding: WASMJSBinding,
@@ -178,6 +193,17 @@ export function createElementAPI(
);
return dom;
},
+ __CreateFrame(parentComponentUniqueId) {
+ const dom = document.createElement(
+ LYNX_TAG_TO_HTML_TAG_MAP['frame']!,
+ ) as DecoratedHTMLElement;
+ dom[uniqueIdSymbol] = wasmContext.create_element_common(
+ parentComponentUniqueId,
+ dom,
+ new WeakRef(dom),
+ );
+ return dom;
+ },
__CreateRawText(text) {
const dom = document.createElement('raw-text') as DecoratedHTMLElement;
dom.setAttribute('text', text);
@@ -610,6 +636,9 @@ export function createElementAPI(
backgroundThread.markTiming('ui_operation_flush_start', pipelineId);
rootDom.appendChild(page);
(rootDom.host as HTMLElement).style.display = 'flex';
+ dispatchLynxViewLoadEvent(
+ rootDom.host as HTMLElement & { url?: string },
+ );
backgroundThread.markTiming('ui_operation_flush_end', pipelineId);
backgroundThread.markTiming('layout_end', pipelineId);
backgroundThread.markTiming('dispatch_end', pipelineId);
diff --git a/packages/web-platform/web-core/ts/constants.ts b/packages/web-platform/web-core/ts/constants.ts
index 621e30f607..24eeb69de3 100644
--- a/packages/web-platform/web-core/ts/constants.ts
+++ b/packages/web-platform/web-core/ts/constants.ts
@@ -87,6 +87,7 @@ export const LYNX_TAG_TO_HTML_TAG_MAP: Record =
'input': 'x-input',
'x-input-ng': 'x-input',
'svg': 'x-svg',
+ 'frame': 'lynx-view',
}),
);
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 ca438c38f8..98a0e174b6 100644
--- a/packages/web-platform/web-core/ts/server/elementAPIs/createElementAPI.ts
+++ b/packages/web-platform/web-core/ts/server/elementAPIs/createElementAPI.ts
@@ -20,6 +20,7 @@ import type {
AppendElementPAPI,
CreateComponentPAPI,
CreateElementPAPI,
+ CreateFramePAPI,
CreateImagePAPI,
CreateListPAPI,
CreatePagePAPI,
@@ -303,6 +304,15 @@ export function createElementAPI(
);
return { [uniqueIdSymbol]: id } as unknown as DecoratedHTMLElement;
}) as CreateImagePAPI,
+ __CreateFrame: ((parentComponentUniqueId: number) => {
+ const htmlTag = LYNX_TAG_TO_HTML_TAG_MAP['frame']!;
+ const id = wasmContext.create_element(htmlTag, parentComponentUniqueId);
+ const el = { [uniqueIdSymbol]: id };
+ if (!config.enableCSSSelector) {
+ wasmContext.set_attribute(id, lynxUniqueIdAttribute, id.toString());
+ }
+ return el as unknown as DecoratedHTMLElement;
+ }) as CreateFramePAPI,
__CreateRawText: ((text: string) => {
const id = wasmContext.create_element('raw-text');
wasmContext.set_attribute(id, 'text', text);
diff --git a/packages/web-platform/web-core/ts/types/IElementPAPI.ts b/packages/web-platform/web-core/ts/types/IElementPAPI.ts
index d53d692fa4..7433095291 100644
--- a/packages/web-platform/web-core/ts/types/IElementPAPI.ts
+++ b/packages/web-platform/web-core/ts/types/IElementPAPI.ts
@@ -225,6 +225,8 @@ export type CreateRawTextPAPI = (text: string) => HTMLElement;
export type CreateImagePAPI = CreateViewPAPI;
+export type CreateFramePAPI = CreateViewPAPI;
+
export type CreateScrollViewPAPI = CreateViewPAPI;
export type CreateWrapperElementPAPI = CreateViewPAPI;
@@ -393,6 +395,7 @@ export interface ElementPAPIs {
__CreateText: CreateTextPAPI;
__CreateRawText: CreateRawTextPAPI;
__CreateImage: CreateImagePAPI;
+ __CreateFrame: CreateFramePAPI;
__CreateScrollView: CreateScrollViewPAPI;
__CreateWrapperElement: CreateWrapperElementPAPI;
__CreateComponent: CreateComponentPAPI;