• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Using Same-Layer Rendering
2
3In the system, applications can use the **Web** component to load web pages. If the capability or performance of non-system UI components is inferior to that of system components, you can use the ArkUI component to render these components (same-layer components).
4
5## When to Use
6### On the Web Page
7To improve the performance of an applet, you can use the ArkUI **XComponent** component to render the map component, and use the ArkUI **TextInput** component to render the input box component.
8- On the web page, you can render the UI components (same-layer tags) such as **\<embed>** and **\<object>** at the same layer based on certain rules. For details, see [Specifications and Constraints](#specifications-and-constraints).
9
10- On the application, you can use the same-layer rendering event reporting API of the **Web** component to detect the lifecycle and input event of the HTML5 same-layer tags, and process the service logic of the same-layer rendered components.
11
12- In addition, you can use ArkUI APIs such as **NodeContainer** to construct same-layer rendered components corresponding to HTML5 same-layer tags. Common ArkUI components that support same-layer rendering: [TextInput](../reference/apis-arkui/arkui-ts/ts-basic-components-textinput.md), [XComponent](../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md), [Canvas](../reference/apis-arkui/arkui-ts/ts-components-canvas-canvas.md), [Video](../reference/apis-arkui/arkui-ts/ts-media-components-video.md), [Web](../reference/apis-arkweb/arkts-basic-components-web.md). For details, see [Specifications and Constraints](#specifications-and-constraints).
13
14### On the Third-Party UI Framework
15Flutter provides the **PlatformView** and **Texture** abstract components that can be rendered using system components, which complete the functionalities of the Flutter components. In the Weex 2.0 framework, the **Camera**, **Video**, and **Canvas** components can be rendered using system components to enhance functionality and performance.
16
17- Since third-party frameworks such as Flutter and Weex are not operated in the OS, the available third-party framework UI components that can be rendered at the same layer are not listed in the following.
18
19- On the application, you can use ArkUI APIs such as **NodeContainer** to construct same-layer rendered components corresponding to third-party framework same-layer tags. Common ArkUI components that support same-layer rendering: [TextInput](../reference/apis-arkui/arkui-ts/ts-basic-components-textinput.md), [XComponent](../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md), [Canvas](../reference/apis-arkui/arkui-ts/ts-components-canvas-canvas.md), [Video](../reference/apis-arkui/arkui-ts/ts-media-components-video.md), [Web](../reference/apis-arkweb/arkts-basic-components-web.md). For details, see [Specifications and Constraints](#specifications-and-constraints).
20
21## Overall Architecture
22The ArkWeb same-layer rendering feature supports same-layer tag lifecycle and event hit forwarding.
23
24The lifecycle of same-layer tags is associated with front-end tags (\<embed>/\<object>). Events that hit the same-layer tags are reported to you, and you should distribute them to the corresponding component tree. The following figure shows the overall framework.
25
26**Figure 1** Overall architecture of same-layer rendering
27
28![web-same-layer](figures/web-same-layer-develop-architecture.png)
29
30## Specifications and Constraints
31### ArkUI Components That Can Be Rendered at the Same Layer
32
33The following specifications take effect in both web pages and third-party frameworks.
34
35**Supported Components**:
36
37- Basic components: [AlphabetIndexer](../reference/apis-arkui/arkui-ts/ts-container-alphabet-indexer.md), [Blank](../reference/apis-arkui/arkui-ts/ts-basic-components-blank.md), [Button](../reference/apis-arkui/arkui-ts/ts-basic-components-button.md), [CalendarPicker](../reference/apis-arkui/arkui-ts/ts-basic-components-calendarpicker.md), [Checkbox](../reference/apis-arkui/arkui-ts/ts-basic-components-checkbox.md), [CheckboxGroup](../reference/apis-arkui/arkui-ts/ts-basic-components-checkboxgroup.md), [ContainerSpan](../reference/apis-arkui/arkui-ts/ts-basic-components-containerspan.md), [DataPanel](../reference/apis-arkui/arkui-ts/ts-basic-components-datapanel.md), [DatePicker](../reference/apis-arkui/arkui-ts/ts-basic-components-datepicker.md), [Divider](../reference/apis-arkui/arkui-ts/ts-basic-components-divider.md), [Gauge](../reference/apis-arkui/arkui-ts/ts-basic-components-gauge.md), [Hyperlink](../reference/apis-arkui/arkui-ts/ts-container-hyperlink.md), [Image](../reference/apis-arkui/arkui-ts/ts-basic-components-image.md), [ImageAnimator](../reference/apis-arkui/arkui-ts/ts-basic-components-imageanimator.md), [ImageSpan](../reference/apis-arkui/arkui-ts/ts-basic-components-imagespan.md), [LoadingProgress](../reference/apis-arkui/arkui-ts/ts-basic-components-loadingprogress.md), [Marquee](../reference/apis-arkui/arkui-ts/ts-basic-components-marquee.md), [PatternLock](../reference/apis-arkui/arkui-ts/ts-basic-components-patternlock.md), [Progress](../reference/apis-arkui/arkui-ts/ts-basic-components-progress.md), [QRCode](../reference/apis-arkui/arkui-ts/ts-basic-components-qrcode.md), [Radio](../reference/apis-arkui/arkui-ts/ts-basic-components-radio.md), [Rating](../reference/apis-arkui/arkui-ts/ts-basic-components-rating.md), [Refresh](../reference/apis-arkui/arkui-ts/ts-container-refresh.md), [ScrollBar](../reference/apis-arkui/arkui-ts/ts-container-scroll.md), [Search](../reference/apis-arkui/arkui-ts/ts-basic-components-search.md), [Span](../reference/apis-arkui/arkui-ts/ts-basic-components-span.md), [Select](../reference/apis-arkui/arkui-ts/ts-basic-components-select.md), [Slider](../reference/apis-arkui/arkui-ts/ts-basic-components-slider.md), [Text](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md), [TextArea](../reference/apis-arkui/arkui-ts/ts-basic-components-textarea.md), [TextClock](../reference/apis-arkui/arkui-ts/ts-basic-components-textclock.md), [TextInput](../reference/apis-arkui/arkui-ts/ts-basic-components-textinput.md), [TextPicker](../reference/apis-arkui/arkui-ts/ts-basic-components-textpicker.md), [TextTimer](../reference/apis-arkui/arkui-ts/ts-basic-components-texttimer.md), [TimePicker](../reference/apis-arkui/arkui-ts/ts-basic-components-timepicker.md), [Toggle](../reference/apis-arkui/arkui-ts/ts-basic-components-toggle.md)
38
39- Container components: [Badge](../reference/apis-arkui/arkui-ts/ts-container-badge.md), [Column](../reference/apis-arkui/arkui-ts/ts-container-column.md), [ColumnSplit](../reference/apis-arkui/arkui-ts/ts-container-columnsplit.md), [Counter](../reference/apis-arkui/arkui-ts/ts-container-counter.md), [Flex](../reference/apis-arkui/arkui-ts/ts-container-flex.md), [GridCol](../reference/apis-arkui/arkui-ts/ts-container-gridcol.md), [GridRow](../reference/apis-arkui/arkui-ts/ts-container-gridrow.md), [Grid](../reference/apis-arkui/arkui-ts/ts-container-grid.md), [GridItem](../reference/apis-arkui/arkui-ts/ts-container-griditem.md) and [List](../reference/apis-arkui/arkui-ts/ts-container-list.md), [ListItem](../reference/apis-arkui/arkui-ts/ts-container-listitem.md), [ListItemGroup](../reference/apis-arkui/arkui-ts/ts-container-listitemgroup.md), [RelativeContainer](../reference/apis-arkui/arkui-ts/ts-container-relativecontainer.md), [Row](../reference/apis-arkui/arkui-ts/ts-container-row.md), [RowSplit](../reference/apis-arkui/arkui-ts/ts-container-rowsplit.md), [Scroll](../reference/apis-arkui/arkui-ts/ts-container-scroll.md), [Stack](../reference/apis-arkui/arkui-ts/ts-container-stack.md), [Swiper](../reference/apis-arkui/arkui-ts/ts-container-swiper.md), [Tabs](../reference/apis-arkui/arkui-ts/ts-container-tabs.md), [TabContent](../reference/apis-arkui/arkui-ts/ts-container-tabcontent.md), [NodeContainer](../reference/apis-arkui/arkui-ts/ts-basic-components-nodecontainer.md), [SideBarContainer](../reference/apis-arkui/arkui-ts/ts-container-sidebarcontainer.md), [Stepper](../reference/apis-arkui/arkui-ts/ts-basic-components-stepper.md), [StepperItem](../reference/apis-arkui/arkui-ts/ts-basic-components-stepperitem.md), [WaterFlow](../reference/apis-arkui/arkui-ts/ts-container-waterflow.md), [FlowItem](../reference/apis-arkui/arkui-ts/ts-container-flowitem.md)
40
41- Self-drawing components: [XComponent](../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md), [Canvas](../reference/apis-arkui/arkui-ts/ts-components-canvas-canvas.md), [Video](../reference/apis-arkui/arkui-ts/ts-media-components-video.md), [Web](../reference/apis-arkweb/arkts-basic-components-web.md)
42
43- Command-based custom drawing nodes: [BuilderNode](../reference/apis-arkui/js-apis-arkui-builderNode.md), [ComponentContent](../reference/apis-arkui/js-apis-arkui-ComponentContent.md), [ContentSlot](../reference/apis-arkui/arkui-ts/ts-components-contentSlot.md), [FrameNode](../reference/apis-arkui/js-apis-arkui-frameNode.md), [Graphics](../reference/apis-arkui/js-apis-arkui-graphics.md), [NodeController](../reference/apis-arkui/js-apis-arkui-nodeController.md), [RenderNode](../reference/apis-arkui/js-apis-arkui-renderNode.md), [XComponentNode](../reference/apis-arkui/js-apis-arkui-xcomponentNode.md), [AttributeUpdater](../reference/apis-arkui/js-apis-arkui-AttributeUpdater.md) and [CAPI](../reference/apis-arkui/_ark_u_i___native_module.md) (The components that support same-layer rendering are the same as that of ArkTS.)
44
45**Supported Common Component Attributes and Events**:
46
47- Common attributes that are not supported: [restoreId](../reference/apis-arkui/arkui-ts/ts-universal-attributes-restoreId.md) and [Special Effect Drawing Combination](../reference/apis-arkui/arkui-ts/ts-universal-attributes-use-effect.md).
48
49- Other attributes, events, and component capabilities that are not clearly marked as not supported are supported by default.
50
51### Same-Layer Rendering Tags of the Web Page
52This specification applies only to web pages and does not apply to third-party frameworks.
53
54If an application needs to use same-layer rendering on a web page loaded by the **Web** component, you need to specify the **\<embed>** and **\<object>** tags on the web page as the same-layer rendered components based on the following specifications.
55
56**Supported HTML5 Tags**:
57- **\<embed>**: After same-layer rendering is enabled, only tags whose type is prefixed with **native** can be identified as same-layer components. Attributes cannot be customized.
58
59- **\<object>**: After same-layer rendering is enabled, the **\<object>** tag of the non-standard **MIME** type can be identified as a same-layer component and parsed based on the custom **param**/**value** attribute.
60
61- W3C standard tags (such as **\<input>** and **\<video>**) cannot be defined as same-layer tags.
62
63- The **\<object>** and **\<embed>** tags cannot be configured as the same-layer tags at the same time.
64
65- The tag types contain only English characters and are case insensitive.
66
67**Supported CSS Attributes**:
68
69**display**, **position**, **z-index**, **visibility**, **opacity**, **background-color**, **background-image**, **width**, **height**, **padding**, **padding-left**, **padding-top**, **padding-right**, **padding-bottom**, **margin**, **margin-left**, **margin-top**, **margin-right**, **margin-bottom**, **border-width**, **border-style**, **border-color**, **border-left-width**, **border-left-style**, **border-left-color**, **border-top-width**, **border-top-style**, **border-top-color**, **border-right-width**, **border-right-style**, **border-right-color**, **border-bottom-width**, **border-bottom-style**, **border-bottom-color**, **border-left**, **border-right**, **border-top**, **border-bottom**, **border**, **border-top-left-radius**, **border-top-right-radius**, **border-bottom-left-radius**, **border-bottom-right-radius**, **border-radius**, **transition**, and **transform** (Only **translate** and **scale** are supported. The value of **scale** must be greater than or equal to 0.)
70
71 Other CSS attributes, such as **rotate** and **skew** in the **transform** attribute, may not meet the expectation.
72
73**Lifecycle Management**:
74When the lifecycle of a tag at the same layer changes, the [onNativeEmbedLifecycleChange()](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedlifecyclechange11) callback is triggered.
75
76- Creation, destruction, and position width and height change are supported.
77
78- Web pages containing same-layer components support back-forward cache.
79
80**Distribution and Processing of the Input Events**:
81- The **DOWN**, **UP**, **MOVE**, and **CANCEL** touch events are supported. The [onnativeembedgestureevent11](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedgestureevent11) can be configured. By default, the touch event is consumed on the application side.
82
83- Application pages with same-layer tags cannot be scaled, and scaling APIs such as [initialScale](../reference/apis-arkweb/arkts-basic-components-web-attributes.md#initialscale9), [zoom](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#zoom), [zoomIn](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#zoomin) and [zoomOut](../reference/apis-arkweb/arkts-apis-webview-WebviewController.md#zoomout) are not supported.
84
85- Mouse, keyboard, and touchpad events are not supported.
86
87- By default, the mouse and touchpad left button events (**MousePress**/**MouseRelease**/**MouseMOVE**) can be converted into touch events (**TouchDOWN**/**TouchUP**/**TouchMOVE**) for reporting.
88
89**Visibility Changes**:
90The [onNativeEmbedVisibilityChange](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedvisibilitychange12) callback is triggered when the visibility of the same-layer tag changes.
91
92- The visibility of the same-layer tag relative to the viewport can be reported.
93
94- By default, visibility changes caused by the CSS style or size change of the same-layer tag are not reported. For details, see [onNativeEmbedVisibilityChange](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedvisibilitychange12).
95
96**Constraints**:
97
98- A maximum of five same-layer tag can be displayed on a web page. Otherwise, the rendering performance deteriorates.
99
100- Due to GPU restrictions, the maximum height and texture size of a same-layer tag are 8000 px.
101
102- After same-layer rendering is enabled, all web pages opened by the **Web** component do not support the synchronous [rendering mode](../reference/apis-arkweb/arkts-basic-components-web-e.md#rendermode12).
103
104- When the non-full-screen mode is changed to the full-screen mode, the **Video** component is exported through non-texture mode and the video playback status remains unchanged. When the non-full-screen mode is restored, the **Video** component is exported through texture mode and the video playback status remains unchanged.
105
106- The **Web** component supports only same-layer rendering nesting at one layer. Input events such as swiping, tapping, and touching and holding are supported. Dragging, rotating, and zooming are not supported.
107
108- In the page layout of ArkUI components (such as **TextInput**), you are advised to use a **Stack** component to wrap the same-layer **NodeContainer** and **BuilderNode** and ensure that they are in the same position. In addition, the **NodeContainer** must be aligned with the **\<embed>**/**\<object>** tag to ensure proper component interaction. If the positions of the two components are different, the following problems may occur: The position of the text selection box attached to the **TextInput**/**TextArea** component is incorrect (as shown in the following figure). The animation start and stop of the **LoadingProgress**/**Marquee** component do not match the visibility status of the component.
109
110  **Figure 2** Misplaced **TextInput** without **Stack**
111
112  ![web-same-layer-develop](figures/web-same-layer-develop-textinput1.png)
113
114  **Figure 3** Proper **TextInput** with **Stack**
115
116  ![web-same-layer-develop](figures/web-same-layer-develop-textinput2.png)
117
118## Rendering Text Boxes at the Same Layer on Web Pages
119On web pages, you can render the system ArkUI **TextInput** components at the same layer. The following figure shows the effect of three text boxes that are rendered at the same layer.
120
121**Figure 4** Same-layer rendering text boxes
122
123  ![web-same-layer-develop](figures/web-same-layer-develop-input.png)
124
1251. Mark the HTML tags that need to be rendered at the same layer on the web page.
126
127   The **\<embed>** and **\<object>** tags support same-layer rendering, and the **type** can be specified randomly. They are case insensitive and will be converted to lowercase letters by the ArkWeb kernel. The **tag** string is matched using the entire string, and the **type** string is matched using the prefix.
128
129   If this API is not used or receives an invalid string (empty string), the ArkWeb kernel uses the default setting, that is, "embed" + "native/" prefix. If the specified **type** is the same as the W3C standard **object** or **embed** type, for example, **registerNativeEmbedRule** ("**object**," "**application**/**pdf**"), ArkWeb will comply with the W3C standard behavior and will not identify it as a same-layer tag.
130
131   - Use the \<embed> tags.
132
133     ```html
134     <!--HAP's src/main/resources/rawfile/text.html-->
135     <!DOCTYPE html>
136     <html>
137     <head>
138         <title>Same-Layer Rendering HTML</title>
139         <meta name="viewport">
140     </head>
141
142     <body style="background:white">
143
144     <embed id = "input1" type="native/view" style="width: 100%; height: 100px; margin: 30px; margin-top: 600px"/>
145
146     <embed id = "input2" type="native/view2" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"/>
147
148     <embed id = "input3" type="native/view3" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"/>
149
150     </body>
151     </html>
152     ```
153
154   - Use the \<object> tags.
155
156     Call **registerNativeEmbedRule** to register a **\<object>** tag.
157     ```ts
158     // ...
159     Web({src: $rawfile("text.html"), controller: this.browserTabController})
160       // Register the same-layer tag of "object" and type of "test."
161       .registerNativeEmbedRule("object", "test")
162       // ...
163     ```
164
165     Example of using **registerNativeEmbedRule** on the frontend page, with the tag of "object" and type of "test":
166
167      ```html
168      <!--HAP's src/main/resources/rawfile/text.html-->
169      <!DOCTYPE html>
170      <html>
171      <head>
172          <title>Same-Layer Rendering HTML</title>
173          <meta name="viewport">
174      </head>
175
176      <body style="background:white">
177
178      <object id = "input1" type="test/input" style="width: 100%; height: 100px; margin: 30px; margin-top: 600px"></object>
179
180      <object id = "input2" type="test/input" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"></object>
181
182      <object id = "input3" type="test/input" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"></object>
183
184      </body>
185      </html>
186      ```
187
1882. Use **enableNativeEmbedMode** to enable same-layer rendering on the application.
189
190   The same-layer rendering feature is disabled by default. To enable this feature, set [enableNativeEmbedMode](../reference/apis-arkweb/arkts-basic-components-web-attributes.md#enablenativeembedmode11).
191
192   ```ts
193   // xxx.ets
194   import { webview } from '@kit.ArkWeb';
195   @Entry
196   @Component
197   struct WebComponent {
198     controller: webview.WebviewController = new webview.WebviewController();
199
200     build() {
201       Column() {
202         Web({ src: 'www.example.com', controller: this.controller })
203           // Enable same-layer rendering.
204           .enableNativeEmbedMode(true)
205       }
206     }
207   }
208   ```
209
2103. Create a custom component.
211
212   The custom component is displayed as a system component in the corresponding area after same-layer rendering is enabled.
213
214   ```ts
215   @Component
216   struct TextInputComponent {
217     @Prop params: Params
218     @State bkColor: Color = Color.White
219
220     build() {
221       Column() {
222         TextInput({text: '', placeholder: 'please input your word...'})
223           .placeholderColor(Color.Gray)
224           .id(this.params?.elementId)
225           .placeholderFont({size: 13, weight: 400})
226           .caretColor(Color.Gray)
227           .width(this.params?.width)
228           .height(this.params?.height)
229           .fontSize(14)
230           .fontColor(Color.Black)
231       }
232       // The width and height of the outermost custom container component must be the same as those of the same-layer tag.
233       .width(this.params.width)
234       .height(this.params.height)
235     }
236   }
237
238   @Builder
239   function TextInputBuilder(params:Params) {
240     TextInputComponent({params: params})
241       .width(params.width)
242       .height(params.height)
243       .backgroundColor(Color.White)
244   }
245   ```
246
2474. Create a node controller.
248
249   The node controller is used to control and report node behaviors of the corresponding NodeContainer.
250
251   ```ts
252   class MyNodeController extends NodeController {
253     private rootNode: BuilderNode<[Params]> | undefined | null;
254     private embedId_: string = "";
255     private surfaceId_: string = "";
256     private renderType_: NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;
257     private width_: number = 0;
258     private height_: number = 0;
259     private type_: string = "";
260     private isDestroy_: boolean = false;
261
262     setRenderOption(params: NodeControllerParams) {
263       this.surfaceId_ = params.surfaceId;
264       this.renderType_ = params.renderType;
265       this.embedId_ = params.embedId;
266       this.width_ = params.width;
267       this.height_ = params.height;
268       this.type_ = params.type;
269     }
270
271     // Method that must be overridden. It is used to build the number of nodes and return the number of nodes that will be mounted to the corresponding NodeContainer.
272     // Called when the corresponding NodeContainer is created or called by the rebuild method.
273     makeNode(uiContext: UIContext): FrameNode | null {
274       if (this.isDestroy_) { // rootNode is null.
275         return null;
276       }
277       if (!this.rootNode) { // When rootNode is set to undefined
278         this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_ });
279         if(this.rootNode) {
280           this.rootNode.build(wrapBuilder(TextInputBuilder), {  textOne: "myTextInput", width: this.width_, height: this.height_  })
281           return this.rootNode.getFrameNode();
282         }else{
283           return null;
284         }
285       }
286       // Return the FrameNode object.
287       return this.rootNode.getFrameNode();
288     }
289
290     updateNode(arg: Object): void {
291       this.rootNode?.update(arg);
292     }
293
294     getEmbedId(): string {
295       return this.embedId_;
296     }
297
298     setDestroy(isDestroy: boolean): void {
299       this.isDestroy_ = isDestroy;
300       if (this.isDestroy_) {
301         this.rootNode = null;
302       }
303     }
304
305     postEvent(event: TouchEvent | undefined): boolean {
306       return this.rootNode?.postTouchEvent(event) as boolean
307     }
308   }
309   ```
310
3115. Listen for the lifecycle changes of the same-layer rendered tags.
312
313   After this feature is enabled, the ArkWeb kernel triggers the callback registered by [onNativeEmbedLifecycleChange](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedlifecyclechange11) each time a same-layer rendered tag is used on the web page.
314
315   Call [onNativeEmbedLifecycleChange](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedlifecyclechange11) to listen for the lifecycle changes of the same-layer rendered tags.
316
317    ```ts
318    build() {
319      Row() {
320        Column() {
321          Stack() {
322            ForEach(this.componentIdArr, (componentId: string) => {
323              NodeContainer(this.nodeControllerMap.get(componentId))
324                .position(this.positionMap.get(componentId))
325                .width(this.widthMap.get(componentId))
326                .height(this.heightMap.get(componentId))
327            }, (embedId: string) => embedId)
328            // Load the local text.html page.
329            Web({src: $rawfile("text.html"), controller: this.browserTabController})
330              // Enable same-layer rendering.
331              .enableNativeEmbedMode(true)
332                // Register the same-layer tag of "object" and type of "test."
333              .registerNativeEmbedRule("object", "test")
334                // Obtain the lifecycle change data of the embed tag.
335              .onNativeEmbedLifecycleChange((embed) => {
336                console.log("NativeEmbed surfaceId" + embed.surfaceId);
337                // If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page.
338                const componentId = embed.info?.id?.toString() as string
339                if (embed.status == NativeEmbedStatus.CREATE) {
340                  console.log("NativeEmbed create" + JSON.stringify(embed.info));
341                  // Create a NodeController instance, set parameters, and rebuild.
342                  let nodeController = new MyNodeController()
343                  // The unit of embed.info.width and embed.info.height is px, which needs to be converted to the default unit vp on the eTS side.
344                  nodeController.setRenderOption({surfaceId : embed.surfaceId as string,
345                    type : embed.info?.type as string,
346                    renderType : NodeRenderType.RENDER_TYPE_TEXTURE,
347                    embedId : embed.embedId as string,
348                    width : this.uiContext.px2vp(embed.info?.width),
349                    height : this.uiContext.px2vp(embed.info?.height)})
350                  this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
351                  nodeController.setDestroy(false);
352                  // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key.
353                  this.nodeControllerMap.set(componentId, nodeController);
354                  this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width));
355                  this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height));
356                  this.positionMap.set(componentId, this.edges);
357                  // Save the Id attribute of the embed tag passed in by the Web component to the @State decorated variable for dynamically creating a nodeContainer. The push action must be executed after the set action.
358                  this.componentIdArr.push(componentId)
359                } else if (embed.status == NativeEmbedStatus.UPDATE) {
360                  let nodeController = this.nodeControllerMap.get(componentId);
361                  console.log("NativeEmbed update" + JSON.stringify(embed));
362                  this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
363                  this.positionMap.set(componentId, this.edges);
364                  this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width));
365                  this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height));
366                  nodeController?.updateNode({textOne: 'update', width: this.uiContext.px2vp(embed.info?.width), height: this.uiContext.px2vp(embed.info?.height)} as ESObject)
367                } else if (embed.status == NativeEmbedStatus.DESTROY) {
368                  console.log("NativeEmbed destroy" + JSON.stringify(embed));
369                  let nodeController = this.nodeControllerMap.get(componentId);
370                  nodeController?.setDestroy(true)
371                  this.nodeControllerMap.clear();
372                  this.positionMap.delete(componentId);
373                  this.widthMap.delete(componentId);
374                  this.heightMap.delete(componentId);
375                  this.componentIdArr.filter((value: string) => value != componentId)
376                } else {
377                  console.log("NativeEmbed status" + embed.status);
378                }
379              })
380          }.height("80%")
381        }
382      }
383    }
384    ```
385
3866. Listen for gesture events of same-layer rendered region.
387
388   When this feature is enabled, the ArkWeb kernel triggers the callback registered by [onNativeEmbedGestureEvent](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedgestureevent11) each time a touch operation is performed in the same-layer rendered region.
389
390   Call [onNativeEmbedGestureEvent](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedgestureevent11) to listen for gesture events of the same-layer rendered region.
391
392    ```ts
393    build() {
394      Row() {
395        Column() {
396          Stack() {
397            ForEach(this.componentIdArr, (componentId: string) => {
398              NodeContainer(this.nodeControllerMap.get(componentId))
399                .position(this.positionMap.get(componentId))
400                .width(this.widthMap.get(componentId))
401                .height(this.heightMap.get(componentId))
402            }, (embedId: string) => embedId)
403            // Load the local text.html page.
404            Web({src: $rawfile("text.html"), controller: this.browserTabController})
405              // Enable same-layer rendering.
406              .enableNativeEmbedMode(true)
407                // Obtain the lifecycle change data of the embed tag.
408              .onNativeEmbedLifecycleChange((embed) => {
409                // Implement lifecycle changes.
410              })
411              .onNativeEmbedGestureEvent((touch) => {
412                console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent));
413                this.componentIdArr.forEach((componentId: string) => {
414                  let nodeController = this.nodeControllerMap.get(componentId);
415                  // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region.
416                  if(nodeController?.getEmbedId() == touch.embedId) {
417                    let ret = nodeController?.postEvent(touch.touchEvent)
418                    if(ret) {
419                      console.log("onNativeEmbedGestureEvent success " + componentId);
420                    } else {
421                      console.log("onNativeEmbedGestureEvent fail " + componentId);
422                    }
423                    if(touch.result) {
424                      // Notify the Web component of the consumption result of the gesture event.
425                      touch.result.setGestureEventResult(ret);
426                    }
427                  }
428                })
429              })
430          }
431        }
432      }
433    }
434    ```
435
4367. Listen for mouse events of the same-layer rendered region.
437
438   After this feature is enabled, the ArkWeb kernel triggers the callback registered by [onNativeEmbedMouseEvent](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedmouseevent20) when the following operations are performed in the same-layer rendered area:
439
440   - Tapping or holding with the left, middle, or right mouse button.
441   - Tapping or holding the left, middle, or right mouse button using the touchpad.
442
443   Call [onNativeEmbedMouseEvent](../reference/apis-arkweb/arkts-basic-components-web-events.md#onnativeembedmouseevent20) to listen for the mouse events of the same-layer rendered region.
444
445    ```ts
446    build() {
447      Row() {
448        Column() {
449          Stack() {
450            ForEach(this.componentIdArr, (componentId: string) => {
451              NodeContainer(this.nodeControllerMap.get(componentId))
452                .position(this.positionMap.get(componentId))
453                .width(this.widthMap.get(componentId))
454                .height(this.heightMap.get(componentId))
455            }, (embedId: string) => embedId)
456            // Load the local text.html page.
457            Web({src: $rawfile("text.html"), controller: this.browserTabController})
458              // Enable same-layer rendering.
459              .enableNativeEmbedMode(true)
460                // Obtain the lifecycle change data of the embed tag.
461              .onNativeEmbedLifecycleChange((embed) => {
462                // Implement lifecycle changes.
463              })
464              .onNativeEmbedGestureEvent((touch) => {
465                // Process the gesture event at the same layer.
466              })
467              .onNativeEmbedMouseEvent((mouse) => {
468                console.log("NativeEmbed onNativeEmbedMouseEvent" + JSON.stringify(mouse.mouseEvent));
469                this.componentIdArr.forEach((componentId: string) => {
470                  let nodeController = this.nodeControllerMap.get(componentId);
471                  // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region.
472                  if(nodeController?.getEmbedId() == mouse.embedId) {
473                    let ret = nodeController?.postInputEvent(mouse.touchEvent)
474                    if(ret) {
475                      console.log("onNativeEmbedMouseEvent success " + componentId);
476                    } else {
477                      console.log("onNativeEmbedMouseEvent fail " + componentId);
478                    }
479                    if(mouse.result) {
480                      // Notify the Web component of the consumption result of the mouse event.
481                      mouse.result.setMouseEventResult(ret);
482                    }
483                  }
484                })
485              })
486          }
487        }
488      }
489    }
490    ```
491**Sample Code**
492
493To start with, add the Internet permission to the **module.json5** file. For details, see [Declaring Permissions](../security/AccessToken/declare-permissions.md).
494
495  ```
496  "requestPermissions":[
497      {
498        "name" : "ohos.permission.INTERNET"
499      }
500    ]
501  ```
502
503Code on the application side:
504
505  ```ts
506  // Create a NodeController instance.
507  import { webview } from '@kit.ArkWeb';
508  import { UIContext } from '@kit.ArkUI';
509  import { NodeController, BuilderNode, NodeRenderType, FrameNode } from '@kit.ArkUI';
510
511  @Observed
512  declare class Params{
513    elementId: string
514    textOne: string
515    textTwo: string
516    width: number
517    height: number
518  }
519
520  declare class NodeControllerParams {
521    surfaceId: string
522    type: string
523    renderType: NodeRenderType
524    embedId: string
525    width: number
526    height: number
527  }
528
529  // The NodeController instance must be used with a NodeContainer for controlling and feeding back the behavior of the nodes in the container.
530  class MyNodeController extends NodeController {
531    private rootNode: BuilderNode<[Params]> | undefined | null;
532    private embedId_: string = "";
533    private surfaceId_: string = "";
534    private renderType_: NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;
535    private width_: number = 0;
536    private height_: number = 0;
537    private type_: string = "";
538    private isDestroy_: boolean = false;
539
540    setRenderOption(params: NodeControllerParams) {
541      this.surfaceId_ = params.surfaceId;
542      this.renderType_ = params.renderType;
543      this.embedId_ = params.embedId;
544      this.width_ = params.width;
545      this.height_ = params.height;
546      this.type_ = params.type;
547    }
548
549    // Method that must be overridden. It is used to build the number of nodes and return the number of nodes that will be mounted to the corresponding NodeContainer.
550    // Called when the corresponding NodeContainer is created or called by the rebuild method.
551    makeNode(uiContext: UIContext): FrameNode | null {
552      if (this.isDestroy_) { // rootNode is null.
553        return null;
554      }
555      if (!this.rootNode) { // When rootNode is set to undefined
556        this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_ });
557        if(this.rootNode) {
558          this.rootNode.build(wrapBuilder(TextInputBuilder), {  textOne: "myTextInput", width: this.width_, height: this.height_  })
559          return this.rootNode.getFrameNode();
560        }else{
561          return null;
562        }
563      }
564      // Return the FrameNode object.
565      return this.rootNode.getFrameNode();
566    }
567
568    updateNode(arg: Object): void {
569      this.rootNode?.update(arg);
570    }
571
572    getEmbedId(): string {
573      return this.embedId_;
574    }
575
576    setDestroy(isDestroy: boolean): void {
577      this.isDestroy_ = isDestroy;
578      if (this.isDestroy_) {
579        this.rootNode = null;
580      }
581    }
582
583    postEvent(event: TouchEvent | undefined): boolean {
584      return this.rootNode?.postTouchEvent(event) as boolean
585    }
586
587    postInputEvent(event: MouseEvent | undefined): boolean {
588      return this.rootNode?.postInputEvent(event) as boolean
589    }
590  }
591
592  @Component
593  struct TextInputComponent {
594    @Prop params: Params
595    @State bkColor: Color = Color.White
596
597    build() {
598      Column() {
599        TextInput({text: '', placeholder: 'please input your word...'})
600          .placeholderColor(Color.Gray)
601          .id(this.params?.elementId)
602          .placeholderFont({size: 13, weight: 400})
603          .caretColor(Color.Gray)
604          .fontSize(14)
605          .fontColor(Color.Black)
606      }
607      // The width and height of the outermost custom container component must be the same as those of the same-layer tag.
608      .width(this.params.width)
609      .height(this.params.height)
610    }
611  }
612
613  // In @Builder, add the specific dynamic component content.
614  @Builder
615  function TextInputBuilder(params:Params) {
616    TextInputComponent({params: params})
617      .width(params.width)
618      .height(params.height)
619      .backgroundColor(Color.White)
620  }
621
622  @Entry
623  @Component
624  struct Page{
625    browserTabController: WebviewController = new webview.WebviewController()
626    private nodeControllerMap: Map<string, MyNodeController> = new Map();
627    @State componentIdArr: Array<string> = [];
628    @State widthMap: Map<string, number> = new Map();
629    @State heightMap: Map<string, number> = new Map();
630    @State positionMap: Map<string, Edges> = new Map();
631    @State edges: Edges = {};
632    uiContext: UIContext = this.getUIContext();
633
634    build() {
635      Row() {
636        Column() {
637          Stack() {
638            ForEach(this.componentIdArr, (componentId: string) => {
639              NodeContainer(this.nodeControllerMap.get(componentId))
640                .position(this.positionMap.get(componentId))
641                .width(this.widthMap.get(componentId))
642                .height(this.heightMap.get(componentId))
643            }, (embedId: string) => embedId)
644            // Load the local text.html page.
645            Web({src: $rawfile("text.html"), controller: this.browserTabController})
646              // Enable same-layer rendering.
647              .enableNativeEmbedMode(true)
648              // Obtain the lifecycle change data of the embed tag.
649              .onNativeEmbedLifecycleChange((embed) => {
650                 console.log("NativeEmbed surfaceId" + embed.surfaceId);
651                 // If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page.
652                 const componentId = embed.info?.id?.toString() as string
653                 if (embed.status == NativeEmbedStatus.CREATE) {
654                   console.log("NativeEmbed create" + JSON.stringify(embed.info));
655                   // Create a NodeController instance, set parameters, and rebuild.
656                   let nodeController = new MyNodeController()
657                   // The unit of embed.info.width and embed.info.height is px, which needs to be converted to the default unit vp on the eTS side.
658                   nodeController.setRenderOption({surfaceId : embed.surfaceId as string,
659                     type : embed.info?.type as string,
660                     renderType : NodeRenderType.RENDER_TYPE_TEXTURE,
661                     embedId : embed.embedId as string,
662                     width : this.uiContext.px2vp(embed.info?.width),
663                     height : this.uiContext.px2vp(embed.info?.height)})
664                   this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
665                   nodeController.setDestroy(false);
666                   // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key.
667                   this.nodeControllerMap.set(componentId, nodeController);
668                   this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width));
669                   this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height));
670                   this.positionMap.set(componentId, this.edges);
671                   // Save the Id attribute of the embed tag passed in by the Web component to the @State decorated variable for dynamically creating a nodeContainer. The push action must be executed after the set action.
672                   this.componentIdArr.push(componentId)
673                 } else if (embed.status == NativeEmbedStatus.UPDATE) {
674                   let nodeController = this.nodeControllerMap.get(componentId);
675                   console.log("NativeEmbed update" + JSON.stringify(embed));
676                   this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
677                   this.positionMap.set(componentId, this.edges);
678                   this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width));
679                   this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height));
680                   nodeController?.updateNode({textOne: 'update', width: this.uiContext.px2vp(embed.info?.width), height: this.uiContext.px2vp(embed.info?.height)} as ESObject)
681                 } else if (embed.status == NativeEmbedStatus.DESTROY) {
682                   console.log("NativeEmbed destroy" + JSON.stringify(embed));
683                   let nodeController = this.nodeControllerMap.get(componentId);
684                   nodeController?.setDestroy(true)
685                   this.nodeControllerMap.clear();
686                   this.positionMap.delete(componentId);
687                   this.widthMap.delete(componentId);
688                   this.heightMap.delete(componentId);
689                   this.componentIdArr.filter((value: string) => value != componentId)
690                 } else {
691                   console.log("NativeEmbed status" + embed.status);
692                 }
693               })// Obtain the touch event information of components for same-layer rendering.
694              .onNativeEmbedGestureEvent((touch) => {
695                console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent));
696                this.componentIdArr.forEach((componentId: string) => {
697                  let nodeController = this.nodeControllerMap.get(componentId);
698                  // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region.
699                  if(nodeController?.getEmbedId() == touch.embedId) {
700                    let ret = nodeController?.postEvent(touch.touchEvent)
701                    if(ret) {
702                      console.log("onNativeEmbedGestureEvent success " + componentId);
703                    } else {
704                      console.log("onNativeEmbedGestureEvent fail " + componentId);
705                    }
706                    if(touch.result) {
707                      // Notify the Web component of the consumption result of the gesture event.
708                      touch.result.setGestureEventResult(ret);
709                    }
710                  }
711                })
712              })
713              .onNativeEmbedMouseEvent((mouse) => {
714                console.log("NativeEmbed onNativeEmbedMouseEvent" + JSON.stringify(mouse.mouseEvent));
715                this.componentIdArr.forEach((componentId: string) => {
716                  let nodeController = this.nodeControllerMap.get(componentId);
717                  // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region.
718                  if(nodeController?.getEmbedId() == mouse.embedId) {
719                    let ret = nodeController?.postInputEvent(mouse.mouseEvent)
720                    if(ret) {
721                      console.log("onNativeEmbedMouseEvent success " + componentId);
722                    } else {
723                      console.log("onNativeEmbedMouseEvent fail " + componentId);
724                    }
725                    if(mouse.result) {
726                      // Notify the Web component of the consumption result of the mouse event.
727                      mouse.result.setMouseEventResult(ret);
728                    }
729                  }
730                })
731              })
732          }
733        }
734      }
735    }
736  }
737  ```
738
739## Drawing the XComponent+AVPlayer and Button Components
740
741- Example of using same-layer rendering on the application side:
742
743  ```ts
744  // HAP's src/main/ets/pages/Index.ets
745  // Create a NodeController instance.
746  import { webview } from '@kit.ArkWeb';
747  import { UIContext, NodeController, BuilderNode, NodeRenderType, FrameNode } from "@kit.ArkUI";
748  import { AVPlayerDemo } from './PlayerDemo';
749
750  @Observed
751  declare class Params {
752    textOne : string
753    textTwo : string
754    width : number
755    height : number
756  }
757
758  declare class NodeControllerParams {
759    surfaceId : string
760    type : string
761    renderType : NodeRenderType
762    embedId : string
763    width : number
764    height : number
765  }
766
767  // The NodeController instance must be used with a NodeContainer for controlling and feeding back the behavior of the nodes in the container.
768  class MyNodeController extends NodeController {
769    private rootNode: BuilderNode<[Params]> | undefined | null;
770    private embedId_ : string = "";
771    private surfaceId_ : string = "";
772    private renderType_ :NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;
773    private width_ : number = 0;
774    private height_ : number = 0;
775    private type_ : string = "";
776    private isDestroy_ : boolean = false;
777
778    setRenderOption(params : NodeControllerParams) {
779      this.surfaceId_ = params.surfaceId;
780      this.renderType_ = params.renderType;
781      this.embedId_ = params.embedId;
782      this.width_ = params.width;
783      this.height_ = params.height;
784      this.type_ = params.type;
785    }
786    // Method that must be overridden. It is used to build the number of nodes and return the number of nodes that will be mounted to the corresponding NodeContainer.
787    // Called when the corresponding NodeContainer is created or called by the rebuild method.
788    makeNode(uiContext: UIContext): FrameNode | null{
789      if (this.isDestroy_) { // rootNode is null.
790        return null;
791      }
792      if (!this.rootNode) { // When rootNode is set to undefined
793        this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_});
794        if (this.type_ === 'native/video') {
795          this.rootNode.build(wrapBuilder(VideoBuilder), {textOne: "myButton", width : this.width_, height : this.height_});
796        } else {
797          // other
798        }
799      }
800      // Return the FrameNode object.
801      return this.rootNode.getFrameNode();
802    }
803
804    updateNode(arg: Object): void {
805      this.rootNode?.update(arg);
806    }
807    getEmbedId() : string {
808      return this.embedId_;
809    }
810
811    setDestroy(isDestroy : boolean) : void {
812      this.isDestroy_ = isDestroy;
813      if (this.isDestroy_) {
814        this.rootNode = null;
815      }
816    }
817
818    postEvent(event: TouchEvent | undefined) : boolean {
819      return this.rootNode?.postTouchEvent(event) as boolean
820    }
821
822    postInputEvent(event: MouseEvent | undefined): boolean {
823      return this.rootNode?.postInputEvent(event) as boolean
824    }
825  }
826
827  @Component
828  struct VideoComponent {
829    @ObjectLink params: Params
830    @State bkColor: Color = Color.Red
831    mXComponentController: XComponentController = new XComponentController();
832    @State player_changed: boolean = false;
833    player?: AVPlayerDemo;
834
835    build() {
836      Column() {
837        Button(this.params.textOne)
838
839        XComponent({ id: 'video_player_id', type: XComponentType.SURFACE, controller: this.mXComponentController})
840          .border({width: 1, color: Color.Red})
841          .onLoad(() => {
842            this.player = new AVPlayerDemo();
843            this.player.setSurfaceID(this.mXComponentController.getXComponentSurfaceId());
844            this.player_changed = !this.player_changed;
845            this.player.avPlayerLiveDemo()
846          })
847          .width(300)
848          .height(200)
849      }
850      // The width and height of the outermost custom container component must be the same as those of the same-layer tag.
851      .width(this.params.width)
852      .height(this.params.height)
853    }
854  }
855  // In @Builder, add the specific dynamic component content.
856  @Builder
857  function VideoBuilder(params: Params) {
858    VideoComponent({ params: params })
859      .backgroundColor(Color.Gray)
860  }
861
862  @Entry
863  @Component
864  struct WebIndex {
865    browserTabController: WebviewController = new webview.WebviewController()
866    private nodeControllerMap: Map<string, MyNodeController> = new Map();
867    @State componentIdArr: Array<string> = [];
868    @State widthMap: Map<string, number> = new Map();
869    @State heightMap: Map<string, number> = new Map();
870    @State positionMap: Map<string, Edges> = new Map();
871    @State edges: Edges = {};
872    uiContext: UIContext = this.getUIContext();
873
874    aboutToAppear() {
875      // Enable web frontend page debugging.
876      webview.WebviewController.setWebDebuggingAccess(true);
877    }
878
879    build(){
880      Row() {
881        Column() {
882          Stack() {
883            ForEach(this.componentIdArr, (componentId: string) => {
884              NodeContainer(this.nodeControllerMap.get(componentId))
885                .position(this.positionMap.get(componentId))
886                .width(this.widthMap.get(componentId))
887                .height(this.heightMap.get(componentId))
888            }, (embedId: string) => embedId)
889            // Load the local test.html page.
890            Web({ src: $rawfile("test.html"), controller: this.browserTabController })
891              // Enable same-layer rendering.
892              .enableNativeEmbedMode(true)
893                // Obtain the lifecycle change data of the embed tag.
894              .onNativeEmbedLifecycleChange((embed) => {
895                console.log("NativeEmbed surfaceId" + embed.surfaceId);
896                // 1. If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page.
897                const componentId = embed.info?.id?.toString() as string
898                if (embed.status == NativeEmbedStatus.CREATE) {
899                  console.log("NativeEmbed create" + JSON.stringify(embed.info))
900                  // Create a NodeController instance, set parameters, and rebuild.
901                  let nodeController = new MyNodeController()
902                  // 1. The unit of embed.info.width and embed.info.height is px, which needs to be converted to the default unit vp on the eTS side.
903                  nodeController.setRenderOption({surfaceId : embed.surfaceId as string, type : embed.info?.type as string,
904                    renderType : NodeRenderType.RENDER_TYPE_TEXTURE, embedId : embed.embedId as string,
905                    width : this.uiContext.px2vp(embed.info?.width), height : this.uiContext.px2vp(embed.info?.height)})
906                  this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
907                  nodeController.setDestroy(false);
908                  // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key.
909                  this.nodeControllerMap.set(componentId, nodeController)
910                  this.widthMap.set(componentId,  this.uiContext.px2vp(embed.info?.width));
911                  this.heightMap.set(componentId,  this.uiContext.px2vp(embed.info?.height));
912                  this.positionMap.set(componentId, this.edges);
913                  // Save the Id attribute of the embed tag passed in by the Web component to the @State decorated variable for dynamically creating a nodeContainer. The push action must be executed after the set action.
914                  this.componentIdArr.push(componentId)
915                } else if (embed.status == NativeEmbedStatus.UPDATE) {
916                  let nodeController = this.nodeControllerMap.get(componentId)
917                  this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
918                  this.positionMap.set(componentId, this.edges);
919                  this.widthMap.set(componentId,  this.uiContext.px2vp(embed.info?.width));
920                  this.heightMap.set(componentId,  this.uiContext.px2vp(embed.info?.height));
921                  nodeController?.updateNode({textOne: 'update', width: this.uiContext.px2vp(embed.info?.width), height: this.uiContext.px2vp(embed.info?.height)} as ESObject)
922                } else if (embed.status == NativeEmbedStatus.DESTROY) {
923                  let nodeController = this.nodeControllerMap.get(componentId);
924                  nodeController?.setDestroy(true)
925                  this.nodeControllerMap.clear();
926                  this.positionMap.delete(componentId);
927                  this.widthMap.delete(componentId);
928                  this.heightMap.delete(componentId);
929                  this.componentIdArr.filter((value: string) => value != componentId)
930                } else {
931                  console.log("NativeEmbed status" + embed.status);
932                }
933              })// Obtain the touch event information of components for same-layer rendering.
934              .onNativeEmbedGestureEvent((touch) => {
935                console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent));
936                this.componentIdArr.forEach((componentId: string) => {
937                  let nodeController = this.nodeControllerMap.get(componentId)
938                  // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region.
939                  if (nodeController?.getEmbedId() === touch.embedId) {
940                    let ret = nodeController?.postEvent(touch.touchEvent)
941                    if (ret) {
942                      console.log("onNativeEmbedGestureEvent success " + componentId)
943                    } else {
944                      console.log("onNativeEmbedGestureEvent fail " + componentId)
945                    }
946                    if (touch.result) {
947                      // Notify the Web component of the consumption result of the gesture event.
948                      touch.result.setGestureEventResult(ret);
949                    }
950                  }
951                })
952              })
953              .onNativeEmbedMouseEvent((mouse) => {
954                console.log("NativeEmbed onNativeEmbedMouseEvent" + JSON.stringify(mouse.mouseEvent));
955                this.componentIdArr.forEach((componentId: string) => {
956                  let nodeController = this.nodeControllerMap.get(componentId);
957                  // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region.
958                  if(nodeController?.getEmbedId() == mouse.embedId) {
959                    let ret = nodeController?.postInputEvent(mouse.mouseEvent)
960                    if(ret) {
961                      console.log("onNativeEmbedMouseEvent success " + componentId);
962                    } else {
963                      console.log("onNativeEmbedMouseEvent fail " + componentId);
964                    }
965                    if(mouse.result) {
966                      // Notify the Web component of the consumption result of the mouse event.
967                      mouse.result.setMouseEventResult(ret);
968                    }
969                  }
970                })
971              })
972          }
973        }
974      }
975    }
976  }
977  ```
978
979- Code example of video playback on the application side. Replace the URL with the correct video URL in practice.
980
981  ```ts
982  // HAP's src/main/ets/pages/PlayerDemo.ets
983  import { media } from '@kit.MediaKit';
984  import { BusinessError } from '@ohos.base';
985
986  export class AVPlayerDemo {
987    private count: number = 0;
988    private surfaceID: string = ''; // The surfaceID parameter specifies the window used to display the video. Its value is obtained through XComponent.
989    private isSeek: boolean = true; // Specify whether the seek operation is supported.
990
991    setSurfaceID(surface_id: string){
992      console.log('setSurfaceID : ' + surface_id);
993      this.surfaceID = surface_id;
994    }
995    // Set AVPlayer callback functions.
996    setAVPlayerCallback(avPlayer: media.AVPlayer) {
997      // Callback function for the seek operation.
998      avPlayer.on('seekDone', (seekDoneTime: number) => {
999        console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
1000      })
1001      // Callback function for errors. If an error occurs during the operation on the AVPlayer, reset() is called to reset the AVPlayer.
1002      avPlayer.on('error', (err: BusinessError) => {
1003        console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
1004        avPlayer.reset();
1005      })
1006      // Callback for state changes.
1007      avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
1008        switch (state) {
1009          case 'idle': // This state is reported upon a successful callback of reset().
1010            console.info('AVPlayer state idle called.');
1011            avPlayer.release(); // Call release() to release the instance.
1012            break;
1013          case 'initialized': // This state is reported when the AVPlayer sets the playback source.
1014            console.info('AVPlayer state initialized called.');
1015            avPlayer.surfaceId = this.surfaceID; // Set the window to display the video. This setting is not required when a pure audio asset is to be played.
1016            avPlayer.prepare();
1017            break;
1018          case 'prepared': // This state is reported upon a successful callback of prepare().
1019            console.info('AVPlayer state prepared called.');
1020            avPlayer.play(); // Call play() to start playback.
1021            break;
1022          case 'playing': // This state is reported upon a successful callback of play().
1023            console.info('AVPlayer state prepared called.');
1024            if(this.count !== 0) {
1025              if (this.isSeek) {
1026                console.info('AVPlayer start to seek.');
1027                avPlayer.seek(avPlayer.duration); // Call seek() to seek to the end of the video clip.
1028              } else {
1029                // When the seek operation is not supported, the playback continues until it reaches the end.
1030                console.info('AVPlayer wait to play end.');
1031              }
1032            } else {
1033              avPlayer.pause(); // Call pause() to pause the playback.
1034            }
1035            this.count++;
1036            break;
1037          case 'paused': // This state is reported upon a successful callback of pause().
1038            console.info('AVPlayer state paused called.');
1039            avPlayer.play(); // Call play() again to start playback.
1040            break;
1041          case 'completed': // This state is reported upon the completion of the playback.
1042            console.info('AVPlayer state paused called.');
1043            avPlayer.stop(); // Call the playback API.
1044            break;
1045          case 'stopped': // This state is reported upon a successful callback of stop().
1046            console.info('AVPlayer state stopped called.');
1047            avPlayer.reset(); // Call reset() to reset the AVPlayer.
1048            break;
1049          case 'released': // This state is reported upon the release of the AVPlayer.
1050            console.info('AVPlayer state released called.');
1051            break;
1052          default:
1053            break;
1054        }
1055      })
1056    }
1057
1058    // Set the live stream source through the URL.
1059    async avPlayerLiveDemo(){
1060      // Create an AVPlayer instance.
1061      let avPlayer: media.AVPlayer = await media.createAVPlayer();
1062      // Create a callback for state changes.
1063      this.setAVPlayerCallback(avPlayer);
1064      this.isSeek = false; // The seek operation is not supported.
1065      // Replace the URL with the actual URL of the video source.
1066      avPlayer.url = 'https://xxx.xxx/demo.mp4';
1067    }
1068  }
1069  ```
1070
1071- Example of the frontend page:
1072
1073  ```html
1074  <!--HAP's src/main/resources/rawfile/test.html-->
1075  <!DOCTYPE html>
1076  <html>
1077  <head>
1078      <title>Same-Layer Rendering Test HTML</title>
1079      <meta name="viewport">
1080  </head>
1081  <body>
1082  <div>
1083      <div id="bodyId">
1084          <embed id="nativeVideo" type = "native/video" width="1000" height="1500" src="test" style = "background-color:red"/>
1085      </div>
1086  </div>
1087  </body>
1088  </html>
1089  ```
1090
1091- Demo:
1092
1093  ![web-same-layer](figures/web-same-layer.png)
1094
1095## Setting the Same-Layer Tag to the Highest Level
1096
1097The same-layer rendering supports the private attribute **arkwebnativestyle**, which takes effect only in **\<embed>** and **\<object>** after same-layer rendering is enabled. The **display** attribute of **arkwebnativestyle** is used to set the level of the same-layer tag to be above other web elements. When multiple same-layer tags have the same **display** value in **arkwebnativestyle**, their stacking order follows the W3C standard: first by **z-index**, then by their order in the DOM if **z-index** values are the same. The values of the **display** attribute are listed in the following table.
1098
1099| Value| Description|
1100| - | - |
1101| overlay | Sets the level of the same-layer tag to be higher than that of other web elements.|
1102| overlay-infinity | Sets the level of the same-layer tag to be higher than that of other web elements and the same-layer tag with the **overlay** value.|
1103
1104- Code on the application side:
1105  ```ts
1106  import { webview } from '@kit.ArkWeb';
1107  import { UIContext } from '@kit.ArkUI';
1108  import { NodeController, BuilderNode, NodeRenderType, FrameNode } from '@kit.ArkUI';
1109
1110  @Observed
1111  declare class Params{
1112    elementId: string
1113    textOne: string
1114    textTwo: string
1115    width: number
1116    height: number
1117  }
1118
1119  declare class NodeControllerParams {
1120    surfaceId: string
1121    type: string
1122    renderType: NodeRenderType
1123    embedId: string
1124    width: number
1125    height: number
1126  }
1127
1128  // The NodeController instance must be used with a NodeContainer for controlling and feeding back the behavior of the nodes in the container.
1129  class MyNodeController extends NodeController {
1130    private rootNode: BuilderNode<[Params]> | undefined | null;
1131    private embedId_: string = "";
1132    private surfaceId_: string = "";
1133    private renderType_: NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;
1134    private width_: number = 0;
1135    private height_: number = 0;
1136    private type_: string = "";
1137    private isDestroy_: boolean = false;
1138
1139    setRenderOption(params: NodeControllerParams) {
1140      this.surfaceId_ = params.surfaceId;
1141      this.renderType_ = params.renderType;
1142      this.embedId_ = params.embedId;
1143      this.width_ = params.width;
1144      this.height_ = params.height;
1145      this.type_ = params.type;
1146    }
1147
1148    // Method that must be overridden. It is used to build the number of nodes and return the number of nodes that will be mounted to the corresponding NodeContainer.
1149    // Called when the corresponding NodeContainer is created or called by the rebuild method.
1150    makeNode(uiContext: UIContext): FrameNode | null {
1151      if (this.isDestroy_) { // rootNode is null.
1152        return null;
1153      }
1154      if (!this.rootNode) { // When rootNode is set to undefined
1155        this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_ });
1156        if (this.type_ == 'native/view1') {
1157          this.rootNode.build(wrapBuilder(TextInputBuilder1), {  textOne: "myTextInput", width: this.width_, height: this.height_  })
1158          return this.rootNode.getFrameNode();
1159        } else if (this.type_ == 'native/view2') {
1160          this.rootNode.build(wrapBuilder(TextInputBuilder2), {  textOne: "myTextInput", width: this.width_, height: this.height_  })
1161          return this.rootNode.getFrameNode();
1162        } else{
1163          return null;
1164        }
1165      }
1166      // Return the FrameNode object.
1167      return this.rootNode.getFrameNode();
1168    }
1169
1170    updateNode(arg: Object): void {
1171      this.rootNode?.update(arg);
1172    }
1173
1174    getEmbedId(): string {
1175      return this.embedId_;
1176    }
1177
1178    setDestroy(isDestroy: boolean): void {
1179      this.isDestroy_ = isDestroy;
1180      if (this.isDestroy_) {
1181        this.rootNode = null;
1182      }
1183    }
1184
1185    postEvent(event: TouchEvent | undefined): boolean {
1186      return this.rootNode?.postTouchEvent(event) as boolean
1187    }
1188  }
1189
1190  @Component
1191  struct TextInputComponent1 {
1192    @Prop params: Params;
1193    @State bkColor: Color = Color.White;
1194
1195    build() {
1196      Column() {
1197        Text("display:overlay-infinity")
1198        TextInput({text: '', placeholder: 'please input your word...'})
1199          .placeholderColor(Color.Gray)
1200          .id(this.params?.elementId)
1201          .placeholderFont({size: 13, weight: 400})
1202          .caretColor(Color.Gray)
1203          .fontSize(14)
1204          .fontColor(Color.Black)
1205      }
1206      // The width and height of the outermost custom container component must be the same as those of the same-layer tag.
1207      .width(this.params.width)
1208      .height(this.params.height)
1209    }
1210  }
1211
1212  // In @Builder, add the specific dynamic component content.
1213  @Builder
1214  function TextInputBuilder1(params:Params) {
1215    TextInputComponent1({params: params})
1216      .width(params.width)
1217      .height(params.height)
1218      .backgroundColor(Color.Pink)
1219  }
1220
1221  @Component
1222  struct TextInputComponent2 {
1223    @Prop params: Params;
1224    @State bkColor: Color = Color.White;
1225
1226    build() {
1227      Column() {
1228        Text("display:overlay")
1229        TextInput({text: '', placeholder: 'please input your word...'})
1230          .placeholderColor(Color.Gray)
1231          .id(this.params?.elementId)
1232          .placeholderFont({size: 13, weight: 400})
1233          .caretColor(Color.Gray)
1234          .fontSize(14)
1235          .fontColor(Color.Black)
1236      }
1237      // The width and height of the outermost custom container component must be the same as those of the same-layer tag.
1238      .width(this.params.width)
1239      .height(this.params.height)
1240    }
1241  }
1242
1243
1244  // In @Builder, add the specific dynamic component content.
1245  @Builder
1246  function TextInputBuilder2(params:Params) {
1247    TextInputComponent2({params: params})
1248      .width(params.width)
1249      .height(params.height)
1250      .backgroundColor(Color.Gray)
1251  }
1252
1253  @Entry
1254  @Component
1255  struct Page{
1256    browserTabController: webview.WebviewController = new webview.WebviewController();
1257    private nodeControllerMap: Map<string, MyNodeController> = new Map();
1258    @State componentIdArr: Array<string> = [];
1259    @State widthMap: Map<string, number> = new Map();
1260    @State heightMap: Map<string, number> = new Map();
1261    @State positionMap: Map<string, Edges> = new Map();
1262    @State edges: Edges = {};
1263    uiContext: UIContext = this.getUIContext();
1264
1265    build() {
1266      Row() {
1267        Column() {
1268          Stack() {
1269            ForEach(this.componentIdArr, (componentId: string) => {
1270              NodeContainer(this.nodeControllerMap.get(componentId))
1271                .position(this.positionMap.get(componentId))
1272                .width(this.widthMap.get(componentId))
1273                .height(this.heightMap.get(componentId))
1274            }, (embedId: string) => embedId)
1275            // Load the local text.html page.
1276            Web({src: $rawfile("overlay.html"), controller: this.browserTabController})
1277              // Enable same-layer rendering.
1278              .enableNativeEmbedMode(true)
1279                // Obtain the lifecycle change data of the embed tag.
1280              .onNativeEmbedLifecycleChange((embed) => {
1281                console.log("NativeEmbed surfaceId" + embed.surfaceId);
1282                // If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page.
1283                const componentId = embed.info?.id?.toString() as string
1284                if (embed.status == NativeEmbedStatus.CREATE) {
1285                  console.log("NativeEmbed create" + JSON.stringify(embed.info));
1286                  // Create a NodeController instance, set parameters, and rebuild.
1287                  let nodeController = new MyNodeController()
1288                  // The unit of embed.info.width and embed.info.height is px, which needs to be converted to the default unit vp on the eTS side.
1289                  nodeController.setRenderOption({surfaceId : embed.surfaceId as string,
1290                    type : embed.info?.type as string,
1291                    renderType : NodeRenderType.RENDER_TYPE_TEXTURE,
1292                    embedId : embed.embedId as string,
1293                    width : this.uiContext.px2vp(embed.info?.width),
1294                    height : this.uiContext.px2vp(embed.info?.height)})
1295                  this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
1296                  nodeController.setDestroy(false);
1297                  // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key.
1298                  this.nodeControllerMap.set(componentId, nodeController);
1299                  this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width));
1300                  this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height));
1301                  this.positionMap.set(componentId, this.edges);
1302                  // Save the Id attribute of the embed tag passed in by the Web component to the @State decorated variable for dynamically creating a nodeContainer. The push action must be executed after the set action.
1303                  this.componentIdArr.push(componentId)
1304                } else if (embed.status == NativeEmbedStatus.UPDATE) {
1305                  let nodeController = this.nodeControllerMap.get(componentId);
1306                  console.log("NativeEmbed update" + JSON.stringify(embed));
1307                  this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
1308                  this.positionMap.set(componentId, this.edges);
1309                  this.widthMap.set(componentId, this.uiContext.px2vp(embed.info?.width));
1310                  this.heightMap.set(componentId, this.uiContext.px2vp(embed.info?.height));
1311                  nodeController?.updateNode({textOne: 'update', width: this.uiContext.px2vp(embed.info?.width), height: this.uiContext.px2vp(embed.info?.height)} as ESObject)
1312                } else if (embed.status == NativeEmbedStatus.DESTROY) {
1313                  console.log("NativeEmbed destroy" + JSON.stringify(embed));
1314                  let nodeController = this.nodeControllerMap.get(componentId);
1315                  nodeController?.setDestroy(true)
1316                  this.nodeControllerMap.clear();
1317                  this.positionMap.delete(componentId);
1318                  this.widthMap.delete(componentId);
1319                  this.heightMap.delete(componentId);
1320                  this.componentIdArr.filter((value: string) => value != componentId)
1321                } else {
1322                  console.log("NativeEmbed status" + embed.status);
1323                }
1324              })// Obtain the touch event information of components for same-layer rendering.
1325              .onNativeEmbedGestureEvent((touch) => {
1326                console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent));
1327                this.componentIdArr.forEach((componentId: string) => {
1328                  let nodeController = this.nodeControllerMap.get(componentId);
1329                  // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region.
1330                  if(nodeController?.getEmbedId() == touch.embedId) {
1331                    let ret = nodeController?.postEvent(touch.touchEvent)
1332                    if(ret) {
1333                      console.log("onNativeEmbedGestureEvent success " + componentId);
1334                    } else {
1335                      console.log("onNativeEmbedGestureEvent fail " + componentId);
1336                    }
1337                    if(touch.result) {
1338                      // Notify the Web component of the consumption result of the gesture event.
1339                      touch.result.setGestureEventResult(ret);
1340                    }
1341                  }
1342                })
1343              })
1344              .border({width: 2, color: Color.Gray})
1345              .height("50%")
1346          }
1347        }
1348      }
1349    }
1350  }
1351  ```
1352
1353- Example of a frontend page:
1354
1355  The sample code uses the **embed** tag. If the **object** tag is used, register the **object** tag and **type** on the eTS side.
1356  ```html
1357  <!--HAP's src/main/resources/rawfile/overlay.html-->
1358  <!DOCTYPE html>
1359  <html>
1360  <head>
1361      <title>Same-Layer Rendering HTML</title>
1362      <meta name="viewport" content="initial-scale=1.0">
1363  </head>
1364  <body>
1365  <div>
1366      <div id = "test" style = "position: absolute; z-index: 9999; text-align: center; background-color: rgb(61, 157, 180); top: 40px; left: 30px; width: 300px; height: 120px">
1367          z-index: 9999
1368      </div>
1369
1370      <embed id = "input1" type = "native/view1" arkwebnativestyle = "display:overlay-infinity" style = "position: absolute; top: 60px; left: 50px; width: 300px; height: 100px">
1371
1372      <embed id = "input2" type = "native/view2" arkwebnativestyle = "display:overlay" style = "position: absolute; top: 150px; left: 40px; width: 300px; height: 100px">
1373  </div>
1374  </body>
1375  </html>
1376  ```
1377
1378- Demo:
1379
1380  The **display** attribute of **arkwebnativestyle** is not set.
1381
1382  ![web-same-layer](figures/web-same-layer-nativeEmbedOverlay1.png)
1383
1384  The **display** attribute of **arkwebnativestyle** is set.
1385
1386  ![web-same-layer](figures/web-same-layer-nativeEmbedOverlay2.png)
1387
1388## FAQs
1389### What should I do if the same-layer rendered components are stretched?
1390
1391- The component height is too high.
1392
1393  Due to GPU limitations, the maximum height of the same-layer tag is 8000 px. If this limit is exceeded, the component will be stretched. In this case, you need to set the height of the same-layer tag to less than 8000 px.
1394
1395- The width and height of the custom component do not match those of the same-layer rendered tag.
1396
1397  The width and height of the custom same-layer rendered component must be the same as those of the same-layer tag. The following is an example:
1398  ```ts
1399    @Component
1400    struct TextInputComponent {
1401      @Prop params: Params
1402      @State bkColor: Color = Color.White
1403
1404      build() {
1405        Column() {
1406          TextInput({text: '', placeholder: 'please input your word...'})
1407            .fontColor(Color.Black)
1408        }
1409        // The width and height of the outermost custom container component must be the same as those of the same-layer tag.
1410        .width(this.params.width)
1411        .height(this.params.height)
1412      }
1413    }
1414  ```
1415
1416### How do I transparently transmit the events captured by the same-layer rendered component to the web front end?
1417For gesture events of the same-layer rendered tag, you can set the gesture event consumption result through [setGestureEventResult()](../reference/apis-arkweb/arkts-basic-components-web-EventResult.md#setgestureeventresult14). The gesture events can be consumed on the system component side or ArkWeb side. To consume gesture events on the system component side and ArkWeb side at the same time, set **stopPropagation** to **false** in [setGestureEventResult()](../reference/apis-arkweb/arkts-basic-components-web-EventResult.md#setgestureeventresult14). In this way, gesture events are propagated to ArkWeb when being consumed on the system component side.
1418
1419### What should I do if plug-ins cannot be displayed on the same-layer rendered page?
1420
1421- The same-layer rendering [enableNativeEmbedMode](../reference/apis-arkweb/arkts-basic-components-web-attributes.md#enablenativeembedmode11) is not enabled.
1422
1423  Explicitly enable same-layer rendering.
1424  ```ts
1425  Web({ src: $rawfile("text.html"), controller: this.controller })
1426    // Enable same-layer rendering.
1427    .enableNativeEmbedMode(true)
1428  ```
1429
1430- The same-layer tag is incorrectly used.
1431
1432  To use the **\<embed>** tag, you need to explicitly write "embed" and use the **type** starting with "native/". To use the **\<object>** tag, you need to register it and **type**.
1433
1434### What should I do if the cursor and text box of the ArkUI component (such as TextInput) that involves UI interaction are misplaced?
1435You need to use a **Stack** to wrap the same-layer component container and **BuilderNode**. Then you need to bind **NodeContainer** to the position of the same-layer tag. The following is an example:
1436```ts
1437ForEach(this.componentIdArr, (componentId: string) => {
1438  NodeContainer(this.nodeControllerMap.get(componentId))
1439    // The same-layer component container should be bound to the width, height, and position of the same-layer tag.
1440    .position(this.positionMap.get(componentId))
1441    .width(this.widthMap.get(componentId))
1442    .height(this.heightMap.get(componentId))
1443}, (embedId: string) => embedId)
1444```
1445