diff --git a/ .gitignore b/ .gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1aedc34a9b666584109b593f67ebbd59c262bf77
--- /dev/null
+++ b/ .gitignore
@@ -0,0 +1,14 @@
+.gitignore
+/node_modules
+/oh_modules
+/local.properties
+/.idea
+**/build
+/.hvigor
+.cxx
+/.clangd
+/.clang-format
+/.clang-tidy
+**/.test
+/.appanalyzer
+oh-package-lock.json5
\ No newline at end of file
diff --git a/AppScope/app.json5 b/AppScope/app.json5
new file mode 100644
index 0000000000000000000000000000000000000000..b889a21f0135c690245b87d93292a5210ef2a34a
--- /dev/null
+++ b/AppScope/app.json5
@@ -0,0 +1,10 @@
+{
+ "app": {
+ "bundleName": "com.example.webcrossdomain",
+ "vendor": "example",
+ "versionCode": 1000000,
+ "versionName": "1.0.0",
+ "icon": "$media:layered_image",
+ "label": "$string:app_name"
+ }
+}
diff --git a/AppScope/resources/base/element/string.json b/AppScope/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..1080233f01384411ec684b58955cb8808746fdd3
--- /dev/null
+++ b/AppScope/resources/base/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "app_name",
+ "value": "MyApplication"
+ }
+ ]
+}
diff --git a/AppScope/resources/base/media/background.png b/AppScope/resources/base/media/background.png
new file mode 100644
index 0000000000000000000000000000000000000000..923f2b3f27e915d6871871deea0420eb45ce102f
Binary files /dev/null and b/AppScope/resources/base/media/background.png differ
diff --git a/AppScope/resources/base/media/foreground.png b/AppScope/resources/base/media/foreground.png
new file mode 100644
index 0000000000000000000000000000000000000000..97014d3e10e5ff511409c378cd4255713aecd85f
Binary files /dev/null and b/AppScope/resources/base/media/foreground.png differ
diff --git a/AppScope/resources/base/media/layered_image.json b/AppScope/resources/base/media/layered_image.json
new file mode 100644
index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a
--- /dev/null
+++ b/AppScope/resources/base/media/layered_image.json
@@ -0,0 +1,7 @@
+{
+ "layered-image":
+ {
+ "background" : "$media:background",
+ "foreground" : "$media:foreground"
+ }
+}
\ No newline at end of file
diff --git a/LocalServer/app.js b/LocalServer/app.js
new file mode 100644
index 0000000000000000000000000000000000000000..c100780611acfd791d147f867cd8bb20e945e87a
--- /dev/null
+++ b/LocalServer/app.js
@@ -0,0 +1,93 @@
+const express = require('express');
+const vhost = require('vhost');
+const cors = require('cors');
+const path = require('path');
+const cookieParser = require('cookie-parser');
+
+// Creating a Primary Server
+const app = express();
+
+app.use(express.json());
+
+app.use(cookieParser());
+
+app.use(express.static(path.join(__dirname, './web')));
+
+// Create the server for a.com
+const appA = express();
+
+appA.get('/api/echo', (req, res) => {
+ res.json({
+ status: 'success',
+ port: 8080,
+ message: '请求成功',
+ received: req.query,
+ });
+});
+
+appA.post('/api/setCookies', (req, res) => {
+ console.log('setCookies收到请求:', req.body);
+ const { name, value } = req.body;
+
+ if (!name || !value) {
+ return res.status(400).json({ message: 'Cookie名称和值不能为空' });
+ }
+
+ res.cookie(name, value, {
+ maxAge: 900000,
+ httpOnly: true,
+ secure: false,
+ sameSite: 'lax'
+ });
+
+ res.json({
+ message: 'a.com域名已设置cookie',
+ cookie: { name, value }
+ });
+});
+
+// Create the server for b.com
+const appB = express();
+
+// [Start CORS_configuration]
+appB.use(cors({
+ origin: 'http://www.a.com:8080',
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
+ allowedHeaders: ['Content-Type', 'Authorization'],
+ credentials: true
+}));
+// [End CORS_configuration]
+
+appB.post('/api/getCookies', (req, res) => {
+ console.log('请求携带的 Cookie:', req.cookies);
+ res.json({
+ message: 'b.com域名已接收到cookie',
+ cookies: req.cookies
+ });
+});
+
+// Create the server for c.com
+const appC = express();
+
+appC.get('/api/data', (req, res) => {
+ res.json({
+ status: 'success',
+ port: 8080,
+ message: '请求成功',
+ received: req.query,
+ });
+});
+
+app.use(vhost('www.a.com', appA));
+app.use(vhost('www.b.com', appB));
+app.use(vhost('www.c.com', appC));
+
+// Start the server and listen on port 8080.
+const PORT = 8080;
+app.listen(PORT, () => {
+ console.log(`服务器运行在端口 ${PORT}`);
+ console.log('访问地址:');
+ console.log('http://www.a.com:8080');
+ console.log('http://www.b.com:8080');
+ console.log('http://www.c.com:8080');
+});
diff --git a/LocalServer/package.json b/LocalServer/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..ff9c7495bd7c9c922de93cba26a6b779e42f99a3
--- /dev/null
+++ b/LocalServer/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "serve",
+ "version": "1.0.0",
+ "main": "app.js",
+ "scripts": {
+ "forever:start": "forever start app.js",
+ "forever:stop": "forever stop app.js",
+ "forever:restart": "forever restart app.js",
+ "forever:list": "forever list",
+ "forever:kill": "forever stopall"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "description": "",
+ "dependencies": {
+ "cookie-parser": "^1.4.7",
+ "cors": "^2.8.5",
+ "express": "^5.1.0",
+ "forever": "^4.0.3",
+ "vhost": "^3.0.2"
+ }
+}
diff --git a/LocalVue/README.md b/LocalVue/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..935a5d6dd964e16b1e551fe4e0ecc5af2da46b7c
--- /dev/null
+++ b/LocalVue/README.md
@@ -0,0 +1,39 @@
+# Web页面跨域解决方案
+
+### 简介
+
+本工程为【基于ArkWeb拦截器和Cookie管理能力实现Web页面跨域】的Vue工程,主要用于为该最佳实践的Sample工程提供场景所需的H5页面资源。
+
+### 工程目录
+
+```
+├──src
+│ ├──assets/css
+│ │ └──global.css // 公共css样式
+│ ├──components
+│ │ ├──CookiesSettingsA.vue // 跨域Cookies设置场景A域名页面
+│ │ ├──CookiesSettingsB.vue // 跨域Cookies设置场景B域名页面
+│ │ ├──CustomProtocol.vue // 自定义协议跨域场景页面
+│ │ ├──FatalPage.vue // 通用错误页面
+│ │ ├──LocalResource.vue // 本地资源跨域场景页面
+│ │ └──RemoteRequest.vue // 远程请求跨域场景页面
+│ ├──App.vue // Vue根组件
+│ └──main.js // 应用入口文件
+└──vue.config.js // Vue构建配置文件
+```
+
+### 运行步骤
+
+1. 修改vue.config.js文件的publicPath和outputDir配置项,选择你想要构建的页面打包路径。
+
+2. 修改App.vue文件,选择你想要构建的页面组件。
+
+3. 在根目录下执行以下命令,用于构建页面:
+
+ ```sh
+ npm i
+ npm run build
+ ```
+
+
+
diff --git a/LocalVue/babel.config.js b/LocalVue/babel.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..e9558405fdcc02f12d757acb308e02937a7444f1
--- /dev/null
+++ b/LocalVue/babel.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ presets: [
+ '@vue/cli-plugin-babel/preset'
+ ]
+}
diff --git a/LocalVue/jsconfig.json b/LocalVue/jsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..4aafc5f6ed86fe6dff8d4b6be59290cbdeb61656
--- /dev/null
+++ b/LocalVue/jsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "module": "esnext",
+ "baseUrl": "./",
+ "moduleResolution": "node",
+ "paths": {
+ "@/*": [
+ "src/*"
+ ]
+ },
+ "lib": [
+ "esnext",
+ "dom",
+ "dom.iterable",
+ "scripthost"
+ ]
+ }
+}
diff --git a/LocalVue/package.json b/LocalVue/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..74a176e540b479c933025d8d048eba4be75d847d
--- /dev/null
+++ b/LocalVue/package.json
@@ -0,0 +1,44 @@
+{
+ "name": "localvue",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "serve": "vue-cli-service serve",
+ "build": "vue-cli-service build",
+ "lint": "vue-cli-service lint"
+ },
+ "dependencies": {
+ "axios": "^1.11.0",
+ "core-js": "^3.8.3",
+ "vue": "^3.2.13"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.12.16",
+ "@babel/eslint-parser": "^7.12.16",
+ "@vue/cli-plugin-babel": "~5.0.0",
+ "@vue/cli-plugin-eslint": "~5.0.0",
+ "@vue/cli-service": "~5.0.0",
+ "eslint": "^7.32.0",
+ "eslint-plugin-vue": "^8.0.3"
+ },
+ "eslintConfig": {
+ "root": true,
+ "env": {
+ "node": true
+ },
+ "extends": [
+ "plugin:vue/vue3-essential",
+ "eslint:recommended"
+ ],
+ "parserOptions": {
+ "parser": "@babel/eslint-parser"
+ },
+ "rules": {}
+ },
+ "browserslist": [
+ "> 1%",
+ "last 2 versions",
+ "not dead",
+ "not ie 11"
+ ]
+}
diff --git a/LocalVue/public/favicon.ico b/LocalVue/public/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..df36fcfb72584e00488330b560ebcf34a41c64c2
Binary files /dev/null and b/LocalVue/public/favicon.ico differ
diff --git a/LocalVue/public/index.html b/LocalVue/public/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..3e5a13962197105f2078d2a224cc57dfa09b4893
--- /dev/null
+++ b/LocalVue/public/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+ <%= htmlWebpackPlugin.options.title %>
+
+
+
+ We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.
+
+
+
+
+
diff --git a/LocalVue/src/App.vue b/LocalVue/src/App.vue
new file mode 100644
index 0000000000000000000000000000000000000000..4e077637d000edf186aa759db9a9897588ef0d68
--- /dev/null
+++ b/LocalVue/src/App.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LocalVue/src/assets/css/global.css b/LocalVue/src/assets/css/global.css
new file mode 100644
index 0000000000000000000000000000000000000000..124faf1c05fa5dd564d5c33b0eacb5fd7a8af69f
--- /dev/null
+++ b/LocalVue/src/assets/css/global.css
@@ -0,0 +1,18 @@
+/* 基础样式重置 */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ user-select: none;
+}
+body {
+ margin: 0;
+ padding: 0;
+ line-height: 1.6;
+ color: #333;
+ background-color: #f1f3f5;
+}
+
+a {
+ text-decoration: none;
+}
\ No newline at end of file
diff --git a/LocalVue/src/components/CookiesSettingsA.vue b/LocalVue/src/components/CookiesSettingsA.vue
new file mode 100644
index 0000000000000000000000000000000000000000..e39291f2dd903a0167073588d96d6b3712acaa0e
--- /dev/null
+++ b/LocalVue/src/components/CookiesSettingsA.vue
@@ -0,0 +1,112 @@
+
+
+
+
+
加载a.com域名下H5页面,自动调用setCookies接口,为a.com域名设置cookies,服务器返回信息显示在日志区。
+
点击下方按钮,将a.com域名的cookies设置到b.com域名上。
+
页面路径:
+
http://www.a.com:8080/CookieSettingsA/dist/index.html
+
接口路径:
+
http://www.a.com:8080/api/setCookies
+
+
+
日志区
+
+ {{ result }}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LocalVue/src/components/CookiesSettingsB.vue b/LocalVue/src/components/CookiesSettingsB.vue
new file mode 100644
index 0000000000000000000000000000000000000000..27cd5b829be5c35bc4e84908350848dcbd7a9a06
--- /dev/null
+++ b/LocalVue/src/components/CookiesSettingsB.vue
@@ -0,0 +1,110 @@
+
+
+
+
+
加载b.com域名下H5页面,自动调用getCookies接口,给服务器传递从a.com域名拿到的cookies,服务器返回信息显示在日志区。
+
页面路径:
+
http://www.b.com:8080/CookieSettingsB/dist/index.html
+
接口路径:
+
http://www.b.com:8080/api/getCookies
+
+
+
日志区
+
+ {{ result }}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LocalVue/src/components/CustomProtocol.vue b/LocalVue/src/components/CustomProtocol.vue
new file mode 100644
index 0000000000000000000000000000000000000000..ce55d72db66520d1f6548b020913370491b958b6
--- /dev/null
+++ b/LocalVue/src/components/CustomProtocol.vue
@@ -0,0 +1,80 @@
+
+
+
+
+
加载自定义协议H5页面,拦截并替换为本地H5页面,点击下方按钮调用自定义协议请求,拦截并弹出系统提示框。
+
页面路径:
+
app://index.html
+
接口路径:
+
app://api/openDialog
+
+
+
自定义协议页面部分代码(资源文件使用自定义协议引用)
+
...
+ {{ '' }}
+ {{ '' }}
+ {{ ' ' }}
+ ...
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LocalVue/src/components/FatalPage.vue b/LocalVue/src/components/FatalPage.vue
new file mode 100644
index 0000000000000000000000000000000000000000..2f66b23b465808de1489e882faa1f668fc4e72d8
--- /dev/null
+++ b/LocalVue/src/components/FatalPage.vue
@@ -0,0 +1,108 @@
+
+
+
+
+
加载页面出现错误!
+
请检查服务器是否正常启动,设备端口是否转发。
+
+
+
日志区
+
+ {{ result }}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LocalVue/src/components/LocalResource.vue b/LocalVue/src/components/LocalResource.vue
new file mode 100644
index 0000000000000000000000000000000000000000..91752b61cc10a850c07cec5e97c2bc4dca78f3b3
--- /dev/null
+++ b/LocalVue/src/components/LocalResource.vue
@@ -0,0 +1,111 @@
+
+
+
+
+
加载本地H5页面,点击下方按钮调用Nodejs服务器API,服务器返回信息显示在日志区。
+
页面路径:
+
/data/storage/el1/bundle/entry/resource/resfile/LocalResource/dist/index.html
+
接口路径:
+
http://www.a.com:8080/api/echo
+
+
+
日志区
+
+ {{ result }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LocalVue/src/components/RemoteRequest.vue b/LocalVue/src/components/RemoteRequest.vue
new file mode 100644
index 0000000000000000000000000000000000000000..b1ca52506a4478381bbf1984fb29588e5062ac76
--- /dev/null
+++ b/LocalVue/src/components/RemoteRequest.vue
@@ -0,0 +1,112 @@
+
+
+
+
+
加载a.com域名下H5页面,点击下方按钮调用c.com域名下的API,服务器返回信息显示在日志区。
+
页面路径:
+
http://www.a.com:8080/RemoteRequest/dist/index.html
+
接口路径:
+
http://www.c.com:8080/api/data
+
+
+
日志区
+
+ {{ result }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LocalVue/src/main.js b/LocalVue/src/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..de71c75377976da916fb47793d9d0117a90fe9cf
--- /dev/null
+++ b/LocalVue/src/main.js
@@ -0,0 +1,5 @@
+import { createApp } from 'vue'
+import App from './App.vue'
+import './assets/css/global.css'
+
+createApp(App).mount('#app')
diff --git a/LocalVue/vue.config.js b/LocalVue/vue.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..105dc45d950a28b4d17dc57f95905f3dd5882c97
--- /dev/null
+++ b/LocalVue/vue.config.js
@@ -0,0 +1,19 @@
+module.exports = {
+ publicPath: './',
+ // publicPath: 'app://', // 设置自定义协议
+ outputDir: '../entry/src/main/resources/resfile/LocalResource/dist', // 本地资源跨域场景打包路径
+ // outputDir: '../entry/src/main/resources/resfile/CustomProtocol/dist', // 自定义协议跨域场景打包路径
+ // outputDir: '../LocalServer/web/RemoteRequest/dist', // 远程请求跨域场景打包路径
+ // outputDir: '../LocalServer/web/CookiesSettingsA/dist', // 跨域Cookies设置场景A打包路径
+ // outputDir: '../LocalServer/web/CookiesSettingsB/dist', // 跨域Cookies设置场景B打包路径
+ // outputDir: '../entry/src/main/resources/resfile/FatalPage/dist', // 通用错误页面打包路径
+ devServer: {
+ proxy: {
+ '/api': {
+ target: 'http://localhost:8081',
+ ws: true,
+ changeOrigin: true
+ }
+ }
+ }
+};
diff --git a/README.en.md b/README.en.md
index 9cd10ad24a6f1c5556591be8e520cb80f99336e8..97de81f3b7f1e117e0ac949e5ad6873908369343 100644
--- a/README.en.md
+++ b/README.en.md
@@ -1,36 +1,73 @@
-# WebCrossDomain
+# Web Page Cross-Domain
-#### Description
-{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
+## Overview
-#### Software Architecture
-Software architecture description
+This example implements web page cross-domain based on ArkWeb interceptor and cookie management capabilities, focusing on web page cross-domain issues. Through typical scenario cases, it intuitively demonstrates cross-domain solutions. It covers scenarios such as local resource cross-domain, remote request cross-domain, cross-domain cookie setting, and custom protocol cross-domain, providing reference implementation ideas and code examples to help developers understand cross-domain principles and quickly solve cross-domain problems in actual projects quickly.
-#### Installation
+## Preview
-1. xxxx
-2. xxxx
-3. xxxx
+| Home Page | Local Resource Cross-Domain | Remote Request Cross-Domain |
+|:----:|:------------------:|:------------------:|
+|  |  |  |
+| **Cross-Domain Cookie Setting** | **Custom Protocol Cross-Domain** | |
+|  |  | |
-#### Instructions
+## How to Use
-1. xxxx
-2. xxxx
-3. xxxx
+1. Open this project with DevEco, open the console/terminal in the root directory, and execute the `hvigorw startServer` command to start the local server.
+2. Click Run and wait for the compilation to complete.
+3. Restart the local server and execute the `hvigorw restartServer` command.
+4. Stop the local server and execute the `hvigorw stopServer` command.
-#### Contribution
+## Usage Instructions
-1. Fork the repository
-2. Create Feat_xxx branch
-3. Commit your code
-4. Create Pull Request
+1. On the local resource cross-domain page, click the button to send a request, and the request result will be displayed in the log area.
+2. On the remote request cross-domain page, click the button to send a request, and the request result will be displayed in the log area.
+3. On the cross-domain cookie setting page, when entering the page, a request will be automatically sent to domain A to set cookies, and the request result will be displayed in the log area.
+4. On the cross-domain cookie setting page, click the button to obtain cookies under domain A and pass them to domain B, load the page of domain B and send a request to domain B, and the request result will be displayed in the log area.
+5. On the custom protocol cross-domain page, click the button to send a request, and a system prompt box will pop up, showing "Custom protocol intercepted".
+## Project Directory
-#### Gitee Feature
+```
+├── entry/src/main/ets
+│ ├── common
+│ │ ├── Constants.ets // Static constant data
+│ │ ├── HttpProxy.ets // Network request proxy
+│ │ ├── Logger.ets // Log file
+│ │ └── PageLoadError.ets // Web page loading error page
+│ ├── component
+│ │ ├── CardItem.ets // Home page list item
+│ │ └── Header.ets // Detail page header
+│ ├── entryability
+│ │ └── EntryAbility.ets // Program entry
+│ ├── pages
+│ │ └── Index.ets // Home page
+│ └── view
+│ ├── CookiesSettings.ets // Cross-domain cookie setting scenario detail page
+│ ├── CustomProtocol.ets // Custom protocol cross-domain scenario detail page
+│ ├── LocalResource.ets // Local resource cross-domain scenario detail page
+│ └── RemoteRequest.ets // Remote request cross-domain scenario detail page
+├── entry/src/main/resources // Application static resource directory
+├── LocalVue // H5 pages
+├── LocalServer // Local server
+└── scripts/commandTask.ts // Command file
+```
-1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
-2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
-3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
-4. The most valuable open source project [GVP](https://gitee.com/gvp)
-5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
-6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
+## How to Implement
+
+1. For local resource cross-domain, set a path list through `setPathAllowingUniversalAccess()`. When accessing resources in this list using the file protocol, cross-domain access to local files is allowed.
+2. For remote request cross-domain, set a WebSchemeHandler interceptor in WebView to intercept requests from the web page, use rcp requests as proxy requests for cross-domain requests, and forward them to the target remote server. The communication between the proxy request and the remote server is not restricted by the browser, so the server's response result can be received. However, when passing the result back to WebView, cross-domain response headers still need to be configured to solve the cross-domain problem.
+3. For cross-domain cookie setting, use `putAcceptCookieEnabled()` to set the WebCookieManager instance to have the permission to send and receive cookies, use `fetchCookiesync()` to obtain cookies under domain A, use `configCookiesync()` to set cookies for domain B, and cookies will be automatically carried when sending requests to domain B.
+4. For custom protocol cross-domain, set a WebSchemeHandler interceptor in WebView to intercept requests and call system capabilities to pop up a system prompt box.
+
+## Permissions
+
+ohos.permission.INTERNET: allows an application to access the Internet.
+
+## Constraints
+
+1. This example only supports running on standard systems, supported devices: Huawei phones.
+2. HarmonyOS system: HarmonyOS 5.0.5 Release and above.
+3. DevEco Studio version: DevEco Studio 5.0.5 Release and above.
+4. HarmonyOS SDK version: HarmonyOS 5.0.5 Release SDK and above.
\ No newline at end of file
diff --git a/README.md b/README.md
index 50e31bded15e2b8b5b26c30fb8a86f7f63e35b96..682d95eca9e892254a407fc1530f6a9e1ba1eefd 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,76 @@
-# WebCrossDomain
-
-#### 介绍
-{**以下是 Gitee 平台说明,您可以替换此简介**
-Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
-无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
-
-#### 软件架构
-软件架构说明
-
-
-#### 安装教程
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### 使用说明
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### 参与贡献
-
-1. Fork 本仓库
-2. 新建 Feat_xxx 分支
-3. 提交代码
-4. 新建 Pull Request
-
-
-#### 特技
-
-1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
-2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
-3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
-4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
-5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
-6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
+# 基于ArkWeb拦截器和Cookie管理能力实现web页面跨域
+
+## 简介
+
+本示例基于ArkWeb拦截器和Cookie管理能力实现web页面跨域,聚焦web页面跨域问题,通过典型场景案例,直观演示跨域解决方案。涵盖本地资源跨域、远程请求跨域、跨域Cookies设置场景及自定义协议跨域,提供可参考的实现思路与代码示例,助力开发者理解跨域原理,快速解决实际项目中的跨域难题。
+
+## 效果预览
+
+| 首页 | 本地资源跨域场景详情页 | 远程请求跨域场景详情页 |
+|:----:|:------------------:|:------------------:|
+|  |  |  |
+| **跨域缓存数据设置场景详情页** | **自定义协议跨域场景详情页** | |
+|  |  | |
+
+## 运行步骤
+
+1. 使用DevEco打开本工程,根目录打开控制台/终端,执行`hvigorw startServer`命令,启动本地服务器。
+2. 点击运行,等待编译完成。
+3. 重启本地服务器,执行`hvigorw restartServer`命令。
+4. 关闭本地服务器,执行`hvigorw stopServer`命令。
+
+## 使用说明
+
+1. 在本地资源跨域页面,点击按钮发出请求,日志区显示请求结果。
+2. 在远程请求跨域页面,点击按钮发出请求,日志区显示请求结果。
+3. 在跨域Cookies设置页面,进入页面会自动向A域名发出请求,设置cookie,日志区显示请求结果。
+4. 在跨域Cookies设置页面点击按钮,获取A域名下的cookie传递给B域名,加载B域名页面并向B域名发出请求,日志区显示请求结果。
+5. 在自定义协议跨域页面,点击按钮发出请求,弹出系统提示框,显示“已拦截自定义协议”。
+
+## 工程目录
+
+```
+├──entry/src/main/ets
+│ ├──common
+│ │ ├──Constants.ets // 静态常量数据
+│ │ ├──HttpProxy.ets // 网络请求代理
+│ │ ├──Logger.ets // 日志文件
+│ │ └──PageLoadError.ets // web页面加载错误页
+│ ├──component
+│ │ ├──CardItem.ets // 主页列表项
+│ │ └──Header.ets // 详情页头部
+│ ├──entryability
+│ │ └──EntryAbility.ets // 程序入口
+│ ├──pages
+│ │ └──Index.ets // 首页
+│ └──view
+│ ├──CookiesSettings.ets // 跨域Cookies设置场景详情页
+│ ├──CustomProtocol.ets // 自定义协议跨域场景详情页
+│ ├──LocalResource.ets // 本地资源跨域场景详情页
+│ └──RemoteRequest.ets // 远程请求跨域场景详情页
+├──entry/src/main/resources // 应用静态资源目录
+├──LocalVue // H5页面
+├──LocalServer // 本地服务器
+└──scripts/commandTask.ts // 命令文件
+```
+
+## 具体实现
+
+1. 在本地资源跨域时,通过setPathAllowingUniversalAccess()设置一个路径列表。当使用file协议访问该列表中的资源时,允许进行跨域访问本地文件。
+2. 在远程请求跨域时,在WebView中设置WebSchemeHandler拦截器,将Web页面的请求拦截,使用rcp请求作为跨域请求的代理请求,转发到目标远程服务器。代理请求与远程服务器的通信不受浏览器限制,因此可以接收到服务器的响应结果,但将结果传回WebView时仍需配置跨域响应头来解决跨域问题。
+3. 在跨域Cookies设置时,使用putAcceptCookieEnabled()设置WebCookieManager实例拥有发送和接收cookie的权限,使用fetchCookiesync()获取A域名下的cookie,使用configCookiesync()给B域名设置cookie,向B域名发送请求时自动携带cookie。
+4. 在自定义协议跨域时,在WebView中设置WebSchemeHandler拦截器,拦截请求并调用系统能力,弹出系统提示框。
+
+## 相关权限
+
+Internet网络权限:ohos.permission.INTERNET。
+
+## 约束与限制
+
+1. 本示例仅支持标准系统上运行,支持设备:华为手机。
+
+2. HarmonyOS系统:HarmonyOS 5.0.5 Release及以上。
+
+3. DevEco Studio版本:DevEco Studio 5.0.5 Release及以上。
+
+4. HarmonyOS SDK版本:HarmonyOS 5.0.5 Release SDK及以上。
diff --git a/build-profile.json5 b/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..9e87e7e6f19020fe21192844709f192cec80fd44
--- /dev/null
+++ b/build-profile.json5
@@ -0,0 +1,42 @@
+{
+ "app": {
+ "signingConfigs": [],
+ "products": [
+ {
+ "name": "default",
+ "signingConfig": "default",
+ "targetSdkVersion": "5.0.5(17)",
+ "compatibleSdkVersion": "5.0.5(17)",
+ "runtimeOS": "HarmonyOS",
+ "buildOption": {
+ "strictMode": {
+ "caseSensitiveCheck": true,
+ "useNormalizedOHMUrl": true
+ }
+ }
+ }
+ ],
+ "buildModeSet": [
+ {
+ "name": "debug",
+ },
+ {
+ "name": "release"
+ }
+ ]
+ },
+ "modules": [
+ {
+ "name": "entry",
+ "srcPath": "./entry",
+ "targets": [
+ {
+ "name": "default",
+ "applyToProducts": [
+ "default"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/entry/build-profile.json5 b/entry/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..4d611879c7913fb0610c686e2399258ab3a6dad1
--- /dev/null
+++ b/entry/build-profile.json5
@@ -0,0 +1,28 @@
+{
+ "apiType": "stageMode",
+ "buildOption": {
+ },
+ "buildOptionSet": [
+ {
+ "name": "release",
+ "arkOptions": {
+ "obfuscation": {
+ "ruleOptions": {
+ "enable": false,
+ "files": [
+ "./obfuscation-rules.txt"
+ ]
+ }
+ }
+ }
+ },
+ ],
+ "targets": [
+ {
+ "name": "default"
+ },
+ {
+ "name": "ohosTest",
+ }
+ ]
+}
\ No newline at end of file
diff --git a/entry/hvigorfile.ts b/entry/hvigorfile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b0e3a1ab98a91bc918d6404b2413111a5011f14a
--- /dev/null
+++ b/entry/hvigorfile.ts
@@ -0,0 +1,6 @@
+import { hapTasks } from '@ohos/hvigor-ohos-plugin';
+
+export default {
+ system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
+ plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
+}
\ No newline at end of file
diff --git a/entry/obfuscation-rules.txt b/entry/obfuscation-rules.txt
new file mode 100644
index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b
--- /dev/null
+++ b/entry/obfuscation-rules.txt
@@ -0,0 +1,23 @@
+# Define project specific obfuscation rules here.
+# You can include the obfuscation configuration files in the current module's build-profile.json5.
+#
+# For more details, see
+# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5
+
+# Obfuscation options:
+# -disable-obfuscation: disable all obfuscations
+# -enable-property-obfuscation: obfuscate the property names
+# -enable-toplevel-obfuscation: obfuscate the names in the global scope
+# -compact: remove unnecessary blank spaces and all line feeds
+# -remove-log: remove all console.* statements
+# -print-namecache: print the name cache that contains the mapping from the old names to new names
+# -apply-namecache: reuse the given cache file
+
+# Keep options:
+# -keep-property-name: specifies property names that you want to keep
+# -keep-global-name: specifies names that you want to keep in the global scope
+
+-enable-property-obfuscation
+-enable-toplevel-obfuscation
+-enable-filename-obfuscation
+-enable-export-obfuscation
\ No newline at end of file
diff --git a/entry/oh-package.json5 b/entry/oh-package.json5
new file mode 100644
index 0000000000000000000000000000000000000000..248c3b7541a589682a250f86a6d3ecf7414d2d6a
--- /dev/null
+++ b/entry/oh-package.json5
@@ -0,0 +1,10 @@
+{
+ "name": "entry",
+ "version": "1.0.0",
+ "description": "Please describe the basic information.",
+ "main": "",
+ "author": "",
+ "license": "",
+ "dependencies": {}
+}
+
diff --git a/entry/src/main/ets/common/Constants.ets b/entry/src/main/ets/common/Constants.ets
new file mode 100644
index 0000000000000000000000000000000000000000..7f18f105d306752a89ee83f107c4078b7992cc4c
--- /dev/null
+++ b/entry/src/main/ets/common/Constants.ets
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export default class Constants {
+ /**
+ * Full Screen width
+ */
+ static readonly FULL_WIDTH: string = '100%';
+ /**
+ * Full Screen height
+ */
+ static readonly FULL_HEIGHT: string = '100%';
+ /**
+ * Padding-top
+ */
+ static readonly PADDING_TOP_150: number = 150;
+ /**
+ * All breakpoints
+ */
+ static readonly BREAKPOINTS: string[] = ['sm', 'md', 'lg'];
+}
diff --git a/entry/src/main/ets/common/HttpProxy.ets b/entry/src/main/ets/common/HttpProxy.ets
new file mode 100644
index 0000000000000000000000000000000000000000..d4cca182e0cd796d6029db2dad1a61addb0156b0
--- /dev/null
+++ b/entry/src/main/ets/common/HttpProxy.ets
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { WebNetErrorList, webview } from '@kit.ArkWeb';
+import { BusinessError } from '@kit.BasicServicesKit';
+import { rcp } from '@kit.RemoteCommunicationKit';
+import Logger from './Logger';
+
+// [Start create_rcp_session]
+export class HttpProxy {
+ session?: rcp.Session;
+
+ constructor(sessionConfiguration?: rcp.SessionConfiguration | undefined) {
+ try {
+ this.session = rcp.createSession(sessionConfiguration);
+ } catch (error) {
+ Logger.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
+ }
+ }
+
+ // [StartExclude create_rcp_session]
+ // [Start handle_response]
+ private handleResponse(res: rcp.Response, resourceHandler: webview.WebResourceHandler,
+ options: HttpProxyOptions): void {
+ let response = new webview.WebSchemeHandlerResponse();
+ response.setStatus(200)
+ response.setStatusText('OK')
+ response.setMimeType(options.mimeType || 'application/json')
+ response.setEncoding('utf-8')
+ response.setNetErrorCode(WebNetErrorList.NET_OK)
+ response.setHeaderByName('Access-Control-Allow-Origin', options.requestOrigin, true);
+ response.setHeaderByName('Access-Control-Allow-Credentials', 'true', true);
+ response.setHeaderByName('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE', true);
+ response.setHeaderByName('Access-Control-Allow-Headers', 'Content-Type, Authorization', true);
+ try {
+ resourceHandler.didReceiveResponse(response);
+ resourceHandler.didReceiveResponseBody(res.body);
+ resourceHandler.didFinish();
+ } catch (error) {
+ Logger.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
+ }
+ }
+ // [End handle_response]
+
+ // [EndExclude create_rcp_session]
+ public get(request: string, resourceHandler: webview.WebResourceHandler, options: HttpProxyOptions): void {
+ try {
+ this.session?.get(request).then((res) => {
+ this.handleResponse(res, resourceHandler, options)
+ }).catch((error: BusinessError) => {
+ Logger.error(`ErrorCode: ${error.code}, Message: ${error.message}`);
+ })
+ } catch (error) {
+ Logger.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
+ }
+ }
+
+ // [StartExclude create_rcp_session]
+ public post(request: string, resourceHandler: webview.WebResourceHandler, options: HttpProxyOptions): void {
+ try {
+ this.session?.post(request).then((res) => {
+ this.handleResponse(res, resourceHandler, options)
+ }).catch((error: BusinessError) => {
+ Logger.error(`ErrorCode: ${error.code}, Message: ${error.message}`);
+ })
+ } catch (error) {
+ Logger.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
+ }
+ }
+ // [EndExclude create_rcp_session]
+}
+// [End create_rcp_session]
+
+@Observed
+export
+class HttpProxyOptions {
+ public mimeType?: string;
+ public requestOrigin?: string;
+}
diff --git a/entry/src/main/ets/common/Logger.ets b/entry/src/main/ets/common/Logger.ets
new file mode 100644
index 0000000000000000000000000000000000000000..cf240ea9fc467f810bf698c3cc8c882f2f55dd5e
--- /dev/null
+++ b/entry/src/main/ets/common/Logger.ets
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { hilog } from '@kit.PerformanceAnalysisKit';
+
+class Logger {
+ private domain: number;
+ private prefix: string;
+ private format: string = '%{public}s, %{public}s';
+
+ /**
+ * constructor.
+ *
+ * @param prefix Identifies the log tag.
+ * @param domain Indicates the service domain, which is a hexadecimal integer ranging from 0x0 to 0xFFFFF
+ * @param args Indicates the log parameters.
+ */
+ constructor(prefix: string = '', domain: number = 0xFF00) {
+ this.prefix = prefix;
+ this.domain = domain;
+ }
+
+ debug(...args: string[]): void {
+ hilog.debug(this.domain, this.prefix, this.format, args);
+ }
+
+ info(...args: string[]): void {
+ hilog.info(this.domain, this.prefix, this.format, args);
+ }
+
+ warn(...args: string[]): void {
+ hilog.warn(this.domain, this.prefix, this.format, args);
+ }
+
+ error(...args: string[]): void {
+ hilog.error(this.domain, this.prefix, this.format, args);
+ }
+}
+
+export default new Logger('WebCrossDomain');
\ No newline at end of file
diff --git a/entry/src/main/ets/common/PageLoadError.ets b/entry/src/main/ets/common/PageLoadError.ets
new file mode 100644
index 0000000000000000000000000000000000000000..c176c47da5a68a1a306a32d655f317e5d99df0ed
--- /dev/null
+++ b/entry/src/main/ets/common/PageLoadError.ets
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { webview } from '@kit.ArkWeb';
+import { BusinessError } from '@kit.BasicServicesKit';
+import Logger from './Logger';
+
+export class PageLoadError {
+ public static handleErrorReceive(event: OnErrorReceiveEvent, controller: webview.WebviewController): void {
+ if (event && event.request.isMainFrame()) {
+ try {
+ controller.loadUrl(`resource://resfile/FatalPage/dist/index.html?errorInfo=${event.error.getErrorInfo()}`)
+ } catch (error) {
+ Logger.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/component/CardItem.ets b/entry/src/main/ets/component/CardItem.ets
new file mode 100644
index 0000000000000000000000000000000000000000..f0d6e30b1a7045203d03e42acc71854de86b3568
--- /dev/null
+++ b/entry/src/main/ets/component/CardItem.ets
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@Component
+export struct CardItem {
+ textContent?: Resource;
+ onCardItemClick: Function = () => {
+ };
+
+ build() {
+ Row() {
+ Text(this.textContent)
+ .fontSize($r('sys.float.Subtitle_M'))
+ .fontWeight(FontWeight.Medium)
+ .fontColor($r('sys.color.font_primary'))
+ .margin({ left: $r('sys.float.padding_level4') })
+ Blank()
+ SymbolGlyph($r('sys.symbol.chevron_forward'))
+ .fontSize($r('sys.float.Subtitle_L'))
+ .fontColor([$r('sys.color.icon_fourth')])
+ }
+ .width('100%')
+ .height(56)
+ .onClick(() => {
+ this.onCardItemClick();
+ })
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/component/Header.ets b/entry/src/main/ets/component/Header.ets
new file mode 100644
index 0000000000000000000000000000000000000000..7f52bcf375ef07b863a3f81645bab3ceef604338
--- /dev/null
+++ b/entry/src/main/ets/component/Header.ets
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Constants from '../common/Constants';
+
+@Component
+export default struct Header {
+ @Consume('pathInfos') pathInfos: NavPathStack;
+ @State backIconBgColor: ResourceColor = $r('sys.color.comp_background_tertiary');
+ @Prop headerTitle: Resource;
+
+ build() {
+ Row() {
+ Button({ type: ButtonType.Circle }) {
+ SymbolGlyph($r('sys.symbol.chevron_backward'))
+ .fontColor([$r('sys.color.icon_primary')])
+ .fontSize($r('sys.float.Title_M'))
+ }
+ .height($r('app.float.back_button_height'))
+ .aspectRatio(1)
+ .margin({ right: $r('sys.float.padding_level4') })
+ .backgroundColor(this.backIconBgColor)
+ .onClick(() => this.pathInfos.pop())
+ .onHover((isHover: boolean) => {
+ this.backIconBgColor = isHover ? $r('sys.color.comp_background_tertiary') : Color.Transparent;
+ })
+
+ Text(this.headerTitle)
+ .fontSize(20)
+ .fontColor($r('sys.color.font_primary'))
+ .fontWeight(FontWeight.Bold)
+ .textAlign(TextAlign.Start)
+ .maxLines(1)
+ .textOverflow({ overflow: TextOverflow.Ellipsis })
+ .layoutWeight(1)
+ }
+ .alignItems(VerticalAlign.Center)
+ .justifyContent(FlexAlign.SpaceBetween)
+ .width(Constants.FULL_WIDTH)
+ .height($r('app.float.header_height'))
+ .backgroundColor(Color.Transparent)
+ .padding({ left: $r('sys.float.padding_level8'), right: $r('sys.float.padding_level8') })
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets
new file mode 100644
index 0000000000000000000000000000000000000000..7e06de422996ab07bf55a1d4cf46023bb190b00e
--- /dev/null
+++ b/entry/src/main/ets/entryability/EntryAbility.ets
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ConfigurationConstant, UIAbility } from '@kit.AbilityKit';
+import { window } from '@kit.ArkUI';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+
+const DOMAIN = 0x0000;
+
+export default class EntryAbility extends UIAbility {
+ onCreate(): void {
+ try {
+ this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
+ } catch (err) {
+ hilog.error(DOMAIN, 'testTag', 'Failed to set colorMode. Cause: %{public}s', `ErrorCode: ${err.code}, Message: ${err.message}`);
+ }
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
+ }
+
+ onDestroy(): void {
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
+ }
+
+ onWindowStageCreate(windowStage: window.WindowStage): void {
+ // Main window is created, set main page for this ability
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
+
+ windowStage.loadContent('pages/Index', (err) => {
+ if (err.code) {
+ hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', `ErrorCode: ${err.code}, Message: ${err.message}`);
+ return;
+ }
+ hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
+ });
+ }
+
+ onWindowStageDestroy(): void {
+ // Main window is destroyed, release UI related resources
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
+ }
+
+ onForeground(): void {
+ // Ability has brought to foreground
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
+ }
+
+ onBackground(): void {
+ // Ability has back to background
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets
new file mode 100644
index 0000000000000000000000000000000000000000..27961b9a5d5fc3b8988ea6c7c714d395449fae18
--- /dev/null
+++ b/entry/src/main/ets/pages/Index.ets
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { webview } from '@kit.ArkWeb';
+import { BusinessError } from '@kit.BasicServicesKit';
+import Constants from '../common/Constants';
+import Logger from '../common/Logger';
+import { CardItem } from '../component/CardItem';
+import { LocalResource } from '../views/LocalResource';
+import { RemoteRequest } from '../views/RemoteRequest';
+import { CookiesSettings } from '../views/CookiesSettings';
+import { CustomProtocol } from '../views/CustomProtocol';
+import { connection } from '@kit.NetworkKit';
+
+@Entry
+@Component
+struct Index {
+ @StorageProp('currentBreakpoint') curBp: string = Constants.BREAKPOINTS[1];
+ @Provide('pathInfos') pathInfos: NavPathStack = new NavPathStack();
+ private routes: string[] = ['LocalResource', 'RemoteRequest', 'CookiesSettings', 'CustomProtocol'];
+ scheme: webview.WebCustomScheme = { schemeName: 'app', isSupportCORS: true, isSupportFetch: true };
+
+ aboutToAppear(): void {
+ try {
+ webview.WebviewController.customizeSchemes([this.scheme]);
+ connection.addCustomDnsRule('www.a.com', ['127.0.0.1']).catch((error: BusinessError) => {
+ Logger.error('addCustomDnsRule', `ErrorCode: ${error.code}, Message: ${error.message}`)
+ })
+ connection.addCustomDnsRule('www.b.com', ['127.0.0.1']).catch((error: BusinessError) => {
+ Logger.error('addCustomDnsRule', `ErrorCode: ${error.code}, Message: ${error.message}`)
+ })
+ connection.addCustomDnsRule('www.c.com', ['127.0.0.1']).catch((error: BusinessError) => {
+ Logger.error('addCustomDnsRule', `ErrorCode: ${error.code}, Message: ${error.message}`)
+ })
+ } catch (error) {
+ Logger.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
+ }
+ }
+
+ @Builder
+ PageMap(name: string) {
+ NavDestination() {
+ if (name === 'LocalResource') {
+ LocalResource()
+ } else if (name === 'RemoteRequest') {
+ RemoteRequest()
+ } else if (name === 'CookiesSettings') {
+ CookiesSettings()
+ } else if (name === 'CustomProtocol') {
+ CustomProtocol()
+ }
+ }
+ .hideTitleBar(true)
+ .backgroundColor($r('sys.color.background_secondary'))
+ }
+
+ build() {
+ Navigation(this.pathInfos) {
+ Column() {
+ Text($r('app.string.project_name'))
+ .fontSize($r('app.float.title_font_size'))
+ .fontWeight(FontWeight.Bold)
+ .margin({ left: $r('sys.float.padding_level8') })
+ List() {
+ ListItemGroup() {
+ ForEach(this.routes, (item: string, index: number) => {
+ ListItem({ style: ListItemStyle.CARD }) {
+ CardItem({
+ textContent: $r('app.string.scene_name' + (index + 1)),
+ onCardItemClick: () => {
+ this.pathInfos.pushPath({ name: item });
+ },
+ })
+ }
+ .height(undefined)
+ .width('100%')
+ }, (item: string) => JSON.stringify(item))
+ }
+ .margin({ top: $r('sys.float.padding_level8'), bottom: $r('sys.float.padding_level8') })
+ .padding($r('sys.float.padding_level2'))
+ .backgroundColor($r('sys.color.comp_background_primary'))
+ .borderRadius($r('sys.float.corner_radius_level8'))
+ .divider({
+ strokeWidth: 1,
+ color: $r('sys.color.ohos_id_color_text_field_sub_bg'),
+ startMargin: 20,
+ endMargin: 10
+ })
+ }
+ .width('100%')
+ .height('100%')
+ .padding(16)
+
+ }
+ .width(Constants.FULL_WIDTH)
+ .height(Constants.FULL_HEIGHT)
+ .padding({ top: Constants.PADDING_TOP_150 })
+ .justifyContent(FlexAlign.Start)
+ .alignItems(HorizontalAlign.Start)
+ }
+ .mode(NavigationMode.Stack)
+ .hideTitleBar(true)
+ .navDestination(this.PageMap)
+ .backgroundColor($r('sys.color.background_secondary'))
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/views/CookiesSettings.ets b/entry/src/main/ets/views/CookiesSettings.ets
new file mode 100644
index 0000000000000000000000000000000000000000..8de78e38fdc2752f5848dfa91f14f6c726427ab6
--- /dev/null
+++ b/entry/src/main/ets/views/CookiesSettings.ets
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { webview } from '@kit.ArkWeb';
+import { BusinessError } from '@kit.BasicServicesKit';
+import Constants from '../common/Constants';
+import Logger from '../common/Logger';
+import { PageLoadError } from '../common/PageLoadError';
+import Header from '../component/Header';
+
+@Component
+export struct CookiesSettings {
+ @StorageProp('currentBreakpoint') curBp: string = Constants.BREAKPOINTS[1];
+ @State webError: boolean = false;
+ controller: WebviewController = new webview.WebviewController();
+
+ build() {
+ Column() {
+ Header({ headerTitle: $r('app.string.scene_name3') })
+ Row() {
+ Web({ src: 'http://www.a.com:8080/CookiesSettingsA/dist/index.html', controller: this.controller })
+ .onErrorReceive((event) => {
+ PageLoadError.handleErrorReceive(event, this.controller);
+ })
+ .javaScriptAccess(true)
+ .fileAccess(true)
+ .domStorageAccess(true)
+ .imageAccess(true)
+ .backgroundColor($r('sys.color.background_secondary'))
+ }
+ .layoutWeight(1)
+
+ Column() {
+ Button($r('app.string.CookiesSettings_button'))
+ .width(Constants.FULL_WIDTH)
+ .height(48)
+ .onClick(() => {
+ try {
+ // Obtain the cookie for domain A and set it for domain B.
+ // [Start put_accept_cookie]
+ webview.WebCookieManager.putAcceptCookieEnabled(true);
+ webview.WebCookieManager.putAcceptThirdPartyCookieEnabled(true)
+ let value = webview.WebCookieManager.fetchCookieSync('http://www.a.com');
+ webview.WebCookieManager.configCookieSync('http://www.b.com',
+ value + '; PATH=/; Max-Age=900000; HttpOnly; SameSite=Lax');
+ // [End put_accept_cookie]
+ this.controller.loadUrl('http://www.b.com:8080/CookiesSettingsB/dist/index.html');
+ } catch (error) {
+ Logger.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
+ }
+ })
+ }
+ .visibility(this.webError ? Visibility.Hidden : Visibility.Visible)
+ .padding({ left: $r('sys.float.padding_level8'), right: $r('sys.float.padding_level8'), bottom: $r('sys.float.padding_level12') })
+ }
+ .width(Constants.FULL_WIDTH)
+ .justifyContent(FlexAlign.Center)
+ }
+}
diff --git a/entry/src/main/ets/views/CustomProtocol.ets b/entry/src/main/ets/views/CustomProtocol.ets
new file mode 100644
index 0000000000000000000000000000000000000000..7d66fb640b96d81a502818115ef54aec9064dca7
--- /dev/null
+++ b/entry/src/main/ets/views/CustomProtocol.ets
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { buffer } from '@kit.ArkTS';
+import { PromptAction } from '@kit.ArkUI';
+import { WebNetErrorList, webview } from '@kit.ArkWeb';
+import { BusinessError } from '@kit.BasicServicesKit';
+import { fileIo as fs } from '@kit.CoreFileKit';
+import Constants from '../common/Constants';
+import Logger from '../common/Logger';
+import { PageLoadError } from '../common/PageLoadError';
+import Header from '../component/Header';
+
+// Obtaining the File Type
+function getContentType(ext: string) {
+ if (ext.includes('.html')) {
+ return 'text/html';
+ } else if (ext.includes('.js')) {
+ return 'text/javascript';
+ } else if (ext.includes('.css')) {
+ return 'text/css';
+ } else if (ext.includes('.json')) {
+ return 'application/json';
+ } else if (ext.includes('.png')) {
+ return 'image/png';
+ } else if (ext.includes('.jpeg')) {
+ return 'image/jpeg';
+ } else if (ext.includes('.svg')) {
+ return 'image/svg+xml';
+ } else {
+ return 'application/octet-stream';
+ }
+}
+
+@Component
+export struct CustomProtocol {
+ @StorageProp('currentBreakpoint') curBp: string = Constants.BREAKPOINTS[1];
+ controller: WebviewController = new webview.WebviewController();
+ schemeHandler: webview.WebSchemeHandler = new webview.WebSchemeHandler();
+ uiContext: UIContext = this.getUIContext();
+ promptAction: PromptAction = this.uiContext.getPromptAction();
+ @State toastId: number = 0;
+
+ build() {
+ Column() {
+ Header({ headerTitle: $r('app.string.scene_name4') })
+ Row() {
+ Web({ src: 'app://index.html', controller: this.controller })
+ .onControllerAttached(() => {
+ try {
+ // [Start bind_interceptor]
+ this.schemeHandler.onRequestStart((request: webview.WebSchemeHandlerRequest,
+ resourceHandler: webview.WebResourceHandler) => {
+
+ // [StartExclude bind_interceptor]
+ // Determine whether it is a pop-up request
+ // [Start open_dialog]
+ if (request.getRequestUrl().includes('openDialog')) {
+ this.promptAction.showToast({
+ message: $r('app.string.CustomProtocol_toast'),
+ duration: 3000,
+ })
+ return true;
+ }
+ // [End open_dialog]
+ // Read Local Files
+ // [Start read_local_file]
+ let pathArray = request.getRequestUrl().split('app://');
+ let filePath = this.uiContext.getHostContext()!.resourceDir + '/CustomProtocol/dist/' +
+ pathArray[pathArray.length-1];
+ let file = fs.readTextSync(filePath);
+
+ let response = new webview.WebSchemeHandlerResponse();
+ response.setNetErrorCode(WebNetErrorList.NET_OK);
+ response.setStatus(200);
+ response.setStatusText('OK');
+ response.setMimeType(getContentType(filePath));
+ response.setEncoding('utf-8');
+
+ // Convert to buffer type
+ let buf = buffer.from(file)
+ try {
+ if (buf.length === 0) {
+ resourceHandler.didFail(WebNetErrorList.ERR_FAILED);
+ } else {
+ resourceHandler.didReceiveResponse(response);
+ resourceHandler.didReceiveResponseBody(buf.buffer);
+ resourceHandler.didFinish();
+ }
+ } catch (error) {
+ Logger.error(`[schemeHandler] ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
+ }
+ // [End read_local_file]
+ // [EndExclude bind_interceptor]
+ return true;
+ })
+
+ // Bind an interceptor to the app protocol
+ this.controller.setWebSchemeHandler('app', this.schemeHandler);
+ // [End bind_interceptor]
+ } catch (error) {
+ Logger.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
+ }
+ })
+ .onErrorReceive((event) => {
+ PageLoadError.handleErrorReceive(event, this.controller)
+ })
+ .javaScriptAccess(true)
+ .fileAccess(true)
+ .domStorageAccess(true)
+ .imageAccess(true)
+ .backgroundColor($r('sys.color.background_secondary'))
+ }
+ }
+ .width(Constants.FULL_WIDTH)
+ .justifyContent(FlexAlign.Center)
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/views/LocalResource.ets b/entry/src/main/ets/views/LocalResource.ets
new file mode 100644
index 0000000000000000000000000000000000000000..c5bb0483785de9cb4b2616aad63d6a8e9f53da34
--- /dev/null
+++ b/entry/src/main/ets/views/LocalResource.ets
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { webview } from '@kit.ArkWeb';
+import { BusinessError } from '@kit.BasicServicesKit';
+import Constants from '../common/Constants';
+import Logger from '../common/Logger';
+import { PageLoadError } from '../common/PageLoadError';
+import Header from '../component/Header';
+
+@Component
+export struct LocalResource {
+ @StorageProp('currentBreakpoint') curBp: string = Constants.BREAKPOINTS[1];
+ controller: WebviewController = new webview.WebviewController();
+ uiContext: UIContext = this.getUIContext();
+
+ build() {
+ Column() {
+ Header({ headerTitle: $r('app.string.scene_name1') })
+ Row() {
+ // [Start local_resource]
+ Web({ src: 'resource://resfile/LocalResource/dist/index.html', controller: this.controller })
+ .onControllerAttached(() => {
+ try {
+ // Set the list of paths that allow cross-origin access
+ this.controller.setPathAllowingUniversalAccess([
+ this.uiContext.getHostContext()!.resourceDir + '/LocalResource',
+ ])
+ } catch (error) {
+ Logger.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
+ }
+ })
+ // [End local_resource]
+ .onErrorReceive((event) => {
+ PageLoadError.handleErrorReceive(event, this.controller)
+ })
+ .javaScriptAccess(true)
+ .fileAccess(true)
+ .domStorageAccess(true)
+ .imageAccess(true)
+ .backgroundColor($r('sys.color.background_secondary'))
+ }
+ }
+ .width(Constants.FULL_WIDTH)
+ .justifyContent(FlexAlign.Center)
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/ets/views/RemoteRequest.ets b/entry/src/main/ets/views/RemoteRequest.ets
new file mode 100644
index 0000000000000000000000000000000000000000..4663bb60b5a966f8598ecb6be7748340752706d8
--- /dev/null
+++ b/entry/src/main/ets/views/RemoteRequest.ets
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { WebNetErrorList, webview } from '@kit.ArkWeb';
+import { BusinessError } from '@kit.BasicServicesKit';
+import { rcp } from '@kit.RemoteCommunicationKit';
+import Constants from '../common/Constants';
+import { HttpProxy } from '../common/HttpProxy';
+import Logger from '../common/Logger';
+import { PageLoadError } from '../common/PageLoadError';
+import Header from '../component/Header';
+
+@Component
+export struct RemoteRequest {
+ @StorageProp('currentBreakpoint') curBp: string = Constants.BREAKPOINTS[1];
+ controller: WebviewController = new webview.WebviewController();schemeHandler: webview.WebSchemeHandler = new webview.WebSchemeHandler();
+ uiContext: UIContext = this.getUIContext();
+ staticDnsRules: rcp.StaticDnsRules = [
+ { host: 'www.c.com', port: 8080, ipAddresses: ['127.0.0.1'] },
+ ];
+ // Create a proxy request class
+ httpProxy: HttpProxy = new HttpProxy({
+ requestConfiguration: {
+ dns: {
+ dnsRules: this.staticDnsRules,
+ }
+ }
+ });
+
+ build() {
+ Column() {
+ Header({ headerTitle: $r('app.string.scene_name2') })
+ Row() {
+ // [Start remote_request]
+ Web({ src: 'http://www.a.com:8080/RemoteRequest/dist/index.html', controller: this.controller })
+ .onControllerAttached(() => {
+ try {
+ // Using interceptor to intercept requests
+ this.schemeHandler.onRequestStart((request: webview.WebSchemeHandlerRequest, resourceHandler: webview.WebResourceHandler) => {
+ Logger.info('[schemeHandler] onRequestStart');
+ if (request.getRequestUrl().includes('www.c.com')) {
+ // Through proxy request server
+ this.httpProxy.get(request.getRequestUrl(), resourceHandler, {
+ mimeType: 'application/json',
+ requestOrigin: 'http://www.a.com:8080'
+ })
+ return true;
+ } else {
+ return false;
+ }
+ })
+
+ // Bind an interceptor to the HTTP protocol.
+ this.controller.setWebSchemeHandler('http', this.schemeHandler);
+ } catch (error) {
+ Logger.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
+ }
+ })
+ // [End remote_request]
+ .onErrorReceive((event) => {
+ PageLoadError.handleErrorReceive(event, this.controller);
+ })
+ .javaScriptAccess(true)
+ .fileAccess(true)
+ .domStorageAccess(true)
+ .imageAccess(true)
+ .backgroundColor($r('sys.color.background_secondary'))
+ }
+ }
+ .width(Constants.FULL_WIDTH)
+ .justifyContent(FlexAlign.Center)
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5
new file mode 100644
index 0000000000000000000000000000000000000000..666a4f644c5287472e79c575f3d2c324cfd7abfb
--- /dev/null
+++ b/entry/src/main/module.json5
@@ -0,0 +1,48 @@
+{
+ "module": {
+ "name": "entry",
+ "type": "entry",
+ "description": "$string:module_desc",
+ "mainElement": "EntryAbility",
+ "deviceTypes": [
+ "phone"
+ ],
+ "deliveryWithInstall": true,
+ "installationFree": false,
+ "pages": "$profile:main_pages",
+ "abilities": [
+ {
+ "name": "EntryAbility",
+ "srcEntry": "./ets/entryability/EntryAbility.ets",
+ "description": "$string:EntryAbility_desc",
+ "icon": "$media:layered_image",
+ "label": "$string:EntryAbility_label",
+ "startWindowIcon": "$media:startIcon",
+ "startWindowBackground": "$color:start_window_background",
+ "exported": true,
+ "skills": [
+ {
+ "entities": [
+ "entity.system.home"
+ ],
+ "actions": [
+ "ohos.want.action.home"
+ ]
+ }
+ ]
+ }
+ ],
+ "requestPermissions": [
+ {
+ "name": "ohos.permission.INTERNET",
+ "reason": "$string:internet_reason",
+ "usedScene": {
+ "abilities": [
+ "EntryAbility"
+ ],
+ "when": "inuse"
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/resources/base/element/color.json b/entry/src/main/resources/base/element/color.json
new file mode 100644
index 0000000000000000000000000000000000000000..d66f9a7d4ac61fb8d215239ab3620b7bcd77bf33
--- /dev/null
+++ b/entry/src/main/resources/base/element/color.json
@@ -0,0 +1,8 @@
+{
+ "color": [
+ {
+ "name": "start_window_background",
+ "value": "#FFFFFF"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/entry/src/main/resources/base/element/float.json b/entry/src/main/resources/base/element/float.json
new file mode 100644
index 0000000000000000000000000000000000000000..b93a27ae280923d1eae1d5225e3cd0c17b3e5eca
--- /dev/null
+++ b/entry/src/main/resources/base/element/float.json
@@ -0,0 +1,20 @@
+{
+ "float": [
+ {
+ "name": "page_text_font_size",
+ "value": "50fp"
+ },
+ {
+ "name": "title_font_size",
+ "value": "30vp"
+ },
+ {
+ "name": "header_height",
+ "value": "56vp"
+ },
+ {
+ "name": "back_button_height",
+ "value": "40vp"
+ }
+ ]
+}
diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..e96b15e0da9d3d0c1609ce823753b8e02fba5b90
--- /dev/null
+++ b/entry/src/main/resources/base/element/string.json
@@ -0,0 +1,52 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "module description"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "Web Cross-Domain"
+ },
+ {
+ "name": "internet_reason",
+ "value": "You need to obtain your network permission to obtain network resource display data."
+ },
+ {
+ "name": "project_name",
+ "value": "Web Cross-Domain Scenario Case"
+ },
+ {
+ "name": "router_back",
+ "value": "Back"
+ },
+ {
+ "name": "scene_name1",
+ "value": "Local resource cross-domain"
+ },
+ {
+ "name": "scene_name2",
+ "value": "Remote request cross-domain"
+ },
+ {
+ "name": "scene_name3",
+ "value": "Cross-Domain Cookies Settings"
+ },
+ {
+ "name": "scene_name4",
+ "value": "User-defined protocol cross-domain"
+ },
+ {
+ "name": "CustomProtocol_toast",
+ "value": "Custom protocol has been intercepted"
+ },
+ {
+ "name": "CookiesSettings_button",
+ "value": "Click the button to pass the cookie"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/entry/src/main/resources/base/media/background.png b/entry/src/main/resources/base/media/background.png
new file mode 100644
index 0000000000000000000000000000000000000000..923f2b3f27e915d6871871deea0420eb45ce102f
Binary files /dev/null and b/entry/src/main/resources/base/media/background.png differ
diff --git a/entry/src/main/resources/base/media/foreground.png b/entry/src/main/resources/base/media/foreground.png
new file mode 100644
index 0000000000000000000000000000000000000000..97014d3e10e5ff511409c378cd4255713aecd85f
Binary files /dev/null and b/entry/src/main/resources/base/media/foreground.png differ
diff --git a/entry/src/main/resources/base/media/layered_image.json b/entry/src/main/resources/base/media/layered_image.json
new file mode 100644
index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a
--- /dev/null
+++ b/entry/src/main/resources/base/media/layered_image.json
@@ -0,0 +1,7 @@
+{
+ "layered-image":
+ {
+ "background" : "$media:background",
+ "foreground" : "$media:foreground"
+ }
+}
\ No newline at end of file
diff --git a/entry/src/main/resources/base/media/startIcon.png b/entry/src/main/resources/base/media/startIcon.png
new file mode 100644
index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b
Binary files /dev/null and b/entry/src/main/resources/base/media/startIcon.png differ
diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json
new file mode 100644
index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce
--- /dev/null
+++ b/entry/src/main/resources/base/profile/main_pages.json
@@ -0,0 +1,5 @@
+{
+ "src": [
+ "pages/Index"
+ ]
+}
diff --git a/entry/src/main/resources/dark/element/color.json b/entry/src/main/resources/dark/element/color.json
new file mode 100644
index 0000000000000000000000000000000000000000..438d5bc43bb23c59c210d586b96635a72da5b64a
--- /dev/null
+++ b/entry/src/main/resources/dark/element/color.json
@@ -0,0 +1,8 @@
+{
+ "color": [
+ {
+ "name": "start_window_background",
+ "value": "#000000"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/entry/src/main/resources/en_US/element/string.json b/entry/src/main/resources/en_US/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..aac3a3fa46ad17f7fb977cae72abcf859f3e3dde
--- /dev/null
+++ b/entry/src/main/resources/en_US/element/string.json
@@ -0,0 +1,52 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "module description"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "Web Cross-Domain"
+ },
+ {
+ "name": "internet_reason",
+ "value": "You need to obtain your network permission to obtain network resource display data."
+ },
+ {
+ "name": "project_name",
+ "value": "Web Cross-Domain Scenario Case"
+ },
+ {
+ "name": "router_back",
+ "value": "Back"
+ },
+ {
+ "name": "scene_name1",
+ "value": "Local resource cross-domain"
+ },
+ {
+ "name": "scene_name2",
+ "value": "Remote request cross-domain"
+ },
+ {
+ "name": "scene_name3",
+ "value": "Cross-Domain Cookies Settings"
+ },
+ {
+ "name": "scene_name4",
+ "value": "User-defined protocol cross-domain"
+ },
+ {
+ "name": "CustomProtocol_toast",
+ "value": "Custom protocol has been intercepted"
+ },
+ {
+ "name": "CookiesSettings_button",
+ "value": "Click the button to pass the cookie"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/entry/src/main/resources/zh_CN/element/string.json b/entry/src/main/resources/zh_CN/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..b68486d5c651a7fabdb809a027861d50ba4b1791
--- /dev/null
+++ b/entry/src/main/resources/zh_CN/element/string.json
@@ -0,0 +1,52 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "module description"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "description"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "Web跨域场景案例"
+ },
+ {
+ "name": "internet_reason",
+ "value": "您需要获取网络权限以获取网络资源显示数据。"
+ },
+ {
+ "name": "project_name",
+ "value": "Web跨域场景案例"
+ },
+ {
+ "name": "router_back",
+ "value": "返回"
+ },
+ {
+ "name": "scene_name1",
+ "value": "本地资源跨域"
+ },
+ {
+ "name": "scene_name2",
+ "value": "远程请求跨域"
+ },
+ {
+ "name": "scene_name3",
+ "value": "跨域Cookies设置"
+ },
+ {
+ "name": "scene_name4",
+ "value": "自定义协议跨域"
+ },
+ {
+ "name": "CustomProtocol_toast",
+ "value": "已拦截自定义协议"
+ },
+ {
+ "name": "CookiesSettings_button",
+ "value": "点击按钮,传递cookie"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hvigor/hvigor-config.json5 b/hvigor/hvigor-config.json5
new file mode 100644
index 0000000000000000000000000000000000000000..5bebc9755447385d82ce4138f54d991b1f85f348
--- /dev/null
+++ b/hvigor/hvigor-config.json5
@@ -0,0 +1,22 @@
+{
+ "modelVersion": "5.0.5",
+ "dependencies": {
+ },
+ "execution": {
+ // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */
+ // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */
+ // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */
+ // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */
+ // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */
+ },
+ "logging": {
+ // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */
+ },
+ "debugging": {
+ // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */
+ },
+ "nodeOptions": {
+ // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/
+ // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/
+ }
+}
diff --git a/hvigorfile.ts b/hvigorfile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cf3bb65d3991d12cc2f1209002861a8433807dd1
--- /dev/null
+++ b/hvigorfile.ts
@@ -0,0 +1,31 @@
+import { appTasks } from '@ohos/hvigor-ohos-plugin';
+import { getNode } from '@ohos/hvigor';
+import { runTask, rerunTask, stopTask } from './scripts/commandTask.ts';
+
+const node: HvigorNode = getNode(__filename);
+
+node.registerTask({
+ name: 'startServer',
+ run() {
+ runTask();
+ }
+})
+
+node.registerTask({
+ name: 'restartServer',
+ run() {
+ rerunTask();
+ }
+})
+
+node.registerTask({
+ name: 'stopServer',
+ run() {
+ stopTask();
+ }
+})
+
+export default {
+ system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
+ plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
+}
\ No newline at end of file
diff --git a/oh-package.json5 b/oh-package.json5
new file mode 100644
index 0000000000000000000000000000000000000000..a8aff0c5aff22d78aa26fd19c3861f4320e951ff
--- /dev/null
+++ b/oh-package.json5
@@ -0,0 +1,10 @@
+{
+ "modelVersion": "5.0.5",
+ "description": "Please describe the basic information.",
+ "dependencies": {
+ },
+ "devDependencies": {
+ "@ohos/hypium": "1.0.21",
+ "@ohos/hamock": "1.0.0"
+ }
+}
diff --git a/screenshots/Screenshot_CookiesSettings.png b/screenshots/Screenshot_CookiesSettings.png
new file mode 100644
index 0000000000000000000000000000000000000000..9b8aa7840fd080fa6a376368db66b75170a18c50
Binary files /dev/null and b/screenshots/Screenshot_CookiesSettings.png differ
diff --git a/screenshots/Screenshot_CustomProtocol.png b/screenshots/Screenshot_CustomProtocol.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd60c2f5af953560a50567548c81e0a404f11717
Binary files /dev/null and b/screenshots/Screenshot_CustomProtocol.png differ
diff --git a/screenshots/Screenshot_LocalResource.png b/screenshots/Screenshot_LocalResource.png
new file mode 100644
index 0000000000000000000000000000000000000000..2281eff3a82afad637ab3e25e750e7a066da5402
Binary files /dev/null and b/screenshots/Screenshot_LocalResource.png differ
diff --git a/screenshots/Screenshot_RemoteRequest.png b/screenshots/Screenshot_RemoteRequest.png
new file mode 100644
index 0000000000000000000000000000000000000000..f89899cf76c09b6688f4ebcea6537d02ed6f5bbb
Binary files /dev/null and b/screenshots/Screenshot_RemoteRequest.png differ
diff --git a/screenshots/Screenshot_WebCrossDomain.png b/screenshots/Screenshot_WebCrossDomain.png
new file mode 100644
index 0000000000000000000000000000000000000000..43c846ec5b5089d7fe9fa456bac288e49148eaae
Binary files /dev/null and b/screenshots/Screenshot_WebCrossDomain.png differ
diff --git a/scripts/commandTask.ts b/scripts/commandTask.ts
new file mode 100644
index 0000000000000000000000000000000000000000..98a1310b4240cda2b92bce7e6942b94180104546
--- /dev/null
+++ b/scripts/commandTask.ts
@@ -0,0 +1,91 @@
+const { execSync } = require('child_process');
+let serverFlag: boolean = false;
+
+export function addPort() {
+ try {
+ const result = execSync('hdc rport tcp:8080 tcp:8080', { encoding: 'utf8' });
+ console.log(result);
+ } catch (err) {
+ console.error('Execution failed:', err.message);
+ }
+}
+
+export function delPort() {
+ try {
+ const result = execSync('hdc fport rm tcp:8080 tcp:8080', { encoding: 'utf8' });
+ console.log(result);
+ } catch (err) {
+ console.error('Execution failed:', err.message);
+ }
+}
+
+export function installServer() {
+ try {
+ const result = execSync('npm i', {cwd: './LocalServer', encoding: 'utf8' });
+ console.log(result);
+ } catch (err) {
+ console.error('Execution failed:', err.message);
+ }
+}
+
+export function startServer() {
+ try {
+ serverFlag = true;
+ const result = execSync('npm run forever:start', {cwd: './LocalServer', encoding: 'utf8' });
+ console.log(result);
+ } catch (err) {
+ console.error('Execution failed:', err.message);
+ }
+}
+
+export function restartServer() {
+ try {
+ const result = execSync('npm run forever:restart', {cwd: './LocalServer', encoding: 'utf8' });
+ console.log(result);
+ } catch (err) {
+ console.error('Execution failed:', err.message);
+ }
+}
+
+export function stopServer() {
+ try {
+ serverFlag = false;
+ const result = execSync('npm run forever:stop', {cwd: './LocalServer', encoding: 'utf8' });
+ console.log(result);
+ } catch (err) {
+ console.error('Execution failed:', err.message);
+ }
+}
+
+export function runTask() {
+ try {
+ addPort();
+ if (!serverFlag) {
+ installServer();
+ startServer();
+ } else {
+ restartServer();
+ }
+ } catch (err) {
+ console.error('Execution failed:', err.message);
+ }
+}
+
+export function rerunTask() {
+ try {
+ delPort();
+ addPort();
+ restartServer();
+ } catch (err) {
+ console.error('Execution failed:', err.message);
+ }
+}
+
+export function stopTask() {
+ try {
+ delPort();
+ stopServer();
+ } catch (err) {
+ console.error('Execution failed:', err.message);
+ }
+}
\ No newline at end of file