chromium设立UI的实现
在打开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))); ...... }