前言
chromium官方给出的制作webui的资料,共3部分信息。
* 制作web-ui
* 使web-ui中的js和chromium中的c++实现互操作
* 在web-ui中建立弹出的子web-ui
第一个实验做完了.
这周末在做第二个实验. 做实验的分支是68.0.3440.84, 发现跟着官方文档玩,根本编译不过。
去google下,发现官方文档和跟在官方文档后面的同学做的实验,都一个样子,基本就是汉化了一下。都不是完整的实验,就说了一下js和c交互的要点。做实验的chromium版本也没说.
谁知道跟在官方文档后面那些同学,有没有做实验?
做了实验的同学,他们实验有没有成功?
也不怀疑我自己,因为官方文档实验步骤写的还是蛮清晰的。 我实验思路,读代码,调试的能力也是刚刚的。
不敢怀疑官方的文档,但是有点怀疑,官方文档是针对最新版本的chromium.可能在68.0.3440.84上实现有细节的不同。但是68.0和最新的版本也没隔多长时间,因为68.0是最新的稳定版.
官方文档描述的大方向肯定是对的。但是细节确实编译不过,我自己读其他同级的web-ui实现,尝试着修改,实验自己的web-ui版本,最后有官方文档上描述的js和c++互操作的效果了。
chromium工程中,web-ui实现有很多。如果一开始,就看已有的web-ui实现,不容易看清楚。做完自己的web-ui实现后,web-ui编程的小框架清楚了,再去看已有的web-ui实现,很容易看。
官方的例子,将web-ui实现和js-c++交互放在一起。
已有的web-ui实现,有些将web-ui实现和js-c++交互分开了, 这样维护起来好些。
实验- js和c在chromium工程中交互
在chromium工程中叫做hello_world*.*的文件有好几个版本,怕被其他实现版本影响了,重新对实现文件,实现类改名.
新建Z:\chromium\src\chrome\browser\resources\my_hello_world.html
<!--
@file Z:\chromium\src\chrome\browser\resources\my_hello_world.html
@note 如果修改了html内容, 需要重新编译chromium工程才能看到效果
-->
<!DOCTYPE HTML>
<html i18n-values="dir:textdirection">
<head>
<meta charset="utf-8">
<title i18n-content="my_helloWorldTitle"></title>
<link rel="stylesheet" href="my_hello_world.css">
<script src="chrome://resources/js/cr.js"></script>
<script src="chrome://resources/js/load_time_data.js"></script>
<script src="chrome://resources/js/util.js"></script>
<script src="strings.js"></script>
<script src="my_hello_world.js"></script>
</head>
<body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize">
<!-- my_helloWorldTitle 是变量, 在程序中用c++和js交互的接口赋值 -->
<h1 i18n-content="my_helloWorldTitle"></h1>
<!-- 静态html语法,直接显示 -->
<h1>My First Heading -- this is static html code</h1>
<p>My first paragraph. -- this is static html code</p>
<p>test by ls -- this is static html code</p>
<!-- id 指示变量内容是从js文件中来的 -->
<!-- my_welcome-message 是变量, 在程序中用c++和js交互的接口赋值 -->
<!-- 如果这个id改名了, my_hello_world.js initialize() 中的变量my_welcome_message也要改名, 这个值是那里用js赋值的 -->
<p id="my_welcome_message"></p>
<!-- i18n-content 指示变量内容是从资源文件中来的 -->
<p i18n-content="my_welcome_message"></p>
<script src="chrome://resources/js/i18n_template.js"></script>
</body>
</html>
新建Z:\chromium\src\chrome\browser\resources\my_hello_world.css
/**
* @file Z:\chromium\src\chrome\browser\resources\my_hello_world.css
*/
p {
white-space: pre-wrap;
}
新建Z:\chromium\src\chrome\browser\resources\my_hello_world.js
/**
* @file Z:\chromium\src\chrome\browser\resources\my_hello_world.js
*/
cr.define('my_page', function() {
'use strict';
/**
* Be polite and insert translated hello world strings for the user on loading.
*/
function initialize() {
$('my_welcome_message').textContent = loadTimeData.getStringF('my_welcome_message',
loadTimeData.getString('userName'));
// 这里可以搞成按钮的onclick() 或 下列列表按钮的事件触发
alert('begin : js send request to buind_in url');
chrome.send('addNumbers', [66, 88]);
alert('over : js send request to buind_in url');
}
// 如果js函数写错了(e.g. "addResult"), 不会报错, 但是没效果(没有调用到js文件中的addResult函数)
// c代码中必须写 CallJavascriptFunction("my_page.addResult", result); 才会进入异步的addResult(result)函数
function addResult(result) {
alert('enter addResult(result)');
alert('The result of our C++ arithmetic: 66 + 88 = ' + result);
}
// Return an object with all of the exports.
return {
addResult: addResult,
initialize: initialize,
};
});
/**
这里执行的函数的命令空间要和上面一样 e.g. my_page
*/
document.addEventListener('DOMContentLoaded', my_page.initialize);
编辑Z:\chromium\src\chrome\browser\browser_resources.grd
加入网页实现资源 IDR_HELLO_WORLD_X
<structure name="IDR_SIGNIN_SHARED_CSS_HTML" file="resources\signin\signin_shared_css.html" preprocess="true" allowexternalscript="true" type="chrome_html" />
<if expr="not is_android and not chromeos">
<structure name="IDR_WELCOME_CSS" file="resources\welcome\welcome.css" type="chrome_html" preprocess="true"/>
<structure name="IDR_WELCOME_HTML" file="resources\welcome\welcome.html" type="chrome_html" preprocess="true"/>
<structure name="IDR_WELCOME_JS" file="resources\welcome\welcome.js" type="chrome_html" preprocess="true"/>
<structure name="IDR_DICE_WELCOME_CSS" file="resources\welcome\dice_welcome\welcome.css" type="chrome_html" preprocess="true"/>
<structure name="IDR_DICE_WELCOME_HTML" file="resources\welcome\dice_welcome\welcome.html" type="chrome_html" preprocess="true"/>
<structure name="IDR_DICE_WELCOME_BROWSER_PROXY_HTML" file="resources\welcome\dice_welcome\welcome_browser_proxy.html" type="chrome_html" preprocess="true"/>
<structure name="IDR_DICE_WELCOME_BROWSER_PROXY_JS" file="resources\welcome\dice_welcome\welcome_browser_proxy.js" type="chrome_html" preprocess="true"/>
<structure name="IDR_DICE_WELCOME_APP_HTML" file="resources\welcome\dice_welcome\welcome_app.html" type="chrome_html" preprocess="true"/>
<structure name="IDR_DICE_WELCOME_APP_JS" file="resources\welcome\dice_welcome\welcome_app.js" type="chrome_html" preprocess="true"/>
</if>
<if expr="is_win">
<structure name="IDR_WELCOME_WIN10_CSS" file="resources\welcome\welcome_win10.css" type="chrome_html" />
<structure name="IDR_WELCOME_WIN10_HTML" file="resources\welcome\welcome_win10.html" type="chrome_html" />
<structure name="IDR_WELCOME_WIN10_JS" file="resources\welcome\welcome_win10.js" type="chrome_html" />
</if>
</structures>
<includes>
<include name="IDR_HELLO_WORLD_HTML" file="resources\my_hello_world.html" type="BINDATA" />
<include name="IDR_HELLO_WORLD_CSS" file="resources\my_hello_world.css" type="BINDATA" />
<include name="IDR_HELLO_WORLD_JS" file="resources\my_hello_world.js" type="BINDATA" />
<if expr="is_win or is_macosx or desktop_linux or chromeos">
<include name="IDR_ABOUT_DISCARDS_CSS" file="resources\discards\discards.css" type="BINDATA" />
<include name="IDR_ABOUT_DISCARDS_HTML" file="resources\discards\discards.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_ABOUT_DISCARDS_JS" file="resources\discards\discards.js" type="BINDATA" />
<include name="IDR_ABOUT_DISCARDS_MOJO_JS" file="${root_gen_dir}\chrome\browser\ui\webui\discards\discards.mojom.js" use_base_dir="false" type="BINDATA" />
</if>
编辑Z:\chromium\src\chrome\common\webui_url_constants.h
加入自己的web-url的外部声明
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Contains constants for WebUI UI/Host/SubPage constants. Anything else go in
// chrome/common/url_constants.h.
#ifndef CHROME_COMMON_WEBUI_URL_CONSTANTS_H_
#define CHROME_COMMON_WEBUI_URL_CONSTANTS_H_
#include <stddef.h>
#include "build/build_config.h"
#include "chrome/common/buildflags.h"
#include "content/public/common/url_constants.h"
#include "media/media_buildflags.h"
#include "printing/buildflags/buildflags.h"
namespace chrome {
// chrome: components (without schemes) and URLs (including schemes).
// e.g. kChromeUIFooHost = "foo" and kChromeUIFooURL = "chrome://foo/"
// Not all components have corresponding URLs and vice versa. Only add as
// needed.
// Please keep in alphabetical order, with OS/feature specific sections below.
extern const char kChromeUIHelloWorldURL[];
extern const char kChromeUIHelloWorldHost[];
extern const char kChromeUIAboutHost[];
extern const char kChromeUIAboutURL[];
编辑Z:\chromium\src\chrome\common\webui_url_constants.cc
加入自己的web-url的声明
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/webui_url_constants.h"
#include "base/macros.h"
#include "components/nacl/common/buildflags.h"
#include "components/safe_browsing/web_ui/constants.h"
#include "extensions/buildflags/buildflags.h"
namespace chrome {
// Please keep this file in the same order as the header.
// Note: Add hosts to |kChromePaths| in browser_about_handler.cc to be listed by
// chrome://chrome-urls (about:about) and the built-in AutocompleteProvider.
const char kChromeUIHelloWorldURL[] = "chrome://my_page/";
const char kChromeUIHelloWorldHost[] = "my_page";
const char kChromeUIAboutHost[] = "about";
const char kChromeUIAboutURL[] = "chrome://about/";
const char kChromeUIAppLauncherPageHost[] = "apps";
编辑Z:\chromium\src\chrome\app\generated_resources.grd
加入一些规定的不用修改的资源字符串
<!-- Printing specific strings -->
<if expr="enable_printing">
<part file="printing_strings.grdp" />
</if>
<!-- TODO add all of your "string table" messages here. Remember to
change nontranslateable parts of the messages into placeholders (using the
<ph> element). You can also use the 'grit add' tool to help you identify
nontranslateable parts and create placeholders for them. -->
<message name="IDS_LS_WEB_UI_TITLE" desc="A happy message saying hello to the world">
<<lostspeed web ui>> -- this content from resource file : Z:\\chromium\\src\\chrome\\app\\generated_resources.grd IDS_LS_WEB_UI_TITLE
</message>
<message name="IDS_LS_HELLO_WORLD_WELCOME_TEXT" desc="Message welcoming the user to the hello world page">
my Welcome to this fancy Hello World page <ph name="WELCOME_NAME">$1<ex>Chromium User</ex></ph>!
</message>
<if expr="is_win">
<message name="IDS_BACKGROUND_APP_INSTALLED_BALLOON_TITLE" desc="The title of the balloon that is displayed when a background app is installed">
New background app added
</message>
<message name="IDS_BACKGROUND_APP_INSTALLED_BALLOON_BODY" desc="The contents of the balloon that is displayed when a background app is installed">
<ph name="APP_NAME">$1<ex>Background App</ex></ph> will launch at system startup and continue to run in the background even once you've closed all other <ph name="PRODUCT_NAME">$2<ex>Google Chrome</ex></ph> windows.
</message>
</if>
新建z:\chromium\src\chrome\browser\ui\webui\hello_world_ui.h
这里是处理网页上显示的固定资源字符串, js和c++交互的类声明
使用了多继承, 从content::WebUIController, content::WebUIMessageHandler继承一个子类.这么做不知道对不对,官方文档上没说,我这实验正常:)
// @file Z:\chromium\src\components\hello_world\hello_world_ui.h
#ifndef COMPONENTS_HELLO_WORLD_HELLO_WORLD_UI_H_
#define COMPONENTS_HELLO_WORLD_HELLO_WORLD_UI_H_
#pragma once
#include "base/macros.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "content/browser/devtools/protocol/protocol.h"
#include "chrome/browser/devtools/protocol/forward.h"
namespace base {
class ListValue;
} // namespace base
// The WebUI for chrome://hello-world
class HelloWorldUI : public content::WebUIController, public content::WebUIMessageHandler {
public:
explicit HelloWorldUI(content::WebUI* web_ui);
~HelloWorldUI() override;
void RegisterMessages() override;
bool OverrideHandleWebUIMessage(const GURL& source_url,
const std::string& name,
const base::ListValue& args) override;
private:
void AddNumbers(const base::ListValue* args);
DISALLOW_COPY_AND_ASSIGN(HelloWorldUI);
};
#endif // COMPONENTS_HELLO_WORLD_HELLO_WORLD_UI_H_
新建z:\chromium\src\chrome\browser\ui\webui\hello_world_ui.cc
实现web-ui的资源字符串显示, js和c的交互
// @file Z:\chromium\src\chrome\browser\ui\webui\hello_world_ui.cc
#include "chrome/browser/ui/webui/hello_world_ui.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
HelloWorldUI::HelloWorldUI(content::WebUI* web_ui)
: content::WebUIController(web_ui)
{
// give web_ui to content::WebUIMessageHandler
// IsJavascriptAllowed 和执行js函数会用到web_ui, 不能为空
set_web_ui(web_ui);
// Set up the chrome://hello-world source.
content::WebUIDataSource* html_source =
content::WebUIDataSource::Create(chrome::kChromeUIHelloWorldHost);
// Register callback handler.
web_ui->RegisterMessageCallback(
"addNumbers",
base::Bind(&HelloWorldUI::AddNumbers,
base::Unretained(this)));
// Localized strings.
// html_source->AddLocalizedString是添加资源中固定的字符串
html_source->AddLocalizedString("my_helloWorldTitle", IDS_LS_WEB_UI_TITLE);
html_source->AddLocalizedString("my_welcome_message", IDS_LS_HELLO_WORLD_WELCOME_TEXT);
// As a demonstration of passing a variable for JS to use we pass in the name "Bob".
// html_source->AddString是添加动态的字符串, 这函数实用
html_source->AddString("userName", "Bob");
html_source->SetJsonPath("strings.js");
// Add required resources.
html_source->AddResourcePath("my_hello_world.css", IDR_HELLO_WORLD_CSS);
html_source->AddResourcePath("my_hello_world.js", IDR_HELLO_WORLD_JS);
html_source->SetDefaultResource(IDR_HELLO_WORLD_HTML);
content::BrowserContext* browser_context =
web_ui->GetWebContents()->GetBrowserContext();
content::WebUIDataSource::Add(browser_context, html_source);
}
HelloWorldUI::~HelloWorldUI() {
}
void HelloWorldUI::RegisterMessages()
{
}
bool HelloWorldUI::OverrideHandleWebUIMessage(const GURL& source_url,
const std::string& name,
const base::ListValue& args)
{
if (source_url.spec() != chrome::kChromeUIHelloWorldURL) {
return false;
}
// on js the sender content below:
// chrome.send('addNumbers', [66, 88]);
if (name == "addNumbers") {
AddNumbers(&args);
return true;
}
// 如果有其他的js请求,也照"addNumbers"一并处理了
return false;
}
void HelloWorldUI::AddNumbers(const base::ListValue* args) {
int term1 = 0;
int term2 = 0;
AllowJavascript(); // 需要允许运行js
if (!args->GetInteger(0, &term1) || !args->GetInteger(1, &term2)) {
return;
}
base::Value result(term1 + term2);
// CallJavascriptFunction 最后调用了 MessagePipeReader::Send
// 如果js函数写错了(e.g. "addResult"), 不会报错, 但是没效果(没有调用到js文件中的addResult函数)
CallJavascriptFunction("my_page.addResult", result);
}
编辑Z:\chromium\src\chrome\browser\ui\BUILD.gn
将hello_world_ui.*交给编译器, 编进ui_0.lib
# TODO(estade): this class should be deleted in favor of a combobox model.
# See crbug.com/590850
"content_settings/content_setting_media_menu_model.cc",
"content_settings/content_setting_media_menu_model.h",
"javascript_dialogs/javascript_dialog_cocoa.h",
"javascript_dialogs/javascript_dialog_cocoa.mm",
"javascript_dialogs/javascript_dialog_mac.cc",
"proximity_auth/proximity_auth_error_bubble_stub.cc",
"startup/session_crashed_infobar_delegate.cc",
"startup/session_crashed_infobar_delegate.h",
]
}
sources = [
"accelerator_utils.h",
"app_list/app_list_util.cc",
"app_list/app_list_util.h",
# All other browser/ui/app_list files go under enable_app_list below.
"webui/hello_world_ui.cc",
"webui/hello_world_ui.h",
"autofill/autofill_dialog_models.cc",
"autofill/autofill_dialog_models.h",
"autofill/autofill_popup_controller.h",
"autofill/autofill_popup_controller_impl.cc",
"autofill/autofill_popup_controller_impl.h",
"autofill/autofill_popup_controller_impl_mac.h",
"autofill/autofill_popup_controller_impl_mac.mm",
编辑Z:\chromium\src\chrome\browser\ui\webui\chrome_web_ui_controller_factory.cc
根据输入的url,创建对应的web-ui
#include "chrome/browser/ui/webui/hello_world_ui.h"
// Returns a function that can be used to create the right type of WebUI for a
// tab, based on its URL. Returns NULL if the URL doesn't have WebUI associated
// with it.
WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
Profile* profile,
const GURL& url) {
// This will get called a lot to check all URLs, so do a quick check of other
// schemes to filter out most URLs.
if (!url.SchemeIs(content::kChromeDevToolsScheme) &&
!url.SchemeIs(content::kChromeUIScheme)) {
return NULL;
}
// @todo ls
if (url.host() == chrome::kChromeUIHelloWorldHost) {
return &NewWebUI<HelloWorldUI>;
}
/****************************************************************************
* Please keep this in alphabetical order. If #ifs or special logics are
* required, add it below in the appropriate section.
***************************************************************************/
// We must compare hosts only since some of the Web UIs append extra stuff
// after the host name.
// All platform builds of Chrome will need to have a cloud printing
// dialog as backup. It's just that on Chrome OS, it's the only
// print dialog.
if (url.host_piece() == chrome::kChromeUIBluetoothInternalsHost)
return &NewWebUI<BluetoothInternalsUI>;
if (url.host_piece() == chrome::kChromeUIComponentsHost)
return &NewWebUI<ComponentsUI>;
重新生成工程文件
Z:\chromium\src>gn gen --ide=vs out\Default_68_0_3440_84
重新编译工程
Z:\chromium\src>autoninja -C out\Default_68_0_3440_84 chrome
实验运行新加入的web_ui的效果