WebKit 原生扩展正在复活3MB 浏览器带火的一门『旧』技术【免费下载链接】SearchA small, fast WebKit browser for macOS, by Office Commun.项目地址: https://gitcode.com/gh_mirrors/search59/Search2026 年的浏览器市场几乎每一场新品发布都在讲同一件事把 Chromium 再包一层壳。Arc 讲空间与标签页Dia 讲极简Comet 讲 AI。但在这股潮流之下有一个被忽略多年的旧技术正在悄悄复活——WebKit 的原生扩展引擎。Safari 的扩展需要打包成 App、过审、上架开发者积极性长期不高Chrome 的 Manifest V3 生态则几乎成了浏览器扩展的唯一大陆。而一个由设计工作室 Office Commun 开发的 3MB 极简浏览器 Search正在用一门旧引擎撬动整个 Chrome 生态——它不内置 Chromium却能把 Chrome Web Store 里的扩展直接装进 WebKit 里跑。这篇文章将拆解这门旧技术的历史处境并以 Search 仓库源码为证据说明一个 3MB、约 4.2 万行 Swift、零第三方依赖的浏览器是如何把 Chrome 扩展兼容做成核心卖点的。一门旧技术的沉寂与复活WebKit 原生扩展并不是新东西。Safari 的扩展引擎长期以来就基于 WebKit 框架内部的扩展体系也就是后来公开化的WKWebExtension相关 API。它的处境一直很尴尬一方面Safari 扩展必须打包成 App、经过 App Store 审核开发和分发门槛远高于 Chrome 商店里的填个 manifest 就能装另一方面开发者为数不多的精力都流向了用户量最大的 Chrome 生态Safari 扩展市场因此长期萧条形成越没人做、越没人用的负循环。但在较新的 macOS 系统里情况出现了一个关键变化WebKit 的扩展引擎 API 从仅供 Safari 使用走向了普通浏览器可调用。Search 的代码库中所有扩展相关类都标注着available(macOS 15.4, *)并在 Extensions.swift 里直接持有WKWebExtensionController实例——这正是 Safari 跑扩展时使用的同一套引擎。换句话说苹果在系统层面把运行 WebExtension 的能力从 Safari 里拆出来开放给了任何第三方浏览器。Search 精准地押注了这一点。README 里写得很直白It runs on WebKits own extension engine — the one Safari uses。一个不内置 Chromium 的浏览器第一次有可能直接消费 Chrome 积累了十几年的扩展生态。3MB 浏览器如何把 Chrome 扩展做成卖点直连 Chrome Web Store 的安装链路Search 安装扩展的方式不是让用户去下载 .crx 文件手动拖入而是直接在浏览器里粘贴一个 Chrome Web Store 链接或 32 位扩展 id——这依赖 Crx.swift 里一段相当硬核的工程实现。首先它伪装成 Chrome 140 访问 Chrome 官方的公开更新地址clients2.google.com/service/update2/crx请求crx3格式的签名包static let chromeVersion 140.0.0.0 static func downloadURL(for id: String) - URL { var parts URLComponents(string: https://clients2.google.com/service/update2/crx)! parts.queryItems [ URLQueryItem(name: response, value: redirect), URLQueryItem(name: prodversion, value: chromeVersion), URLQueryItem(name: acceptformat, value: crx3), URLQueryItem(name: x, value: id\(id)installsourceondemanduc), ] return parts.url! }真正的关键在签名校验。Chrome 扩展的 id 不是随意起的名字——它是扩展公钥 SHA-256 哈希的前 16 个字节以 a–p 十六个字母编码而成。所以 Crx.swift 的verifiedZip会做三重验证头部必须携带能哈希出该 id 的公钥、该公钥对签名数据的 RSA 签名必须成立、zip 内容必须与签名一致。传输途中被篡改的文件、冒用他人 id 的扩展都会在解包前被拒绝。解包后还会用ditto展开并整体拒绝符号链接防止包内链接把注入脚本引到扩展目录之外。用户侧则是一句提示语级别的体验在 ExtensionsUI.swift 的设置页里粘贴链接或 id、点 Add或者直接在扩展的商店页上按 Add to Search还能从 Chrome、Arc、Brave 等浏览器里一键Bring them over把已装扩展搬过来每个都要你亲自确认权限。chrome-extension:// 协议让站点认出扩展装好的扩展页面地址是什么Search 做了一个很有深意的决定沿用 Chrome 的chrome-extension://id/协议而不是 WebKit 自带的webkit-extension://。Extensions.swift 中保留了从旧协议自动迁移的逻辑static let scheme chrome-extension static let formerScheme webkit-extension为什么这么较真注释给出了两个现实理由一是服务器会按 origin 放行自己的扩展比如 Raindrop 拒绝任何其他 origin 的页面二是站点脚本会按chrome-extension://寻找自己的扩展来通信。用 Chrome 的 id、Chrome 的协议、Chrome 的用户代理伪装shim 会让扩展的navigator.userAgent显示为 Chrome是为了让扩展认为自己在 Chrome 里这一前提尽可能成立——这是兼容层工程里最朴素也最有效的一招。shim把 Chrome API 补全成 WebKit 的原生调用引擎是 WebKit 的但 Chrome 扩展生态里很多 API WebKit 并没有。文件头注释里列得清清楚楚Safari 的引擎覆盖 tabs、storage、scripting、request rules、cookies、menus、alarms 和 messaging而 Chrome 扩展还常用 bookmarks、history、downloads、side panel、offscreen documents、tab groups、OAuth——这些 API 在 WebKit 下是 undefined扩展一调用就崩。Search 的解法在 ExtensionShims.swift扩展安装时在它的 worker、每个页面和 content script 前注入一个search-shim.js把chrome.bookmarks、chrome.history、chrome.downloads等全部定义为对本机原生通道的调用由浏览器用自己的书签、历史、下载数据来回答。对扩展来说看起来就是 Chrome。注入发生在商店签名校验通过之后并且只做加法同时用.search-shim戳记文件记录版本避免启动时重复处理。这背后还有一整套权限工程shim 会悄悄给 manifest 追加nativeMessaging必要时还有scripting权限但会把加了什么写进.search-added文件——扩展在权限面板里被描述为它自己申请了什么而不是Search 给了它什么。每次更新索要更多权限时Extensions.swift 的Installed.permissions记录会触发重新询问而不是静默放行。处理 WebKit 与 Chrome 的行为差异一场漫长的排雷兼容层最难的不是有 API而是行为一致。仓库的 CHANGELOG 和源码注释记录了大量这类排雷个个都是真实用户场景1Password 的 WebSocket 死锁。WebKit 在 worker 主线程上同步等待 WebSocket 通道建立worker 等自己永远卡死扩展的每个页面跟着冻结——sign-in 一成功账号从未被添加弹窗不再响应。Search 的 ExtensionSocket.swift 干脆在 worker 里把 WebSocket 换成本机实现用 URLSession 建连接帧数据走 native port 桥接Origin 和 user agent 伪装成 Chrome 会发的样子。Bitwarden 的 offscreen document。Chrome 有offscreenAPI 让扩展在无窗口 DOM 里干活WebKit 没有。ExtensionOffscreen.swift 用WKWebExtensionTab适配器造出一个永不出现在标签栏的标签连麦克风录制、后台节流策略都按 Chrome 语义处理。claude.ai 的externally_connectable。站点可以向 manifest 里点名的扩展发消息比如 claude.ai 给 Claude 传登录态。ExtensionExternal.swift 只给扩展真正列出的站点注入chrome.runtime——而不是给每个网站都贴一个这是 Chrome的标签否则 Google 系的站点会用 reCAPTCHA 把浏览器拒之门外。Loom 等录屏扩展。屏幕录制涉及系统权限与隐私提示ExtensionCapture.swift 负责在扩展首次请求时弹出命名确认并在录制时于屏幕底部显示一个小药丸指示器。原生消息 Native Messaging。Chrome 扩展通过runtime.sendNativeMessage与桌面端 App如密码管理器通信宿主在NativeMessagingHosts文件夹里注册。ExtensionNative.swift 直接读取 Chrome、Chromium、Edge、Brave、Arc、Vivaldi、Opera 的注册文件跑同一个程序、说同一套4 字节长度 JSON 行协议并按扩展 id 校验 allowed_origins。这些细节共同构成了一个判断Search 的Chrome 扩展兼容不是调用几个 API 的 demo而是一个在真实扩展生态里反复打磨的兼容层其工程密度远超一个 3MB 应用的体量预期。轻量本身也是卖点的一部分兼容 Chrome 扩展这件事在 Search 的整体设计里不是孤立的。它之所以能轻是因为整个浏览器建立在 WebKit 之上——没有第二份 Chromium 需要下载、更新、常驻内存。广告拦截是编译一次后塞进 WebKit 网络层的WKContentRuleListShield.swift运行时零开销隐私靠钥匙串和本地 JSON 文件无账号、无同步、无遥测。3MB 的体量 约 10ms 的新标签页创建速度 可选的 Chrome 扩展能力构成了一个此前不存在的组合一个安静到近乎消失的浏览器却能跑满 Chrome 生态里的扩展。对 Safari 生态与独立开发者的潜在影响对 Safari被提前消费的扩展引擎Search 最值得玩味的地方在于它并没有发明任何引擎——它只是把 Safari 一直拥有、却因分发模式而沉寂的扩展能力用 Chrome 生态的玩法重新激活了。Safari 扩展长期被打包成 App、过审、上架的模式锁死而 Search 证明了同一套 WebKit 引擎完全可以支撑 Chrome Web Store 直装体验。这给 Safari 生态提了一个问题如果第三方浏览器都能零成本地消费 Chrome 扩展Safari 自己的扩展门槛还有什么存在理由至少从开发者意愿来看未来先写一份 Manifest V3将变得更加理所当然。对开发者一份代码两个落点对扩展开发者而言Search 的出现意味着为 Chrome 写扩展不再等于只为 Chrome 写。同一份 Manifest V3 代码在 Chrome 跑在 Search 也能跑——不需要学习 Safari 的 App Extension 打包流程不需要处理双套 API。仓库里Tests/Extension*系列测试如 ExtensionPortTests.swift、ExtensionScriptingTests.swift表明这套兼容层是用可重复的自动化测试在维护的而不是一次性 hack。当然边界仍然存在。WebKit 没覆盖的 API 全靠 shim 手工补意味着每个 Chrome 新 API 都是一轮新的排雷某些能力比如单标签页录屏、屏幕标签页声音采集在 WebKit 下依然无解macOS 15.4 的系统门槛也把一部分用户挡在门外。这是兼容层方案的天花板也是它未来持续迭代的空间。结语Search 的 3MB 极简主义和 WebKit 原生扩展的复活其实是同一件事的两面当浏览器不再需要背负一个 Chromium 内核时它才有余力把注意力放回扩展生态这种真正影响日常使用的东西上。WebKit 扩展引擎这门旧技术因为一个愿意啃兼容层硬骨头的 3MB 浏览器正在从 Safari 的角落走向主流视野。对于厌倦了 Chromium 同质化的用户和寻找第二落点的开发者来说这或许是一个值得长期观察的信号——WebKit 不只是Safari 的内核它正在成为 Chrome 生态之外的另一种可能。【免费下载链接】SearchA small, fast WebKit browser for macOS, by Office Commun.项目地址: https://gitcode.com/gh_mirrors/search59/Search创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考