在打开chromium的设置时,chromium会导向一个chrome://settings/的页面,这种UI的实现方式跟其它的UI实现方式有一些区别的,它本质上是一个html页面,能通过javascript与浏览器中的C++代码通信,这种实现称为WebUI,类似的页面可以参考chrome://about/中列出来的页面。类似设置页面这种UI,本身并没有特别复杂的功能,只负责一些数据显示和存储工作,而html+javscript与平台无关,使用起来比较方便,避免使用特定平台的UI库来实现用户UI。
在阅读下文之前,也可以选阅读文章以下文章,使得对WebUI的过程不会生疏。
http://www.chromium.org/developers/webui
通常WebUI的实现需要以下几部分:
- 一个对应的html页面,页面中包含基本的元素,可以通过javascript为其填充数据,这就是显示界面的html文件。
- js脚本文件,与后台C++代码的通信由js完成,后台传递过来的数据也通过js进行显示。
- 一个继承自content::WebUIController的类,它负责与js进行通信。
以下是设置页面的实现说明,它会比官网中例子复杂一些。
1、设置页面使用的url在src/chrome/common/url_constants.h和src/chrome/common/url_constants.cc中定义。
url_constants.h
extern const char kChromeUISettingsURL[];
url_constants.cc
const char kChromeUISettingsURL[] = "chrome://settings/";
在chrome://settings/中,设置部分只是一个frame,它的实现其实就是chrome://settings-frame/settings,在url_constants.cc中定义了这个名称。
const char kChromeUISettingsFrameHost[] = "settings-frame";
2、在src/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc中,根据kChromeUISettingsFrameHost会实例化OptionsUI。
WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui, Profile* profile, const GURL& url) {
.......
if (url.host() == chrome::kChromeUISettingsFrameHost)
return &NewWebUI<options::OptionsUI>;
......
}
3、在src\chrome\browser\resources\options_resources.grd中定义了设置页面相关的两个文件IDR_OPTIONS_BUNDLE_JS和IDR_OPTIONS_HTML。这两个文件位于src\chrome\browser\resources\options目录下。
<?xml version="1.0" encoding="UTF-8"?>
<grit latest_public_release="0" current_release="1">
<outputs>
<output filename="grit/options_resources.h" type="rc_header">
<emit emit_type='prepend'></emit>
</output>
<output filename="options_resources.pak" type="data_package" />
</outputs>
<release seq="1">
<structures>
<structure name="IDR_OPTIONS_BUNDLE_JS" file="options/options_bundle.js" flattenhtml="true" type="chrome_html" />
<structure name="IDR_OPTIONS_HTML" file="options/options.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
</structures>
</release>
</grit>
4、chrome://settings-frame/settings的html页面和js文件在OptionsUI的构造函数中获取,这里读取的就是上一步骤中的两个文件。
OptionsUI::OptionsUI(content::WebUI* web_ui)
: WebUIController(web_ui),
WebContentsObserver(web_ui->GetWebContents()),
initialized_handlers_(false) {
......
OptionsUIHTMLSource* html_source =
new OptionsUIHTMLSource(localized_strings);
// Set up the chrome://settings-frame/ source.
Profile* profile = Profile::FromWebUI(web_ui);
content::URLDataSource::Add(profile, html_source);
......
}
5、WebUI中有两个重要的函数
virtual void AddMessageHandler(WebUIMessageHandler* handler) = 0;
virtual void RegisterMessageCallback(const std::string& message,
const MessageCallback& callback) = 0;
WebUI::AddMessageHandler函数会调用WebUIMessageHandler的RegisterMessages函数。
OptionsPageUIHandler继承WebUIMessageHandler,它有很多子类,分别负责处理特定的消息,并实现该消息的处理函数。
WebUIMessageHandler::RegisterMessages函数会把OptionsPageUIHandler对应的消息处理函数注册到web_ui_中,而这个注册过程则是调用WebUI::RegisterMessageCallback函数实现的。
在OptionsUI的构造函数中会调用AddOptionsPageUIHander()把OptionsUI使用的OptionsPageUIHander通过WebUI::AddMessageHandler函数进行注册。
以下是OptionsUI的类图。
6、以网络内容设置为例,说明设置页面的消息传递过程。
options.html中有以下代码
<div id="main-content">
<div id="mainview">
<div id="mainview-content">
<div id="page-container">
<!-- Please keep the main pages in desired order of display. This will
allow search results to display in the desired order. -->
<include src="search_box.html">
<include src="search_page.html">
<include src="browser_options.html">
</div>
</div>
</div>
</div>
进入browser_options.html,找到网络内容对应的HTML代码段,在选择字体大小后,对应的id是defaultFontSize
<section id="web-content-section">
<h3 i18n-content="advancedSectionTitleContent"></h3>
<div>
<div class="settings-row">
<label class="web-content-select-label">
<span i18n-content="defaultFontSizeLabel"></span>
<select id="defaultFontSize">
<option value="9" i18n-content="fontSizeLabelVerySmall">
</option>
<option value="12" i18n-content="fontSizeLabelSmall"></option>
<option value="16" i18n-content="fontSizeLabelMedium"></option>
<option value="20" i18n-content="fontSizeLabelLarge"></option>
<option value="24" i18n-content="fontSizeLabelVeryLarge">
</option>
</select>
</label>
<span id="font-size-indicator"
class="controlled-setting-indicator"></span>
<button id="fontSettingsCustomizeFontsButton"
i18n-content="fontSettingsCustomizeFontsButton"></button>
</div>
<div class="settings-row">
<label class="web-content-select-label">
<span i18n-content="defaultZoomFactorLabel"></span>
<select id="defaultZoomFactor" dataType="double"></select>
</label>
</div>
</div>
</section>
在browser_options.js文件中找到defaultFontSize的处理函数,它会往把选择后的font size通过发送消息defaultFontSizeAction到BrowserOptionsHandler进行处理。
$('fontSettingsCustomizeFontsButton').onclick = function(event) {
OptionsPage.navigateToPage('fonts');
chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']);
};
$('defaultFontSize').onchange = function(event) {
var value = event.target.options[event.target.selectedIndex].value;
Preferences.setIntegerPref(
'webkit.webprefs.default_fixed_font_size',
value - OptionsPage.SIZE_DIFFERENCE_FIXED_STANDARD, true);
chrome.send('defaultFontSizeAction', [String(value)]);
};
$('defaultZoomFactor').onchange = function(event) {
chrome.send('defaultZoomFactorAction',
[String(event.target.options[event.target.selectedIndex].value)]);
};
在browser_options_handler.cc中找到defaultFontSizeAction对应的处理函数BrowserOptionsHandler::HandleDefaultFontSize,完成消息的处理。
void BrowserOptionsHandler::RegisterMessages() {
......
web_ui()->RegisterMessageCallback(
"defaultFontSizeAction",
base::Bind(&BrowserOptionsHandler::HandleDefaultFontSize,
base::Unretained(this)));
......
}