引言

在现代浏览器的开发中,前端页面和 C++ 内核之间的通信是一项核心功能。无论是本地设置页(chrome:// 内置 H5)还是在线活动页,前端都可能需要调用浏览器底层 API,实现诸如“设置默认浏览器”、“更改壁纸”、“读取用户配置”等操作。

本文将以 Chromium 内核及其派生浏览器为例,详细解析 SetDefaultView 的创建、OnAppCmd 消息转发机制以及 delegate 承载业务逻辑的设计思路,并讨论这种机制在本地页和在线页的通用性。


一、SetDefaultView 的创建与初始化

浏览器弹窗(如“设置默认浏览器”对话框)通常是一个独立的 View 对象,承载前端页面并接收消息。核心创建入口是 SetDefaultView::CreateSetDefaultView

SetDefaultView* SetDefaultView::CreateSetDefaultView( gfx::NativeWindow parent, const GURL& url, Delegate* delegate, Browser* browser) { Browser* browser_active = BrowserList::GetInstance()->GetLastActive(); if (!browser_active) browser_active = browser; gfx::NativeWindow parent_active = parent; SeBrowserView* browser_view_active = SeBrowserView::GetBrowserViewForBrowser(browser_active); if (browser_view_active) parent_active = browser_view_active->frame()->GetNativeWindow(); SetDefaultView* set_view = new SetDefaultView(url, browser_active, delegate, true); if (nullptr == set_view) return nullptr; set_view->set_parent_window(parent_active); set_view->SetBrowserView(browser_view_active); set_view->set_use_focusless(true); set_view->set_close_on_deactivate(false); views::SeBubbleDelegateView::CreateBubble(set_view); return set_view; } 

关键解析

  1. 选择活跃 Browser
    使用 BrowserList::GetInstance()->GetLastActive() 获取当前活跃的浏览器实例,如果没有,则使用传入的 browser
    这样弹窗总能挂载到一个活跃浏览器上下文。

  2. 确定父窗口
    利用 SeBrowserView::GetBrowserViewForBrowser 获取 BrowserView,并将其 Frame 的原生窗口作为父窗口。
    父窗口的存在保证弹窗能正确附着在浏览器 UI 上。

  3. 实例化 SetDefaultView

    new SetDefaultView(url, browser_active, delegate, true); 
    • url:要加载的页面,可以是本地页或在线页。

    • delegate:后端业务逻辑的承载者。

  4. 配置 View 属性

    • set_use_focusless(true):无焦点模式。

    • set_close_on_deactivate(false):失去焦点不关闭。
      这些属性保证弹窗在用户操作时稳定显示。

  5. 注册 Bubble

    views::SeBubbleDelegateView::CreateBubble(set_view); 

    这一行是关键,它将 SetDefaultView 挂载到 UI 树中,同时初始化内部 WebHostView,确保前端 JS 能发消息到后端 C++。


二、JS 消息到 C++ 的转发机制

SetDefaultView 中,核心消息入口是 OnAppCmd

void SetDefaultView::OnAppCmd(WebHostView* sender, int invoke_id, const std::string& module_name, const std::string& function_name, const std::string& p1, const std::string& p2) { if (delegate_) delegate_->OnAppCmd(invoke_id, function_name, p1, p2); } 

调用流程解析

  1. 前端 JS 发起调用
    页面中 JS 调用 appcmd(...)window.external.invoke(...),传入函数名和参数:

appcmd("defaultModule", "setAsDefaultBrowser", "", ""); 
  1. WebHostView 接收消息
    WebHostView 负责拦截前端调用,将参数解析成:

    • invoke_id:调用 ID

    • module_name:模块名

    • function_name:函数名

    • p1p2:参数

  2. SetDefaultView::OnAppCmd 转发
    消息到达 OnAppCmd,但这里并不处理具体业务,而是转发给 delegate。

  3. delegate 处理业务
    delegate_->OnAppCmd(...) 根据 function_name 执行具体逻辑,例如调用系统 API 设置默认浏览器或查询状态。


三、Delegate 的业务逻辑实现示例

典型的 delegate 实现如下:

class SetDefaultController : public SetDefaultView::Delegate { public: void OnAppCmd(int invoke_id, const std::string& function_name, const std::string& p1, const std::string& p2) override { if (function_name == "setAsDefaultBrowser") { HandleSetAsDefaultBrowser(invoke_id); } else if (function_name == "checkDefaultBrowser") { HandleCheckDefaultBrowser(invoke_id); } else { LOG(WARNING) << "Unknown command: " << function_name; } } private: void HandleSetAsDefaultBrowser(int invoke_id) { bool success = ShellIntegration::SetAsDefaultBrowser(); SendResponseToJs(invoke_id, success ? "ok" : "fail"); } void HandleCheckDefaultBrowser(int invoke_id) { bool is_default = ShellIntegration::IsDefaultBrowser(); SendResponseToJs(invoke_id, is_default ? "yes" : "no"); } void SendResponseToJs(int invoke_id, const std::string& result) { base::Value::Dict dict; dict.Set("id", invoke_id); dict.Set("result", result); web_ui()->CallJavascriptFunctionUnsafe("onAppCmdResponse", dict); } }; 

说明

  • delegate 根据 function_name 分发不同业务逻辑。

  • 执行业务后通过 web_ui()->CallJavascriptFunctionUnsafe 回调前端,完成 JS 的响应。


四、调用链总结

完整流程如下:

前端 JS ↓ appcmd / window.external.invoke WebHostView ↓ SetDefaultView::OnAppCmd ↓ delegate_->OnAppCmd ↓ 系统 API 或业务逻辑 ↓ WebUI 回调前端显示结果 

五、本地页与在线页的适用性

1. 本地页(chrome:// 或内置 H5)

  • URLchrome://settingspak 中的 H5 页面

  • 优点

    • 响应快,无网络依赖

    • 前端与后端接口完全可控

  • 应用场景

    • 设置默认浏览器

    • 修改浏览器主题

    • 内置配置页

2. 在线页(https:// 或远程 H5)

  • URL:远程服务器托管页面

  • 优点

    • UI 可热更新

    • 可以进行动态内容、A/B 测试

  • 应用场景

    • 登录/绑定页

    • 活动推广页

    • 数据统计/上报

  • 注意

    • 依赖网络,安全性需校验

    • 消息仍通过 OnAppCmd 转发,不受 URL 来源影响

3. 通用性分析

  • 核心机制只依赖 WebHostView + OnAppCmd + delegate

  • 不论页面来源本地还是远程,前端调用均可安全到达 delegate 执行逻辑


六、总结

  1. SetDefaultView 是浏览器弹窗消息通道的承载者。

  2. CreateSetDefaultView 负责实例化 view、挂载 UI、初始化 WebHostView。

  3. OnAppCmd 作为 JS 消息入口,负责转发给 delegate。

  4. delegate 承载业务逻辑,实现真正的功能操作。

  5. 本地页和在线页都可使用同一机制,差别只在页面 URL 和资源管理。

这种设计具有以下优点:

  • UI 与业务逻辑解耦

  • 消息分发统一,可扩展性强

  • 支持本地和在线页面,便于前端迭代


七、附录:博客扩展建议

  1. 源码截图:展示 CreateSetDefaultViewOnAppCmd、delegate 代码片段

  2. 调用链图:前端 JS → WebHostView → SetDefaultView → delegate → 系统 API

  3. 实际案例:设置默认浏览器、壁纸设置页面

  4. 注意事项

    • delegate 必须在 view 创建前绑定

    • 在线页调用需要考虑跨域和安全

    • 失去焦点或父窗口关闭时的行为设置


通过本文,你可以清晰理解浏览器弹窗从创建到 JS 消息处理的完整闭环,无论本地页还是在线页,都能使用同样的机制实现前端调用本地业务逻辑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/97073.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/97073.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/97073.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

对比视频处理单元(VPU)、图形处理器(GPU)与中央处理器(CPU)

如今选择互联网点播流媒体与直播视频的用户数量已远超传统广播电视&#xff0c;这一转变催生了对高性能媒体转码与OTT流媒体功能专用技术的需求。 我们最新推出的Accelerated Compute云计算解决方案&#xff0c;首次通过NETINT Quadra视频处理单元&#xff08;VPU&#xff09;…

vue3写一个简单的时间轴组件

插件版本&#xff1a;"element-plus": "^2.3.12""vue": "^3.0.0"代码示例&#xff1a;样式文件style.less&#xff1a;改变el-tooltip样式&#xff0c;可以复制代码到公共样式文件.el-popper.o-el-tooltip-popper-class {max-width: 3…

Linex系统网络管理(二)

二、网络连接查看1. netstat作用查看本地服务的网络监听状态查看客户端连接到本地服务的连接状态语法&#xff1a;netstat 选项 &#xff08;-anptu&#xff09;选项作用-n&#xff0c; --numeric显示数字形式地址而不是去解析主机、端口或用户名-a, --all显示所有的监听或连接…

Unity MQTT通讯

首先明确概念&#xff0c;什么是MQTT&#xff1f; MQTT是一种轻量级、基于发布 / 订阅&#xff08;Publish/Subscribe&#xff09;模式的物联网&#xff08;IoT&#xff09;通信协议&#xff0c;在带宽有限、网络不稳定的环境下&#xff0c;实现低功耗、低延迟的设备间通信&am…

JavaSE:类和对象2

一、封装封装的概念面向对象程序三大特性&#xff1a;封装、继承、多态。而类和对象阶段&#xff0c;主要研究的就是封装特性。何为封装呢&#xff1f;简单来说 就是套壳屏蔽细节。例如手机&#xff0c;你看不到任何的内部实现细节&#xff0c;只留下一些公开的接口给你使用&am…

RandAR训练自己的数据集

论文题目:RandAR: Decoder-only Autoregressive Visual Generation in Random Orders(随机顺序下仅解码器的自回归视觉生成) 会议:CVPR2025 摘要:我们介绍了RandAR,一种仅解码器的视觉自回归(AR)模型,能够以任意令牌顺序生成图像。与之前依赖于预定义生成顺序的纯解码器…

基于PHP服装租赁管理系统/基于php的服装管理系统的设计与实现

基于PHP服装租赁管理系统/基于php的服装管理系统的设计与实现

高并发内存池(12)-ThreadCache回收内存

高并发内存池&#xff08;12&#xff09;-ThreadCache回收内存 代码如下&#xff1a; // 释放对象时&#xff0c;链表过长时&#xff0c;回收内存回到中心缓存 void ThreadCache::ListTooLong(FreeList& list, size_t size) {void* start nullptr;void* end nullptr;list…

读大语言模型09超级智能

1. 超级智能1.1. 如果人工智能超越人类智能&#xff0c;可能会成为人类存在的一个重大威胁1.1.1. 对超级人工智能潜在危险最为担忧的群体中&#xff0c;恰恰包括那些否认大语言模型具备真正智能的人1.2. 计算机科学已经成为所有科学领域中不可或缺的重要组成部1.3. GPT具备编写…

阿里云拉取dockers镜像

假如你已经在云服务器上安装了docker需要配置下docker镜像加速代理就行了找到自己的加速网址&#xff1a;然后在云服务器上&#xff0c;修改docker 配置文件&#xff0c;vi /etc/docker/daemon.json没有这个文件的话&#xff0c;需要创建一个。{"default-address-pools&qu…

python自学笔记14 NumPy 线性代数

在Numpy库中有专门的linalg 模块用来做线性代数相关的运算。 本文中线性代数的一般概念不会解释 拆解矩阵 鸢尾花数据矩阵结构如下&#xff08;150 4&#xff09;&#xff1a;取其中的行向量和列向量&#xff1a; # 导入包 import numpy as np from sklearn.datasets import l…

ubuntu20搭建MQTT

sudo apt update sudo apt install mosquitto mosquitto-clients sudo mosquitto_passwd -c /etc/mosquitto/passwd myuser sudo nano /etc/mosquitto/mosquitto.conf# 允许匿名用户连接&#xff08;默认为 true&#xff0c;我们先关闭它&#xff09; allow_anonymous false# 指…

云服务器的主要用途都有哪些?

企业可以利用云服务器构建官方网站&#xff0c;企业官网需要稳定的运行环境来展示产品、服务、公司动态等信息&#xff0c;云服务器提供的高可用性和可扩展性&#xff0c;能保障大量用户同时访问时网站的稳定运行。移动应用的后端服务可以部署在云服务器上&#xff0c;如社交类…

IntelliJ IDEA Debug 模式功能指南

文章目录前言&#x1f4a1; 1. 断点类型与设置&#x1f680; 2. 启动 Debug 模式⚙️ 3. 调试控制按钮详解&#x1f440; 4. 查看与监控变量&#x1f9f0; 5. 高级调试技巧&#x1f48e; 总结前言 作为一名 Java 开发者&#xff0c;熟练掌握调试技巧是提高开发效率的关键。Int…

在pycharmIDE中如何快速掌握一个新模块的使用方法

一、文档使用悬停文档&#xff1a;鼠标悬停在模块/函数上显示文档摘要 (⭐最常用)快速文档&#xff1a;选中标识符按 CtrlQ (Windows/Linux) 或 F1 (Mac)跳转定义&#xff1a;Ctrl左键单击 直接跳转到源码定义处 (⭐最权威)参数提示&#xff1a;输入函数名时自动显示参数列表&a…

win11自定义停止更新方法

一、打开运行窗口&#xff08;winr&#xff09;输入regedit打开注册表编辑器。按照如下路径寻找。计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings二、在Settings页面下右击——>新建——>DWORD(32位)值(D)&#xff0c;并重命名为粉色框中的名字…

Unity委托、匿名方法与事件深度解析:从理论到实战

Unity委托、匿名方法与事件深度解析&#xff1a;从理论到实战 摘要&#xff1a;本文深入剖析Unity中委托、匿名方法与事件的核心机制&#xff0c;结合理论框架与实战案例&#xff0c;帮助开发者掌握高效的事件驱动编程技巧。全文包含12个代码片段及6个核心原理图示框架&#x…

大脑的藏宝图——神经科学如何为自然语言处理(NLP)的深度语义理解绘制新航线

摘要&#xff1a; 截至2025年&#xff0c;大型语言模型&#xff08;LLM&#xff09;已展现出惊人的能力&#xff0c;但其内在的“黑箱”特性和对深层语义理解的局限性也日益凸显。本报告旨在深入探讨一个充满潜力的前沿交叉领域&#xff1a;借鉴地球上最古老、最精密的语言处理…

记录使用ruoyi-flowable开发部署中出现的问题以及解决方法(二)

1.vform的使用与传值 使用动态表单&#xff0c;把当前的用户名传值进动态表单&#xff0c;另外动态表单的上传组件成功后传值会父组件。 在父组件的加载函数中增加&#xff1a; mounted(){this.$refs.vFormRef.addEC("getuploadfile",this);},该方法为给表单加载外…

Apifox 8 月更新|新增测试用例、支持自定义请求示例代码、提升导入/导出 OpenAPI/Swagger 数据的兼容性

Apifox 作为全能 API 工具&#xff0c;正以迅猛之势革新开发者的工作方式&#xff01;想象一下&#xff0c;您正为测试用例编写头疼&#xff0c;或因 OpenAPI 文件导入失败而延误项目&#xff0c;而 Apifox 8 月更新却带来“救命稻草”&#xff1a;新增测试用例功能、自定义请求…