zebkit 0.4.0 发布:把 Chromium 引擎做成可嵌入库
Zebkit – Chromium as an embeddable library
zebkit 0.4.0 发布,把 Chromium 157.0.8103.38 的 Blink、V8、PDFium、FFmpeg 等打包成一个可链接进自有应用的库,不含 Chrome 浏览器和 Electron 运行时,提供 C、Rust、Python、Go、JavaScript 绑定。
译文尚不完整,完整内容请切换到原文。
Chrome 作为库——轻量、快速、随处可用、简单易用。
在 Linux、macOS 和 Windows 上统一一套 API。几行代码即可接入。
简介
zebkit 将 Chromium 的引擎作为一个库,供你链接进自己的应用程序:Blink、包含全部 JIT 层级的 V8、PDFium、FFmpeg 以及 Chrome 的其余组成部分,但不含 Chrome 浏览器的任何部分,也不含 Electron 的运行时。你的应用程序仍然属于你,可用 C、C++、Rust、Python、Go 或 JavaScript 编写,拥有自己的窗口和工具包。需要 Web 时,它添加一个 zebkit 视图;需要 PDF、媒体、压缩或加密时,它调用 zebkit 的 API:用 C,或通过 Rust、Python、Go 和 JavaScript 的绑定(zebkit-js,以及在应用程序自己的页面中的 zebkit.*)。
- 集成它,而不是基于它构建。Electron 是一个基础:应用程序在它内部构建,使用它的运行时、窗口和打包方式。zebkit 是一个部件:应用程序添加它,就像添加 SQLite 一样,并保留自己的进程、工具包和发布方式。它运行在应用程序自己的事件循环中(
zebkit_init()、zebkit_pump()),并绘制在应用程序的窗口中,可以是你绘制的帧,也可以是你提供给它的窗口的子窗口、一个 X11 窗口、一个NSView或一个HWND(examples/native-host),并返回页面所在的窗口供系统对话框使用。 - 按需取用。Web 视图,可在窗口中或离屏使用,帧交给你处理;完整的 PDF API;保存页面播放的内容;自定义 URL 方案;从 JavaScript 使用原生 UI;面向智能体的 Playwright API 和 CDP。用一个,或全部。
- 真正的 Chrome 引擎。页面看到的是 Chromium 157:其用户代理和客户端提示、
window.chrome、未经点击即被拦截的弹窗,且没有附加任何调试器(docs/API.md列出了仍有差异的地方,其中包括 Google 登录)。 - 比 Electron 更轻。与最新的 Electron 45.2.1 相比:体积小 34%,冷启动 2.3 秒对 6.6 秒,页面内存少 29%,以及 DOM、文本布局和 JavaScript 水平(基准测试)。
- 没有 Node。没有第二个 JavaScript 运行时,也没有通过它进行的 IPC。应用程序是原生代码,或是由
zebkit-js在引擎的 V8 上运行的 JavaScript。 - Chromium 的库归你所用。引擎内有 17 个 C 库,在 Linux、macOS 和 Windows 上各有约 7,490 个函数(三者共有 7,487 个,其余为各系统自有),在
zebkit_vendor_*下导出:SQLite、LevelDB、BoringSSL、zlib、Zstandard、Brotli、ICU、HarfBuzz、libxml2、libwebp、libjpeg-turbo、FFmpeg、dav1d、Opus、libvpx、FreeType 和 PDFium。你无需重复打包它们。 - 内置 PDF。基于 Chromium 的 PDFium 的完整 API:渲染与瓦片、带布局的文本、搜索、大纲、链接、如同编辑器所具备的注释(线端、云状边框、带标注和富文本的自由文本、图章、回复与状态、测量、XFDF 和 FDF)、表单、涂黑、页面编辑、合并、附件、元数据,以及校验完整性的签名;为应用程序自有注释类型、外观、带撤销的事件、渲染层、OCR、字体、信任和操作提供钩子。支持 C、Rust、Python 和 JavaScript。
- 2D 绘制与字形处理。基于引擎 Skia 的
<zebkit/canvas.h>和基于其 HarfBuzz 的<zebkit/text.h>,无需再次打包二者。 - 媒体,边播放边保存。页面播放的任何内容——文件、MSE/DASH 或 HLS——都按其发送时的原样写入 MP4 或 WebM,无需重新编码,也无需二次下载(
<zebkit/media.h>)。 - 字节保持原样。页面对应用程序的调用通过其 frame 自己的 Mojo 通道进行,大调用则通过共享内存:无需 base64, 页面的 JSON 按写入时的原样传递,小调用与 Electron 的 IPC 一样快, 大调用则更快。
- 页面周围的窗口,由 JavaScript 驱动。
zebkit.ui使用 Chromium 的 views 工具包 绘制窗口、提示框和对话框:信号、 JSX、Tailwind 类,以及用于测试和代理的自动化。丰富的 UI 是一个 页面;zebkit.ui是它周围的 chrome(docs/UI.md)。 - Playwright 的 API,原生实现。整个 Playwright(定位器、
等到可以执行时才执行的操作、键盘、鼠标和触摸、
路由、对话框、下载、上下文、worker、trace、HAR、视频)都在
引擎内部运行,无需 CDP 或 Node.js 驱动:比通过 CDP 的 Playwright 快 3 到 10 倍,
一次点击只需 3 到 5 毫秒,而不是 33 毫秒
(
<zebkit/automation.h>、docs/AUTOMATION.md)。Playwright 自带的 客户端,无论哪种语言,都能通过chromium.connect()原样连接到它。 - 为代理而打造。
tools/zebkit-mcp将页面、PDF、文件、媒体 和原生窗口作为 MCP 工具提供给它们,通过标准输入和 HTTP,在 其操作者设定的范围内(docs/MCP.md)。它们也可以通过 Playwright 的 API 或通过 CDP(Chromium 自带的 DevTools 服务器)驱动 zebkit,无头或 有窗口均可。 - 从你的代码中定制它,直至桌面。原生钩子决定
引擎的行为,支持 C、Rust、Python 和 JavaScript:权限、
导航、弹窗、对话框、下载、证书和客户端
证书、登录、每个阶段的网络请求和响应、
运行时设置、你自己的 URL 方案、页面可以调用什么、页面自身脚本之前
运行的脚本(
docs/ROADMAP_HOOKS.md)。页面或 引擎会向人展示或向系统请求的一切都经过 应用程序:窗口的框架(系统、覆盖或无,以页面作为 标题栏)、菜单和上下文菜单、JavaScript 对话框和登录、 文件和设备选择器、屏幕捕获、通知、托盘、 Dock 和任务栏、共享、系统主题、电源和显示器 (融入桌面、docs/SYSTEM_INTEGRATION.md)。 - 默认安全。除非应用程序
按视图、按来源和按能力授予,页面没有
zebkit对象; 来源是浏览器的,绝不是页面的说法;渲染器保持 沙箱化;浏览器会询问的权限不会被授予。
| 引擎 | Chromium 157.0.8103.38(upstream.lock):内容层、Blink、带所有 JIT 层级的 V8 |
| Linux | x86-64,glibc 2.17 或更新版本(CentOS 7 及更高版本),X11 和(--ozone-platform=wayland)Wayland:libzebkit.so.1 和 zebkit-helper |
| macOS | Apple 芯片:Zebkit.framework,原生构建或在 Linux 上交叉构建(docs/MACOS.md) |
| Windows | x64,Windows 10 或更新版本:zebkit.dll 和 zebkit-helper.exe,在 Linux 上交叉构建(docs/WINDOWS.md) |
| 版本 | VERSION:0.4.0 |
快速开始
获取引擎
一个包是一个目录,包含库、辅助程序、引擎的数据
和 include/。可以解压某个发行版的归档文件
(来自 GitHub releases 的 zebkit-<version>-<target>.tar.xz),或者构建一个:
mise run r # dist/release (Linux), dist/release-macos (macOS), dist/release-windows; hours the first time每个归档文件还包含工具,为其系统构建,与库放在一起,
解压后即可运行:zebkit-run、zebkit-js 和
zebkit-mcp(Windows 上为 .exe,在那里同样沙箱化)。
在安装了完整 Xcode 的 Apple 芯片 Mac 上,scripts/dist.sh release
将引擎本地构建到 dist/release。然后运行
scripts/example.sh macos-basic 来构建并启动 dist/example/Zebkit Embedded Browser.app 中的 C 嵌入演示。
构建 Chromium 的代码树需要约 30 GB(docs/BUILD.md)。下文中的
$PKG 是包目录。
将其添加到已有的程序中
zebkit 是一个库:你的程序保留自己的 main() 并调用它。这个示例将 PDF 的第一页渲染为 PNG 并读取其文本,PDFium 和图像编码器都在 zebkit 内部,无需安装其他东西:
#include <stdio.h> #include <zebkit/pdf.h> #include <zebkit/services.h> /* Your program, your main(): zebkit is just a library it calls. */ int main(int argc, char **argv) { char *error = NULL; zebkit_pdf *pdf = zebkit_pdf_open_file(argv[1], NULL, &error); if (!pdf) { fprintf(stderr, "%s\n", error); return 1; } /* The first page as a PNG, at twice its size. */ zebkit_pdf_render_options options = {.scale = 2, .background = 0xffffffff}; int width, height; uint8_t *rgba = zebkit_pdf_render(pdf, 0, &options, &width, &height, &error); size_t size; uint8_t *png = zebkit_image_encode(ZEBKIT_IMAGE_PNG, rgba, width, height, 0, &size, &error); fwrite(png, 1, size, fopen("page1.png", "wb")); /* And its text. */ char *text = zebkit_pdf_text(pdf, 0, &error); printf("%d pages; page 1 says: %.80s\n", zebkit_pdf_page_count(pdf), text); zebkit_free(text), zebkit_free(png), zebkit_free(rgba); zebkit_pdf_close(pdf); return 0; }
cc thumb.c -I$PKG/include -L$PKG -l:libzebkit.so.1 -Wl,-rpath,$PKG -o thumb # Linux cc thumb.c -I$PKG/include -F$PKG -framework Zebkit -Wl,-rpath,$PKG -o thumb # macOS ./thumb report.pdf # 2 pages; page 1 says: Quarterly report ...
PDF API、压缩、加密、URL、图像编解码器、ICU 以及引擎内部的库(zebkit_vendor_*,其中包括 FFmpeg 的库)不需要引擎循环(docs/API.md);只有通过引擎网络栈的 HTTP 和网页才需要。
添加 Web 视图
网页需要引擎运行。这里引擎运行循环:程序用 zebkit_main() 启动它,并在其回调中工作。拥有自己循环的程序(GTK、Qt、AppKit、winit)用 zebkit_init() 启动它,并调用 zebkit_pump()(examples/native-host)。
#include <stdio.h> #include <zebkit/zebkit.h> static void printed(zebkit_view *view, const char *json, const char *error, void *data) { printf("%s\n", json ? json : error); zebkit_quit(); } static void loaded(zebkit_view *view, zebkit_load_event event, const char *url, void *data) { if (event == ZEBKIT_LOAD_FINISHED) zebkit_view_evaluate(view, "document.title", printed, NULL); } static void ready(void *data) { zebkit_view *view = zebkit_view_new("https://example.com/", 1024, 768); zebkit_view_on_load(view, loaded, NULL); } int main(int argc, char **argv) { return zebkit_main(argc, argv, ready, NULL); }
cc app.c -I$PKG/include -L$PKG -l:libzebkit.so.1 -Wl,-rpath,$PKG -o app # Linux cc app.c -I$PKG/include -F$PKG -framework Zebkit -Wl,-rpath,$PKG -o app # macOS clang-cl /MT app.c /I%PKG%\include %PKG%\zebkit.dll.lib %PKG%\zebkit_sandbox.lib # Windows ./app --remote-debugging-port=9222 # with CDP for Puppeteer and Playwright
在 Windows 上,可执行文件参与 Chromium 的沙箱:它链接 zebkit_sandbox.lib,并在 main() 中首先调用 zebkit_win_startup()(docs/WINDOWS.md)。
没有非特权用户命名空间时,引擎拒绝启动并说明原因:安装其辅助程序,或在其自己的容器中添加 --no-sandbox(docs/SANDBOX.md)。
页面与应用程序
应用程序通过一个 scheme 提供自己的页面,并赋予它们可调用的函数;除非被授予,页面没有 zebkit 的任何内容。
/* The page calls zebkit.app.greet("Ada"). */ static void greet(zebkit_view *view, const char *origin, const char *args_json, zebkit_call *call, void *data) { char answer[256]; snprintf(answer, sizeof answer, "{\"hello\": %s}", args_json); zebkit_call_resolve(call, answer); } /* app://main/... from ./ui, in ranges for media. A real handler refuses "..". */ static void serve(zebkit_request *request, void *data) { char path[1024]; snprintf(path, sizeof path, "ui%s", zebkit_request_url(request) + sizeof "app://main" - 1); zebkit_request_respond_file(request, path, NULL); } static void closed(zebkit_view *view, void *data) { zebkit_quit(); } static void ready(void *data) { zebkit_view *view = zebkit_view_new("app://main/index.html", 1024, 768); zebkit_view_grant(view, "app://main", ZEBKIT_ALLOW_APP | ZEBKIT_ALLOW_PDF, NULL); zebkit_view_bind(view, "greet", greet, NULL); zebkit_view_on_close(view, closed, NULL); } int main(int argc, char **argv) { zebkit_scheme_register("app", ZEBKIT_SCHEME_STANDARD | ZEBKIT_SCHEME_SECURE | ZEBKIT_SCHEME_FETCH); zebkit_scheme_handle("app", serve, NULL); return zebkit_main(argc, argv, ready, NULL); }
在页面中,每次调用都是一个 promise,字节是 Uint8Array:
const reply = await zebkit.app.greet('Ada'); // the application's function const pdf = await zebkit.pdf.open(await (await fetch('report.pdf')).bytes()); const rgba = await pdf.render(0, {scale: 2}); // a page as pixels const hits = await pdf.search('revenue'); const packed = await zebkit.compress('zstd', bytes, 19); const hash = await zebkit.crypto.digest('SHA-256', 'abc');
融入桌面
引擎要向人展示的每样东西,或向系统请求的每样东西,都有一个可用的默认值,以及一个可替换它的应用程序函数。没有应用程序可以做出的决定,任何东西都不会到达系统;没有函数时,页面得到谨慎浏览器所给予的东西,而在浏览器会询问的地方则什么也得不到。
import zebkit zebkit.scheme_register("app") # served by a scheme_handle(), as in the C example above zebkit.init(["app"]) # The page is the title bar: the window's buttons over it, dragged by its CSS. view = zebkit.View("app://main/index.html", 1000, 700, options={ "frame": "overlay", "controls": {"height": 40, "color": "#1b1d2a", "symbolColor": "#ffffff"}, "dragRegions": {"source": "css", "origins": ["app://main"]}}) # The application's menu bar (macOS) and keys, with state that changes in place. zebkit.app_set_menu([{"title": "File", "items": [{"title": "Autosave", "id": "autosave", "checked": True}]}], lambda item: zebkit.app_update_menu_item(item, checked=False)) # A tray icon, the Dock's or the taskbar's badge, and the page's notifications as the application's. tray = zebkit.Tray(open("icon.png", "rb").read(), "My application") zebkit.app_set_badge("3") zebkit.on_notification(lambda event: print("show in my own UI:", event)) # What a page asks for: a USB device, a screen to capture, a colour, a share. view.on_device_chooser(lambda request: request.respond(request.metadata["devices"][0]["id"])) view.on_media_access(lambda request: request.respond(True, None, "view")) view.on_share(lambda request: request.respond(True)) # The system: its theme and its changes, the session's lock, the screens. zebkit.on_environment_changed(lambda env: print("dark" if env["colorScheme"] == "dark" else "light")) zebkit.on_power_event(lambda event: print(event["event"])) # suspend, resume, lock, unlock print(zebkit.displays())
| 默认 | 应用程序的 | |
|---|---|---|
| 窗口框架 | 系统的 | overlay(Window Controls Overlay)或 none、标题、颜色、拖动区域、状态、操作 |
| 菜单 | macOS 上的菜单栏;默认上下文菜单 | 按 id 带状态的项;上下文菜单被替换、扩展或运行 |
| 对话框 | 系统的警报;有窗口时引擎的提示和登录 | zebkit_view_on_dialog、on_http_auth、on_client_certificate、取色器 |
| 文件与设备 | 系统的文件对话框;无设备 | 文件选择器和 File System Access;WebUSB、WebHID 和 Web Serial 选择器 |
| 捕获与媒体 | 经许可的摄像头和麦克风;无屏幕 | getDisplayMedia() 的来源:视图本身或屏幕 |
| 通知 | 未决定;然后是系统自己的服务 | 在应用程序的 UI 中显示,点击和关闭会回传给页面 |
| 桌面 | 无 | 托盘、Dock 和任务栏的徽章、进度和注意力、Dock 菜单、Web Share、打开 URL 和文件夹 |
| 系统 | 浅色页面 | 实时跟随系统的深色模式和对比度;电源、锁定和显示事件;保持唤醒 |
docs/SYSTEM_INTEGRATION.md 有每个界面、其默认值、在每个系统上的状态及原因;docs/API.md 有四种语言中的调用;examples/custom-titlebar 是一个标题栏即页面的窗口。
Rust
crates.io 上的 zebkit,用 zebkit-build 将引擎放在你的程序旁边;构建时会为你的系统下载一次引擎并校验其 SHA-256。
[dependencies] zebkit = "0.4.0" [build-dependencies] zebkit-build = "0.4.0"
// build.rs: puts the engine beside the executable fn main() { zebkit_build::bundle(); } // main.rs fn main() { std::process::exit(zebkit::run(|| { let view = zebkit::View::new(Some("https://example.com/"), 1024, 768); view.grant("https://example.com", zebkit::Allow::APP, None); view.bind("greet", |call| { let answer = format!("\"hello, {}\"", call.args_json()); call.resolve(&answer) }); view.on_close(zebkit::quit); })); }
ZEBKIT_DIR 将构建指向一个包(bindings/rust/README.md)。
Python
PyPI 上的 zebkit 是绑定;引擎是同一版本 releases 中适用于你系统的归档,用 ZEBKIT_DIR 指向它:
pip install zebkit
export ZEBKIT_DIR=/path/to/zebkit-0.4.0-x86_64-unknown-linux-gnuimport zebkit # pure Python on ctypes: nothing to compile def ready(): view = zebkit.View("https://example.com/") view.grant("https://example.com", zebkit.ALLOW_APP) view.bind("greet", lambda name, origin: f"hello, {name}") zebkit.main(ready) # the engine is found in $ZEBKIT_DIR
Go
bindings/golang,基于 cgo:视图、页面调用 Go 函数、应用程序的 scheme、页面钩子和系统服务,在 Linux 和 macOS 上。使用引擎的头文件和库构建:
go get github.com/ikey4u/zebkit/bindings/golang@main export CGO_CFLAGS="-I$ZEBKIT_DIR/include" CGO_LDFLAGS="-L$ZEBKIT_DIR -Wl,-rpath,$ZEBKIT_DIR"
os.Exit(zebkit.Main(func() { view := zebkit.NewView("", 1024, 768) view.Grant("https://example.com", zebkit.AllowApp, "") view.Bind("greet", func(call *zebkit.Call) { call.Resolve(fmt.Sprintf("\"hello, %s\"", call.Args)) }) view.OnClose(zebkit.Quit) view.Load("https://example.com/") }))
JavaScript,无需 Node
zebkit-js 在引擎的 V8 上运行 ES 模块,适用于用 JavaScript 编写的应用程序,具有视图、相同的 API 和原生窗口:
// zebkit-js main.mjs const view = new zebkit.View('app://main/index.html', {width: 1024, height: 768}); view.grant('app://main', ['app', 'pdf']); view.bind('save', async (name, text) => { await zebkit.fs.writeFile(`${zebkit.args[0]}/${name}`, text); return {saved: true}; }); view.on('close', () => zebkit.quit());
// zebkit-js examples/ui/todo.mjs: a window of native widgets, styled with // Tailwind's classes and kept up to date by signals (an excerpt) const {h, signal, computed, For, window} = zebkit.ui; const [tasks, setTasks] = signal([]); const done = computed(() => tasks().filter((t) => t.done).length); const Task = (task) => h('view', {class: 'flex-row items-center gap-3 p-3 rounded-xl bg-[#141a2e]'}, h('button', {class: 'w-8 h-8 p-0 rounded-full bg-[#7c5cff] text-white', onClick: () => toggle(task)}, task.done ? '✓' : ''), h('text', {class: 'grow text-[#e6e8f2]'}, task.text)); window({title: 'Today', width: 460, height: 680}, () => h('view', {class: 'flex-col gap-4 p-6 bg-[#0b1020]'}, h('text', {class: 'text-3xl font-bold text-white'}, 'Ship something great'), h('text', {class: 'text-sm text-[#8a91aa]'}, () => `${done()} of ${tasks().length} done`), h(For, {each: tasks}, Task)));
代理
Playwright 的 API 运行在引擎内部(docs/AUTOMATION.md):Playwright 协议的每条命令都在应用进程内执行,不经过 CDP,也不依赖 Node.js。从 C 语言调用只需一次调用,zebkit_automation_call(view, "Frame.click", "{\"selector\": \"#go\"}", done, data);Python 中则以 Playwright 的名称提供:
import zebkit from zebkit.automation import Page, expect, run async def test(): page = Page() await page.goto("https://example.com/") await expect(page).to_have_title("Example Domain") await expect(page.get_by_text("documentation examples")).to_be_visible() await page.screenshot(path="example.png") zebkit.quit() zebkit.main(lambda: run(test()), ["python", "--headless"])
Playwright 自带的客户端(包括其测试运行器和其他语言绑定)无需改动即可连接到引擎的 Playwright 端点:
tools/zebkit-run --headless --playwright-port=0 # prints "Playwright listening on ws://127.0.0.1:<port>/<token>"const browser = await chromium.connect('ws://127.0.0.1:<port>/<token>'); const page = await (await browser.newContext()).newPage();
使用 CDP 的智能体和工具可以像驱动 Chrome 一样驱动 zebkit:
tools/zebkit-run --headless --remote-debugging-port=9222 https://example.com/
任何 CDP 客户端都可以连接到 http://127.0.0.1:9222。
An MCP client gets the engine's capabilities as tools from tools/zebkit-mcp, a
separate program, with file access and origins denied until its operator grants
them (docs/MCP.md):
ZEBKIT_DIR=<package> cargo build --release --manifest-path tools/zebkit-mcp/Cargo.toml # or the archive's zebkit-mcp --allow-origin https://example.com --allow-write ~/agent-files # on standard input and output zebkit-mcp --http 8931 --allow-origin '*' # on 127.0.0.1, with a token
示例
mise example:linux:basic # examples/window.c: a browser window mise example:rust:window # the same in Rust mise example:ffmpeg # examples/ffmpeg.cpp: a library inside the engine (cd examples/gpui-browser && cargo run --release) # a tabbed browser in GPUI-KIT
基准测试
与最新的稳定版 Electron 45.2.1(Chromium 156)对比,采用相同方式测量:同一测试框架、相同页面、两个执行相同操作的轻量宿主、统计每个进程、5 到 11 次轮流运行的中位数,Linux x86-64,2026-11-06 和 07(scripts/electron-compare.sh,docs/reports/electron-compare.md)。
| zebkit 0.2.0 | Electron 45.2.1 | ||
|---|---|---|---|
按应用发布时的状态,未压缩 / .tar.xz |
171.2 / 59.4 MiB | 260.5 / 87.1 MiB | 小 34% |
| 冷启动至首次绘制 | 2.3 秒 | 6.6 秒 | 少 66% |
| 热启动至首次绘制 | 116 毫秒 | 236 毫秒 | 少 51% |
| 内存,空白页面 | 179 MB | 250 MB | 少 29% |
| 从页面到宿主再返回的一次调用,1 MiB / 16 MiB | 3.3 / 58 毫秒 | 4.3 / 66 毫秒 | 少 23% / 12% |
| DOM,创建并布局 20,000 个元素 | 280 毫秒 | 278 毫秒 | 持平 |
| JavaScript 和 DOM,十项工作负载 | 1139 毫秒 | 1193 毫秒 | 持平 |
| 文本布局,一本书的一章 | 8.1 毫秒 | 7.6 毫秒 | 持平,在噪声范围内 |
| 各自的查看器中打开一个 756 页的 PDF:CPU / 内存 | 0.75 秒 / 261 MB | 1.6 秒 / 399 MB | 少 53% / 35% |
| 十个热门网站:加载 CPU / 内存 | 21.7 秒 / 4756 MB | 25.2 秒 / 5060 MB | 少 14% / 6% |
| 进程数,一个页面 | 7 | 6 | 多一个:存储沙箱独立,与 Chrome 相同 |
- 体积。 zebkit 保留了应用所使用的整个 Web 平台:HTML、CSS、包含所有 JIT 层级的 JavaScript、WebAssembly、WebGL、支持 H.264 和 AAC 的媒体、HTTP/3、PDF。
- 速度。 Chrome 自身的配置文件引导优化能够作用于 Blink,因为 zebkit 与 Chrome 一样使用 CFI 构建;每项速度改动只有在收益大于体积代价时才会保留(
docs/SIZE_AND_SPEED.md)。 - Electron 领先之处:每个页面少一个进程,以及在十个网站中的两个——YouTube 和 The Verge——上的内存表现,这两个网站由广告和视频决定。
- 每个数字的计算方式:
docs/SIZE_AND_SPEED.md。
同样的 Playwright 命令三种方式,无头模式,中位数(docs/reports/automation-bench.md,scripts/automation-bench.sh):
| zebkit 的 API,进程内 | 你的 Playwright 脚本,连接到 zebkit | Playwright 配合 Chrome 156 | |
|---|---|---|---|
locator.click() |
2.0 毫秒 | 3.5 毫秒 | 33.3 毫秒 |
locator.fill() |
2.1 毫秒 | 2.0 毫秒 | 2.9 毫秒 |
locator.press('Enter') |
0.59 毫秒 | 0.98 毫秒 | 2.6 毫秒 |
locator.textContent() |
0.23 毫秒 | 0.70 毫秒 | 0.81 毫秒 |
allTextContents() 共 200 个 |
0.34 毫秒 | 1.9 毫秒 | 1.9 毫秒 |
page.goto() |
11.2 毫秒 | 13.0 毫秒 | 18.2 毫秒 |
page.screenshot() |
12.4 毫秒 | 16.7 毫秒 | 33.8 毫秒 |
page.screenshot({type: 'jpeg'}) |
13.9 毫秒 | 14.4 毫秒 | 32.9 毫秒 |
| 截图的像素,未编码 | 9.3 毫秒 |
- zebkit 的 API,进程内:你的程序通过
<zebkit/automation.h>、Python 的zebkit.automation或 Rust 的zebkit::automation调用命令;它在引擎内部运行,不依赖 Node.js,也不经过协议。 - 你的 Playwright 脚本,连接到 zebkit:Playwright 自带的 Node.js 客户端,无需改动,使用
chromium.connect()连接到 zebkit 的端点;其测试套件(v1.64.1)在此通过了 4,046 项测试,Chrome 为 4,196 项(docs/AUTOMATION.md,第 7 节)。 - Playwright 配合 Chrome:照常使用 Playwright,通过 CDP。
- 为什么更快:在渲染器里一次调用,而 Playwright 的服务器要通过 CDP 进行多次往返,进程之间没有 JSON,也没有 Node.js 进程(仅 Playwright 自身就达到 175 MiB);并且点击检查所等待的帧是一次性生成的,而不是在没有显示的页面上以 60 Hz 生成。
- 截图快 2.7 倍:它获取的新帧是一次性生成的,而不是在下一个 60 Hz 节拍时生成;PNG 使用 Up 过滤器以级别 1 压缩(耗时 2 ms,而 Chrome 的默认级别需要 13 ms,在此页面上为 59 KB 而非 40 KB);图片的共享内存是一次调用创建的,而不是一次一个缺页。12 ms 中剩下的部分是页面的帧(4 ms)、从合成器复制图片(4 ms)和编码(4 ms)。像素本身,不编码(
zebkit_automation_screenshot_pixels(),zebkit 自己的),需要 9 ms。通过 CDP,zebkit 做 Chrome 所做的事:60 Hz 的帧,Chrome 的 PNG。
参考
文档
| 文档 | 涵盖内容 |
|---|---|
| docs/API.md | 应用程序可以调用的所有内容,支持 C、Rust、Python 和 JavaScript |
| docs/BUILD.md | 获取 Chromium、构建、打包、测试、测量和发布;配置文件和补丁 |
| docs/ARCHITECTURE.md | 引擎是如何构建的,以及此仓库中什么是什么 |
| docs/AUTOMATION.md | 不使用 CDP 的 Playwright API:Playwright 如何工作,zebkit 做了什么替代,端点,成本 |
| docs/MCP.md | tools/zebkit-mcp:作为代理的 MCP 工具的引擎,其边界,薄弱之处 |
| docs/SIZE_AND_SPEED.md | 体积去向,削减和保留了什么,编译器与速度的对比,Electron 基线 |
| docs/MACOS.md | macOS 框架,原生和交叉构建 |
| docs/WINDOWS.md | Windows DLL,其沙箱,在 Linux 上交叉构建,移植状态 |
| docs/SANDBOX.md | 每个系统上的沙箱,以及无沙箱运行 |
| docs/LICENSING.md | zebkit 的许可证,引擎内的第三方许可证,以及发布它涉及的内容 |
| docs/ROADMAP.md | 库和引擎:需求,已完成,下一步 |
| docs/ROADMAP_HOOKS.md | 钩子:应用程序如何从原生代码决定引擎的行为 |
| docs/UI.md | zebkit.ui:引擎自己的小型 UI,它的用途和局限 |
| bindings/rust/README.md | Rust crate |
| bindings/golang/README.md | Go 绑定:涵盖内容,使用 cgo 构建 |
docs/releases/ |
每个版本:包含什么,测试了什么,缺少什么 |
docs/reports/ |
体积报告,由脚本重新生成 |
仓库
| 路径 | 内容 |
|---|---|
upstream.lock |
构建的 Chromium 版本,固定的 depot_tools,对比测量的 Electron 版本 |
profiles/ |
作为 GN 参数的构建变体:slim.gn(产品),slim-macos.gn、slim-windows.gn、baseline.gn(Chromium 的默认值) |
patches/ |
对 Chromium 的更改,按 patches/series 的顺序应用 |
src/engine/ |
zebkit 自己的代码,作为 //zebkit/engine 链接到 Chromium:库、其 C API 和头文件 |
src/ffmpeg/ |
zebkit 的 FFmpeg 配置,来自 scripts/ffmpeg-config.sh |
bindings/rust/、bindings/python/、bindings/golang/ |
Rust crate、Python 绑定和 Go 绑定 |
tools/ |
zebkit-run(打开页面,提供 CDP 或 Playwright 的端点),zebkit-js(JavaScript 应用程序)和 zebkit-mcp(作为 MCP 工具的引擎) |
tests/ |
针对包测试 API 每一层的程序;速度工作负载 |
examples/ |
C 中的浏览器窗口,zebkit.ui,引擎内的库,Electron 基线 |
scripts/ |
每一步,一个脚本;mise tasks 列出简写形式 |
upstream/(Chromium 的树,约 30 GB),out/、dist/ 和 .cache/ 是生成的,不被跟踪。
zebkit 是 Chromium 加上一小组补丁(patches/),从源码构建。当 Chromium 缺少应用所需的某项功能时,zebkit 会直接给 Chromium 打补丁,而不是绕过它(docs/BUILD.md)。
许可证
zebkit 是自由软件,采用 GNU 宽通用公共许可证第 2.1 版或(由你选择)任何更新版本:LICENSE。任何许可证(开源或闭源)的应用都可以将其作为库使用。
该引擎包含 Chromium 及其使用的库(Blink、V8、Skia、PDFium、采用 LGPL 配置的 FFmpeg、ICU、SQLite、BoringSSL 等),每个库都遵循各自的许可证。每个发布归档都附带 LICENSE、一份说明其源码位置的 NOTICE,以及包含所有第三方许可证文本的 THIRD_PARTY_NOTICES.txt。docs/LICENSING.md 说明了分发 zebkit 的应用需要做什么。
贡献按同一许可证接受:发送拉取请求即表示你同意你的贡献按 LGPL 2.1 或更新版本授权,与 zebkit 其余部分一致。
来源:Hacker News · github.com
