uBO Lite 深度解析:基于 MV3 声明式 API 的内容拦截架构与规则集构建机制
本文以 uBO Lite 官方描述文档 为核心,讲解 uBlock Origin 的 MV3 分支(uBO Lite,简称 uBOL)如何完全基于声明式 API(Declarative Net Request + 注册式 Content Scripts)实现内容拦截,梳理其默认规则集与 uBlock Origin 默认过滤集的对应关系、如何在选项页扩展规则集,并结合仓库源码剖析从过滤列表文本到声明式规则集的完整构建流水线(make-rulesets.js)与运行时的脚本注入机制。读完本文,你将理解 uBOL "零常驻进程" 拦截模型的设计原理、规则集配置的每个字段含义,以及构建产物中各类规则文件(DNR 规则、strictblock、popup 防弹窗、CSS/scriptlet 注入脚本)的生成逻辑。
什么是 uBO Lite:一个完全声明式的 MV3 内容拦截器
uBOL 的定位在 描述文档 中一句话概括:"an efficient MV3 API-based content blocker"(一个高效的、基于 MV3 API 的内容拦截器)。它与传统内容拦截器最根本的区别在于"声明式"(entirely declarative)这一设计立场,文档给出了三层含义:
- 拦截过程不需要 uBOL 的常驻进程。网络请求的放行/阻断判定完全由浏览器内核依据
declarative_net_request规则完成,扩展本身不参与每次请求的判定链路; - CSS/JS 注入式内容过滤由浏览器自己可靠地执行。uBOL 在构建阶段把过滤列表编译成静态脚本,运行时通过 MV3 的
scripting.registerContentScriptsAPI 一次性注册给浏览器,由浏览器在页面生命周期中自动注入——这正是文档引用 Chrome 官方文档registerContentScripts的原因; - 拦截持续进行期间 uBOL 不消耗 CPU/内存资源。文档强调:uBOL 的 service worker 进程只在你与 popup 面板或选项页交互时才被需要。
这一架构在 Chromium 平台 manifest 中有直接印证:
"declarative_net_request": { "rule_resources": [] }—— 声明式网络拦截规则资源入口(构建时由make-rulesets.js填充,见下文);"background": { "service_worker": "/js/background.js", "type": "module" }—— 后台是标准 MV3 service worker,可被浏览器随时休眠;"permissions"列表包含declarativeNetRequest、scripting、offscreen、userScripts等,全部服务于"声明式 + 浏览器代管注入"模型;"minimum_chrome_version": "122.0"—— 声明适用前提:Chromium 122 及以上(Edge 基于 Chromium 同源构建,见 make-rulesets.js 中edge平台会追加chromium环境标识)。
默认规则集:与 uBlock Origin 默认过滤集对齐
文档明确指出:uBOL 的默认规则集至少对应 uBlock Origin 的默认过滤集,具体包括:
- uBlock Origin 内置过滤列表(built-in filter lists);
- EasyList;
- EasyPrivacy;
- Peter Lowe's Ad and tracking server list。
这一声明与仓库中的 rulesets.json 精确对应。该文件是 uBOL 全部规则集的清单与订阅地址,其中 group: "default" 且 enabled: true 的条目恰好就是上述四份列表:
| 规则集 id | 名称 | 过滤列表来源 |
|---|---|---|
ublock-filters |
uBlock filters – Ads, trackers, and more | uAssets 的 quick-fixes.min.txt、unbreak.min.txt、filters.min.txt、privacy.min.txt、ubol-filters.txt(rulesets.json) |
easylist |
EasyList | thirdparties/easylist.txt(rulesets.json) |
easyprivacy |
EasyPrivacy | thirdparties/easyprivacy.txt(rulesets.json) |
pgl |
Peter Lowe – Ads, trackers, and more | pgl.yoyo.org 主机格式列表(rulesets.json) |
注意 ublock-filters 除常规四份 min 化列表外,还额外订阅了一份 ubol-filters.txt,这是 uBOL 专属的补充过滤集。此外,清单中还预置了若干默认开启的非 default 组规则集(如 ublock-badware、urlhaus-full,均属 malware 组)以及大量默认关闭、供用户按需启用的规则集(annoyances、privacy、regions 组等),其中 regions 组覆盖阿拉伯语、中文、德语、法语、日语、韩语、俄语、波兰语等数十个语言/地区列表,每条都带有 lang 与 tags 字段用于选项页的语言/标签过滤展示。
如何添加更多规则集:popup 齿轮图标与选项页
文档给出的操作路径是:打开选项页 → 点击 popup 面板中的齿轮(Cogs)图标。选项页即 dashboard.html(manifest 中 "options_page": "dashboard.html")。
从源码结构看,选项页的规则集管理由 ruleset-manager.js 与 filter-lists.js 等模块承担,而规则集的启用状态最终落到两个层面:
- 静态层:构建期生成的规则文件(
rulesets/main/*.json等,下文详述)通过declarative_net_request.rule_resources注册,用户在选项页切换规则集时,service worker 会更新 DNR 静态规则的启用状态(chrome.declarativeNetRequest.updateDynamicRules之外的updateRulesetInfo一类的静态资源启停,对应 background.js 中大量调用registerContentScripts()的同步逻辑——规则集变化会联动重新注册注入脚本); - 注入层:每启用/停用一份带 CSS/scriptlet 的规则集,scripting-manager.js 会重新调用
registerContentScripts注册浏览器侧的内容脚本,保证注入行为始终与当前启用的规则集一致。
scripting-manager.js 中的实现值得注意(scripting-manager.js):
export async function registerContentScripts() {
registerContentScripts.pendingOp =
registerContentScripts.pendingOp.then(( ) => registerContentScripts.register());
return registerContentScripts.pendingOp;
}
它用一个串行化的 promise 链(pendingOp)保证多次并发触发时注册操作不会交错执行——先 unregisterContentScripts 清空,再 registerContentScripts(toAdd) 全量注册。这正是文档所说"注入由浏览器可靠执行"的落地代码:扩展只负责"告诉浏览器要注入什么",注入时机与执行完全交给浏览器。
而 admin.js 中的多处 await registerContentScripts() 调用则对应管理员/导入导出等管理操作后的脚本重注册,与 background.js 中的生命周期事件(安装、启动、规则变化)形成完整的联动闭环。
构建流水线:从过滤列表文本到声明式规则文件
uBOL 的"声明式"不是运行时把过滤列表动态翻译成 DNR 规则(那样会持续消耗 service worker),而是在构建期一次性完成转换。README 给出了构建步骤(以 Linux 环境为例):
git clone https://github.com/gorhill/uBlock.git
cd uBlock
git submodule init
git submodule update
make mv3-[platform] # [platform] 取 chromium / edge / firefox / safari 之一
构建完成后,扩展包分别位于 dist/build/uBOLite.[platform] 目录;dist/build/mv3-data 缓存从远端服务器抓取的过滤列表(避免重复构建时反复下载,可用 make cleanassets 清除);构建过程日志写入 dist/build/uBOLite.[platform]/log.txt;构建入口是 tools/make-mv3.sh [platform],它最终调用 make-rulesets.js(要求 Node.js 17.5.0+)。
make-rulesets.js 的处理管线
结合 make-rulesets.js 的源码,整个转换管线可以拆解为以下环节:
1. 命令行参数与平台环境(L52-L90)。脚本解析 platform=、output=、env= 参数;默认平台为 chromium;构建环境标识数组 env 固定包含 platform、native_css_has、mv3、ublock、ubol,edge 平台额外追加 chromium。过滤列表中的 !#if 条件注释会依据这些环境标识求值——这就是为什么同一份 uAssets 列表能为 MV2/MV3、不同平台生成不同结果。
2. 列表抓取与本地缓存(L125-L155, L257-L283)。fetchText 先查 mv3-data 本地缓存(URL 转文件名),未命中再远端抓取;对 raw.githubusercontent.com 上的 master 分支 URL 还有 gh api 的 fallback 通道。列表抓取上下文携带 env、构建密钥 secret(L201-L208,随机生成后写入缓存目录,用于签署插入的 !#trusted on/off ${secret} 指令)以及 trustedPrefixes(uAssets 的 filters 目录),使可信列表中的受信指令生效。
3. 核心转换:dnrRulesetFromRawLists(L1028-L1031)。过滤列表文本经 uBO 解析器逐行处理后,能映射到 DNR 语义的过滤器被转换为 block/allow/redirect/modifyHeaders/urlskip 等动作的规则;无法映射的过滤器计入 rejected 并在日志中报告(Rejected filter count)。env、extensionPaths(重定向资源映射,L1009-L1026,把 noop.js、noop.css 等 web_accessible_resources 映射为扩展内路径)、secret 都作为参数传入。
4. 规则分流:DNR / strictblock / popup(L502-L541)。splitDnrRules 把规则拆成三类:
- 常规 DNR 规则(
rulesets/main/<id>.json):静态规则经patchRuleset按平台打补丁(如 Firefox 侧由 firefox/patch-ruleset.js 处理 DNR 不兼容项),再经minimizeRuleset合并压缩;正则类规则单独输出到rulesets/regex/<id>.json; - strictblock 规则(
rulesets/strictblock/<id>.json):针对main_frame阻断的规则被改写为redirect到/strictblock.html#匹配片段(toStrictBlockRule,L422-L475),从而在浏览器侧用重定向实现"严格阻断"页面(对应扩展包中的 strictblock.html); - popup 防弹窗规则(
rulesets/scripting/popup/<id>.js):popup资源类型规则被提取出来,连同主机名/正则集合编译进 prevent-popup.template.js 模板,生成由浏览器注入的防弹窗脚本——因为 DNR 对 popup 窗口的拦截能力有限,改用脚本侧window.open守卫实现。
urlskip(URL 跳过规则,DNR 无法表达)则被单独聚合为 rulesets/urlskip/<id>.json,供运行时的 userScripts 侧逻辑处理。
5. 外观(cosmetic)过滤的两条路径(L680-L886)。这是文档所说"CSS 注入由浏览器执行"的具体来源:
- 通用 CSS 隐藏(
processGenericCosmeticFilters,L722-L813):把##selector形式的通用过滤器按 DJB2 变体哈希(hashFromStr,L815-L823,注释注明"必须与 content script surveyor 版本保持一致")分桶,连同"特定主机例外"(#@#)一起注入 css-generic.template.js 模板,生成scripting/generic/<id>.js; - 特定 CSS 隐藏(
processCosmeticFilters,L841-L853):##domain.com##selector这类按主机关联的过滤器编译为scripting/specific/<id>.js+ 数据 JSON; - scriptlet 过滤器(
processScriptletFilters,L857-L886):$script类规则按 make-scriptlets.js 编译为 MAIN / ISOLATED 两种宿主环境的注入脚本,输出到scripting/scriptlet/main|isolated/<id>.js。
6. 汇总与 manifest 回填(L1164-L1242)。main() 以构建时刻生成日期版本号(YYYY.MMM.DHHMM),写入 ruleset-details.json(每份规则集的过滤/规则统计)、scriptlet-details.json、generic-details.json 三份清单(供选项页展示与启停控制),把规则用到的重定向资源复制进扩展包,最后回填 manifest:declarative_net_request.rule_resources 填入各规则集的 path 与 enabled 初始状态,web_accessible_resources 追加按需生成的重定向资源条目,version 更新为本次构建版本。这解释了为何仓库中 Chromium manifest 的 rule_resources 目前是空数组——它是模板,真正的规则资源清单在构建时注入。
资源与适用前提小结
- 适用平台:
make mv3-[chromium|edge|firefox|safari]四种目标(README);Safari 侧有独立的 safari/patch-ruleset.js 与 safari/manifest.json;Firefox 侧有 ext-offscreen.js 处理 offscreen 文档差异;rulesets.json中excludedPlatforms: ["safari"]的规则集(如pgl、ublock-badware、urlhaus-full)在 Safari 构建时会被跳过(make-rulesets.js)。 - 运行前提:Chromium 平台要求 Chrome 122+(
minimum_chrome_version),依赖scripting.registerContentScripts与declarativeNetRequest静态规则能力。 - 构建依赖:Node.js ≥ 17.5.0;
git submodule拉取第三方库(platform/mv3/extension/lib 等);远程过滤列表缓存于dist/build/mv3-data。 - 与完整版 uBlock Origin 的差异:uBOL 拦截判定发生在浏览器内核侧(声明式规则 + 浏览器注入脚本),扩展进程只在交互(popup/选项页)时活跃;这是文档"uBOL itself does not consume CPU/memory resources while content blocking is ongoing"这一论断的实现基础,而非营销说法——
make-rulesets.js构建期完成的规则编译与scripting-manager.js运行期的一次性注册共同构成了这一模型的两端。
参考路径索引
| 内容 | 路径 |
|---|---|
| uBO Lite 官方描述文档 | platform/mv3/description/en.md |
| 规则集清单与订阅地址 | platform/mv3/rulesets.json |
| 构建说明 | platform/mv3/README.md |
| 构建脚本入口 | platform/mv3/make-rulesets.js |
| 构建 shell 入口 | tools/make-mv3.sh |
| Chromium manifest(模板) | platform/mv3/chromium/manifest.json |
| 内容脚本注册实现 | platform/mv3/extension/js/scripting-manager.js |
| 后台生命周期联动 | platform/mv3/extension/js/background.js |
| CSS/scriptlet 注入模板 | platform/mv3/scriptlets/css-generic.template.js、prevent-popup.template.js |
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00