Delphi 集成 Chromium:CEF4Delphi 控件从入门到请求拦截实战
简介CEF4Delphi控件是一套面向Delphi开发者的Chromium谷歌内核浏览器集成方案适合需要在桌面应用中嵌入现代网页渲染能力的开发者覆盖从Delphi 7到Delphi 10.2的多个版本作者已亲自测试调试安装正常。资源包共462个文件约1.47MB以pas源码、dfm窗体、dpr与dproj工程文件、inc头文件及bat批处理脚本为主另含dpk包、res资源、lrs与lpi等Lazarus相关文件并附带详细帮助文档方便查阅接口与配置说明。需注意Chromium 32位与64位框架需另行下载属于运行必需组件。目前已有785人学习关注适合希望以较低成本为Delphi项目引入浏览器内核、研究控件封装结构或排查安装配置问题的开发者参考使用。1. 当 Delphi 遇上 ChromiumCEF4Delphi 控件到底能解决什么如果你手头维护着一套 Delphi 桌面系统又不得不把网页内容嵌进窗体里大概率经历过这种尴尬TWebBrowser 用的是系统自带的旧内核打开现代前端页面要么白屏要么脚本报错CSS 栅格全乱。客户拿着 Chrome 里跑得好好的页面截图来问“为什么你们软件里显示成这样”你只能解释内核版本问题。CEF4Delphi 就是冲着这个场景来的——它把 Chromium 内核封装成一套 Delphi 的 VCL/FMX 控件让你在 Object Pascal 里直接创建浏览器实例、加载页面、执行 JavaScript、拦截网络请求。适合两类人一是需要把 Web 管理后台、在线报表、地图服务嵌进桌面客户端的 Delphi 开发者二是想用 Pascal 做自动化页面操作、抓取渲染后 DOM 的工程师。它不是一个独立浏览器而是一层绑定理解这一点后面的配置和踩坑才有落脚点。2. 拆开 CEF4Delphi 的封装进程模型与核心单元2.1 为什么它必须多进程单进程为什么跑不起来Chromium 的架构决定了它不能像普通控件那样在单个进程里跑完。渲染、GPU 加速、网络请求、插件各自跑在独立子进程里主进程只负责调度和窗口消息。CEF4Delphi 把这套模型原样暴露给 Delphi你的 exe 启动后主进程加载 libcef.dll然后由 CEF 去拉起子进程。子进程的可执行文件默认就是你的 exe 本身靠命令行参数区分角色。这意味着两件事。第一你的程序目录里必须同时存在 libcef.dll、chrome_elf.dll、icudtl.dat、snapshot_blob.bin、v8_context_snapshot.bin 以及 locales 文件夹缺一个都可能启动即崩。第二子进程启动路径必须能被 CEF 找到否则你会看到主窗口出来了页面区域一片空白任务管理器里却多出好几个同名进程在反复重启。常见做法是在项目里放一个uCEFApplication单元在Application.Initialize之后、创建主窗体之前调用全局初始化// 项目主文件中的初始化顺序顺序错了会直接崩 program MyBrowserApp; uses Vcl.Forms, uCEFApplication, // CEF 全局初始化单元 uMainForm in uMainForm.pas {MainForm}; begin // 必须在 Application.Initialize 之后 Application.Initialize; // 全局初始化指定子进程路径、缓存目录、日志级别 // 参数含义见下方说明 GlobalCEFApp.StartMainProcess; Application.CreateForm(TMainForm, MainForm); Application.Run; end.GlobalCEFApp是全局单例StartMainProcess内部会检查命令行参数判断当前是主进程还是子进程。如果是子进程它会走另一条分支直接执行 CEF 的子进程逻辑不会创建窗体。参数上GlobalCEFApp.BrowserSubprocessPath一般不用改默认取当前 exeGlobalCEFApp.Cache建议显式设成一个可写目录比如ExtractFilePath(ParamStr(0)) cache否则默认往系统临时目录写多用户环境下容易权限打架GlobalCEFApp.LogFile在排查启动失败时非常关键设成固定路径后CEF 自己的错误会写进去比 Windows 事件查看器直观得多。2.2 核心单元分工别把所有逻辑塞进一个 FormCEF4Delphi 的单元命名有规律理解分工能少走很多弯路。uCEFApplication管全局生命周期uCEFChromium是浏览器实例的核心负责创建、加载、导航、执行脚本uCEFWindowParent是一个 TWinControl 容器Chromium 的窗口句柄挂到它上面uCEFBrowser和uCEFLoadHandler、uCEFRequestHandler这些是事件接口的实现基类你要拦截请求、修改响应头、处理下载就继承对应的 Handler。一个典型的窗体创建流程是这样的// 在 Form 的 OnCreate 或 OnShow 中创建浏览器 procedure TMainForm.FormCreate(Sender: TObject); begin // Chromium 组件在窗体上WindowParent 作为容器 Chromium1.DefaultURL : about:blank; // 关键必须在主窗口句柄就绪后再创建 if not Chromium1.CreateBrowser(CEFWindowParent1) then Timer1.Enabled : True; // 句柄未就绪延迟重试 end; // 延迟重试的定时器只跑一次 procedure TMainForm.Timer1Timer(Sender: TObject); begin Timer1.Enabled : False; Chromium1.CreateBrowser(CEFWindowParent1); end;CreateBrowser返回 False 是新手最常遇到的“玄学”问题原因通常是父容器句柄还没生成。CEF 需要一个有效的窗口句柄来挂载渲染表面而 Delphi 窗体的句柄在OnCreate阶段可能还没创建完。稳妥做法是放在OnShow里或者像上面这样用定时器兜一次。DefaultURL设成about:blank而不是留空能避免某些版本下创建时就去请求空地址导致的超时。2.3 事件驱动模型LoadHandler 与 RequestHandler 的介入时机CEF 的绝大多数行为都是异步回调你不能在LoadURL之后立刻去读 DOM因为页面还没加载完。uCEFLoadHandler提供了OnLoadStart、OnLoadEnd、OnLoadingStateChange等回调OnLoadEnd触发时 DOM 基本就绪但如果有异步脚本还在跑内容可能仍在变。真正稳妥的“页面稳定”判断常见做法是结合OnLoadingStateChange里isLoading变为 False再配合一个短延时。请求拦截则走uCEFRequestHandler。比如你要给所有请求加自定义头或者拦截某个 API 返回本地 mock 数据// 继承 TCEFRequestHandler重写 GetResourceHandler 或 OnBeforeResourceLoad type TMyRequestHandler class(TCEFRequestHandler) protected function OnBeforeResourceLoad(const browser: ICefBrowser; const frame: ICefFrame; const request: ICefRequest; const callback: ICefCallback): Boolean; override; end; function TMyRequestHandler.OnBeforeResourceLoad(...): Boolean; begin // 给所有请求追加一个标识头服务端据此区分来源 request.SetHeaderByName(X-Client, DelphiApp, True); Result : False; // False 表示不取消请求继续加载 end;SetHeaderByName第三个参数overwrite为 True 时会覆盖同名头。返回 False 是放行返回 True 是取消该请求。注意这个回调在 IO 线程上执行不要在里面直接操作 VCL 控件需要同步回主线程就用TThread.Queue或TCEFApplication提供的调度方法。3. 从零跑通第一个页面目录、参数与调试3.1 二进制包与 Delphi 版本的对应关系CEF4Delphi 本身是 Pascal 源码但它依赖 CEF 的二进制发行包。这两者的版本必须匹配CEF4Delphi 的某个版本对应某个 CEF 分支CEF 分支又对应某个 Chromium 大版本。拿错组合的典型症状是编译能过运行时libcef.dll加载失败或者初始化直接抛异常。选型时先确认你的 Delphi 版本。老版本 Delphi如 2007 以前对 Unicode 和泛型支持有限需要用较旧的 CEF4Delphi 分支Delphi 10.x 和 11/12 Alexandria 系列则用较新的。常见做法是去 CEF4Delphi 的发布页看它标注的“CEF version”和“Delphi compatibility”然后下载对应的 CEF 二进制包。目录结构一般是这样目录/文件作用放错位置的后果libcef.dllChromium 核心动态库找不到则启动即报错chrome_elf.dll早期加载的辅助库缺失导致初始化崩溃icudtl.dat国际化数据缺失时页面文字乱码或空白snapshot_blob.binV8 快照缺失导致 JS 引擎起不来v8_context_snapshot.binV8 上下文快照同上新版本必需locales/多语言资源缺失时界面语言异常resources/部分版本需要影响开发者工具等这些文件必须和你的 exe 在同一目录或者通过GlobalCEFApp.FrameworkDirPath、GlobalCEFApp.ResourcesDirPath显式指定。我一般直接全放 exe 旁边省得路径拼接出错。3.2 最小可运行工程窗体、容器、Chromium 三件套新建一个 VCL 应用放一个TCEFWindowParent和一个TChromium再放一个按钮触发加载。关键属性设置如下// 窗体 OnShow 中初始化此时句柄已就绪 procedure TMainForm.FormShow(Sender: TObject); begin // 指定缓存目录避免写系统临时目录 GlobalCEFApp.Cache : ExtractFilePath(ParamStr(0)) cache; // 开启开发者工具调试页面时用 GlobalCEFApp.EnableDevTools : True; Chromium1.DefaultURL : https://example.com; if not Chromium1.CreateBrowser(CEFWindowParent1) then ShowMessage(浏览器创建失败检查句柄和二进制文件); end; // 按钮点击加载新地址 procedure TMainForm.Button1Click(Sender: TObject); begin Chromium1.LoadURL(https://example.com/page2); end;EnableDevTools打开后可以调用Chromium1.ShowDevTools弹出调试窗口看 Console 报错、Network 请求这一步对排查页面白屏极其有用。Cache目录如果设成只读路径CEF 会静默失败或反复重试表现是页面加载极慢。3.3 执行 JavaScript 与取返回值嵌入页面的核心诉求之一是和页面脚本交互。CEF4Delphi 提供ExecuteJavaScript但取返回值是异步的不能像函数调用那样直接拿结果// 执行脚本并通过回调拿结果 procedure TMainForm.ButtonEvalClick(Sender: TObject); begin Chromium1.ExecuteJavaScript( document.title, // 要执行的表达式 about:blank, // 脚本所属的 frame URL空则主 frame 0 // 起始行号一般填 0 ); end; // 在 Chromium 的 OnExecuteJavaScript 事件里接收结果 procedure TMainForm.Chromium1ExecuteJavaScript(Sender: TObject; const browser: ICefBrowser; const frame: ICefFrame; const script: string; const result: ICefValue); begin // result 是 ICefValue按类型取值 if Assigned(result) then Memo1.Lines.Add(标题 result.GetString); end;ExecuteJavaScript的第二个参数指定在哪个 frame 执行多 iframe 页面里如果填错脚本会在错误的上下文里跑拿到的结果自然不对。result的类型取决于脚本返回值字符串用GetString数字用GetDouble对象和数组需要进一步遍历。注意这个事件也在非 UI 线程触发更新 Memo 要同步。4. 避坑与排查那些让程序起不来的细节4.1 现象程序启动后无窗口进程列表里一堆同名进程原因子进程启动失败主进程在等待子进程就绪时卡死或退出。常见触发点是BrowserSubprocessPath指向了不存在的路径或者 exe 所在目录没有写权限导致子进程无法创建缓存。解决先设GlobalCEFApp.LogFile和GlobalCEFApp.LogSeverity : LOGSEVERITY_INFO跑一次看日志里子进程启动命令和报错。确认 exe 目录可写必要时把缓存目录改到用户目录下。如果用了自定义的BrowserSubprocessPath确保那个路径是绝对路径且文件存在。4.2 现象页面能加载但中文全是方块原因locales文件夹缺失或路径不对CEF 找不到对应的 locale pak 文件回退到默认语言但字体渲染资源不全。解决确认locales目录和libcef.dll同级里面至少有zh-CN.pak和en-US.pak。如果通过GlobalCEFApp.LocalesDirPath指定了其他位置检查路径拼接有没有多一层或少一层。另外GlobalCEFApp.Locale可以显式设成zh-CN。4.3 现象ExecuteJavaScript 拿不到返回值回调不触发原因脚本执行所在的 frame 还没加载完或者OnExecuteJavaScript事件没有正确挂接。另一个常见原因是脚本本身抛了异常CEF 不会把异常当返回值传回。解决把ExecuteJavaScript放到OnLoadEnd之后再调用确保 DOM 就绪。在脚本里用try/catch包一层把错误信息作为字符串返回这样至少能在回调里看到线索。确认Chromium1.OnExecuteJavaScript已经在设计器或代码里赋值。4.4 现象多显示器或缩放比例变化时页面模糊原因CEF 默认按 100% 缩放渲染Windows 高 DPI 下系统做了位图拉伸。解决在项目主文件里调用SetProcessDpiAwareness或设置清单文件声明 DPI 感知然后通过GlobalCEFApp.ForceDeviceScaleFactor指定缩放比或者让 CEF 跟随系统。Delphi 11 以后对高 DPI 支持较好但仍需确认TCEFWindowParent的Align和父容器缩放设置一致。4.5 现象关闭窗体时进程残留原因CEF 的子进程和浏览器实例没有正确销毁主进程退出时子进程变成孤儿。解决在窗体OnClose里先调用Chromium1.CloseBrowser(True)再在OnBeforeClose回调里释放资源最后在项目结束时调用GlobalCEFApp.Free。顺序反了会导致访问已释放对象。如果用了多个 Chromium 实例每个都要走一遍关闭流程。5. 进阶用 RequestHandler 做本地 mock 与资源替换把 CEF4Delphi 用顺之后最有价值的进阶方向是拦截请求、替换响应这在离线演示、接口联调、页面注入场景里非常实用。核心是继承TCEFRequestHandler并重写GetResourceHandler返回一个自定义的ICefResourceHandler由它来提供数据。下面是一个把特定 URL 替换成本地 HTML 的骨架type // 自定义资源处理器返回一段内存中的 HTML TLocalHtmlResourceHandler class(TCefResourceHandlerOwn) protected function ProcessRequest(const request: ICefRequest; const callback: ICefCallback): Boolean; override; procedure GetResponseHeaders(const response: ICefResponse; out responseLength: Int64; out redirectUrl: string); override; function ReadResponse(const dataOut: Pointer; bytesToRead: Integer; out bytesRead: Integer; const callback: ICefCallback): Boolean; override; end; function TLocalHtmlResourceHandler.ProcessRequest(...): Boolean; begin Result : True; // 表示已处理不再走网络 end; procedure TLocalHtmlResourceHandler.GetResponseHeaders(...); begin response.Status : 200; response.MimeType : text/html; responseLength : Length(FHtmlContent); // 内容长度必须准确 end; function TLocalHtmlResourceHandler.ReadResponse(...): Boolean; begin // 把 FHtmlContent 按 bytesToRead 分段拷到 dataOut // 返回 False 表示读取结束 end;然后在TMyRequestHandler.GetResourceHandler里判断 URL命中就返回这个 handler 的实例。GetResponseHeaders里的responseLength必须和实际内容长度一致否则浏览器会一直等待或提前截断。ReadResponse要处理分段读取不能假设一次读完。另一个常用技巧是注入脚本。与其在页面加载后手动ExecuteJavaScript不如在OnLoadStart时通过frame.ExecuteJavaScript注入一段初始化代码这样脚本在页面自身脚本之前执行能提前挂载钩子或修改全局对象。注意注入时机太早可能 DOM 还不存在需要脚本内部自己监听DOMContentLoaded。验证替换是否生效最直接的办法是打开 DevTools 的 Network 面板看对应请求的 Status 和 Size 列。如果显示(from cache)或状态异常检查GetResourceHandler的匹配条件是否写错以及ProcessRequest是否返回了 True。我一般会在 handler 里加一行日志输出命中的 URL跑一次就能确认拦截范围对不对。从那以后我每次集成 CEF4Delphi都会先把日志级别开到 INFO跑通一个空白页再逐步加拦截和脚本注入绝不一次性把所有功能堆上去。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Python字典的__missing__魔术方法:缺失键的优雅处理机制

Python字典的__missing__魔术方法:缺失键的优雅处理机制

就像所有键都找不到时会手足无措的字典一样,Python里的dict其实留了一扇专门处理这种情况的门,这门就是魔术方法__missing__。它是字典变体类的核心钩子:当d[key]访问的键不在字典里时,解释器不会直接抛KeyError,而是先…

2026/10/13 14:00:49 阅读更多 →
SELinux强制访问控制实战:从Permissive调试到Enforcing上线

SELinux强制访问控制实战:从Permissive调试到Enforcing上线

1. 这不是“开个开关”那么简单:SELinux到底在防什么,又为什么总被关掉?“ENGINEER 02:系统安全保护 启用SELinux保护的详细教程步骤,学不会来找我”——这个标题里藏着一个行业内心照不宣的真相:绝大多数人…

2026/10/13 14:00:49 阅读更多 →
Canvas图编辑器核心实现:数据驱动、交互机制与性能优化

Canvas图编辑器核心实现:数据驱动、交互机制与性能优化

做了几年前端和图形方向的东西,我最大的感受是:绘图类的项目,难的不是画一根线、拖一个框,而是把“交互”和“数据”揉在一起还能保持稳定。最近我在做一个内部工具,名字就叫 diagram-design,用途是给业务系…

2026/10/13 14:00:49 阅读更多 →

最新新闻

Paramics动态交通分配(DTA)从配置到收敛的实战指南

Paramics动态交通分配(DTA)从配置到收敛的实战指南

早高峰路网仿真相对于静态分配,永远是两个江湖。用过Paramics的同行应该都见过这种场面:模型搭得很细,信号周期也对得上,可当你把四阶段法分好的OD矩阵往路网上一放,奇怪的事情就来了——有的路段车子堵得水泄不通&…

2026/10/13 14:42:54 阅读更多 →
移除PDF口令与数字证书限制:从识别到实操的完整指南

移除PDF口令与数字证书限制:从识别到实操的完整指南

简介:PDF文档加密常限制正常编辑与访问,这份资源面向需要解除PDF打开密码或权限密码的用户,提供专门的处理工具与配套说明。资源包共13个文件,压缩后仅775KB,包含主程序exe、运行所需的dll组件、汉化说明txt、操作帮助…

2026/10/13 14:42:54 阅读更多 →
JavaWeb实战:黑马商城SSM项目部署、踩坑与优化

JavaWeb实战:黑马商城SSM项目部署、踩坑与优化

简介:这是一份基于前后端分离架构的黑马商城小项目,适合正在学习Java Web开发的初学者,也适合需要完成课程设计或毕业设计的学生。项目完整实现了电商平台核心业务流程,包括商品列表展示、商品详情、购物车增删改、订单提交与支付…

2026/10/13 14:42:54 阅读更多 →
Java并发编程核心知识详解:线程、锁与线程池实战

Java并发编程核心知识详解:线程、锁与线程池实战

1. 线程基础:先搞清楚线程到底是什么线程这块内容,可以说是Java面试里出场率最高的知识点,没有之一。不管是校招还是社招,只要是后端方向,几乎必问线程相关的概念。我这些年面试别人也被别人面试,发现线程八…

2026/10/13 14:42:54 阅读更多 →
知乎动态渲染数据采集:SSR快照与API复现实战指南

知乎动态渲染数据采集:SSR快照与API复现实战指南

1. 这不是“爬知乎”的教程,而是对一个失效接口的逆向解剖现场“puppeteer爬取知乎答案列表”——这个标题在2023年之前几乎等于“稳如老狗”,2024年之后却成了新手踩坑率最高的关键词之一。我最近帮某高校实验室调试一个课程设计项目时,发现…

2026/10/13 14:42:54 阅读更多 →
Windows沙箱运行AI模型崩溃根因:EPT页表竞争与GPU上下文丢失

Windows沙箱运行AI模型崩溃根因:EPT页表竞争与GPU上下文丢失

1. 项目概述:一次Windows沙箱环境崩溃背后的系统级真相“2026-10-08_Codex-Windows沙箱故障复盘报告”这个标题乍看像是一份内部运维日志,但拆开来看,它其实浓缩了一个高频、高痛、却极少被公开深挖的技术现场——Codex类AI辅助开发工具在Win…

2026/10/13 14:41:52 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →