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-native UI components (same-layer components) is inferior to that of native components, you can use the ArkUI component to render these 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 rendering components.
11
12- In addition, you can use ArkUI APIs such as **NodeContainer** to construct same-layer rendering 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/ts-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 native components, which complete the functions of the Flutter components. Weex2.0 framework supports the **Camera**, **Video**, and **Canvas** components.
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 rendering 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/ts-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_en.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/ts-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 the 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 rendering components based on the following specifications.
55
56**Supported Devices**:
57Currently, only mobile phones and tablets are supported.
58
59**Supported HTML5 Tags**:
60- **\<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.
61
62- **\<object>**: After the 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.
63
64- W3C standard tags (such as **\<input>** and **\<video>**) cannot be defined as same-layer tags.
65
66- The **\<object>** and **\<embed>** tags cannot be configured as the same-layer tags at the same time.
67
68- The tag types contain only English characters and are case insensitive.
69
70**Supported Attributes of Same-Layer Tags**:
71CSS attributes that comply with the W3C standard.
72
73**Lifecycle Management of Same-Layer Tags**:
74The [onNativeEmbedLifecycleChange()](../reference/apis-arkweb/ts-basic-components-web.md#onnativeembedlifecyclechange11) callback is triggered when the lifecycle of the **Embed** tag changes.
75
76- Creation, destruction, and position width and height change are supported. The visibility status change is not supported.
77
78- Web pages containing same-layer components support back-forward cache.
79
80**Distributing and Processing the Input Events of Same-Layer Tags**:
81- The **DOWN**, **UP**, **MOVE**, and **CANCEL** touch events are supported. The [onnativeembedgestureevent11](../reference/apis-arkweb/ts-basic-components-web.md#onnativeembedgestureevent11) can be configured. By default, the touch event is consumed on the application side.
82
83- The application page containing same-layer components cannot be scaled, and the scaling APIs such as [initialScale](../reference/apis-arkweb/ts-basic-components-web.md#initialscale), [zoom](../reference/apis-arkweb/js-apis-webview.md#zoom), [zoomIn](../reference/apis-arkweb/js-apis-webview.md#zoomin) and [zoomOut](../reference/apis-arkweb/js-apis-webview.md#zoomout) are not supported.
84
85- Mouse, keyboard, and touchpad events are not supported.
86
87**Constraints**:
88
89- Configure a maximum of five same-layer tags on a web page. Otherwise, the rendering performance deteriorates.
90
91- Due to GPU restrictions, the maximum height and texture size of a same-layer tag are 8000 px.
92
93- When same-layer rendering is enabled, web pages opened by the **Web** component do not support the unified [RenderMode](../reference/apis-arkweb/ts-basic-components-web.md#rendermode12).
94
95- 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.
96
97- The **Web** component supports only same-layer rendering nesting at one layer. The input events such as swipe, tap, zoom, and long-press are supported, while drag and rotate events are not supported.
98
99- 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.
100
101  **Figure 2** Misplaced **TextInput** without **Stack**
102
103  ![web-same-layer-develop](figures/web-same-layer-develop-textinput1.png)
104
105  **Figure 3** Proper **TextInput** with **Stack**
106
107  ![web-same-layer-develop](figures/web-same-layer-develop-textinput2_en.png)
108
109## Rendering Text Boxes at the Same Layer on Web Pages
110On web pages, you can render the native ArkUI **TextInput** components at the same layer. The following figure shows the effect of three text boxes that are rendered at the same layer.
111
112**Figure 4** Same-layer rendering text boxes
113
114  ![web-same-layer-develop](figures/web-same-layer-develop-input.png)
115
1161. Mark the HTML tags that need to be rendered at the same layer on the web page.
117
118   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.
119
120   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.
121
122   - Use the \<embed> tags.
123
124     ```html
125     <!--HAP's src/main/resources/rawfile/text.html-->
126     <!DOCTYPE html>
127     <html>
128     <head>
129         <title>Same-Layer Rendering Test HTML</title>
130         <meta name="viewport">
131     </head>
132
133     <body style="background:white">
134
135     <embed id = "input1" type="native/view" style="width: 100%; height: 100px; margin: 30px; margin-top: 600px"/>
136
137     <embed id = "input2" type="native/view2" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"/>
138
139     <embed id = "input3" type="native/view3" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"/>
140
141     </body>
142     </html>
143     ```
144
145   - Use the \<object> tags.
146
147     Call **registerNativeEmbedRule** to register a **\<object>** tag.
148     ```ts
149     // ...
150     Web({src: $rawfile("text.html"), controller: this.browserTabController})
151       // Register the same-layer tag of "object" and type of "test."
152       .registerNativeEmbedRule("object", "test")
153       // ...
154     ```
155
156     Example of using **registerNativeEmbedRule** on the frontend page, with the tag of "object" and type of "test":
157
158      ```html
159      <!--HAP's src/main/resources/rawfile/text.html-->
160      <!DOCTYPE html>
161      <html>
162      <head>
163          <title>Same-Layer Rendering Test HTML</title>
164          <meta name="viewport">
165      </head>
166
167      <body style="background:white">
168
169      <object id = "input1" type="test/input" style="width: 100%; height: 100px; margin: 30px; margin-top: 600px"></object>
170
171      <object id = "input2" type="test/input" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"></object>
172
173      <object id = "input3" type="test/input" style="width: 100%; height: 100px; margin: 30px; margin-top: 50px"></object>
174
175      </body>
176      </html>
177      ```
178
1792. Use **enableNativeEmbedMode** to enable the same-layer rendering on the application,
180
181   which is disabled by default.
182
183   ```ts
184   // xxx.ets
185   import { webview } from '@kit.ArkWeb';
186   @Entry
187   @Component
188   struct WebComponent {
189     controller: webview.WebviewController = new webview.WebviewController();
190
191     build() {
192       Column() {
193         Web({ src: 'www.example.com', controller: this.controller })
194           // Enable same-layer rendering.
195           .enableNativeEmbedMode(true)
196       }
197     }
198   }
199   ```
200
2013. Create a custom component,
202
203   which is displayed as a native component in the corresponding area after the same-layer rendering is enabled.
204
205   ```ts
206   @Component
207   struct TextInputComponent {
208     @Prop params: Params
209     @State bkColor: Color = Color.White
210
211     build() {
212       Column() {
213         TextInput({text: '', placeholder: 'please input your word...'})
214           .placeholderColor(Color.Gray)
215           .id(this.params?.elementId)
216           .placeholderFont({size: 13, weight: 400})
217           .caretColor(Color.Gray)
218           .width(this.params?.width)
219           .height(this.params?.height)
220           .fontSize(14)
221           .fontColor(Color.Black)
222       }
223       // The width and height of the outermost custom container component must be the same as those of the tag at the same layer.
224       .width(this.params.width)
225       .height(this.params.height)
226     }
227   }
228
229   @Builder
230   function TextInputBuilder(params:Params) {
231     TextInputComponent({params: params})
232       .width(params.width)
233       .height(params.height)
234       .backgroundColor(Color.White)
235   }
236   ```
237
2384. Create a node controller,
239
240   which is used to control and report node behaviors of the corresponding NodeContainer.
241
242   ```ts
243   class MyNodeController extends NodeController {
244     private rootNode: BuilderNode<[Params]> | undefined | null;
245     private embedId_: string = "";
246     private surfaceId_: string = "";
247     private renderType_: NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;
248     private width_: number = 0;
249     private height_: number = 0;
250     private type_: string = "";
251     private isDestroy_: boolean = false;
252
253     setRenderOption(params: nodeControllerParams) {
254       this.surfaceId_ = params.surfaceId;
255       this.renderType_ = params.renderType;
256       this.embedId_ = params.embedId;
257       this.width_ = params.width;
258       this.height_ = params.height;
259       this.type_ = params.type;
260     }
261
262     // 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.
263     // Called when the corresponding NodeContainer is created or called by the rebuild method.
264     makeNode(uiContext: UIContext): FrameNode | null {
265       if (this.isDestroy_) { // rootNode is null.
266         return null;
267       }
268       if (!this.rootNode) { // When rootNode is set to undefined
269         this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_ });
270         if(this.rootNode) {
271           this.rootNode.build(wrapBuilder(TextInputBuilder), {  textOne: "myTextInput", width: this.width_, height: this.height_  })
272           return this.rootNode.getFrameNode();
273         }else{
274           return null;
275         }
276       }
277       // Return the FrameNode object.
278       return this.rootNode.getFrameNode();
279     }
280
281     setBuilderNode(rootNode: BuilderNode<Params[]> | null): void {
282       this.rootNode = rootNode;
283     }
284
285     getBuilderNode(): BuilderNode<[Params]> | undefined | null {
286       return this.rootNode;
287     }
288
289     updateNode(arg: Object): void {
290       this.rootNode?.update(arg);
291     }
292
293     getEmbedId(): string {
294       return this.embedId_;
295     }
296
297     setDestroy(isDestroy: boolean): void {
298       this.isDestroy_ = isDestroy;
299       if (this.isDestroy_) {
300         this.rootNode = null;
301       }
302     }
303
304     postEvent(event: TouchEvent | undefined): boolean {
305       return this.rootNode?.postTouchEvent(event) as boolean
306     }
307   }
308   ```
309
3105. Call [onNativeEmbedLifecycleChange](../reference/apis-arkweb/ts-basic-components-web.md#onnativeembedlifecyclechange11) to listen for the lifecycle changes of the same-layer rendering tags.
311
312   After this function is enabled, the ArkWeb kernel triggers the callback registered by [onNativeEmbedLifecycleChange](../reference/apis-arkweb/ts-basic-components-web.md#onnativeembedlifecyclechange11) each time a same-layer rendering tag is used on a web page.
313
314
315
316    ```ts
317    build() {
318      Row() {
319        Column() {
320          Stack() {
321            ForEach(this.componentIdArr, (componentId: string) => {
322              NodeContainer(this.nodeControllerMap.get(componentId))
323                .position(this.positionMap.get(componentId))
324                .width(this.widthMap.get(componentId))
325                .height(this.heightMap.get(componentId))
326            }, (embedId: string) => embedId)
327            // Load the local text.html page.
328            Web({src: $rawfile("text.html"), controller: this.browserTabController})
329              // Enable same-layer rendering.
330              .enableNativeEmbedMode(true)
331                // Register the same-layer tag of "object" and type of "test."
332              .registerNativeEmbedRule("object", "test")
333                // Obtain the lifecycle change data of the embed tag.
334              .onNativeEmbedLifecycleChange((embed) => {
335                console.log("NativeEmbed surfaceId" + embed.surfaceId);
336                // If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page.
337                const componentId = embed.info?.id?.toString() as string
338                if (embed.status == NativeEmbedStatus.CREATE) {
339                  console.log("NativeEmbed create" + JSON.stringify(embed.info));
340                  // Create a NodeController instance, set parameters, and rebuild.
341                  let nodeController = new MyNodeController()
342                  // 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.
343                  nodeController.setRenderOption({surfaceId : embed.surfaceId as string,
344                    type : embed.info?.type as string,
345                    renderType : NodeRenderType.RENDER_TYPE_TEXTURE,
346                    embedId : embed.embedId as string,
347                    width : px2vp(embed.info?.width),
348                    height : px2vp(embed.info?.height)})
349                  this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
350                  nodeController.setDestroy(false);
351                  // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key.
352                  this.nodeControllerMap.set(componentId, nodeController);
353                  this.widthMap.set(componentId, px2vp(embed.info?.width));
354                  this.heightMap.set(componentId, px2vp(embed.info?.height));
355                  this.positionMap.set(componentId, this.edges);
356                  // 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.
357                  this.componentIdArr.push(componentId)
358                } else if (embed.status == NativeEmbedStatus.UPDATE) {
359                  let nodeController = this.nodeControllerMap.get(componentId);
360                  console.log("NativeEmbed update" + JSON.stringify(embed));
361                  this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
362                  this.positionMap.set(componentId, this.edges);
363                  this.widthMap.set(componentId, px2vp(embed.info?.width));
364                  this.heightMap.set(componentId, px2vp(embed.info?.height));
365                  nodeController?.updateNode({textOne: 'update', width: px2vp(embed.info?.width), height: px2vp(embed.info?.height)} as ESObject)
366                } else if (embed.status == NativeEmbedStatus.DESTROY) {
367                  console.log("NativeEmbed destroy" + JSON.stringify(embed));
368                  let nodeController = this.nodeControllerMap.get(componentId);
369                  nodeController?.setDestroy(true)
370                  this.nodeControllerMap.clear();
371                  this.positionMap.delete(componentId);
372                  this.widthMap.delete(componentId);
373                  this.heightMap.delete(componentId);
374                  this.componentIdArr.filter((value: string) => value != componentId)
375                } else {
376                  console.log("NativeEmbed status" + embed.status);
377                }
378              })
379          }.height("80%")
380        }
381      }
382    }
383    ```
384
3856. Call [onNativeEmbedGestureEvent](../reference/apis-arkweb/ts-basic-components-web.md#onnativeembedgestureevent11) to listen for gesture events that are rendered at the same layer.
386
387   When gesture events are listened, the ArkWeb kernel triggers the callback registered by [onNativeEmbedGestureEvent](../reference/apis-arkweb/ts-basic-components-web.md#onnativeembedgestureevent11) each time a touch operation is performed in the same-layer rendering region.
388
389
390
391    ```ts
392    build() {
393      Row() {
394        Column() {
395          Stack() {
396            ForEach(this.componentIdArr, (componentId: string) => {
397              NodeContainer(this.nodeControllerMap.get(componentId))
398                .position(this.positionMap.get(componentId))
399                .width(this.widthMap.get(componentId))
400                .height(this.heightMap.get(componentId))
401            }, (embedId: string) => embedId)
402            // Load the local text.html page.
403            Web({src: $rawfile("text.html"), controller: this.browserTabController})
404              // Enable same-layer rendering.
405              .enableNativeEmbedMode(true)
406                // Obtain the lifecycle change data of the embed tag.
407              .onNativeEmbedLifecycleChange((embed) => {
408                // Implement lifecycle changes.
409              })
410              .onNativeEmbedGestureEvent((touch) => {
411                console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent));
412                this.componentIdArr.forEach((componentId: string) => {
413                  let nodeController = this.nodeControllerMap.get(componentId);
414                  // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region.
415                  if(nodeController?.getEmbedId() == touch.embedId) {
416                    let ret = nodeController?.postEvent(touch.touchEvent)
417                    if(ret) {
418                      console.log("onNativeEmbedGestureEvent success " + componentId);
419                    } else {
420                      console.log("onNativeEmbedGestureEvent fail " + componentId);
421                    }
422                    if(touch.result) {
423                      // Notify the Web component of the gesture event consumption result.
424                      touch.result.setGestureEventResult(ret);
425                    }
426                  }
427                })
428              })
429          }
430        }
431      }
432    }
433    ```
434
435**Sample Code**
436
437To start with, add the Internet permission to the **module.json5** file. For details, see [Declaring Permissions](../security/AccessToken/declare-permissions.md).
438
439  ```
440  "requestPermissions":[
441      {
442        "name" : "ohos.permission.INTERNET"
443      }
444    ]
445  ```
446
447Code on the application side:
448
449  ```ts
450  // Create a NodeController instance.
451  import webview from '@ohos.web.webview';
452  import { UIContext } from '@ohos.arkui.UIContext';
453  import { NodeController, BuilderNode, NodeRenderType, FrameNode } from "@ohos.arkui.node";
454
455  @Observed
456  declare class Params{
457    elementId: string
458    textOne: string
459    textTwo: string
460    width: number
461    height: number
462  }
463
464  declare class nodeControllerParams {
465    surfaceId: string
466    type: string
467    renderType: NodeRenderType
468    embedId: string
469    width: number
470    height: number
471  }
472
473  // The NodeController instance must be used with a NodeContainer for controlling and feeding back the behavior of the nodes in the container.
474  class MyNodeController extends NodeController {
475    private rootNode: BuilderNode<[Params]> | undefined | null;
476    private embedId_: string = "";
477    private surfaceId_: string = "";
478    private renderType_: NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;
479    private width_: number = 0;
480    private height_: number = 0;
481    private type_: string = "";
482    private isDestroy_: boolean = false;
483
484    setRenderOption(params: nodeControllerParams) {
485      this.surfaceId_ = params.surfaceId;
486      this.renderType_ = params.renderType;
487      this.embedId_ = params.embedId;
488      this.width_ = params.width;
489      this.height_ = params.height;
490      this.type_ = params.type;
491    }
492
493    // 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.
494    // Called when the corresponding NodeContainer is created or called by the rebuild method.
495    makeNode(uiContext: UIContext): FrameNode | null {
496      if (this.isDestroy_) { // rootNode is null.
497        return null;
498      }
499      if (!this.rootNode) { // When rootNode is set to undefined
500        this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_ });
501        if(this.rootNode) {
502          this.rootNode.build(wrapBuilder(TextInputBuilder), {  textOne: "myTextInput", width: this.width_, height: this.height_  })
503          return this.rootNode.getFrameNode();
504        }else{
505          return null;
506        }
507      }
508      // Return the FrameNode object.
509      return this.rootNode.getFrameNode();
510    }
511
512    setBuilderNode(rootNode: BuilderNode<Params[]> | null): void {
513      this.rootNode = rootNode;
514    }
515
516    getBuilderNode(): BuilderNode<[Params]> | undefined | null {
517      return this.rootNode;
518    }
519
520    updateNode(arg: Object): void {
521      this.rootNode?.update(arg);
522    }
523
524    getEmbedId(): string {
525      return this.embedId_;
526    }
527
528    setDestroy(isDestroy: boolean): void {
529      this.isDestroy_ = isDestroy;
530      if (this.isDestroy_) {
531        this.rootNode = null;
532      }
533    }
534
535    postEvent(event: TouchEvent | undefined): boolean {
536      return this.rootNode?.postTouchEvent(event) as boolean
537    }
538  }
539
540  @Component
541  struct TextInputComponent {
542    @Prop params: Params
543    @State bkColor: Color = Color.White
544
545    build() {
546      Column() {
547        TextInput({text: '', placeholder: 'please input your word...'})
548          .placeholderColor(Color.Gray)
549          .id(this.params?.elementId)
550          .placeholderFont({size: 13, weight: 400})
551          .caretColor(Color.Gray)
552          .fontSize(14)
553          .fontColor(Color.Black)
554      }
555      // The width and height of the outermost custom container component must be the same as those of the tag at the same layer.
556      .width(this.params.width)
557      .height(this.params.height)
558    }
559  }
560
561  // In @Builder, add the specific dynamic component content.
562  @Builder
563  function TextInputBuilder(params:Params) {
564    TextInputComponent({params: params})
565      .width(params.width)
566      .height(params.height)
567      .backgroundColor(Color.White)
568  }
569
570  @Entry
571  @Component
572  struct Page{
573    browserTabController: WebviewController = new webview.WebviewController()
574    private nodeControllerMap: Map<string, MyNodeController> = new Map();
575    @State componentIdArr: Array<string> = [];
576    @State posMap: Map<string, Position | undefined> = new Map();
577    @State widthMap: Map<string, number> = new Map();
578    @State heightMap: Map<string, number> = new Map();
579    @State positionMap: Map<string, Edges> = new Map();
580    @State edges: Edges = {};
581
582    build() {
583      Row() {
584        Column() {
585          Stack() {
586            ForEach(this.componentIdArr, (componentId: string) => {
587              NodeContainer(this.nodeControllerMap.get(componentId))
588                .position(this.positionMap.get(componentId))
589                .width(this.widthMap.get(componentId))
590                .height(this.heightMap.get(componentId))
591            }, (embedId: string) => embedId)
592            // Load the local text.html page.
593            Web({src: $rawfile("text.html"), controller: this.browserTabController})
594              // Enable same-layer rendering.
595              .enableNativeEmbedMode(true)
596              // Obtain the lifecycle change data of the embed tag.
597              .onNativeEmbedLifecycleChange((embed) => {
598                 console.log("NativeEmbed surfaceId" + embed.surfaceId);
599                 // If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page.
600                 const componentId = embed.info?.id?.toString() as string
601                 if (embed.status == NativeEmbedStatus.CREATE) {
602                   console.log("NativeEmbed create" + JSON.stringify(embed.info));
603                   // Create a NodeController instance, set parameters, and rebuild.
604                   let nodeController = new MyNodeController()
605                   // 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.
606                   nodeController.setRenderOption({surfaceId : embed.surfaceId as string,
607                     type : embed.info?.type as string,
608                     renderType : NodeRenderType.RENDER_TYPE_TEXTURE,
609                     embedId : embed.embedId as string,
610                     width : px2vp(embed.info?.width),
611                     height : px2vp(embed.info?.height)})
612                   this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
613                   nodeController.setDestroy(false);
614                   // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key.
615                   this.nodeControllerMap.set(componentId, nodeController);
616                   this.widthMap.set(componentId, px2vp(embed.info?.width));
617                   this.heightMap.set(componentId, px2vp(embed.info?.height));
618                   this.positionMap.set(componentId, this.edges);
619                   // 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.
620                   this.componentIdArr.push(componentId)
621                 } else if (embed.status == NativeEmbedStatus.UPDATE) {
622                   let nodeController = this.nodeControllerMap.get(componentId);
623                   console.log("NativeEmbed update" + JSON.stringify(embed));
624                   this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
625                   this.positionMap.set(componentId, this.edges);
626                   this.widthMap.set(componentId, px2vp(embed.info?.width));
627                   this.heightMap.set(componentId, px2vp(embed.info?.height));
628                   nodeController?.updateNode({textOne: 'update', width: px2vp(embed.info?.width), height: px2vp(embed.info?.height)} as ESObject)
629                 } else if (embed.status == NativeEmbedStatus.DESTROY) {
630                   console.log("NativeEmbed destroy" + JSON.stringify(embed));
631                   let nodeController = this.nodeControllerMap.get(componentId);
632                   nodeController?.setDestroy(true)
633                   this.nodeControllerMap.clear();
634                   this.positionMap.delete(componentId);
635                   this.widthMap.delete(componentId);
636                   this.heightMap.delete(componentId);
637                   this.componentIdArr.filter((value: string) => value != componentId)
638                 } else {
639                   console.log("NativeEmbed status" + embed.status);
640                 }
641               })// Obtain the touch event information of components for same-layer rendering.
642              .onNativeEmbedGestureEvent((touch) => {
643                console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent));
644                this.componentIdArr.forEach((componentId: string) => {
645                  let nodeController = this.nodeControllerMap.get(componentId);
646                  // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region.
647                  if(nodeController?.getEmbedId() == touch.embedId) {
648                    let ret = nodeController?.postEvent(touch.touchEvent)
649                    if(ret) {
650                      console.log("onNativeEmbedGestureEvent success " + componentId);
651                    } else {
652                      console.log("onNativeEmbedGestureEvent fail " + componentId);
653                    }
654                    if(touch.result) {
655                      // Notify the Web component of the gesture event consumption result.
656                      touch.result.setGestureEventResult(ret);
657                    }
658                  }
659                })
660              })
661          }
662        }
663      }
664    }
665  }
666  ```
667
668## Drawing the XComponent+AVPlayer and Button Components
669
670You can enable or disable same-layer rendering through [enableNativeEmbedMode()](../reference/apis-arkweb/ts-basic-components-web.md#enablenativeembedmode11). To use same-layer rendering, the **\<embed>** element must be explicitly used in the HTML file, and the **type** attribute of the element must start with **native/**. The background of the elements corresponding to the tags at the same layer is transparent.
671
672- Example of using same-layer rendering on the application side:
673
674  ```ts
675  // HAP's src/main/ets/pages/Index.ets
676  // Create a NodeController instance.
677  import { webview } from '@kit.ArkWeb';
678  import { UIContext, NodeController, BuilderNode, NodeRenderType, FrameNode } from "@kit.ArkUI";
679  import { AVPlayerDemo } from './PlayerDemo';
680
681  @Observed
682  declare class Params {
683    textOne : string
684    textTwo : string
685    width : number
686    height : number
687  }
688
689  declare class nodeControllerParams {
690    surfaceId : string
691    type : string
692    renderType : NodeRenderType
693    embedId : string
694    width : number
695    height : number
696  }
697
698  // The NodeController instance must be used with a NodeContainer for controlling and feeding back the behavior of the nodes in the container.
699  class MyNodeController extends NodeController {
700    private rootNode: BuilderNode<[Params]> | undefined | null;
701    private embedId_ : string = "";
702    private surfaceId_ : string = "";
703    private renderType_ :NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;
704    private width_ : number = 0;
705    private height_ : number = 0;
706    private type_ : string = "";
707    private isDestroy_ : boolean = false;
708
709    setRenderOption(params : nodeControllerParams) {
710      this.surfaceId_ = params.surfaceId;
711      this.renderType_ = params.renderType;
712      this.embedId_ = params.embedId;
713      this.width_ = params.width;
714      this.height_ = params.height;
715      this.type_ = params.type;
716    }
717    // 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.
718    // Called when the corresponding NodeContainer is created or called by the rebuild method.
719    makeNode(uiContext: UIContext): FrameNode | null{
720      if (this.isDestroy_) { // rootNode is null.
721        return null;
722      }
723      if (!this.rootNode) { // When rootNode is set to undefined
724        this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_});
725        if (this.type_ === 'native/video') {
726          this.rootNode.build(wrapBuilder(VideoBuilder), {textOne: "myButton", width : this.width_, height : this.height_});
727        } else {
728          // other
729        }
730      }
731      // Return the FrameNode object.
732      return this.rootNode.getFrameNode();
733    }
734
735    setBuilderNode(rootNode: BuilderNode<Params[]> | null): void{
736      this.rootNode = rootNode;
737    }
738
739    getBuilderNode(): BuilderNode<[Params]> | undefined | null{
740      return this.rootNode;
741    }
742
743    updateNode(arg: Object): void {
744      this.rootNode?.update(arg);
745    }
746    getEmbedId() : string {
747      return this.embedId_;
748    }
749
750    setDestroy(isDestroy : boolean) : void {
751      this.isDestroy_ = isDestroy;
752      if (this.isDestroy_) {
753        this.rootNode = null;
754      }
755    }
756
757    postEvent(event: TouchEvent | undefined) : boolean {
758      return this.rootNode?.postTouchEvent(event) as boolean
759    }
760  }
761
762  @Component
763  struct VideoComponent {
764    @ObjectLink params: Params
765    @State bkColor: Color = Color.Red
766    mXComponentController: XComponentController = new XComponentController();
767    @State player_changed: boolean = false;
768    player?: AVPlayerDemo;
769
770    build() {
771      Column() {
772        Button(this.params.textOne)
773
774        XComponent({ id: 'video_player_id', type: XComponentType.SURFACE, controller: this.mXComponentController})
775          .border({width: 1, color: Color.Red})
776          .onLoad(() => {
777            this.player = new AVPlayerDemo();
778            this.player.setSurfaceID(this.mXComponentController.getXComponentSurfaceId());
779            this.player_changed = !this.player_changed;
780            this.player.avPlayerLiveDemo()
781          })
782          .width(300)
783          .height(200)
784      }
785      // The width and height of the outermost custom container component must be the same as those of the tag at the same layer.
786      .width(this.params.width)
787      .height(this.params.height)
788    }
789  }
790  // In @Builder, add the specific dynamic component content.
791  @Builder
792  function VideoBuilder(params: Params) {
793    VideoComponent({ params: params })
794      .backgroundColor(Color.Gray)
795  }
796
797  @Entry
798  @Component
799  struct WebIndex {
800    browserTabController: WebviewController = new webview.WebviewController()
801    private nodeControllerMap: Map<string, MyNodeController> = new Map();
802    @State componentIdArr: Array<string> = [];
803
804    aboutToAppear() {
805      // Enable web frontend page debugging.
806      webview.WebviewController.setWebDebuggingAccess(true);
807    }
808
809    build(){
810      Row() {
811        Column() {
812          Stack() {
813            ForEach(this.componentIdArr, (componentId: string) => {
814              NodeContainer(this.nodeControllerMap.get(componentId))
815            }, (embedId: string) => embedId)
816            // Load the local test.html page.
817            Web({ src: $rawfile("test.html"), controller: this.browserTabController })
818              // Enable same-layer rendering.
819              .enableNativeEmbedMode(true)
820                // Obtain the lifecycle change data of the embed tag.
821              .onNativeEmbedLifecycleChange((embed) => {
822                console.log("NativeEmbed surfaceId" + embed.surfaceId);
823                // 1. If embed.info.id is used as the key for mapping nodeController, explicitly specify the ID on the HTML5 page.
824                const componentId = embed.info?.id?.toString() as string
825                if (embed.status == NativeEmbedStatus.CREATE) {
826                  console.log("NativeEmbed create" + JSON.stringify(embed.info))
827                  // Create a NodeController instance, set parameters, and rebuild.
828                  let nodeController = new MyNodeController()
829                  // 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.
830                  nodeController.setRenderOption({surfaceId : embed.surfaceId as string, type : embed.info?.type as string,
831                    renderType : NodeRenderType.RENDER_TYPE_TEXTURE, embedId : embed.embedId as string,
832                    width : px2vp(embed.info?.width), height : px2vp(embed.info?.height)})
833                  nodeController.setDestroy(false);
834                  // Save the nodeController instance to the Map, with the Id attribute of the embed tag passed in by the Web component as the key.
835                  this.nodeControllerMap.set(componentId, nodeController)
836                  // 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.
837                  this.componentIdArr.push(componentId)
838                } else if (embed.status == NativeEmbedStatus.UPDATE) {
839                  let nodeController = this.nodeControllerMap.get(componentId)
840                  nodeController?.updateNode({textOne: 'update', width: px2vp(embed.info?.width), height: px2vp(embed.info?.height)} as ESObject)
841                } else {
842                  let nodeController = this.nodeControllerMap.get(componentId);
843                  nodeController?.setDestroy(true)
844                  this.nodeControllerMap.clear();
845                  this.componentIdArr.length = 0;
846                }
847              })// Obtain the touch event information of components for same-layer rendering.
848              .onNativeEmbedGestureEvent((touch) => {
849                console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent));
850                this.componentIdArr.forEach((componentId: string) => {
851                  let nodeController = this.nodeControllerMap.get(componentId)
852                  // Send the obtained event of the region at the same layer to the nodeController corresponding to embedId of the region.
853                  if (nodeController?.getEmbedId() === touch.embedId) {
854                    let ret = nodeController?.postEvent(touch.touchEvent)
855                    if (ret) {
856                      console.log("onNativeEmbedGestureEvent success " + componentId)
857                    } else {
858                      console.log("onNativeEmbedGestureEvent fail " + componentId)
859                    }
860                    if (touch.result) {
861                      // Notify the Web component of the gesture event consumption result.
862                      touch.result.setGestureEventResult(ret);
863                    }
864                  }
865                })
866              })
867          }
868        }
869      }
870    }
871  }
872  ```
873
874- Example of video playback code on the application side. Replace the video URL with the correct one in practice.
875
876  ```ts
877  // HAP's src/main/ets/pages/PlayerDemo.ets
878  import { media } from '@kit.MediaKit';
879  import { BusinessError } from '@ohos.base';
880
881  export class AVPlayerDemo {
882    private count: number = 0;
883    private surfaceID: string = ''; // The surfaceID parameter specifies the window used to display the video. Its value is obtained through XComponent.
884    private isSeek: boolean = true; // Specify whether the seek operation is supported.
885
886    setSurfaceID(surface_id: string){
887      console.log('setSurfaceID : ' + surface_id);
888      this.surfaceID = surface_id;
889    }
890    // Set AVPlayer callback functions.
891    setAVPlayerCallback(avPlayer: media.AVPlayer) {
892      // Callback function for the seek operation.
893      avPlayer.on('seekDone', (seekDoneTime: number) => {
894        console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
895      })
896      // Callback function for errors. If an error occurs during the operation on the AVPlayer, reset() is called to reset the AVPlayer.
897      avPlayer.on('error', (err: BusinessError) => {
898        console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
899        avPlayer.reset();
900      })
901      // Callback for state changes.
902      avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
903        switch (state) {
904          case 'idle': // This state is reported upon a successful callback of reset().
905            console.info('AVPlayer state idle called.');
906            avPlayer.release(); // Call release() to release the instance.
907            break;
908          case 'initialized': // This state is reported when the AVPlayer sets the playback source.
909            console.info('AVPlayer state initialized called.');
910            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.
911            avPlayer.prepare();
912            break;
913          case 'prepared': // This state is reported upon a successful callback of prepare().
914            console.info('AVPlayer state prepared called.');
915            avPlayer.play(); // Call play() to start playback.
916            break;
917          case 'playing': // This state is reported upon a successful callback of play().
918            console.info('AVPlayer state prepared called.');
919            if(this.count !== 0) {
920              if (this.isSeek) {
921                console.info('AVPlayer start to seek.');
922                avPlayer.seek(avPlayer.duration); // Call seek() to seek to the end of the video clip.
923              } else {
924                // When the seek operation is not supported, the playback continues until it reaches the end.
925                console.info('AVPlayer wait to play end.');
926              }
927            } else {
928              avPlayer.pause(); // Call pause() to pause the playback.
929            }
930            this.count++;
931            break;
932          case 'paused': // This state is reported upon a successful callback of pause().
933            console.info('AVPlayer state paused called.');
934            avPlayer.play(); // Call play() again to start playback.
935            break;
936          case 'completed': // This state is reported upon the completion of the playback.
937            console.info('AVPlayer state paused called.');
938            avPlayer.stop(); // Call stop() to stop the playback.
939            break;
940          case 'stopped': // This state is reported upon a successful callback of stop().
941            console.info('AVPlayer state stopped called.');
942            avPlayer.reset(); // Call reset() to reset the AVPlayer.
943            break;
944          case 'released': // This state is reported upon the release of the AVPlayer.
945            console.info('AVPlayer state released called.');
946            break;
947          default:
948            break;
949        }
950      })
951    }
952
953    // Set the live stream source through the URL.
954    async avPlayerLiveDemo(){
955      // Create an AVPlayer instance.
956      let avPlayer: media.AVPlayer = await media.createAVPlayer();
957      // Set a callback for state changes.
958      this.setAVPlayerCallback(avPlayer);
959      this.isSeek = false; // The seek operation is not supported.
960      // Replace the URL with the actual URL of the video source.
961      avPlayer.url = 'https://xxx.xxx/demo.mp4';
962    }
963  }
964  ```
965
966- Example of the frontend page:
967
968  ```html
969  <!--HAP's src/main/resources/rawfile/test.html-->
970  <!DOCTYPE html>
971  <html>
972  <head>
973      <title>Same-Layer Rendering Test HTML</title>
974      <meta name="viewport">
975  </head>
976  <body>
977  <div>
978      <div id="bodyId">
979          <embed id="nativeVideo" type = "native/video" width="1000" height="1500" src="test" style = "background-color:red"/>
980      </div>
981  </div>
982  </body>
983  </html>
984  ```
985
986  ![web-same-layer](figures/web-same-layer.png)
987