Manifest · 清单成员
protocol_handlers 清单成员
发布于 更新于
protocol_handlers 是一个由 { protocol, url } 对象组成的数组,请求浏览器在安装时把这个 Web 应用
注册为操作系统中若干 URL 协议的处理程序。注册之后,在任何应用里点击 web+note:// 或 mailto:
链接都会在 url 处打开该 Web 应用,%s 被替换为完整链接。
Chrome 96 与 Edge 96 为 Windows、macOS、Linux 上的桌面安装实现了该成员(BCD
html.manifest.protocol_handlers)。Android 上的 Chrome、Samsung Internet、Firefox 157 与 Safari 27
不读取它。运行时 API navigator.registerProtocolHandler() 解决的是另一个场景:它只在浏览器内部注册
当前站点,且每次访问都需要用户手势;清单成员则在安装时注册,并且触达操作系统。
- 类型:对象数组。每个对象有两个必填字符串:
protocol,不带冒号的协议名;url,位于清单scope内、包含字面量%s的 HTTPS URL。相对url按清单 URL 解析。 - 默认值:空数组,不注册任何协议。
- 示例值:
[{ "protocol": "web+note", "url": "/open?note=%s" }]。
protocol 必须是以 web+ 开头、后接小写 ASCII 字母的自定义协议,或是 HTML 标准安全列表中的协议:
bitcoin、ftp、ftps、geo、im、irc、ircs、magnet、mailto、matrix、mms、
news、nntp、openpgp4fpr、sftp、sip、sms、smsto、ssh、tel、urn、webcal、
wtai 与 xmpp。其他值(例如 note 或 ms-word)会被跳过。
条目格式不对时,Chromium 的解析器丢弃的是该条目而不是整个数组:
protocol_handlers entry ignored, required property 'protocol' is invalid. 与
protocol_handlers entry ignored, required property 'url' is invalid. 指出缺失的字段;url 里没有
占位符则产生 The url provided ('/open?note=') does not contain '%s'.。规范还会跳过 url 落在
scope 之外的条目,以及归一化后 url 与前面条目重复的条目。
注册以安装为单位:Chrome 在应用安装时把处理程序写入操作系统,卸载时移除。带该协议的链接第一次 启动应用时,Chrome 会先弹出一个写明应用名称的确认对话框再导航;多个应用声明同一协议时,操作系统 可能让用户选择默认程序。
清单一侧只有几行;处理页面则要应付可能缺失、被编码或协议不符的值。
为笔记应用注册自定义 web+ 协议
Section titled “为笔记应用注册自定义 web+ 协议”一款笔记应用声明 web+note,让邮件和聊天里的 web+note://2026-10-03 链接打开对应条目。url 位于
应用的 scope 之下,并把 %s 放在查询参数里,这样路径的其余部分保持静态。
{ "name": "Notebook", "start_url": "/", "scope": "/", "display": "standalone", "protocol_handlers": [ { "protocol": "web+note", "url": "/open?note=%s" } ]}每个协议只注册一个处理程序;同一个 protocol 写两次、URL 不同时,保留第一条。
在处理页面上解码被替换进来的链接
Section titled “在处理页面上解码被替换进来的链接”浏览器用整条链接替换 %s,所以页面收到的是 web+note://2026-10-03 而不是 2026-10-03,其中
# 之类的字符被百分号编码。自己读取原始查询值并解码:URLSearchParams 会把 + 变成空格,
从而破坏 web+note。值缺失或携带别的协议时走回退分支,渲染普通页面,这也就是直接访问 /open
时的样子。
const raw = location.search.match(/[?&]note=([^&]*)/)?.[1];let link = null;try { link = raw ? decodeURIComponent(raw) : null;} catch { link = null; // 百分号编码损坏:当作没有链接处理}
if (link?.startsWith('web+note://')) { openNote(link.slice('web+note://'.length)); // "2026-10-03"} else { showNoteList(); // 直接访问,或是本页不处理的协议}读取之后用 history.replaceState() 替换掉查询串,这样刷新或收藏不会再次触发处理逻辑。
在清单被忽略的环境回退到运行时 API
Section titled “在清单被忽略的环境回退到运行时 API”在没有该清单成员的浏览器里,navigator.registerProtocolHandler() 只在浏览器内部注册同一个协议,
而且必须在用户手势中调用。对两者都做特性检测,并为运行时路径提供一个按钮。
export function offerProtocolRegistration(button) { const installed = matchMedia('(display-mode: standalone)').matches; if (installed) return; // Chrome/Edge 桌面端:清单已经注册了 web+note if (typeof navigator.registerProtocolHandler !== 'function') return; // Safari:没有可用路径 button.hidden = false; button.addEventListener('click', () => { navigator.registerProtocolHandler('web+note', `${location.origin}/open?note=%s`); });}Firefox 157 接受 web+ 协议的运行时注册;之后该处理程序只对在 Firefox 内部点击的链接生效,
对其他应用里的链接不生效。
- Manifest protocol_handlers:为 PWA 注册自定义 URL 协议
- scope 清单成员
- launch_handler 清单成员
- Manifest Incubations: protocol_handlers member(wicg.github.io)
- Handle protocols in PWAs(learn.microsoft.com)
- Navigator: registerProtocolHandler() method(developer.mozilla.org)
规范
| 规范 | 状态 |
|---|---|
| 清单协议处理器(protocol_handlers) | W3C |
| Manifest Incubations: protocol_handlers member | WICG 草案 |
| HTML Standard: safelisted schemes | WHATWG 现行标准 |
- 图例
- 支持
- 部分支持
- 需开启标志
- 不支持
- 未知
| 浏览器 / 平台 | 支持 | 版本 | 置信度 | 来源 | 备注 |
|---|---|---|---|---|---|
| Chrome (Android) | 不支持 | — | 中 | 来源 | 1 |
| Chrome (Desktop) | 支持 | 96 | 中 | 来源 | — |
| Edge (Desktop) | 支持 | 96 | 中 | 来源 | — |
| Safari (iOS) | 不支持 | — | 中 | 来源 | 2 |
| Safari (macOS) | 不支持 | — | 中 | 来源 | 3 |
| Firefox (Desktop) | 不支持 | — | 中 | 来源 | 4 |
| Samsung Internet | 不支持 | — | 中 | 来源 | 5 |
- 仅桌面端可注册。
- Safari 未实现 `protocol_handlers` manifest 成员(MDN 兼容性表,2026-10-03 核对)。
- Safari 未实现 `protocol_handlers` manifest 成员(MDN 兼容性表,2026-10-03 核对)。
- 桌面版 Firefox 不依据 manifest 安装 Web 应用,也未实现 `protocol_handlers`(MDN 兼容性表,2026-10-03 核对)。
- Chromium 仅在桌面安装中提供 manifest 协议处理程序;Samsung Internet 未列出支持(MDN 兼容性表,2026-10-03 核对)。