Air 系列应用交流/AirWeb(Firefox)· 鸿蒙 PC 适配全记录

AirWeb(Firefox)· 鸿蒙 PC 适配全记录

【Air 系列 · 鸿蒙 PC 适配全记录】53 / 56

■ 应用:AirWeb(AirWeb)
■ 定位:多标签浏览器(追踪保护)
■ 上游:Firefox / Gecko 142 | 路线:Gecko mozilla-central | 版本:1.7.0
■ 开源: https://gitcode.com/Andy88/AirWeb
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917608536829125324
■ 官网(免费下载): https://www.shunidc.cn/

————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/

PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper

适配开源地址:https://gitcode.com/Andy88/AirWeb

环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743

▍一、为什么要适配 AirWeb

浏览器是桌面上使用频率最高、也最难移植的一类应用。它不是单一程序,而是一个由网页排版引擎、JavaScript 引擎、网络栈、多媒体解码、密码学与证书体系、以及一套自绘 chrome UI 共同组成的操作系统级软件。对鸿蒙 PC 而言,没有浏览器就没有完整的桌面生态;而对工程实践而言,把 Firefox 这样的巨型 C++ 项目带上鸿蒙,本身就是对工具链、加载器、权限模型和窗口系统的全面检验。

AirWeb 选择的就是这条最重的路线:以 mozilla-central 的 Firefox/Gecko 142 为上游,直接交叉编译出鸿蒙 arm64 的浏览器内核,而不是用系统 WebView 套壳。工程的主体是 mozilla-central/ 上的未提交移植改动、entry/ 内的 ArkTS 壳与预构建 Gecko 运行时,以及 l10n/、scripts/、tabletkit/ 等配套目录。当前应用版本为 1.6.0,包名 com.airweb.app,deviceTypes 覆盖 phone、tablet、2in1 三种形态,运行时库以 arm64-v8a 为目标 ABI。

选择 Firefox 而非 WebView 还有一个工程理由:chrome 层(浏览器自身界面)是一套用 XUL/CSS/JS 描述、数据驱动渲染的 UI 体系,全部打包在 omni.ja 内。这套体系可以在不重写内核的前提下被"就地手术"——改 XUL 文档、改 CSS、改 ftl 语言文件,就能改变浏览器的形态与语言。AirWeb 后续的手机 UI 与严格汉化,都建立在这条通道之上。

▍二、先划清边界:不把编译通过当成适配完成

Firefox 桌面版的启动链、窗口系统、多进程架构、资源加载都与 Linux/Windows 桌面环境深度绑定。仅把 libxul.so 编译出来,距离一个能浏览、能输网址、能管理标签的浏览器还差整个应用层。AirWeb 从一开始就把边界写清楚:内核层进入当前可运行产物,应用外壳层按双轨演进,多进程能力明确放弃。

层次 原项目实现(Firefox 桌面版) 鸿蒙侧当前方案
应用入口 桌面 XRE 启动链与 GTK/Win32 主循环 Stage 模型 EntryAbility,Want URI 与 -- 参数透传 Gecko
窗口承载 GTK/Win32 顶层窗口 XComponent(SURFACE) + EGL 直绘(FirefoxWebView.ets)
chrome 界面 browser.xhtml XUL 文档 + 桌面 CSS native 轨复用同一 browser.xhtml 就地手术(phone-chrome.css/phone-chrome.js);classic 轨用 ArkTS TabBar/Toolbar 自绘
网页引擎 桌面构建 libxul.so mozilla-central 142 交叉编译 libxul.so,llvm-objcopy --add-section .permission 注入 JIT 权限段
chrome 资源 安装目录内 omni.ja rawfile/omni.ja 首启拷贝为沙箱 GRE 目录,omni_version.txt 版本戳命中跳拷贝
本地化 官方 langpack 安装器 l10n/zh-CN 快照经 scripts/stage_zh_cn.sh 布景进 chrome localization
菜单系统 XUL popup 原生弹出 NativeMenuOverlay(ohos.menu.native_overlay=true)Gecko 菜单 JSON 转 ArkTS 菜单
多进程 e10s 默认多内容进程 单进程(鸿蒙沙箱禁 execve,实测 status 127,判死)

这份边界决定了工作顺序:先让内核在真机上渲染网页,再让 chrome 可控,最后才是形态与语言打磨。反过来,任何"编译产物存在但用户流程走不通"的状态都被视为未完成。

▍三、鸿蒙版的整体架构

鸿蒙工程内部分两层。ArkTS 壳负责 Ability 生命周期、GRE 资源部署、pref 播种、UI 轨道路由与系统服务(下载、文件选择器、光标、输入法桥);Gecko 内核是预构建的 .so 集合,由 NAPI 桥 libgeckoview.so 拉起,绘制面是一块 SURFACE 型 XComponent。

text
EntryAbility(Stage 模型)
├── want URI / 命令行参数解析(-- 开头参数透传 Gecko)
├── GRE 沙箱部署:rawfile/omni.ja → files 目录(omni_version.txt 版本戳)
├── user.js 启动期播种:inset 避让 / ohos.form.factor / 移动 UA / 性能 prefs
├── ui_mode 双轨路由:native → NativeShellPage,classic → BrowserPage
└── FirefoxBridge(NAPI 单例,entry/src/main/ets/native/FirefoxBridge.ets)
├── initFirefox → libgeckoview.so → libxul.so(Gecko 142,XRE_main)
├── XComponent 'firefox_0'(libraryname=geckoxcomp,C 原生触摸/鼠标回调)
├── OHOSMenuBridge → NativeMenuOverlay(菜单 JSON → ArkTS 菜单浮层)
└── DownloadStore / BookmarkStore / HistoryStore(classic 轨数据层)


主要目录如下:

text
AirWeb/
├── AGENTS.md # 项目纪要:构建链与设备纪律
├── docs/ # 手机 UI 总体方案与设计规格(2026-09-29)
├── mozilla-central/ # Firefox/Gecko 142 上游源码(含移植改动)
├── l10n/zh-CN/ # zh-CN 本地化快照(Fx129 期 rev)
├── scripts/
│ ├── repack_omni.sh # omni.ja 重打包与版本戳
│ └── stage_zh_cn.sh # zh-CN 串布景进 chrome localization
├── build_incremental.sh # Gecko 增量构建通道(mach)
├── mozconfig # Gecko 构建配置
├── tabletkit/ # 平板 feature HAP(JIT 权限分包,v229)
└── entry/src/main/
├── ets/
│ ├── entryability/EntryAbility.ets
│ ├── pages/ # NativeShellPage / BrowserPage 双轨入口
│ ├── components/ # FirefoxWebView / TabBar / Toolbar / FindInPageBar 等
│ ├── models/ # TabInfo / BookmarkStore / HistoryStore / SearchEngine 等
│ └── native/ # FirefoxBridge / OHOSMenuBridge
├── cpp/CMakeLists.txt # stage2 占位工程(预构建 .so 拷贝通道)
├── libs/arm64-v8a/ # libxul.so、libgeckoview.so、NSS 族等 Gecko 运行时
└── resources/rawfile/ # omni.ja + omni_version.txt(GRE 资源)


▍四、核心适配过程

### 1. 预构建内核的 stage2 装配通道

Gecko 内核不在 entry 内编译。mozilla-central/ports/ohos 产出直接链接 libxul.so 的 NAPI 模块 libgeckoview.so,entry/src/main/cpp/CMakeLists.txt 只承担一件事:把预构建的 libgeckoview.so、libxul.so、NSS 族、编解码库等十几二十个 .so 在每次构建时拷贝到输出目录,交给 hvigor 的 ProcessLibs 收集打包。注释里记录了一个反例:用 file(COPY) 依赖 configure 阶段会导致缓存命中时 .so 未拷贝、HAP 只含 libc++_shared.so、设备 dlopen 失败闪退。module.json5 同时声明 compressNativeLibs: false——压缩后设备 dlopen 需先解压,对带 .permission 段和 64K 对齐 PT_LOAD 的 libxul.so 会造成对齐失配与加载延迟。

### 2. GRE 资源的沙箱部署与 omni 版本戳

HAP 安装后 rawfile 不能按文件系统路径访问,而 Gecko 必须以目录形式读取 GRE(含 omni.ja)。EntryAbility.ets 的 copyGreToSandbox() 在启动时把 rawfile/omni.ja 复制到沙箱并返回 GRE 根目录。omni.ja 体积可观,工程配合 scripts/repack_omni.sh 维护 omni_version.txt 版本戳,内容未变时跳过拷贝,把启动期 IO 控制在增量范围内。

### 3. 双轨 UI:同一内核上的两种外壳

runtime/ui_mode 决定轨道。native 轨(NativeShellPage.ets)里 ArkTS 只剩一个全屏 XComponent 薄壳,地址栏、标签栏全部由 browser.xhtml 自绘,配 HiddenImeBridge 输入法桥与启动加载遮罩;classic 轨(BrowserPage.ets)是早期 ArkTS chrome:TabBar、Toolbar、FindInPageBar 加书签/历史/下载三个 Store。触控与鼠标事件由 XComponent 的 C 原生回调直送 Gecko(坐标为物理 px,不经 ArkUI 命中测试),滚轮经 onAxisEvent 转发,文件选择经 FirefoxBridge.ets 拉起系统 DocumentViewPicker 后回灌内核。这条双轨设计让 chrome 层的激进改造始终保有回退路径。

### 4. 手机形态:pref 播种 + chrome 就地手术

2026-09-29 的 v233 批次交付了手机 UI 第一形态。写入通道是 EntryAbility.ets 的 ensureFormFactorPref():启动期把 ohos.form.factor(phone/tablet/2in1)写进 profile/user.js——与更早的 ohos.chrome.right_inset_px 避让 pref 同构,每次启动必读且抗崩溃,首帧即匹配。消费端是 mozilla-central 内新增的 browser/themes/shared/phone-chrome.css:全部手机规则包在 @media (-moz-pref("ohos.form.factor","phone")) 内,桌面形态零匹配零开销;browser/base/content/phone-chrome.js 接线底栏五键、加载进度条、标签计数与全屏标签切换器。同一批次的 ensurePhoneUserPrefs() 为手机档写入 Android Firefox 移动 UA 组与性能减法(关平滑滚动、关页面缩略图、放宽 sessionstore 间隔),平板与 2in1 保持桌面 UA 不变。

### 5. 严格汉化:布景与直译双修

汉化不是翻译几个字符串,而是补齐整条 l10n 链路。l10n/zh-CN/ 是 Fx129 期的官方快照,scripts/stage_zh_cn.sh 把 browser/branding/toolkit 等目录布景进 chrome 的 localization 路径。v233 处理了两类快照覆盖不到的残留:necko.ftl 网络状态条在双语协商路径上都到不了,于是一边给 stage 脚本增加 netwerk 布景段、一边把 en-US 侧 11 条连接状态串直译中文,保证任何协商结果下都是中文;appmenu.ftl 的 Profiler 工具族 35 余串则整段手译。这批记录同时沉淀了一条规程:ftl 批量翻译禁用裸正则,必须按消息块结构化处理,否则 Fluent 属性前缀会被吃掉造成语法错误。

▍五、适配中遇到的几个难点

### 难点一:JIT 内存授权的三种形态

SpiderMonkey 需要 W+X 内存。工程在编译期用 llvm-objcopy --add-section .permission 把 ohos.permission.kernel.ALLOW_WRITABLE_CODE_MEMORY 等内核权限声明注入 libgeckoview.so/libxul.so,由内核 PMS 自动授权。但 v229 发现手机形态下该权限受限:工程把此权限移入 tabletkit feature HAP(deviceTypes 限定 tablet/2in1),手机装 entry 单包即无权限申请,XPCOMInit 探测失败自动降级纯解释器,平板双 HAP 合并授权后 JIT 全速。v232 又依据华为政策约束把手机改为无条件禁 JIT——--airweb-force-no-jit 在探测前直接降级,杜绝安装态差异导致的不可复现行为。解释器模式下 chrome JS 与页面 JS 全部承担约 26 倍执行税,这构成了后续性能专项的硬边界。

### 难点二:257MB libxul 的加载税

libxul.so 约 257MB,主线程同步 dlopen 占 0.3-1 秒,XRE_main 前置合计约 4 秒,是启动体验的第一痛点。工程的做法是不追求单点银弹:compressNativeLibs: false 换取 mmap 直读,omni.ja 版本戳削减启动拷贝,compressNativeLibs 与 64K 对齐联动保持加载器 happy path;gkcodecs/avcodec 等大库从 NEEDED 直链改为按需 dlopen 列入实验清单,每项都必须以冷启动分相计时与滚动帧统计验证,避免"优化 A 恶化 B"。

### 难点三:2in1 系统三键的 chrome 避让

MateBook 类 2in1 设备右上角悬浮的系统三键不属于任何 SDK AvoidArea 类型,getWindowAvoidArea 恒返回零宽。chrome 的标签条与地址栏若不避让就会被三键遮死。工程用固定 120vp 兜底值驱动 ohos.chrome.right_inset_px pref,chrome CSS 以 -moz-pref 媒体查询消费。关键教训在落盘方式:最初经运行时 setCharPref 推送,应用一旦崩溃 user pref 永不落盘,冷启动恒为默认值;改为启动期写 user.js 后,每次启动必读、不被 Gecko 覆写,避让天然抗崩溃。同一管道后来复用给了顶部 inset 与 formfactor。

### 难点四:多进程架构在沙箱内判死

Firefox 默认 e10s 多进程,依赖 fork/exec 拉起内容进程。鸿蒙应用沙箱禁 execve(实测 spawn 返回 status 127),平台级阻塞且无工程内解法。工程明确把 e10s 判死、维持单进程架构,把优化精力转向单进程内的启动链与 JS 执行量削减。远期唯一窗口被登记为 OHOS NativeChildProcess API 的适配研究,不作承诺。

### 难点五:omni 手术通道的纪律

手机 UI 与汉化都要求改动 omni.ja 内的上游文件。工程为此立了两条纪律:其一,就地手术必须在改动点打 [OHOS vNNN] 标记,便于上游版本对齐时审计;其二,fluent 文件按消息块整段处理。chrome JS(global-scripts.js 挂载 phone-chrome.js)在桌面形态只付出一次 subscript 加载与 IIFE return 的代价,手机规则在 CSS 层被媒体查询完全隔离,保证单一文档服务三种形态而不互相污染。

▍六、编译、安装与启动

AirWeb 的构建分两段:Gecko 内核段与 HAP 段。内核增量构建沿用 mozilla-central 的 mach 通道,随后由项目脚本打包 HAP:

bash

./build_incremental.sh


devecocli build


仅改动 ArkTS 壳时可直接用标准 hvigor 命令行构建:

bash
export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk

hvigorw --mode module -p module=entry assembleHap --no-daemon


签名产物位于 entry/build/default/outputs/default/entry-default-signed.hap。连接设备后安装并启动:

bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airweb.app


工程 AGENTS.md 对构建链有一条硬约束:ArkTS 构建失败时会误装旧 HAP,因此每一轮都必须确认构建成功再安装验证。

▍七、当前功能边界

当前可运行产物已覆盖的能力:

- Gecko 142 内核真机运行:网页渲染、导航前进后退、多标签、缩放;
- 双轨 UI:native 轨 browser.xhtml 自绘 chrome(含 v233 手机形态:顶部地址胶囊、底栏五键、进度条、全屏标签切换器、长按双动作),classic 轨 ArkTS chrome(标签条/工具栏/页内查找);
- 数据能力:书签、历史、下载(后台线程复制落盘)、搜索引擎与搜索建议;
- 系统集成:系统文件选择器、下载进度事件、指针样式(setPointerStyleSync)、输入法桥、系统菜单浮层(NativeMenuOverlay);
- 形态适配:2in1 三键避让 inset 管道、手机档 formfactor 与移动 UA 默认、平板双 HAP JIT 全速;
- 汉化:zh-CN 快照布景、网络状态条与 Profiler 段残留清理。

尚未覆盖或规划中的能力(均有文档依据):手机菜单的 bottom sheet 形态改造、菜单内"桌面版网站"运行时开关、l10n 快照升级到 Fx142 对齐 rev(207 个 ftl 重铺)、apz 手感校准与 -O3/ThinLTO 构建实验、gkcodecs/avcodec 懒加载、mimalloc 分配器调研、ftl 覆盖率自动化比对、e10s 多进程(已判死)。docs/ 下 2026-09-29 的总体方案将以上各项拆进了 M1-M4 里程碑与任务表。

因此,当前版本更准确的定位是"Gecko 内核完整的鸿蒙三形态浏览器基础版":内核与网页体验是真实的,手机 UI 与汉化完成了第一批交付,其余按边界清单分期推进。

▍八、总结

AirWeb 的适配证明了重型 C++ 桌面应用进入鸿蒙 PC 的一条现实路径:内核层用交叉编译与 ELF 级整备解决"能不能跑",应用层用 omni.ja 就地手术解决"像不像、顺不顺",权限与多进程等平台差异用明确的边界声明代替含糊的承诺。整条链路上没有引入第二套浏览器内核,桌面 Firefox 的网页兼容性与能力面得以保留。

后续工作重心已由 docs/ 方案文档明确:先完成手机形态地基(formfactor 管道、自由旋转、bottom inset)与汉化快照升级,再攻坚底部工具栏与性能批次,最终以覆盖率脚本和三设备回归收口。每一步的验收标准都落在可构建的 HAP 与真机行为上,这与工程从第一天起"不把编译通过当成适配完成"的边界一致。
楼主 8 天前
想参与讨论?登录 或 30 秒注册。