Lynx 引擎共享 API 头文件(core/public)详解:跨 Shell、Runtime 与平台的公共契约层
【免费下载链接】lynxEmpower the Web community and invite more to build across platforms.项目地址: https://gitcode.com/GitHub_Trending/lynx10/lynx
core/public是 Lynx 跨平台渲染引擎(Lynx/TASM)中位于核心引擎与外部世界之间的“契约层”:它是一组 header-only 的 C++ 接口头文件,定义了 shell、renderer、runtime、资源加载、列表处理、devtool 集成和 native module 绑定等各模块之间共享的稳定公共接口。本文基于仓库中的 core/public/AGENTS.md 及其全部头文件源码,梳理这组接口的模块划分、具体契约内容、维护规则,以及如何按“归属模块”定位回归问题,帮助你在阅读或扩展 Lynx 内核时准确把握这条公共 API 边界。
一、目录定位:稳定的核心对外接口面
AGENTS.md 对该目录的职责描述非常明确:
This directory contains stable core-facing interface headers shared across shell, renderer, runtime, resource loading, list handling, devtool integration, and native module bindings.
也就是说,凡是 shell(引擎宿主壳)、renderer(渲染端)、runtime(JS 运行时)、资源加载、列表组件、devtool 以及 native module 需要与核心引擎交互的地方,其接口都收敛在这里。从构建配置看,core/public/BUILD.gn 用lynx_core_source_set("public")注册了这个目标,sources列表全部是.h头文件(如lynx_engine_proxy.h、lynx_resource_loader.h、jsb/lynx_native_module.h、devtool/lynx_devtool_proxy.h等),没有编译实现单元——这印证了文档中“Keep this directory header-only and dependency-light”的编辑规则。
从源码结构看,AGENTS.md的 Module Map 与实际目录内容一一对应:
| 文档中的分组 | 对应头文件(均位于 core/public 下) | 职责 |
|---|---|---|
| shell/runtime/layout 代理 | lynx_engine_proxy.h、lynx_layout_proxy.h、lynx_runtime_proxy.h | 引擎/布局/运行时三线程的任务分发与跨线程调用 |
| 列表契约 | list_container_proxy.h、list_engine_proxy.h、list_data.h | 列表滚动控制与懒加载 diff 数据 |
| 资源加载 | lynx_resource_loader.h | 模板、图片、JS 等资源请求/响应的公共契约 |
| 生命周期与性能 | perf_controller_proxy.h、runtime_lifecycle_observer.h、vsync_observer_interface.h | 计时事件上报、运行时生命周期回调、VSync 观察 |
jsb/ | lynx_native_module.h、lynx_extension_module.h、native_module_factory.h、extension_module_factory.h、lynx_module_callback.h | native module 与扩展模块的 JS 绑定接口 |
devtool/ | lynx_devtool_proxy.h、lynx_inspector_owner.h | devtool 代理与 inspector 归属管理 |
此外目录中还有大量辅助契约头,如pub_value.h(跨层传递的pub::Value值类型)、page_options.h、pipeline_option.h、ui_delegate.h、gesture_handler.h等,它们共同构成lynx_core_source_set("public")的完整头文件清单。
二、三大 Proxy 接口:shell 与引擎/运行时/布局之间的桥梁
Proxy 类是 Lynx 引擎解耦宿主实现与核心逻辑的关键抽象:shell 侧持有 proxy,具体实现由各平台/宿主提供。
2.1 LynxEngineProxy:引擎线程的总入口
LynxEngineProxy 是职责最广泛的接口,命名空间为lynx::shell,核心方法可以按功能域分为四类:
- 任务分发:
DispatchTaskToLynxEngine(base::closure task)是所有跨线程进入引擎线程任务的统一入口; - 事件转发:
SendTouchEvent(带tag、坐标与时间戳,还有基于pub::Value参数的重载)、SendCustomEvent、SendGestureEvent、SendBubbleEvent,以及事件管线控制StartEventGenerate/StartEventCapture/StartEventBubble/StartEventFire、伪类状态OnPseudoStatusChanged; - 列表驱动:
ScrollByListContainer、ScrollToPosition、ScrollStopped,以及复用池操作ObtainListChild/RecycleListChild/RenderListChild/UpdateListChild/GetListData; - 布局与渲染钩子:
TriggerLayout、MarkLayoutDirty、GetDensity、EnableRasterAnimation、OnFirstMeaningfulPaint。
值得注意的两个设计细节:
CustomEventDispatchOptions::emergency:文档注释说明紧急任务会在下一个消息循环边界优先于普通 ready 任务被选中,但不会抢占正在执行的任务。这是事件派发优先级的公共契约;SendCustomEventWithOptions提供了非纯虚的默认实现,直接回退到SendCustomEvent——这是一个典型的“additive change over breaking change”示例:新增带选项的接口时不破坏既有实现者;GetAncestorElements的注释明确要求调用方在“使用 async tasm 时不要调用此函数”,说明接口的线程/架构假设直接写进了公共契约。
2.2 LynxRuntimeProxy:宿主侧对 JS 运行时的调用
LynxRuntimeProxy 定义了 shell 向 runtime 线程发起 JS 调用的五个纯虚方法:
CallJSFunction(module_id, method_id, params):按 module/method 定位并调用 JS 函数;CallJSApiCallbackWithValue(callback_id, params):回调 JS 侧注册的 API 回调;CallJSIntersectionObserver(observer_id, callback_id, params):Intersection Observer 的专用通道;EvaluateScript(url, script, callback_id):动态脚本求值;RejectDynamicComponentLoad(url, callback_id, err_code, err_msg):动态组件加载失败时的 reject 通道。
参数统一采用std::unique_ptr<pub::Value>转移所有权,体现了“值跨线程、所有权唯一”的契约风格。
2.3 LynxLayoutProxy:布局线程的最小接口
LynxLayoutProxy 只有两个方法:DispatchTaskToLynxLayout(base::closure task)与TriggerLayout()。它展示了这组头文件“dependency-light”的原则——布局线程的对外面被刻意压缩到最小。
三、列表契约:滚动控制与懒加载数据
3.1 两级代理结构
列表相关的滚动控制被拆成了两级接口:
- ListEngineProxy:纯虚接口,声明
ScrollByListContainer/ScrollToPosition/ScrollStopped三个滚动方法; - ListContainerProxy:持有
ListEngineProxy*的具体类(标注LYNX_EXPORT),实现同名方法并转发给注入的 engine proxy,构造函数explicit ListContainerProxy(ListEngineProxy* list_engine_proxy)体现了依赖注入关系。
值得注意的是,LynxEngineProxy 中也保留了ScrollByListContainer等列表方法,源码中留有注释 “TODO(chenyouhui): Split the list interface into its own public API.”——从源码结构看,列表接口正处在“从引擎总代理中拆分独立出来”的演进过程中,ListContainerProxy/ListEngineProxy就是拆分后的目标形态。
3.2 ListData:列表懒加载的 diff 契约
ListData(命名空间lynx::tasm)承载<list>懒加载所需的全部 diff 信息,注释指出该信息“由 FE 框架生成”,并在 Fiber 架构下已被标记 deprecated。其字段与语义完整如下:
view_type_names_:视图类型名列表(SetViewTypeNames);new_arch_/diffable_:架构与 diff 能力开关(SetNewArch/SetDiffable);full_span_:跨列(全宽)项索引,setter 会内部std::sort保持有序;sticky_top_/sticky_bottom_:吸顶/吸底项索引,同样有序化;- 六组 diff 操作:
SetInsertions/SetRemovals/SetUpdateFrom/SetUpdateTo/SetMoveFrom/SetMoveTo,均为模板接口,接受任意 vector-like 容器; - 对应的
Get*方法返回const std::vector<int32_t>&(view type 返回const std::vector<std::string>&)。
该类的 getter/setter 全部内联在头文件内,符合“目录不移动实现逻辑”的规则——它只是数据载体契约,真正的 diff 应用在 renderer/list 模块完成。
四、资源加载契约:LynxResourceLoader
lynx_resource_loader.h 是资源加载的公共契约核心,命名空间lynx::pub,包含请求/响应/流式委托四部分:
4.1 资源类型与请求
LynxResourceType枚举定义了 15 种资源类型(源码注释标明 “generated by IDL”):
enum class LynxResourceType : int32_t { kGeneric = 0, kImage = 1, kFont = 2, kLottie = 3, kVideo = 4, kSvg = 5, kTemplate = 6, kLazyBundle = 7, // LazyBundle from js kLynxCoreJs = 8, kExternalJs = 9, kAssets = 11, kI18nText = 12, kGraphics = 13, kTheme = 14, kFrame = 15, kExternalByteCode = 16, // external byteCode loaded from outside; };注意枚举值从 9 跳到 11——中间的值已被占用或移除,这类“保留空洞”正是“additive change、避免破坏 ABI”原则的直接体现。
LynxResourceRequest结构简单:url、type,以及request_in_current_thread(默认true,注释标明当前用于 lazy bundle,长期会被移除)。
4.2 响应与计时
LynxResourceResponse携带std::vector<uint8_t> data、void* bundle(用于平台可返回的 template bundle 指针,源码留有 “make LynxTemplateBundle a public class” 的 TODO)、resource_handle、err_code/err_msg和ResourceLoadTiming。Success()以err_code == 0判定成功。
ResourceLoadTiming提供了一套微秒级加载计时字段,可用于平台侧精细化性能归因:
| 字段 | 含义 |
|---|---|
request_start | 收到客户端请求 |
request_internal_prepare_finish | 内部准备完成(如 URL 检查、fallback 逻辑) |
request_prepare_to_call_fetcher | 开始准备调用 fetcher |
request_send_to_fetcher | 实际把请求发给 fetcher |
response_received_from_fetcher | 实际收到 fetcher 响应 |
response_trigger_callback | 触发客户端回调 |
图片类请求还有LynxImageResponseOptions:fallback_paths(回退路径)、file_cache_name、use_highest_priority、mapped_file_cache_key/mapped_memory_cache_key(缓存键映射)。
4.3 加载入口与流式委托
LynxResourceLoader继承std::enable_shared_from_this,导出LYNX_EXPORT,对外提供:
LoadResource(request, callback)与LoadResourcePath(request, path_callback):非虚的公共入口,内部转发给纯虚的LoadResourceInternal/LoadResourcePathInternal,并叠加 replay 缓存逻辑(SetReplayResourceCache注入tasm::replay::ReplayResourceCache);LoadStream(request, stream_delegate):流式加载,配合LynxStreamDelegate的OnStart(size)/OnData(data)/OnEnd()/OnError(msg)四个回调;LoadBytecode(request, callback):默认实现直接回包err_code = -1、"LoadBytecode is not supported.",需要时由平台覆写;IsLocalResource(url)(默认false)、ShouldRedirectUrl(默认原样返回 URL)、ShouldRedirectUrlAsync(默认报错-1,提示“not supported”)——这些带默认实现的虚函数同样是“新增能力不破坏旧实现”的模式。
五、生命周期与性能:RuntimeLifecycleObserver 与 VSync、Perf 代理
5.1 RuntimeLifecycleObserver
RuntimeLifecycleObserver(命名空间lynx::runtime,标注“Triggered on runtime thread”)定义 runtime 生命周期回调:
OnRuntimeCreate(std::shared_ptr<IVSyncObserver> observer):注意它把 VSync observer 作为参数回传给监听者,说明 VSync 通道是在 runtime 创建时注入的;OnRuntimeInit(int64_t runtime_id);OnAppEnterForeground()/OnAppEnterBackground():前后台切换;OnRuntimeAttach(void* env, const char* runtime_type)/OnRuntimeDetach():运行时附着/脱离。
5.2 IVSyncObserver
IVSyncObserver 是“exported from lynx.so”的 C++ VSync 观察接口,三个方法分别对应帧时序的三个插入点:
RequestAnimationFrame(id, callback):帧动画回调;RequestBeforeAnimationFrame(id, callback):帧前回调;RegisterAfterAnimationFrameListener(callback):帧后监听。
回调签名统一为base::MoveOnlyClosure<void, int64_t, int64_t>(移动语义闭包)。同目录的vsync_monitor_platform_impl.h则是对应的平台实现侧接口,二者构成“接口 + 平台实现”的分层。
5.3 PerfControllerProxy
PerfControllerProxy 是性能数据上行的公共契约,核心方法:
SetHostPlatformType(const std::string& type):设置宿主平台类型(文档注释举例'windowsClay'),影响PipelineEntry中HostPlatformTiming的平台类型标签;MarkTiming(tasm::TimingKey, const tasm::PipelineID&):以 key + 管线 ID 打点;SetTiming(timing_key, timestamp_us, pipeline_id):直接写入微秒时间戳;SetHostPlatformTiming(...):平台侧计时事件写入;GetPlatform():返回当前运行平台(文档注释举例'windows' 'macOS' 'iOS' 'Android' 'Linux');RunTaskInReportThread(base::closure task):向 report 线程投递任务;OnEvent(tasm::report::MoveOnlyEvent&& event):事件上报入口,移动语义传递MoveOnlyEvent。
这里的TimingKey与PipelineID类型来自 core/public/timing_key.h 和 core/public/pipeline_option.h,性能计时的键体系本身就是公共契约的一部分。
六、jsb/:native module 与扩展模块的绑定接口
core/public/jsb/下的五个头文件定义 JS 绑定层(JSB)的模块接口。以最核心的 lynx_native_module.h 为例:
LynxNativeModule标注LYNX_EXPORT_FOR_DEVTOOL,注释说明 “Upper-level modules can inherit from LynxNativeModule to register their own JSB”,即它是所有 native module 的公共基类;- 内部
Delegate类提供InvokeCallback(callback, invoke_pre_func)、RunOnJSThread(func)、RunOnPlatformThread(func)三个纯虚方法,把“回调 JS”“切 JS 线程”“切平台线程”三件跨层高频动作抽象出来,供 module 开发者统一使用; NativeModuleMethod { name, args_count }与NativeModuleMethods(std::unordered_map)用于方法名到参数个数的注册表;- 文件头部还有一段 Objective-C++ 兼容处理:用
#pragma push_macro保护LynxNativeModule这个与 Objective-C 库同名的宏冲突,并在文件尾恢复——这是多平台头文件维护中真实存在的兼容细节。
同目录的lynx_extension_module.h+extension_module_factory.h与lynx_native_module.h+native_module_factory.h构成 native/extension 两族模块的平行结构,lynx_module_callback.h则承载回调句柄。此外 lynx_runtime_proxy.h 被lynx_native_module.h直接包含,说明 native module 到 runtime 的调用通道也走这组公共契约。
七、维护规则:如何安全地修改这组头文件
AGENTS.md 的 Edit Rules 给出了三条维护准则,它们都有源码层面的对应佐证:
- 视同共享 API 面:签名、所有权或类型变更通常需要 shell、runtime、资源加载与平台实现协同更新。证据是
LynxEngineProxy被jsb/lynx_native_module.h包含、ListContainerProxy依赖ListEngineProxy、RuntimeLifecycleObserver依赖IVSyncObserver——头文件之间已形成一张依赖网,改动任何一处都可能波及多模块; - 保持 header-only 与轻依赖:
BUILD.gn的 source set 只有头文件,头文件仅依赖base/include/closure.h、core/base/lynx_export.h等少量基础头; - 优先增量式变更:如前文所述的
SendCustomEventWithOptions默认实现、LoadBytecode/ShouldRedirectUrlAsync的“默认不支持”实现、LynxResourceType的保留空洞,都是“新能力以默认实现/新增枚举值落地,不破坏既有派生类”的模式。
八、回归症状与验证路径:契约变了去哪找问题
当一次“看起来很小”的头文件编辑导致多模块构建失败、或“编译通过但运行时坏掉”,AGENTS.md 归纳了三个典型症状,本质都是公共契约失配:
- 多模块构建失败 → 共享契约(签名/类型)变了;
- proxy 调用编译通过但运行时崩溃 → 所有权(如
std::unique_ptr<pub::Value>)、回调生命周期(如LynxStreamDelegate、base::closure捕获的裸对象)或线程假设(如OnEvent的线程约定、GetAncestorElements的 async tasm 限制)变了,但实现侧未同步; - native module / resource loader / list 集成回归 → 契约失配通常源头在此目录。
由于本目录不定义自己的单测 exec,文档要求“通过归属实现模块验证”,对应到仓库中的构建目标:
| 验证目标 | 覆盖的契约 | 定义位置 |
|---|---|---|
shell_unittests_exec | proxy 与生命周期契约 | core/shell/testing/BUILD.gn |
list_container_testset_exec | 列表面向的契约 | core/list/BUILD.gn |
lazy_bundle_test_exec或 runtime 资源测试 | 资源加载契约 | core/resource/BUILD.gn、core/runtime/BUILD.gn |
runtime_tests_exec或 module-binding 测试 | jsb/契约 | core/runtime/BUILD.gn |
另外 core/renderer/ui_component/list/BUILD.gn 也引用了list_container_testset_exec,说明列表契约的验证同时覆盖 renderer 侧的 list 组件。
九、小结
core/public虽然只是一个“全是头文件”的目录,但它承载了 Lynx 引擎最关键的一条边界:shell 与引擎/运行时/布局三线程之间、宿主平台与 JS 运行时之间、资源系统与模板加载之间、列表容器与 diff 引擎之间,以及 JS 与 native module 之间的全部公共契约。理解这个目录的价值在于:
- 读到
*proxy*.h时,应理解为“shell 侧持有、宿主实现”的注入点; - 读到
LynxResourceLoader的默认实现时,应理解为平台可覆写的扩展缝; - 修改这里任何签名前,先确认“additive over breaking”是否可能,并预先规划 shell、runtime、resource、list 四路实现与对应验证 target 的协同更新。
掌握这一契约层后,你可以在仓库中沿 core/shell、core/runtime、core/renderer、core/list 等实现目录继续深入,追踪每一条公共接口在真实引擎中的落点。
【免费下载链接】lynxEmpower the Web community and invite more to build across platforms.项目地址: https://gitcode.com/GitHub_Trending/lynx10/lynx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考