Firefox for iOS 构建指南:从环境准备到 User Scripts 与许可证维护的完整实践
【免费下载链接】firefox-iosFirefox for iOS项目地址: https://gitcode.com/GitHub_Trending/fi/firefox-ios
导读
firefox-ios/README.md 是 Mozilla Firefox for iOS 应用的主构建文档,它指导开发者完成从环境准备、依赖安装、Xcode 编译到用户脚本(User Scripts)构建与许可证致谢维护的全流程。本篇文章以该文档为核心骨架,结合仓库中 bootstrap.sh、webpack.config.js、package.json 与license_plist_config.yml等源码与配置,深入讲解每一环节的底层实现与参数细节。读完后,你将掌握 Firefox for iOS 的标准编译流程、User Scripts 的聚合打包机制,以及第三方许可证声明文件的生成方法。
说明:当前仓库是同时包含 Firefox for iOS 与 Focus iOS 两个产品的 monorepo,二者共享工具链;本文聚焦
firefox-ios子目录所对应的 Firefox 应用构建链路。
一、构建前环境准备
1.1 工具链与系统要求
根据根目录 README.md 中的徽章信息,当前仓库要求如下:
| 依赖 | 版本要求 |
|---|---|
| Xcode | 26.5(以根 README 徽章标注为准) |
| Swift | 6.2 |
| 最低 iOS 版本 | iOS 15.0+ |
在开始之前,请先确认xcode-select -p指向的正是你的 Xcode 安装路径(默认应为/Applications/Xcode.app/Contents/Developer)。由于这是纯 iOS 工程,Xcode 是硬性前置条件。
1.2 Node.js 安装
User Scripts 的构建依赖 Node.js 与 npm,文档推荐通过n(Node 版本管理器)安装并切换到 LTS 版本:
brew install n # 或跳过 brew:curl -L https://bit.ly/n-install | bash n lts之所以要求 Node.js,是因为仓库根目录的 package.json 声明了 webpack 及其 loader 等构建工具链,并依赖@mozilla/readability、darkreader、dompurify、page-metadata-parser等前端库——这些正是后续 User Scripts 打包所要用到的。
二、克隆与一键初始化:bootstrap.sh 干了什么
2.1 克隆仓库
git clone https://github.com/mozilla-mobile/firefox-ios cd firefox-ios2.2 运行 bootstrap.sh
sh ./bootstrap.sh这一步是文档给出的核心初始化命令。对照仓库中的 bootstrap.sh 源码,可以发现它并非简单的npm install,而是一个多步骤的引导脚本,按顺序完成:
- 安装 SwiftLint:调用 scripts/install-swiftlint.sh 安装仓库固定版本的 SwiftLint(版本由
.swiftlint-version文件钉住),用于后续代码规范检查。 - 下载 Nimbus FML 引导脚本:从 Mozilla application-services 拉取
nimbus-fml.sh,并以./firefox-ios/nimbus.fml.yaml(即 nimbus.fml.yaml)为输入执行,将配置生成到./firefox-ios/bin,用于实验特性(Experiments)的编译期生成。 - 安装 Git hooks:将
.githooks下的钩子复制到.git/hooks并添加可执行权限,保证后续提交走统一的检查流程。 - 安装 npm 依赖并构建用户脚本:依次执行
npm install与npm run build,编译出 User Scripts 的 JS 产物(详见下一节)。
脚本默认参数为firefox,也可传focus走 Focus 分支(该分支还会额外克隆shavar-prod-lists并以固定 commit 检出,用于内容拦截列表)。如果你修改了本地化资源想强制重建,可以附加--force参数(该参数会先清理build目录)。
2.3 SPM 依赖问题的兜底方案
文档专门提示:若在 Xcode 中出现 Swift Package Manager(SPM)依赖异常,可执行:
- Xcode → File → Packages → Reset Package Caches
该操作会清空并重新解析仓库的 SPM 依赖缓存。仓库根目录存在 Package.resolved、BrowserKit/Package.swift、MozillaRustComponents/Package.swift 等多个包清单,任何本地分支切换或版本变更都可能导致依赖缓存与锁文件不一致,重置缓存是官方推荐的恢复手段。
三、在 Xcode 中构建运行
完成初始化后,按以下步骤在 Xcode 中打开并运行:
- 打开
firefox-ios目录下的Client.xcodeproj(即 firefox-ios/Client.xcodeproj)。 - 在 Xcode 中选择
Fennecscheme(注意:不是 Firefox,Fennec 是 Mozilla 内部对 Firefox 的代号,工程中的目标命名沿用此惯例)。 - 选择目标模拟器或真机设备。
- 按
Cmd + R(或点击 Build and Run 按钮)运行。
首次构建时,Xcode 会弹出ModifiedCopySwift 宏的授权确认框,点击Trust & Enable即可继续。这是预期行为——该宏包在 Xcode 26 的构建系统下用于对复制资源进行修改处理;若宏包版本更新,提示会再次出现。
四、深入 User Scripts:注入 WKWebView 的 JavaScript 构建体系
4.1 为什么需要 User Scripts 聚合
Firefox for iOS 通过WKUserScript向WKWebView注入 JavaScript,实现阅读模式、内容拦截统计、密码填充、翻译等能力。但若每个功能都注册一个独立脚本,会显著增加注入数量与性能开销。文档明确给出了解决方案:把脚本按"注入帧(All Frames / Main Frame)× 注入时机(At Document Start / At Document End)"两个维度归并成四类,再用 webpack 分别拼接、压缩。
4.2 目录结构与产物映射
User Scripts 源码位于firefox-ios/Client/Frontend/UserContent/UserScripts目录,其结构与 webpack 的打包入口一一对应:
firefox-ios/Client/Frontend/UserContent/UserScripts |-- AllFrames/ | |-- AtDocumentEnd/ → AllFramesAtDocumentEnd.js | |-- AtDocumentStart/ → AllFramesAtDocumentStart.js |-- MainFrame/ |-- AtDocumentEnd/ → MainFrameAtDocumentEnd.js |-- AtDocumentStart/ → MainFrameAtDocumentStart.js文档指出:"This reduces the total possible number of User Scripts down to four",即最终只保留四个聚合产物。查看 webpack.config.js 的entry配置可以看到,实际打包入口远不止四个:
- 基础四类:
AllFramesAtDocumentStart、AllFramesAtDocumentEnd、MainFrameAtDocumentStart、MainFrameAtDocumentEnd; - 专项脚本:
WebcompatAllFramesAtDocumentStart(站点兼容性)、NightModeAllFramesAtDocumentStart(夜间模式,依赖 Dark Reader)、AutofillAllFramesAtDocumentStart(表单自动填充)、AddressFormManager(地址表单)、TranslationsEngine(翻译引擎)及其运行时动态加载的translations-engine.worker。
从仓库目录 UserScripts 中可以看到,AllFrames/AtDocumentStart下包含__firefox__.js、ContextMenu.js、DownloadHelper.js、LoginsHelper.js、NoImageModeHelper.js、TrackingProtectionStats.js等;MainFrame/AtDocumentStart下则有ReaderMode.js、ReaderModeStyles.js、JSONLD.js、Summarizer.js、TranslationsEntrypoint.js、LanguageSampleExtractor.js等。这些正是打包进各类产物的功能脚本。
webpack 配置中还有一个关键约束:每个"文档开始执行"类别的产物,首个文件必须是__firefox__.js,因为该文件定义了window.__firefox__全局命名空间,后续脚本依赖它进行通信。配置里甚至为此专门写了校验逻辑,如果__firefox__.js不是第一个文件会直接抛错。这也解释了为什么 PDF 内容不会执行 document start 脚本时,需要在 document end 脚本中也引入__firefox__.js。
4.3 编译产物与提交策略
聚合压缩后的产物输出到firefox-ios/Client/Assets目录,命名如下:
AllFramesAtDocumentEnd.jsAllFramesAtDocumentStart.jsMainFrameAtDocumentEnd.jsMainFrameAtDocumentStart.js
产物输出路径同样在 webpack.config.js 的output中指定为firefox-ios/Client/Assets。文档声明这些编译文件是"checked-in to this repository",即把构建结果提交进仓库,从而让不熟悉前端工具链的贡献者也能直接编译工程。不过需要注意,仓库的 .gitignore(第 101~110 行)目前将这类产物列入了忽略名单,意味着它们会在npm run build时重新生成——也就是说,bootstrap.sh中的npm run build步骤是产物可靠性的保证,切勿跳过。
4.4 开发与生产两种构建模式
文档给出了两种构建命令,均在仓库根目录执行:
| 命令 | 模式 | 说明 |
|---|---|---|
npm run dev | 开发模式 + watch | 监听脚本保存并即时重新编译,生成 source map,便于在浏览器/调试器中定位源码行 |
npm run build | 生产模式 | 一次性输出压缩后的正式产物,供 App 发布使用 |
两条命令在 package.json 中对应为webpack --config webpack.config.js --mode development --watch与webpack --config webpack.config.js(默认生产模式)。webpack 配置还处理了桌面端代码中resource://...形式的 URI 导入——通过自定义NormalModuleReplacementPlugin将其替换为Assets/CC_Script/下的本地模块,并依据 Overrides.ios.js 完成 iOS 侧模块覆盖。
五、更新许可证致谢(Settings > Licenses)
Firefox for iOS 在Settings > Licenses界面展示所有使用的开源软件许可证。当引入新的第三方依赖或资源时,必须同步更新致谢列表,官方要求遵循 license_plist_config.yml 的说明操作。
5.1 配置文件与生成命令
该 YAML 文件是 LicensePlist 工具的配置。文档给出的推荐用法是从仓库根目录执行:
license-plist --config-path "./firefox-ios/Client/Assets/About/license_plist_config.yml" \ --package-paths "BrowserKit/Package.swift" \ --xcodeproj-path "firefox-ios/Client.xcodeproj"关键约定:--package-paths必须包含所有Package.swift文件(新增包文件时应追加到命令中);对于无法被自动扫描到的仓库(例如 Mozilla Rust Components),需要按配置手工补充。
5.2 配置项速览
结合配置文件源码,常用配置含义如下:
outputPath: "./LicensePlistTemp":临时输出目录,该目录已被.gitignore忽略,避免无变更时的冗余检查。htmlPath: Licenses.html:生成的最终 HTML 文件名,对应 App 内 Licenses.html 页面。force: true:每次强制重新生成,不依赖缓存。failIfMissingLicense: true:若某个依赖缺少许可证文件则直接失败,从构建层面强制合规。gitHubToken:当 GitHub API 请求被限流时,填入个人访问令牌解除限制。github段:手工罗列无法被自动拾取的远程仓库,如mozilla/readability、darkreader/dompurify、bignerdranch/Deferred等。manual段:补充非远程仓库,例如使用 Mozilla Public License Version 2.txt 的 Mozilla Rust Components,以及因生成 HTML 文本需要手工修正的 Down、EasyList。rename段:将包名显示为人类可读名称,如firefox-ios → Firefox for iOS。exclude段:排除重复条目(如 Down 已在 manual 中手工提供定制 README)。
配置中还有一条 2026 年 2 月的注释提醒:LicensePlist 存在"无法同时在 YAML 中定义xcodeprojPath与packagePaths"的缺陷,因此这两项必须以 CLI 参数形式传入,这也是文档命令必须保留--package-paths与--xcodeproj-path的原因。
六、常见问题排查清单
| 现象 | 处理方式 |
|---|---|
| SPM 依赖解析失败 | Xcode → File → Packages → Reset Package Caches 后重新构建 |
| User Scripts 修改不生效 | 确认在仓库根目录执行过npm run dev(watch 模式)或npm run build |
| 首次构建弹出宏授权 | 点击 Trust & Enable,属正常流程 |
| LicensePlist 生成报错 | 检查是否补全--package-paths下所有Package.swift;确认新依赖已加入github/manual段 |
| GitHub API 限流导致许可证抓取失败 | 在配置中填写gitHubToken |
结语
Firefox for iOS 的构建体系围绕"monorepo + webpack 聚合 User Scripts + LicensePlist 合规管理"三条主线展开:bootstrap.sh承担环境初始化,Fennecscheme 是 Xcode 构建入口,User Scripts 的四类产物设计兼顾了注入性能与开发体验,许可证维护则通过配置驱动保证开源合规。理解这套链路后,无论是编译调试、为浏览器注入新脚本,还是补充第三方依赖声明,都能在仓库中找到对应的落点与标准流程。
【免费下载链接】firefox-iosFirefox for iOS项目地址: https://gitcode.com/GitHub_Trending/fi/firefox-ios
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考