在chromium浏览器中,有两种显示部分。一个是显示网页的部分我们叫它embeded内容,用webui实现。其他部分就是非webui,比如菜单栏,url输入框,前进后退按钮。
一、WebUI
WebUI 是指:
一套专门用于构建基于 HTML/CSS/JS 的内部网页界面系统,运行在 renderer process 中,但与浏览器核心交互。
示例:
-
chrome://settings/设置页面 -
chrome://bookmarks/书签管理器 -
chrome://flags/实验功能开关界面
这些页面虽然运行在浏览器内部,但其实是用 HTML/CSS/JS 构建的,就像普通网页一样,但具有与浏览器进程通信的特权。使用 WebUIController、Mojo 等机制桥接 C++ ↔ JS。
典型路径:
src/chrome/browser/ui/webui/ // 控制器类(C++)
src/chrome/browser/resources/ // HTML/JS/CSS 资源
二、Chromium 的非 WebUI 界面:Views 系统
Chromium 浏览器自身的 UI(浏览器框架壳)是用一个名叫 Views 的 C++ UI 框架开发的:
它包括:
-
地址栏(Omnibox)
-
返回 / 前进 / 刷新 / Home 按钮
-
工具栏、标签栏(TabStrip)
-
浏览器主菜单(... 下拉菜单)
实现位置:
src/ui/views/ # Views 基础控件
src/chrome/browser/ui/views/ # 浏览器的主界面 UI 实现
src/chrome/browser/ui/views/toolbar/ # 工具栏相关控件
src/chrome/browser/ui/views/location_bar/ # 地址栏控件
- 可以参考代码:
# 地址栏控件:
chrome/browser/ui/views/location_bar/location_bar_view.cc
# 工具栏控件:
chrome/browser/ui/views/toolbar/toolbar_view.cc
# 主窗口:
chrome/browser/ui/views/frame/browser_view.cc
🔹1. 为什么 Views 能做到跨平台?
Chromium 使用了一整套自研的 平台抽象 UI 框架,包括:
| 层次 | 作用 | 框架 / 组件 |
|---|---|---|
| UI 控件层 | 通用 UI 控件(按钮、输入框、列表等) | ui/views |
| 绘图层 | 负责绘制矢量图、文字、位图等 | Skia(跨平台绘图引擎) |
| 平台适配层 | 提供原生窗口、输入事件、光标等 | ui/platform_window/ + base/message_loop/ |
✅ 控件定义是“平台无关”的(统一用 C++ 实现):
比如 Label, Button, Textfield,在 ui/views/controls/ 目录下是统一的 C++ 类,不依赖平台 GUI 框架。
✅ 但绘图和窗口管理是“平台相关”的:
-
macOS → 使用 CoreAnimation/NSWindow + Skia
-
Windows → 使用 HWND + Skia + Direct2D/GDI
-
Linux → 使用 X11/Wayland + Skia
Skia 是 Google 自研的跨平台 2D 绘图库,Android、Chrome 都用它。
🔹2. 合成线程影响所有 UI,是为什么?
Chromium 的 整个图形系统(包括 WebContents、Views)都依赖 Compositor 合成输出!
Chromium 图形渲染总流程:
所以不论是 Web 页面还是原生 UI 控件,它们都会生成 Skia draw ops,交给合成线程合成。如果你关闭了合成线程,整个画面“卡在了中间过程”,什么都无法显示,包括地址栏、菜单栏等。
✅ Views 虽然不使用 HTML,但它仍然要“画出来”,靠的是 Skia → Compositor → GPU。
🔹3. 菜单栏、按钮控件在不同平台如何实现?
控件逻辑是平台通用的:
-
所有按钮、菜单栏逻辑都在
ui/views里用 C++ 定义 -
例如
BackButton、ToolbarButton、AppMenuButton
但平台适配部分会使用不同“Widget Host”
例如:
| 平台 | 顶层窗口实现 | 控件绘图方式 |
|---|---|---|
| Windows | HWND + WindowImpl | Skia 渲染到 Direct2D 或 GDI |
| macOS | NSWindow + NSView | Skia 渲染成 CALayer,挂到 NSView 上 |
| Linux | X11Window / WaylandWindow | Skia 绘图输出到 X11/Wayland surface |
Chromium 使用 views::Widget 类统一封装窗口和控件树,但其底层用平台特定代码来适配系统窗口管理器。
📁 几个关键源码位置
| 功能 | 路径 | 说明 |
|---|---|---|
| Views 控件入口 | ui/views/ | 所有 UI 控件定义 |
| 主窗口 | chrome/browser/ui/views/frame/browser_view.cc | 浏览器主 UI 框架 |
| 平台窗口接口 | ui/platform_window/ | 跨平台窗口抽象 |
| 渲染层桥接 | ui/compositor/ | Compositor 层的组织逻辑 |
| Skia 调用 | cc/paint/、ui/gfx/ | SkCanvas、SkImage 的封装 |
🧠 延伸理解:Skia + Compositor 是 Chromium UI 的大一统方案
Chromium 不像传统原生应用用 Qt/WinAPI/AppKit,它把所有东西(HTML + UI 控件)都统一到 Skia 渲染 → Compositor 合成 这一条链上。
这样做的好处:
-
所有 UI 都支持动画、透明度、阴影
-
跨平台一致性强
-
后期统一 GPU 加速、硬件合成优化
代价:
-
对图形系统耦合度高,合成线程关闭会导致 UI 全挂
-
初学者难以理解 “明明不是网页,为何也渲染不出来”
✅ 总结
| 问题 | 答案 |
|---|---|
| Views 是如何跨平台的? | 用统一 C++ 控件 + Skia 绘图 + 平台窗口适配器实现 |
| 合成线程关闭为什么整个 UI 都挂了? | Chromium 的原生控件也依赖 Skia + Compositor 绘图 |
| 菜单栏控件是怎么显示的? | 控件逻辑统一,绘图输出在 Skia,由平台窗口包裹(如 NSWindow/HWND)显示 |
三、BackButton实现讲解
BackButton 是 Chromium 浏览器顶部工具栏(toolbar)左上角那个“返回”箭头按钮,它是 Chromium UI 框架 Views 中的一个控件,由 C++ 代码实现和绘制,不依赖 HTML/CSS/JS。
🧩 一、BackButton 的类层次与路径
🔹 类结构:
BackButton : public ToolbarButton
→ LabelButton
→ Button
→ View
🔍 核心路径:
chrome/browser/ui/views/toolbar/back_button.cc
chrome/browser/ui/views/toolbar/back_button.h
这是它的类定义和实现文件。BackButton 继承自 ToolbarButton,后者又是 Chromium 中一个可点击、可绘图的按钮控件。
🖌️ 二、绘制过程大致流程
BackButton 的绘制是通过 C++ 调用 Skia API(SkCanvas)进行的,整个过程大致如下:
1. 在主窗口中创建 BackButton 实例
在 BrowserView 初始化过程中:
back_button_ = toolbar_->AddChildView(std::make_unique<BackButton>());
2. BackButton 会使用 Vector Icon 绘制箭头图标
在 back_button.cc 中:
SetImageModel(ButtonState::STATE_NORMAL,
ui::ImageModel::FromVectorIcon(kBackArrowIcon));
-
kBackArrowIcon是一个内置的矢量图标,定义在chrome/app/vector_icons/中的.icon文件(其实是 SVG 简化格式)。 -
它会被编译为 C++ 中的 path 描述,最终通过 Skia 绘制。
const gfx::VectorIcon kBackArrowIcon = {
kBackArrowPath, // SkPath 描述路径
base::size(kBackArrowPath),
gfx::VectorIconId::BACK_ARROW,
};
Chromium 有自己的矢量图标处理工具链,支持多种状态(正常 / hover / 按下等)切换不同颜色或透明度。
🎨 三、绘图函数调用栈(Skia)
最终,BackButton 的绘图操作会通过:
ToolbarButton::OnPaint()
→ LabelButton::OnPaint()
→ Button::PaintButtonContents()
→ PaintIcon()
→ ImageSkia::Draw()
→ SkCanvas::drawPath() / drawBitmap()
💡 关键点:
-
实际绘图发生在 Chromium 的绘图层
ui/gfx/下,使用 SkCanvas。 -
所有控件绘图操作会组成 draw op 列表(DisplayItemList),交给
Compositor合成。
🧪 四、如何定制或观察 BackButton 绘图?
你可以:
-
在
back_button.cc里设置自定义图标:SetImageModel(STATE_NORMAL, ui::ImageModel::FromImage(gfx::CreateVectorIcon(kMyCustomIcon, 16, SK_ColorRED))); -
在 OnPaint() 添加 Skia 调试输出:
void BackButton::OnPaint(gfx::Canvas* canvas) { LOG(ERROR) << "Painting BackButton!"; ToolbarButton::OnPaint(canvas); } -
调试绘图流程:
-
启用
--enable-skia-deferred-display-list -
使用 Chromium 的
skia_debugger工具查看画面构建过程
-
💬 五、附加细节:响应事件、动画等
-
鼠标悬浮/点击效果使用
InkDrop动画系统(Material Design 风格) -
可访问性(Accessibility)支持通过
View::GetAccessibleNodeData()提供名称与用途 -
按钮点击事件最终会调用:
chrome::ExecuteCommand(browser_, IDC_BACK);用于执行历史后退导航
✅ 总结
| 项目 | 实现方式 |
|---|---|
| BackButton 类型 | C++ 类 BackButton,继承自 ToolbarButton |
| 图标绘制 | 使用矢量图标 kBackArrowIcon,通过 Skia 绘制 |
| 渲染系统 | Skia + Views + Compositor |
| 不依赖 | HTML、CSS、JS、WebView(完全原生) |
| 代码路径 | chrome/browser/ui/views/toolbar/back_button.cc |
四、自定义控件实现
在 Chromium 的 Views 框架中实现一个自定义控件:“前进 + 后退”组合按钮(ForwardBackButton),功能如下:
🧩 功能设计:
-
左侧是“←”返回按钮,右侧是“→”前进按钮
-
两个图标合在一个控件中
-
鼠标点击左右区域分别触发对应命令:
IDC_BACK和IDC_FORWARD -
布局与绘制完全使用 Views + Skia
📁 目录建议:
你可以将这个控件放在:
chrome/browser/ui/views/toolbar/forward_back_button.{h,cc}
✅ 1. forward_back_button.h
#pragma once
#include "ui/views/view.h"
#include "ui/views/controls/button/button.h"
class Browser;
class ForwardBackButton : public views::View, public views::ButtonListener {
public:
explicit ForwardBackButton(Browser* browser);
// views::View:
void OnPaint(gfx::Canvas* canvas) override;
gfx::Size CalculatePreferredSize() const override;
void Layout() override;
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
private:
raw_ptr<Browser> browser_;
raw_ptr<views::Button> back_button_;
raw_ptr<views::Button> forward_button_;
};
✅ 2. forward_back_button.cc
#include "chrome/browser/ui/views/toolbar/forward_back_button.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/browser_commands.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/vector_icon_utils.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/layout/box_layout.h"
ForwardBackButton::ForwardBackButton(Browser* browser) : browser_(browser) {
auto back = std::make_unique<views::ImageButton>(this);
back->SetImageModel(views::Button::STATE_NORMAL,
ui::ImageModel::FromVectorIcon(kBackArrowIcon));
back->SetAccessibleName(u"Back");
back_button_ = AddChildView(std::move(back));
auto forward = std::make_unique<views::ImageButton>(this);
forward->SetImageModel(views::Button::STATE_NORMAL,
ui::ImageModel::FromVectorIcon(kForwardArrowIcon));
forward->SetAccessibleName(u"Forward");
forward_button_ = AddChildView(std::move(forward));
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 2));
}
void ForwardBackButton::OnPaint(gfx::Canvas* canvas) {
View::OnPaint(canvas); // 默认背景绘制
}
gfx::Size ForwardBackButton::CalculatePreferredSize() const {
gfx::Size back_size = back_button_->GetPreferredSize();
gfx::Size forward_size = forward_button_->GetPreferredSize();
return gfx::Size(back_size.width() + forward_size.width() + 4,
std::max(back_size.height(), forward_size.height()));
}
void ForwardBackButton::Layout() {
back_button_->SetBounds(0, 0,
back_button_->GetPreferredSize().width(), height());
forward_button_->SetBounds(
back_button_->bounds().right() + 4, 0,
forward_button_->GetPreferredSize().width(), height());
}
void ForwardBackButton::ButtonPressed(views::Button* sender,
const ui::Event& event) {
if (sender == back_button_) {
chrome::ExecuteCommand(browser_, IDC_BACK);
} else if (sender == forward_button_) {
chrome::ExecuteCommand(browser_, IDC_FORWARD);
}
}
🚀 3. 如何使用它?
打开 browser_view.cc 或 toolbar_view.cc 中你希望插入的位置,比如:
#include "chrome/browser/ui/views/toolbar/forward_back_button.h"
...
forward_back_button_ = AddChildView(std::make_unique<ForwardBackButton>(browser_));
🎨 4. 可拓展性建议
-
添加 hover 动画(InkDrop)
-
支持禁用状态(不能前进/后退时灰掉)
-
支持长按弹出历史记录菜单(如 Chrome 原始行为)
-
支持 Mac 风格圆角按钮风格
✅ 总结
| 内容 | 说明 |
|---|---|
| 框架 | 使用 Views 实现组合控件 |
| 图标 | 使用 VectorIcon |
| 事件响应 | 使用 ButtonListener |
| 可移植性 | 不依赖平台代码,跨平台支持 |
| 插入方式 | 在 Toolbar 中添加子 View |

4970

被折叠的 条评论
为什么被折叠?



