Chromium WebUI机制介绍

WebUI机制的几个部分

1.WebUI类,主要作用:

(1)从render进程接收js调用发来的消息,执行相应的C++函数;
(2)提供执行js代码的接口;

2.WebUIController类, 主要作用:

(1)控制WebUI页面的行为;
(2)为WebUI页面管理数据源和消息处理器;

3.WebUIDataSource类,主要作用:

为WebUI页面提供各种资源,例如需要加载的HTML文件、css以及js文件,同时还包括png图片资源,本地化的字符串资源。

4.主要结构如下图(图有点丑):

Js调用

在RenderFrame创建的时候,chromium向V8中注册了两个函数,作用是Js调用WebUI的函数以及获取WebUI处理结束之后的结果,如下:

v8::Local<v8::Object> chrome = GetOrCreateChromeObject(isolate,context->Global());
chrome->Set(gin::StringToSymbol(isolate, "send"),gin::CreateFunctionTemplate(isolate, base::Bind(&WebUIExtension::Send))->GetFunction());
chrome->Set(gin::StringToSymbol(isolate, "getVariableValue"),gin::CreateFunctionTemplate(isolate, base::Bind(&WebUIExtension::GetVariableValue))->GetFunction());

主要使用的就是send,js代码通过调用send(),将参数打包然后调用此处的绑定的WebUIExtension::Send()方法,Send()方法中获取当前页面对应的RenderView,然后由RenderView将参数通过IPC发送到Browser端,由WebUI来处理。 细节可参见WebUIExtension::Send()方法的实现。

WebUI工作流程

WebUI创建过程

WebUI的创建流程如下:

1.在加载url时,由WebContents来创建一个WebUI对象;

WebUI* WebContentsImpl::CreateWebUI(const GURL& url,const std::string& frame_name) {WebUIImpl* web_ui = new WebUIImpl(this, frame_name);WebUIController* controller = WebUIControllerFactoryRegistry::GetInstance()->CreateWebUIControllerForURL(web_ui, url);if (controller) {web_ui->AddMessageHandler(new GenericHandler());web_ui->SetController(controller);return web_ui;}delete web_ui;return NULL;
}

以上代码中,首先创建一个WebUI对象,然后再创建WebUIContoller对象,若controller对象创建成功,则将其绑定到WebUI对象中,由WebUI来管理;否则,销毁WebUI对象。

在WebUIController创建时会初始化WebUI中的数据源,这部分在《WebUI资源初始化.md》中。

注册处理函数

在WebUIController(controller)创建时,除了数据源的初始化,还需要向WebUI中注册js调用要执行的函数,调用的WebUI中的方法,如下:

// WebUIImpl中的定义
void WebUIImpl::RegisterMessageCallback(const std::string& message, const MessageCallback& callback) {message_callbacks_.insert(std::make_pair(message, callback));
}// 具体的调用通常在controller的构造或者初始化函数中
web_ui->RegisterMessageCallback("queryHistory",base::Bind(&HistoryUI::HandleQueryHistory,base::Unretained(this)));

以上的调用时OB History页面中的注册查询history的方法,HandleQueryHistory方法就是由开发者实现的方法。”queryHistory”这个字符串同时也是js调用传来的消息的第一个参数。
在WebUIImpl中,OnMessageReceived()方法接收到信息之后,就通过消息的第一个参数,在message_callback_中查询对应的回调函数,然后执行这个回调函数,如下:

bool WebUIImpl::OnMessageReceived(const IPC::Message& message) {bool handled = true;IPC_BEGIN_MESSAGE_MAP(WebUIImpl, message)IPC_MESSAGE_HANDLER(ViewHostMsg_WebUISend, OnWebUISend)IPC_MESSAGE_UNHANDLED(handled = false)IPC_END_MESSAGE_MAP()return handled;
}void WebUIImpl::OnWebUISend(const GURL& source_url,const std::string& message,const base::ListValue& args) {// 略去部分代码...ProcessWebUIMessage(source_url, message, args);
}void WebUIImpl::ProcessWebUIMessage(const GURL& source_url,const std::string& message,const base::ListValue& args) {if (controller_->OverrideHandleWebUIMessage(source_url, message, args))return;// Look up the callback for this message.MessageCallbackMap::const_iterator callback = message_callbacks_.find(message);if (callback != message_callbacks_.end()) {// Forward this message and content on.callback->second.Run(&args);} else {NOTREACHED() << "Unhandled chrome.send(\"" << message << "\");";}
}

以上代码就是从WebUI在OnMessageReceived()中接收到消息之后,一直到执行绑定的回调函数的过程。值得注意的是在ProcessWebUIMessage()中,若想让controller直接处理消息,就重载WebUIController中的OverrideHandleWebUIMessage()方法,处理结束返回true即可,此处默认方法返回是false。

问题:
1.在我们OB的history页面中使用了注册的方式来执行回调函数。但是controller也有OverrideHandleWebUIMessage()方法,为什么不用这个方法呢?

2.chromium中的callback是一个闭包,包含了一个函数执行的环境,其封装原理值得学习。

执行Js代码

WebUI中同样提供了执行Js代码的接口

JavaScript代码向上的调用流程:

1.首先调用WebUI中的CallJavaScript()系列方法,这个函数有几个同类函数,区别就是参数数量不一样,传入js函数名以及参数,这个是WebUI中调用js的入口函数,如下:

void WebUIImpl::CallJavascriptFunction( const std::string& function_name, const base::Value& arg1, const base::Value& arg2) {DCHECK(base::IsStringASCII(function_name));std::vector<const base::Value*> args;args.push_back(&arg1);args.push_back(&arg2);ExecuteJavascript(GetJavascriptCall(function_name, args));
}

上面这个函数,参数个数是2个,参数数量为1~4时,都采用上述方法;如果不带参数,调用时只需要传入函数名,也用不着vector,当参数个数大于4个时,需要调用者自己将参数存进一个vector中,将vector作为参数传进来。

以上函数中有个GetJavaScriptCall()函数,其作用很简单,将函数与参数连接起来,组成一个字符串。

2.然后看看上面调用ExecuteJavascript()方法,参数就是以上组成的字符串,其中获取当前的RenderFrameHost;

void WebUIImpl::ExecuteJavascript(const base::string16& javascript) {RenderFrameHost* target_frame = TargetFrame();if (target_frame) {if (!(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(target_frame->GetProcess()->GetID()) ||// It's possible to load about:blank in a Web UI renderer.// See http://crbug.com/42547target_frame->GetLastCommittedURL().spec() == url::kAboutBlankURL)) {// Don't crash when we try to inject JavaScript into a non-WebUI page, but// upload a crash report anyways. http://crbug.com/516690base::debug::DumpWithoutCrashing();return;}target_frame->ExecuteJavaScript(javascript);}
}

3.调用RenderFrameHost的ExecuteJavascript()方法,将javascript语句打包进IPC消息中,向RenderFrame发送IPC消息FrameMsg_JavaScriptExecuteRequest;

void RenderFrameHostImpl::ExecuteJavaScript(const base::string16& javascript) {CHECK(CanExecuteJavaScript());Send(new FrameMsg_JavaScriptExecuteRequest(routing_id_,javascript,0, false));
}

通过routing_id来确定由哪个frame来接受哦这个消息,消息中的第三个参数表示在执行js之后是否需要返回结果,若要会返回结果,需要调用另外一个函数,并传入一个回到函数,具体参见render_frame_host_impl.cc。

4.RenderFrame收到IPC消息后,将调用WebLocalFrame的函数executeScriptAndReturnValue()方法,传入js语句,如下;

bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) {...IPC_MESSAGE_HANDLER(FrameMsg_JavaScriptExecuteRequest,OnJavaScriptExecuteRequest)...
}void RenderFrameImpl::OnJavaScriptExecuteRequest(const base::string16& jscript,int id,bool notify_result) {TRACE_EVENT_INSTANT0("test_tracing", "OnJavaScriptExecuteRequest",TRACE_EVENT_SCOPE_THREAD);v8::HandleScope handle_scope(v8::Isolate::GetCurrent());v8::Local<v8::Value> result =frame_->executeScriptAndReturnValue(WebScriptSource(jscript));HandleJavascriptExecutionResult(jscript, id, notify_result, result);
}

以上是RenderFrameImpl中的处理,最有一行的HandleJavaScriptExecutionResult()方法就是是否返回结果的实现。

5.RenderFrameImplement中,再往下就是Webkit里面的调用:
WebLocalFrame通过LocalFrame中的script()方法,得到ScriptController,调用Controller的executeScriptInMainWorldAndReturnValue()来执行js语句,并返回结果。最终是调用v8Call()方法来执行语句。

具体可参见ScriptController.cpp

结语

本文档主要介绍了Chromium WebUI作用及工作流程(browser <==> render):

1.WebUI机制就是用来显示本地页面,其中WebUI负责所有对象(controller对象,handler对象等)的管理,同时也是js与C++相互调用的桥梁;

2.WebUIController负责主要的逻辑处理,具体处理js向C++的调用以及js调用的发起,同时还管理数据类WebUIDataSource。

3.WebUIDataSource就是提供数据源,是显示页面的时候,所有数据的提供者。

chromium之WebUI相关推荐

  1. chromium - DISALLOW_IMPLICIT_CONSTRUCTORS

    前言 在看内建web-ui实现时,总能看到类后面有个宏 DISALLOW_COPY_AND_ASSIGN 以前忙着别的,没追究.今天做了c++11的default和delete语法的实验,明白这些宏的 ...

  2. 定制化chromium的修改方法

    由于chromium的代码量很大,且版本也是更新较快,很难短时间内消化吸收,主要方法:通过 cs.chromium.org 来查找关键字,修改完成后需要整体编译. 本次修改大都是按照版本62.0.32 ...

  3. 为什么要用webUI?

    先看看身边有哪些软件已经在用webUI: 1.QQ查找窗口: 2.LOL主界面: 3.EC营销软件功能界面: 三个例子足以说明一切: 1.HTML是目前在用户体验.界面舒适度最先进的语言 2.HTML ...

  4. 搭建webUI自动化及问题解决:Message: ‘chromedriver‘ executable needs to be in PATH.解决办法

    搭建webUI自动化环境 1.conda install selenium即可. 若出现:Message: 'chromedriver' executable needs to be in PATH. ...

  5. chromium aura简介

    Aura 目的 Aura是为了创建一个全新的桌面窗口管理系统,通过硬件加速提供绚丽的UI效果. UI设计 一个视图层级依附于一个widget,widget是一个跨平台类型,依赖于NativeWidge ...

  6. chromium摘抄

    网上介绍chromium知识的文章挺多,省的自己写了,在这里就直接摘录一下 1.WwebUI WebUI就是chrome://xxx/所显示的页面,WebUI也提供了一些新的api. 学习WebUI的 ...

  7. Chromium浏览器(CEF)的命令行列表说明

    找到的Chromium浏览器的命令行说明列表,转放在这里以方便查看. List of Chromium Command Line Switches There are lots of command ...

  8. Chromium命令行开关列表2

    Chromium命令行开关列表 Google Chrome浏览器可以使用很多命令行. 一些更改功能的行为,其他用于调试或试验. 该页面列出了可用的开关,包括其条件和说明. 上一次自动更新发生在2020 ...

  9. chromium gn parameter list

    前言 查资料时,看到如何看gn都提供什么参数列表了. 这样,编译chromium工程时,可以编译的更精细. 实验 可以将参数写到 Z:\chromium\src\out\my_x86_d\args.g ...

  10. centos 安装 aria2 webui 实现网页下载

    centos aria2 webui 安装aria2 安装 rpmforge源 wget http://repository.it4i.cz/mirrors/repoforge/redhat/el6/ ...

最新文章

  1. Windows Azure Pack集成配置SPF
  2. postman怎么传session_十几行代码实现分布式 Session
  3. 需要注意变量作用域的使用
  4. java位运算实例详解——(amp;)、(|)、(~)、(^)、(lt;lt;)、(gt;gt;)
  5. 宝塔面板网站一打开cpu百分百_BT宝塔面板打开这个功能网站快到起飞,降低宝塔面板内存和CPU使用率,降低运行负载...
  6. 关于 Mac OS X 内核技术来源
  7. python高级特性:迭代器与生成器
  8. Windows Win7建立wifi热点,手机共享WIFI上网
  9. Slowquery图形化显示MySQL慢日志工具搭建
  10. nginx tcp转发_Nginx性能优化技巧
  11. CSS3 -- display:flex
  12. 用Julia学习微积分:这有一份高赞数学教程 | 附习题+代码
  13. EF架构~为ObjectContext类型加个Find方法
  14. 通讯(transport)
  15. css中引入下载字体的方法
  16. Ffmpeg常用转码命令
  17. 联想台式电脑一键恢复后桌面没有计算机了,联想笔记本一键恢复功能使用教程...
  18. 信号处理之FIR数字滤波器(Matlab仿真)
  19. 这是一篇能够教会你运营阿里巴巴国际站的文章
  20. 2022电工(初级)上岗证题目及答案

热门文章

  1. AMD处理器与INTEL的区别
  2. 打开Excel2010时提示错误:向程序发送命令时出现问题!
  3. php 调用mp3,使用PHP合并MP3文件的类,兼容php4、php5(2)
  4. linux系统触摸板双击,在Ubuntu 18.04系统中搞定触摸板多点触控
  5. Dubbo本地存根是什么,Dubbo本地伪装又是什么?
  6. 设置Windows Server登录时禁止自动启动服务器管理器
  7. PHP压缩文件下载,提示压缩包损坏及打不开的解决方法
  8. java常用英语单词大全
  9. 史玉柱自述:我是怎么带队伍的
  10. 微信公众号访问 ssm框架根目录下MP_verify_xxxxxx.txt的解决方法