首页
/ uBO Lite 深度解析:基于 MV3 声明式 API 的内容拦截架构与规则集构建机制

uBO Lite 深度解析:基于 MV3 声明式 API 的内容拦截架构与规则集构建机制

2026-09-03 15:35:50作者:邵娇湘

本文以 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)这一设计立场,文档给出了三层含义:

  1. 拦截过程不需要 uBOL 的常驻进程。网络请求的放行/阻断判定完全由浏览器内核依据 declarative_net_request 规则完成,扩展本身不参与每次请求的判定链路;
  2. CSS/JS 注入式内容过滤由浏览器自己可靠地执行。uBOL 在构建阶段把过滤列表编译成静态脚本,运行时通过 MV3 的 scripting.registerContentScripts API 一次性注册给浏览器,由浏览器在页面生命周期中自动注入——这正是文档引用 Chrome 官方文档 registerContentScripts 的原因;
  3. 拦截持续进行期间 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" 列表包含 declarativeNetRequestscriptingoffscreenuserScripts 等,全部服务于"声明式 + 浏览器代管注入"模型;
  • "minimum_chrome_version": "122.0" —— 声明适用前提:Chromium 122 及以上(Edge 基于 Chromium 同源构建,见 make-rulesets.jsedge 平台会追加 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.txtunbreak.min.txtfilters.min.txtprivacy.min.txtubol-filters.txtrulesets.json
easylist EasyList thirdparties/easylist.txtrulesets.json
easyprivacy EasyPrivacy thirdparties/easyprivacy.txtrulesets.json
pgl Peter Lowe – Ads, trackers, and more pgl.yoyo.org 主机格式列表(rulesets.json

注意 ublock-filters 除常规四份 min 化列表外,还额外订阅了一份 ubol-filters.txt,这是 uBOL 专属的补充过滤集。此外,清单中还预置了若干默认开启的非 default 组规则集(如 ublock-badwareurlhaus-full,均属 malware 组)以及大量默认关闭、供用户按需启用的规则集(annoyancesprivacyregions 组等),其中 regions 组覆盖阿拉伯语、中文、德语、法语、日语、韩语、俄语、波兰语等数十个语言/地区列表,每条都带有 langtags 字段用于选项页的语言/标签过滤展示。

如何添加更多规则集:popup 齿轮图标与选项页

文档给出的操作路径是:打开选项页 → 点击 popup 面板中的齿轮(Cogs)图标。选项页即 dashboard.html(manifest 中 "options_page": "dashboard.html")。

从源码结构看,选项页的规则集管理由 ruleset-manager.jsfilter-lists.js 等模块承担,而规则集的启用状态最终落到两个层面:

  1. 静态层:构建期生成的规则文件(rulesets/main/*.json 等,下文详述)通过 declarative_net_request.rule_resources 注册,用户在选项页切换规则集时,service worker 会更新 DNR 静态规则的启用状态(chrome.declarativeNetRequest.updateDynamicRules 之外的 updateRulesetInfo 一类的静态资源启停,对应 background.js 中大量调用 registerContentScripts() 的同步逻辑——规则集变化会联动重新注册注入脚本);
  2. 注入层:每启用/停用一份带 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 固定包含 platformnative_css_hasmv3ublockuboledge 平台额外追加 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)。envextensionPaths(重定向资源映射,L1009-L1026,把 noop.jsnoop.cssweb_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.jsongeneric-details.json 三份清单(供选项页展示与启停控制),把规则用到的重定向资源复制进扩展包,最后回填 manifestdeclarative_net_request.rule_resources 填入各规则集的 pathenabled 初始状态,web_accessible_resources 追加按需生成的重定向资源条目,version 更新为本次构建版本。这解释了为何仓库中 Chromium manifest 的 rule_resources 目前是空数组——它是模板,真正的规则资源清单在构建时注入。

资源与适用前提小结

  • 适用平台make mv3-[chromium|edge|firefox|safari] 四种目标(README);Safari 侧有独立的 safari/patch-ruleset.jssafari/manifest.json;Firefox 侧有 ext-offscreen.js 处理 offscreen 文档差异;rulesets.jsonexcludedPlatforms: ["safari"] 的规则集(如 pglublock-badwareurlhaus-full)在 Safari 构建时会被跳过(make-rulesets.js)。
  • 运行前提:Chromium 平台要求 Chrome 122+(minimum_chrome_version),依赖 scripting.registerContentScriptsdeclarativeNetRequest 静态规则能力。
  • 构建依赖: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.jsprevent-popup.template.js
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
983
503
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384