前面通过一些列文章,我们了解清楚了chromium的线程模型和ipc通信框架ipcz。 接下来我们分析一下chromium的启动流程。
本篇以browser进程启动为例进行分析。
chromium的启动入口在chrome/app/chrome_exe_main_aura.cc中。
这里面Aura是Chromium项目中用于窗口管理的底层框架。它负责管理用户界面的窗口和其他图形元素。Aura提供了一个跨平台的框架,用于构建Chromium浏览器的用户界面组件。
Aura框架被引入以取代Chromium项目中早期使用的称为“views”的窗口系统。Aura提供更好的性能和更大的灵活性,更容易支持多个平台。
值得注意的是,软件项目可能随时间发展而发生变化,因此自我在2022年1月进行更新以来,Chromium和Aura可能已经有了变化或更新。如果有重大变化或新的发布,请查阅官方的Chromium项目文档或发布说明以获取最新信息。
废话不多说我们直接看代码
int main(int argc, const char** argv) {
return ChromeMain(argc, argv);
}
函数直接调用ChromeMain函数。
chrome/app/chrome_main.cc
int ChromeMain(int argc, const char** argv) {
int64_t exe_entry_point_ticks = 0;
,,,,,,
ChromeMainDelegate chrome_main_delegate(
base::TimeTicks::FromInternalValue(exe_entry_point_ticks));
content::ContentMainParams params(&chrome_main_delegate);
#if BUILDFLAG(IS_WIN)
......
#else
params.argc = argc;
params.argv = argv;
base::CommandLine::Init(params.argc, params.argv);
#endif // BUILDFLAG(IS_WIN)
base::CommandLine::Init(0, nullptr);
[[maybe_unused]] base::CommandLine* command_line(
base::CommandLine::ForCurrentProcess());
......
// Chrome-specific process modes.
if (headless::IsHeadlessMode()) {
......
}
......
int rv = content::ContentMain(std::move(params));
if (chrome::IsNormalResultCode(static_cast<chrome::ResultCode>(rv)))
return content::RESULT_CODE_NORMAL_EXIT;
return rv;
}
函数主要准备参数, 并且处理Headless模式启动,我们不多分析,准备好参数之后调用ContentMain函数。
content/app/content_main.cc
int NO_STACK_PROTECTOR ContentMain(ContentMainParams params) {
auto runner = ContentMainRunner::Create();
return RunContentProcess(std::move(params), runner.get());
}
函数先创建ContentMainRunner类,实例为ContentMainRunnerImpl。
ContentMain函数调用RunContentProcess 函数。继续往下看
content/app/content_main.cc
178 int NO_STACK_PROTECTOR
179 RunContentProcess(ContentMainParams params,
180 ContentMainRunner* content_main_runner) {
181 base::FeatureList::FailOnFeatureAccessWithoutFeatureList();
......
// 初始化CommandLine
238 base::CommandLine::Init(argc, argv);
239
240 #if BUILDFLAG(IS_POSIX)
// 初始化文件描述符表
241 PopulateFileDescriptorStoreFromFdTable();
242 #endif
......
247 #endif // !BUILDFLAG(IS_ANDROID)
248
......
300 ui::RegisterPathProvider();
// 初始化content_main_runner
301 exit_code = content_main_runner->Initialize(std::move(params));
302
303 if (exit_code >= 0) {
304 return exit_code;
305 }
306
......
324 if (IsSubprocess())
325 CommonSubprocessInit();
326 exit_code = content_main_runner->Run();
327
......
335
336 return exit_code;
337 }
ContentMainRunnerImpl 主要用于启动浏览器, 301 行先初始化,326行调用ContentMainRunnerImpl.Run() 方法启动浏览器。
我们先看ContentMainRunnerImpl的初始化函数。
784 int ContentMainRunnerImpl::Initialize(ContentMainParams params) {
785 // ContentMainDelegate is used by this class, not forwarded to embedders.
......
821
822 is_initialized_ = true;
......
847
// 创建ContentClient
848 if (!GetContentClient())
849 ContentClientCreator::Create(delegate_);
......
878
// 初始化ContentClient
879 ContentClientInitializer::Set(process_type, delegate_);
880
......
// 加载V8引擎快照
953 LoadV8SnapshotIfNeeded(command_line, process_type);
954
......
1034 // Return -1 to indicate no early termination.
1035 return -1;
1036 }
848创建ContentClient实例子。在Chromium项目中,ContentClient 是一个接口类,它定义了与内容(Content)相关的一些回调和功能。具体来说,ContentClient 提供了一个接口,允许浏览器的不同部分(比如渲染进程)与内容相关的操作进行通信。
889行初始化ContentClient。我们看先ContentClient的初始化。
class ContentClientInitializer {
public:
static void Set(const std::string& process_type,
ContentMainDelegate* delegate) {
ContentClient* content_client = GetContentClient();
if (process_type.empty())
content_client->browser_ = delegate->CreateContentBrowserClient();
base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
if (process_type == switches::kGpuProcess ||
cmd->HasSwitch(switches::kSingleProcess) ||
(process_type.empty() && cmd->HasSwitch(switches::kInProcessGPU)))
content_client->gpu_ = delegate->CreateContentGpuClient();
if (process_type == switches::kRendererProcess ||
cmd->HasSwitch(switches::kSingleProcess))
content_client->renderer_ = delegate->CreateContentRendererClient();
if (process_type == switches::kUtilityProcess ||
cmd->HasSwitch(switches::kSingleProcess))
content_client->utility_ = delegate->CreateContentUtilityClient();
}
};
函数主要初始化了如下成员变量
接下来我们继续看启动流程ContentMainRunnerImpl.Run().
1046 // This function must be marked with NO_STACK_PROTECTOR or it may crash on
1047 // return, see the --change-stack-guard-on-fork command line flag.
1048 int NO_STACK_PROTECTOR ContentMainRunnerImpl::Run() {
......
1052 const base::CommandLine* command_line =
1053 base::CommandLine::ForCurrentProcess();
// 获取进程类型
1054 std::string process_type =
1055 command_line->GetSwitchValueASCII(switches::kProcessType);
1056
// 等待调试器
1057 if (command_line->HasSwitch(switches::kWaitForDebugger)) {
1058 DVLOG(1) << "Start WaitForDebugger1";
1059 base::debug::WaitForDebugger(600, true);
1060 DVLOG(1) << "End WaitForDebugger1";
1061 }
1062
......
1067 // Run this logic on all child processes.
1068 if (!process_type.empty()) {
// process_type 不为空,表示非browser进程
1069 if (process_type != switches::kZygoteProcess) {
// 非browser进程,非zygote进程做的一些初始化工作
......
1086 }
1087
1088 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
1089 // If dynamic Mojo Core is being used, ensure that it's loaded very early in
1090 // the child/zygote process, before any sandbox is initialized. The library
1091 // is not fully initialized with IPC support until a ChildProcess is later
1092 // constructed, as initialization spawns a background thread which would be
1093 // unsafe here.
// 加载mojo库
1094 if (IsMojoCoreSharedLibraryEnabled()) {
1095 CHECK_EQ(mojo::LoadCoreLibrary(GetMojoCoreSharedLibraryPath()),
1096 MOJO_RESULT_OK);
1097 }
1098 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
1099 }
1100
// 准备启动参数
1101 MainFunctionParams main_params(command_line);
1102 main_params.ui_task = std::move(content_main_params_->ui_task);
1103 main_params.created_main_parts_closure =
1104 std::move(content_main_params_->created_main_parts_closure);
1105 main_params.needs_startup_tracing_after_mojo_init =
1106 needs_startup_tracing_after_mojo_init_;
......
1123
1124 if (process_type.empty()) // 启动浏览器进程
1125 return RunBrowser(std::move(main_params), start_minimal_browser);
1126
// 启动其他进程
1127 return RunOtherNamedProcessTypeMain(process_type, std::move(main_params),
1128 delegate_);
1129 }
1054-1055行代码根据启动参数获取进程启动类型。
1057-1061行代码 如果启动参数里面有–wait-for-debugger,则等待调试器。
1068-1099行如果不是browser进程做一些初始化操作,这里不详细分析。
1125 行如果启动的进程类型为browser,则调用RunBrowser启动浏览器。
1127-1128行启动其他类型的进程。
我们这篇文章分析浏览器类型进程启动。 所以看下RunBrowser函数。
1131 int ContentMainRunnerImpl::RunBrowser(MainFunctionParams main_params,
1132 bool start_minimal_browser) {
......
// 创建BrowserTaskExecutor, 用于管理ui线程和io线程
1182 BrowserTaskExecutor::Create();
1183
......
1204 if (has_thread_pool) {
1205 // The FeatureList needs to create before starting the ThreadPool.
// 启动线程池
1206 StartBrowserThreadPool();
1207 }
1208
......
1271 return RunBrowserProcessMain(std::move(main_params), delegate_);
1272 }
函数做了一些初始化工作,包括创建BrowserTaskExceutor 用于管理io线程和ui线程的调度。 启动线程池,然后调用RunBrowserProcessMain函数进一步启动。
int RunBrowserProcessMain(MainFunctionParams main_function_params,
ContentMainDelegate* delegate) {
.......
return BrowserMain(std::move(absl::get<MainFunctionParams>(exit_code)));
}
RunBrowserProcessMain 函数比较简单,直接调用了BrowserMain函数。
content/browser/browser_main.cc
// Main routine for running as the Browser process.
int BrowserMain(MainFunctionParams parameters) {
......
base::CurrentProcess::GetInstance().SetProcessType(
base::CurrentProcessType::PROCESS_BROWSER);
......
std::unique_ptr<BrowserMainRunnerImpl> main_runner(
BrowserMainRunnerImpl::Create());
int exit_code = main_runner->Initialize(std::move(parameters));
......
exit_code = main_runner->Run();
......
return exit_code;
}
函数先创建BrowserMainRunnerImpl实例,然后执行它的Initialize函数进行初始化,最后调用它的Run方法进入到Looper消息循环。我们先看Initialize方法。
content/browser/browser_main_runner_impl.cc
71 int BrowserMainRunnerImpl::Initialize(MainFunctionParams parameters) {
......
// 初始化Skia 相关Grphic 功能
83 SkGraphics::Init();
84
.....
94
// 创建通知服务
95 notification_service_ = std::make_unique<NotificationServiceImpl>();
96
......
// 初始化字体
105 gfx::InitializeFonts();
106
107 auto created_main_parts_closure =
108 std::move(parameters.created_main_parts_closure);
109
// 创建Browser进程的主循环对象
110 main_loop_ = std::make_unique<BrowserMainLoop>(
111 std::move(parameters), std::move(scoped_execution_fence_));
112
113 main_loop_->Init();
114
115 if (created_main_parts_closure) {
116 std::move(created_main_parts_closure).Run(main_loop_->parts());
117 }
118
// 执行早期初始化
119 const int early_init_error_code = main_loop_->EarlyInitialization();
120 if (early_init_error_code > 0) {
121 main_loop_->CreateMessageLoopForEarlyShutdown();
122 return early_init_error_code;
123 }
124
125 // Must happen before we try to use a message loop or display any UI.
// 初始化工具套件
126 if (!main_loop_->InitializeToolkit()) {
127 main_loop_->CreateMessageLoopForEarlyShutdown();
128 return 1;
129 }
130
// 创建MainLoop前执行操作
131 main_loop_->PreCreateMainMessageLoop();
// 创建MainLoop
132 main_loop_->CreateMainMessageLoop();
// 创建MainLoop后执行操作
133 main_loop_->PostCreateMainMessageLoop();
134
135 // WARNING: If we get a WM_ENDSESSION, objects created on the stack here
136 // are NOT deleted. If you need something to run during WM_ENDSESSION add it
137 // to browser_shutdown::Shutdown or BrowserProcess::EndSession.
138
// 初始化输入法相关
139 ui::InitializeInputMethod();
140 }
// 执行启动任务。
141 main_loop_->CreateStartupTasks();
142 int result_code = main_loop_->GetResultCode();
143 if (result_code > 0) {
144 return result_code;
145 }
146
147 // Return -1 to indicate no early termination.
148 return -1;
149 }
150
函数使用BrowserMainLoop 包装线程模型里面的Looper。
110-111行创建BrowserMainLoop实例。
113行执行main_loop_的初始化。
119 行执行浏览器的早期初始化工作。
126 行初始化toolkit。
131-133行执行main_loop_ 初始化方法,在Looper前、中、后执行对应方法。
139行初始化输入法相关。
141行执行启动相关任务。
我们来看一下BrowserMainLoop的init方法。
content/browser/browser_main_loop.cc
void BrowserMainLoop::Init() {
TRACE_EVENT0("startup", "BrowserMainLoop::Init");
if (parameters_.startup_data) {
......
}
parts_ = GetContentClient()->browser()->CreateBrowserMainParts(
!!parameters_.ui_task);
}
函数比较简单,这里创建了BrowserMainParts。 BrowserMainParts 是浏览器的主要部分,是通过不同part来实现浏览器的不同功能。我们来看一下它实现了哪些part。
chrome/browser/chrome_content_browser_client.cc
681 std::unique_ptr<content::BrowserMainParts>
1682 ChromeContentBrowserClient::CreateBrowserMainParts(bool is_integration_test) {
1683 std::unique_ptr<ChromeBrowserMainParts> main_parts;
1684 // Construct the Main browser parts based on the OS type.
1685 #if BUILDFLAG(IS_WIN)
......
1697 #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
// 创建main_parts 实例子, 是平台相关的
1698 main_parts = std::make_unique<ChromeBrowserMainPartsLinux>(
1699 is_integration_test, &startup_data_);
......
1709 #else
......
1713 #endif
1714
// 线程通知相关
1715 main_parts->AddParts(
1716 std::make_unique<ChromeBrowserMainExtraPartsThreadNotifier>(
1717 base::BindOnce(&ChromeContentBrowserClient::InitOnUIThread,
1718 weak_factory_.GetWeakPtr())));
1719
1720 bool add_profiles_extra_parts = true;
......
1736 #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
// 增加LinuxViews 相关的Part
1737 main_parts->AddParts(
1738 std::make_unique<ChromeBrowserMainExtraPartsViewsLinux>());
1739 #else
1740 main_parts->AddParts(std::make_unique<ChromeBrowserMainExtraPartsViews>());
1741 #endif
1742 #endif
1743
......
1756
1757 #if BUILDFLAG(IS_LINUX)
// 添加Linux平台特有的Part
1758 main_parts->AddParts(std::make_unique<ChromeBrowserMainExtraPartsLinux>());
1759 #elif BUILDFLAG(IS_OZONE)
1760 main_parts->AddParts(std::make_unique<ChromeBrowserMainExtraPartsOzone>());
1761 #endif
1762
// 性能监控相关part
1763 main_parts->AddParts(
1764 std::make_unique<ChromeBrowserMainExtraPartsPerformanceMonitor>());
1765
// 性能管理相关part
1766 main_parts->AddParts(
1767 std::make_unique<ChromeBrowserMainExtraPartsPerformanceManager>());
1768
// Profiling(性能优化)相关的part
1769 main_parts->AddParts(
1770 std::make_unique<ChromeBrowserMainExtraPartsProfiling>());
1771
// 内存相关
1772 main_parts->AddParts(std::make_unique<ChromeBrowserMainExtraPartsMemory>());
......
1796
1797 return main_parts;
1798 }
这个函数创建了ChromeBrowserMainPartsLinux这个main_parts, 又向这个main_parts 添加了多个extra_main_part. 下面给出MainParts 和ExtrasMainParts的数据结构。
ChromeBrowserMainPartsLinux 继承自ChromeBrowserMainPartsix,ChromeBrowserMainPartsPosix继承自ChromeBrowserParts, ChromeBrowserParts继承自BrowserMainParts。 ChromeBrowserParts的成员变量chrome_extra_parts_ 指向多个ChromeBrowserMainExtraParts。 BrowserMainParts实现了多个方法,在Chrom的启动周期调用,ChromeBrowserMainExtraParts也实现了这些方法,在调用BrowserMainParts这些方法的时候也会调用ChromeBrowserMainExtraParts的这些方法。我们后面会看到。
在BrowserMainParts定义的注释中给出了它的虚方法的调用时机,我们来看一下。
content/public/browser/browser_main_parts.h
// This class contains different "stages" to be executed by |BrowserMain()|.
//
// Stages:
//
// ** Cross-platform startup stages.
// ** Invoked during BrowserMainRunnerImpl::Initialize(), after
// ContentMainRunner's full initialization.
//
// - PreEarlyInitialization: things to be be done as soon as possible on
// program start (such as setting up signal handlers; checking auto-restarts
// on update; etc.). Core APIs like base::FeatureList,
// base::SingleThreadTaskRunner::CurrentDefaultHandle, and base::ThreadPool
// are already functional at this point (ThreadPool will accept but not run
// tasks until PostCreateThreads).
//
// - PostEarlyInitialization: things to be be done as soon as possible but that
// can/must wait until after the few things in BrowserMainLoop's own
// EarlyInitialization have completed.
//
// - ToolkitInitialized: similar to PostEarlyInitialization but for the UI
// toolkit. Allows an embedder to do any extra toolkit initialization.
//
// - PreCreateMainMessageLoop: things to be done at some generic time before
// the creation of the main message loop.
//
// - PostCreateMainMessageLoop: things to be done as early as possible but that
// need the main message loop to be around (i.e. BrowserThread::UI is up).
//
// - PreCreateThreads: things that don't need to happen super early but still
// need to happen during single-threaded initialization (e.g. immutable
// Singletons that are initialized once and read-only from all threads
// thereafter).
// Note: other threads might exist before this point but no child threads
// owned by content. As such, this is still "single-threaded" initialization
// as far as content and its embedders are concerned and the right place to
// initialize thread-compatible objects:
// https://chromium.googlesource.com/chromium/src/+/main/docs/threading_and_tasks.md#threading-lexicon
//
// - PostCreateThreads: things that should be done as early as possible but
// need browser process threads to be alive (i.e. BrowserThread::IO is up and
// base::ThreadPool is running tasks).
//
// - PreMainMessageLoopRun: IN DOUBT, PUT THINGS HERE. At this stage all core
// APIs have been initialized. Services that must be initialized before the
// browser is considered functional can be initialized from here. Ideally
// only the frontend is initialized here while the backend takes advantage of
// a base::ThreadPool worker to come up asynchronously. Things that must
// happen on the main thread eventually but don't need to block startup
// should post a BEST_EFFORT task from this stage.
//
// ** End of cross-platform startup stages.
// ** Stages above are run as part of startup stages in
// ** BrowserMainLoop::CreateStartupTasks() and can even be run eagerly (e.g.
// ** Android app warmup attempts to run these async.
//
// - WillRunMainMessageLoop: The main thread's RunLoop will be run
// *immediately* upon returning from this method. While PreMainMessageLoopRun
// gives that impression, in practice it's part of initialization phases
// which are triggered independently from MainMessageLoopRun (and can even
// happen async). In browser tests, PreMainMessageLoopRun() will run before
// entering test bodies whereas WillRunMainMessageLoop() won't (the control
// is given to the test rather running the loop). Furthermore, this is only
// called on platforms where BrowserMainLoop::RunMainMessageLoop is called.
// Thus, very few things should be done at this stage. It's mostly intended
// as a way for embedders to override or cancel the default RunLoop if
// needed.
//
// - OnFirstIdle: The main thread reached idle for the first time since
// WillRunMainMessageLoop(). In other words, it's done running any tasks
// posted as part of the above phases and anything else posted from these.
//
// - PostMainMessageLoopRun: stop and cleanup things that can/should be cleaned
// up while base::ThreadPool and BrowserThread::IO are still running.
// Note: Also see BrowserMainLoop::ShutdownThreadsAndCleanUp() which is often
// a good fit to stop services (PostMainMessageLoopRun() is called from it).
//
// - PostDestroyThreads: stop and cleanup things that need to be cleaned up in
// the single-threaded teardown phase (i.e. typically things that had to
// created in PreCreateThreads()).
//
//
// How to add stuff (to existing parts):
// - Figure out when your new code should be executed. What must happen
// before/after your code is executed? Are there performance reasons for
// running your code at a particular time? Document these things!
// - Split out any platform-specific bits. Please avoid #ifdefs it at all
// possible. You have two choices for platform-specific code: (1) Execute it
// from one of the |Pre/Post...()| methods in an embedder's platform-specific
// override (e.g., ChromeBrowserMainPartsWin::PreCreateMainMessageLoop()); do
// this if the code is unique to an embedder and platform type. Or (2)
// execute it from one of the "stages" (e.g.,
// |BrowserMainLoop::EarlyInitialization()|) and provide platform-specific
// implementations of your code (in a virtual method); do this if you need to
// provide different implementations across most/all platforms.
// - Unless your new code is just one or two lines, put it into a separate
// method with a well-defined purpose. (Likewise, if you're adding to an
// existing chunk which makes it longer than one or two lines, please move
// the code out into a separate method.)
//
总结一下:
**跨平台启动部分
**跨平台启动阶段结束。
**以上阶段作为 BrowserMainLoop::CreateStartupTasks() 启动阶段的一部分运行,甚至可以急切地运行(例如,Android 应用程序预热尝试异步运行这些阶段)。
WillRunMainMessageLoop: 从此方法返回后,主线程的 RunLoop 将立即运行。虽然 PreMainMessageLoopRun 给出了这种印象,但实际上它是初始化阶段的一部分,与 MainMessageLoopRun 触发的独立触发的(甚至可能是异步的)。在浏览器测试中,PreMainMessageLoopRun() 将在进入测试体之前运行,而 WillRunMainMessageLoop() 不会(控制权交给测试,而不是运行循环)。此外,仅在调用 BrowserMainLoop::RunMainMessageLoop 的平台上调用。因此,在这个阶段应该尽量少做事情。它主要用于嵌入者覆盖或取消默认的 RunLoop(如果需要的话)。
OnFirstIdle: 主线程自 WillRunMainMessageLoop() 以来首次达到空闲。换句话说,它已完成执行上述阶段和从中发布的任何其他任务。
PostMainMessageLoopRun: 在 base::ThreadPool 和 BrowserThread::IO 仍在运行时停止和清理可以/应该被清理的事物。注意:还请参阅 BrowserMainLoop::ShutdownThreadsAndCleanUp(),通常适用于停止服务(PostMainMessageLoopRun() 是从其中调用的)。
PostDestroyThreads: 在单线程拆卸阶段停止和清理需要在其中创建的事物(通常是在 PreCreateThreads() 中创建的事物)。
注释描写的很细致,生命周期也很细致,但是基于我们当前对chrome的了解,还不能详细说明, 请读者用到的时候再加以分析。
BrowserMainLoop 的parts_成员变量指向BrowserMainParts, BrowserMainLoop也提供对应的生命周期方法,它会调用到BrowserMainParts的对应方法。
我们以BrowserMainLoop::EarlyInitialization() 为例进行说明。
content/browser/browser_main_loop.cc
532 int BrowserMainLoop::EarlyInitialization() {
533 TRACE_EVENT0("startup", "BrowserMainLoop::EarlyInitialization");
534
......
// 调用BrowserMainParts->PreEarlyInitialization() 方法
560 if (parts_) {
561 const int pre_early_init_error_code = parts_->PreEarlyInitialization();
562 if (pre_early_init_error_code != RESULT_CODE_NORMAL_EXIT)
563 return pre_early_init_error_code;
564 }
565
......
571
572 // SetCurrentThreadType relies on CurrentUIThread on some platforms. The
573 // MessagePumpForUI needs to be bound to the main thread by this point.
574 DCHECK(base::CurrentUIThread::IsSet());
575 base::PlatformThread::SetCurrentThreadType(base::ThreadType::kCompositing);
576
......
617
618 if (parsed_command_line_->HasSwitch(switches::kRendererProcessLimit)) {
619 std::string limit_string = parsed_command_line_->GetSwitchValueASCII(
620 switches::kRendererProcessLimit);
621 size_t process_limit;
622 if (base::StringToSizeT(limit_string, &process_limit)) {
623 RenderProcessHost::SetMaxRendererProcessCount(process_limit);
624 }
625 }
626
// 调用BrowserMainParts->PostEarlyInitialization() 方法
627 if (parts_)
628 parts_->PostEarlyInitialization();
629
630 return RESULT_CODE_NORMAL_EXIT;
631 }
我们可以看到在BrowserMainLoop::EarlyInitialization()方法中分别调用了 BrowserMainParts的PreEarlyInitialization() 方法和PostEarlyInitialization() 方法。 由此可推断BrowserMainLoop的其他生命周期方法也会调用BrowserMainParts的其他生命周期方法,请读者自行验证。
我们分析在linux的实例parts_的相应方法。 ChromeBrowserMainPartsLinux 并没有实现PreEarlyInitialization方法,我们看它父类ChromeBrowserMainPartsPosix的实现。
chrome/browser/chrome_browser_main_posix.cc
int ChromeBrowserMainPartsPosix::PreEarlyInitialization() {
const int result = ChromeBrowserMainParts::PreEarlyInitialization();
if (result != content::RESULT_CODE_NORMAL_EXIT)
return result;
// We need to accept SIGCHLD, even though our handler is a no-op because
// otherwise we cannot wait on children. (According to POSIX 2001.)
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_handler = SIGCHLDHandler;
CHECK_EQ(0, sigaction(SIGCHLD, &action, nullptr));
return content::RESULT_CODE_NORMAL_EXIT;
}
这里调用了父类ChromeBrowserMainParts的PreEarlyInitialization方法并设置了信号处理程序。 我们看ChromeBrowserMainParts->PreEarlyInitialization() 方法的实现。
729 int ChromeBrowserMainParts::PreEarlyInitialization() {
730 TRACE_EVENT0("startup", "ChromeBrowserMainParts::PreEarlyInitialization");
731 for (auto& chrome_extra_part : chrome_extra_parts_)
732 chrome_extra_part->PreEarlyInitialization();
733
......
776
777 return load_local_state_result;
778 }
这里有调用了chrome_extra_parts_中的每个extra_part 的PreEarlyInitialization 方法。
关于ChromeBrowserMainParts的基本逻辑和调用流程我们分析完了。 回到BrowserMainRunnerImpl::Initialize() 函数中, 完成各个ChromeBrowserMainParts的初始化工作,就可以执行启动任务了, BrowserMainLoop::CreateStartupTasks() 函数正式这个作用, 创建启动任务。
838 void BrowserMainLoop::CreateStartupTasks() {
839 TRACE_EVENT0("startup", "BrowserMainLoop::CreateStartupTasks");
840
841 DCHECK(!startup_task_runner_);
842 #if BUILDFLAG(IS_ANDROID)
......
851 #else
// 创建StartupTaskRunner
852 startup_task_runner_ = std::make_unique<StartupTaskRunner>(
853 base::OnceCallback<void(int)>(),
854 base::SingleThreadTaskRunner::GetCurrentDefault());
855 #endif
856 StartupTask pre_create_threads = base::BindOnce(
857 &BrowserMainLoop::PreCreateThreads, base::Unretained(this));
// 添加任务调用BrowserMainLoop::PreCreateThreads
858 startup_task_runner_->AddTask(std::move(pre_create_threads));
859
860 StartupTask create_threads =
861 base::BindOnce(&BrowserMainLoop::CreateThreads, base::Unretained(this));
// 添加任务调用BrowserMainLoop::CreateThreads
862 startup_task_runner_->AddTask(std::move(create_threads));
863
864 StartupTask post_create_threads = base::BindOnce(
865 &BrowserMainLoop::PostCreateThreads, base::Unretained(this));
// 添加任务调用BrowserMainLoop::PostCreateThreads
866 startup_task_runner_->AddTask(std::move(post_create_threads));
867
868 StartupTask pre_main_message_loop_run = base::BindOnce(
869 &BrowserMainLoop::PreMainMessageLoopRun, base::Unretained(this));
// 添加任务调用BrowserMainLoop::PreMainMessageLoopRun
870 startup_task_runner_->AddTask(std::move(pre_main_message_loop_run));
871
......
887
888 #if BUILDFLAG(IS_ANDROID)
......
890 #else
891 startup_task_runner_->RunAllTasksNow();
892 #endif
893 }
函数创建StartupTaskRunner结构,我们知道TaskRunner 一般用于投递任务,StartupTaskRunner也是如此,所以就不深分析StartupTaskRunner了, 它就是向当先线程投递任务。 在CreateStartupTasks函数中分别投递了PreCreateThreads、CreateThreads、PostCreateThreads、PreMainMessageLoopRun 四个函数。CreateStartupTasks函数891行调用startup_task_runner_->RunAllTasksNow()函数会依次调用上述四个函数。 从这四个函数名称还看是在创建线程过程中执行的操作,由于chromium 启动过程中初始化了太多的组件,我们无法依依去分析,所以只看一下CreateThreads了解下启动了哪些线程。
content/browser/startup_task_runner.cc
919 int BrowserMainLoop::CreateThreads() {
920 TRACE_EVENT0("startup,rail", "BrowserMainLoop::CreateThreads");
921
922 // Release the ThreadPool's threads.
923 scoped_execution_fence_.reset();
924
925 // The |io_thread| can have optionally been injected into Init(), but if not,
926 // create it here. Thre thread is only tagged as BrowserThread::IO here in
927 // order to prevent any code from statically posting to it before
928 // CreateThreads() (as such maintaining the invariant that PreCreateThreads()
929 // et al. "happen-before" BrowserThread::IO is "brought up").
930 if (!io_thread_) {
931 io_thread_ = BrowserTaskExecutor::CreateIOThread();
932 }
933 io_thread_->RegisterAsBrowserThread();
934 BrowserTaskExecutor::InitializeIOThread();
935
936 // TODO(https://crbug.com/863341): Replace with a better API
937 GetContentClient()->browser()->PostAfterStartupTask(
938 FROM_HERE, base::SequencedTaskRunner::GetCurrentDefault(),
939 base::BindOnce(
940 [](BrowserMainLoop* browser_main_loop) {
941 // Informs BrowserTaskExecutor that startup is complete.
942 content::BrowserTaskExecutor::OnStartupComplete();
943 browser_main_loop->scoped_best_effort_execution_fence_.reset();
944 },
945 // Main thread tasks can't run after BrowserMainLoop destruction.
946 // Accessing an Unretained pointer to BrowserMainLoop from a main
947 // thread task is therefore safe.
948 base::Unretained(this)));
949
950 created_threads_ = true;
951 return result_code_;
952 }
929-934行,将io_thread_注册为BrowserThread。 并初始化BrowserTaskExecutor的iothread, 我们在分析线程的时候已经知道BrowserTaskExecutor 管理浏览器的ui线程和io线程,这里初始化了io线程,ui线程则是浏览器进程的主线程,在作为初始化后会被设置为BrowserTaskExecutor的ui线程。
上面浏览器进程的启动大致流程我们已经分析了,但是我们只看到的进程启动框架,用户要想使用浏览器,必须要将ui展现在屏幕上,并且通过操作ui来执行网页访问,所以我们分析一下ui的初始化过程, 在这个过程中也能看到浏览器实例的创建。
chrome/browser/chrome_browser_main.cc
int ChromeBrowserMainParts::PreMainMessageLoopRun() {
TRACE_EVENT0("startup", "ChromeBrowserMainParts::PreMainMessageLoopRun");
result_code_ = PreMainMessageLoopRunImpl();
for (auto& chrome_extra_part : chrome_extra_parts_)
chrome_extra_part->PreMainMessageLoopRun();
return result_code_;
}
在PreMainMessageLoopRun 调用时,会调用ChromeBrowserMainParts::PreMainMessageLoopRun() 方法, 该方法会调用PreMainMessageLoopRunImpl() 方法。 PreMainMessageLoopRunImpl方法会启动浏览器实例。我们来具体分析。
// PreMainMessageLoopRun calls these extra stages in the following order:
// PreMainMessageLoopRunImpl()
// ... initial setup, including browser_process_ setup.
// PreProfileInit()
// ... additional setup, including CreateProfile()
// PostProfileInit()
// ... additional setup
// PreBrowserStart()
// ... browser_creator_->Start
// PostBrowserStart()
1388 int ChromeBrowserMainParts::PreMainMessageLoopRunImpl() {
......
1401
1402 // Do any initializating in the browser process that requires all threads
1403 // running.
1404 browser_process_->PreMainMessageLoopRun();
1405
1406 // Record last shutdown time into a histogram.
1407 browser_shutdown::ReadLastShutdownInfo();
1408
......
1617 // Desktop construction occurs here, (required before profile creation).
1618 PreProfileInit();
1619
......
1627 // This step is costly and is already measured in Startup.CreateFirstProfile
1628 // and more directly Profile.CreateAndInitializeProfile.
1629 StartupProfileInfo profile_info = CreateInitialProfile(
1630 /*cur_dir=*/base::FilePath(), *base::CommandLine::ForCurrentProcess());
1631 base::UmaHistogramEnumeration(
1632 "ProfilePicker.StartupMode.CreateInitialProfile", profile_info.mode);
1633
1634 if (profile_info.mode == StartupProfileMode::kError)
1635 return content::RESULT_CODE_NORMAL_EXIT;
1636
......
1685
1686 // `profile` may be nullptr if the profile picker is shown.
1687 Profile* profile = profile_info.profile;
1688 // Call `PostProfileInit()`and set it up for profiles created later.
1689 profile_init_manager_ = std::make_unique<ProfileInitManager>(this, profile);
1690
1691 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
1692 // Execute first run specific code after the PrefService has been initialized
1693 // and preferences have been registered since some of the import code depends
1694 // on preferences.
1695 if (first_run::IsChromeFirstRun()) {
1696 // `profile` may be nullptr even on first run, for example when the
1697 // "BrowserSignin" policy is set to "Force". If so, skip the auto import.
1698 if (profile) {
1699 first_run::AutoImport(profile, master_prefs_->import_bookmarks_path);
1700 }
1701
1702 // Note: This can pop-up the first run consent dialog on Linux & Mac.
1703 first_run::DoPostImportTasks(master_prefs_->make_chrome_default_for_user);
1704
1705 // The first run dialog is modal, and spins a RunLoop, which could receive
1706 // a SIGTERM, and call chrome::AttemptExit(). Exit cleanly in that case.
1707 if (browser_shutdown::IsTryingToQuit())
1708 return content::RESULT_CODE_NORMAL_EXIT;
1709 }
1710 #endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
1711
......
1782 PreBrowserStart();
1783
1784 variations::VariationsService* variations_service =
1785 browser_process_->variations_service();
1786 variations_service->PerformPreMainMessageLoopStartup();
1787
......
1817 browser_creator_->Start(*base::CommandLine::ForCurrentProcess(),
1818 base::FilePath(), profile_info,
1819 last_opened_profiles);
.......
1855
1856 PostBrowserStart();
1857
......
1869
1870 return result_code_;
1871 }
函数的主题流程如下:
我们直接看浏览器的启动。
chrome/browser/ui/startup/startup_browser_creator.cc
bool StartupBrowserCreator::Start(const base::CommandLine& cmd_line,
const base::FilePath& cur_dir,
StartupProfileInfo profile_info,
const Profiles& last_opened_profiles) {
TRACE_EVENT0("startup", "StartupBrowserCreator::Start");
SCOPED_UMA_HISTOGRAM_TIMER("Startup.StartupBrowserCreator_Start");
return ProcessCmdLineImpl(cmd_line, cur_dir,
chrome::startup::IsProcessStartup::kYes,
profile_info, last_opened_profiles);
}
函数直接调用了ProcessCmdLineImpl函数。
chrome/browser/ui/startup/startup_browser_creator.cc
909 bool StartupBrowserCreator::ProcessCmdLineImpl(
910 const base::CommandLine& command_line,
911 const base::FilePath& cur_dir,
912 chrome::startup::IsProcessStartup process_startup,
913 StartupProfileInfo profile_info,
914 const Profiles& last_opened_profiles) {
......
1260
1261 // Launch the browser if the profile is unable to open web apps.
1262 if (!CanOpenWebApp(privacy_safe_profile)) {
// 不能启动webapp则直接启动chrome浏览器主页
1263 LaunchBrowserForLastProfiles(command_line, cur_dir, process_startup,
1264 is_first_run, profile_info,
1265 last_opened_profiles);
1266 return true;
1267 }
1268
1269 // Try a platform app launch.
// 启动webapp
1270 if (MaybeLaunchExtensionApp(command_line, cur_dir, is_first_run,
1271 privacy_safe_profile)) {
1272 return true;
1273 }
1274
......
1296
//启动webapp失败,则直接启动chrome浏览器主页
1297 LaunchBrowserForLastProfiles(command_line, cur_dir, process_startup,
1298 is_first_run, profile_info,
1299 last_opened_profiles);
1300 return true;
1301 }
1302
我们不关注webapp。所以直接看使用profiles启动浏览器的过程。
chrome/browser/ui/startup/startup_browser_creator.cc
1303 void StartupBrowserCreator::ProcessLastOpenedProfiles(
1304 const base::CommandLine& command_line,
1305 const base::FilePath& cur_dir,
1306 chrome::startup::IsProcessStartup process_startup,
1307 chrome::startup::IsFirstRun is_first_run,
1308 Profile* last_used_profile,
1309 const Profiles& last_opened_profiles) {
1310 base::CommandLine command_line_without_urls(command_line.GetProgram());
1311 for (auto& switch_pair : command_line.GetSwitches()) {
1312 command_line_without_urls.AppendSwitchNative(switch_pair.first,
1313 switch_pair.second);
1314 }
1315
1316 // Launch the profiles in the order they became active.
1317 for (Profile* profile : last_opened_profiles) {
1318 DCHECK(!profile->IsGuestSession());
1319
......
1333
1334 // Don't launch additional profiles which would only open a new tab
1335 // page. When restarting after an update, all profiles will reopen last
1336 // open pages.
1337 SessionStartupPref startup_pref =
1338 GetSessionStartupPref(command_line, profile);
1339 if (profile != last_used_profile &&
1340 startup_pref.type == SessionStartupPref::DEFAULT &&
1341 !HasPendingUncleanExit(profile)) {
1342 continue;
1343 }
1344 // Only record a launch mode histogram for |last_used_profile|. Pass a
1345 // null launch_mode_recorder for other profiles.
// 为每个profile 启动一个浏览器实例。
1346 LaunchBrowser((profile == last_used_profile) ? command_line
1347 : command_line_without_urls,
1348 profile, cur_dir, process_startup, is_first_run,
1349 profile == last_used_profile
1350 ? std::make_unique<OldLaunchModeRecorder>()
1351 : nullptr);
1352 // We've launched at least one browser.
1353 process_startup = chrome::startup::IsProcessStartup::kNo;
1354 }
1355
// 设置活跃的profile
1356 // Set the |last_used_profile| to activate if a browser is launched for at
1357 // least one profile. Otherwise, show UserManager.
1358 // Note that this must be done after all profiles have
1359 // been launched so the observer knows about all profiles to wait before
1360 // activation this one.
1361 #if !BUILDFLAG(IS_CHROMEOS_ASH)
1362 if (process_startup == chrome::startup::IsProcessStartup::kYes) {
1363 ProfilePicker::Show(ProfilePicker::Params::FromEntryPoint(
1364 ProfilePicker::EntryPoint::kOnStartup));
1365 } else // NOLINT
1366 #endif
1367 {
1368 profile_launch_observer.Get().set_profile_to_activate(last_used_profile);
1369 }
1370 }
在chrome中,使用Profile 代表用户,关联一份用户数据,所以这里对每个Profile 启动一个浏览器实例,启动浏览器实例的函数为LaunchBrowser。 在所有浏览器实例都启动后,要求设置活跃profile,有两种方式,一种是直接设置最后使用的profile为活跃的,或者弹出弹框要求用户选择。 我们只关注浏览器实例的启动过程,所以继续分析LaunchBrowser函数。
652 void StartupBrowserCreator::LaunchBrowser(
653 const base::CommandLine& command_line,
654 Profile* profile,
655 const base::FilePath& cur_dir,
656 chrome::startup::IsProcessStartup process_startup,
657 chrome::startup::IsFirstRun is_first_run,
658 std::unique_ptr<OldLaunchModeRecorder> launch_mode_recorder) {
.....
686 StartupBrowserCreatorImpl lwp(cur_dir, command_line, this, is_first_run);
687 lwp.Launch(profile,
688 in_synchronous_profile_launch_
689 ? chrome::startup::IsProcessStartup::kYes
690 : chrome::startup::IsProcessStartup::kNo,
691 std::move(launch_mode_recorder));
692 }
693 in_synchronous_profile_launch_ = false;
694 profile_launch_observer.Get().AddLaunched(profile);
695 }
686 行创建StartupBrowserCreatorImpl 函数门用于启动单profile对应的Browser。 687-692行调用StartupBrowserCreatorImpl->launch方法启动浏览器.
chrome/browser/ui/startup/startup_browser_creator_impl.cc
182 void StartupBrowserCreatorImpl::Launch(
183 Profile* profile,
184 chrome::startup::IsProcessStartup process_startup,
185 std::unique_ptr<OldLaunchModeRecorder> launch_mode_recorder) {
186 DCHECK(profile);
187 profile_ = profile;
188
189 LaunchResult launch_result = DetermineURLsAndLaunch(process_startup);
......
224 }
函数调用DetermineURLsAndLaunch 启动浏览器。
53 StartupBrowserCreatorImpl::LaunchResult
354 StartupBrowserCreatorImpl::DetermineURLsAndLaunch(
355 chrome::startup::IsProcessStartup process_startup) {
......
// 确定要启动的tab
422 auto result = DetermineStartupTabs(
423 StartupTabProviderImpl(), process_startup, is_incognito_or_guest,
424 is_post_crash_launch, has_incompatible_applications,
425 promotional_tabs_enabled, welcome_enabled, whats_new_enabled,
426 privacy_sandbox_dialog_required);
427 StartupTabs tabs = std::move(result.tabs);
428
.......
// 启动浏览器,并开tabs
463 Browser* browser = RestoreOrCreateBrowser(
464 tabs, behavior, restore_options, process_startup, is_post_crash_launch);
465
......
469 return result.launch_result;
470 }
422-478行决定打开哪些tabs, 如果参数里面没有url默认情况只打开一个主tab, 如果启动参数里面有一个url或者从崩溃中恢复,可以打开多个tab。我们日常使用chrome是这样的,所以DetermineURLsAndLaunch就不用分析了。 这里主要分析RestoreOrCreateBrowser函数。
653 Browser* StartupBrowserCreatorImpl::RestoreOrCreateBrowser(
654 const StartupTabs& tabs,
655 BrowserOpenBehavior behavior,
656 SessionRestore::BehaviorBitmask restore_options,
657 chrome::startup::IsProcessStartup process_startup,
658 bool is_post_crash_launch) {
659 Browser* browser = nullptr;
......
683 browser = OpenTabsInBrowser(
684 browser, process_startup,
685 (tabs.empty()
686 ? StartupTabs({StartupTab(GURL(chrome::kChromeUINewTabURL))})
687 : tabs));
688
......
697 return browser;
698 }
683-659行启动浏览器并打开tabs。
35 Browser* StartupBrowserCreatorImpl::OpenTabsInBrowser(
236 Browser* browser,
237 chrome::startup::IsProcessStartup process_startup,
238 const StartupTabs& tabs) {
239 DCHECK(!tabs.empty());
240
......
246
247 if (!browser || !browser->is_type_normal()) {
......
// 准备创建参数
256 // Startup browsers are not counted as being created by a user_gesture
257 // because of historical accident, even though the startup browser was
258 // created in response to the user clicking on chrome. There was an
259 // incomplete check on whether a user gesture created a window which looked
260 // at the state of the MessageLoop.
261 Browser::CreateParams params = Browser::CreateParams(profile_, false);
262 params.creation_source = Browser::CreationSource::kStartupCreator;
263 #if BUILDFLAG(IS_LINUX)
264 params.startup_id =
265 command_line_->GetSwitchValueASCII("desktop-startup-id");
266 #endif
267 if (command_line_->HasSwitch(switches::kWindowName)) {
268 params.user_title =
269 command_line_->GetSwitchValueASCII(switches::kWindowName);
270 }
271
// 创建浏览器实例
272 browser = Browser::Create(params);
273 }
274
275 bool first_tab = true;
276 custom_handlers::ProtocolHandlerRegistry* registry =
277 profile_ ? ProtocolHandlerRegistryFactory::GetForBrowserContext(profile_)
278 : nullptr;
279 for (auto& tab : tabs) {
.......
// 准备启动tab参数
306 NavigateParams params(browser, tab.url, ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
307 params.disposition = first_tab ? WindowOpenDisposition::NEW_FOREGROUND_TAB
308 : WindowOpenDisposition::NEW_BACKGROUND_TAB;
309 params.tabstrip_add_types = add_types;
......
// 导航到tab
319 Navigate(¶ms);
320 first_tab = false;
321 }
// 设置活跃tab
322 if (!browser->tab_strip_model()->GetActiveWebContents()) {
323 // TODO(sky): this is a work around for 110909. Figure out why it's needed.
324 if (!browser->tab_strip_model()->count())
325 chrome::AddTabAt(browser, GURL(), -1, true);
326 else
327 browser->tab_strip_model()->ActivateTabAt(0);
328 }
329
.....
// 显示window
348 browser->window()->Show();
349
350 return browser;
351 }
247-273行准备browser创建的参数, 并创建browser。
279-321行 通过Navigate创建tab。
348行 一切准备就绪,显示浏览器页面。
我们先来浏览器实例的创建,导航的过程会比较复杂,也是浏览器的核心功能,会单独分析。
chrome/browser/ui/browser.cc
// static
Browser* Browser::Create(const CreateParams& params) {
......
return new Browser(params);
}
函数只是创建了Browser 实例。
458 Browser::Browser(const CreateParams& params)
459 : create_params_(params),
460 type_(params.type),
461 profile_(params.profile),
462 window_(nullptr),
463 tab_strip_model_delegate_(
464 std::make_unique<chrome::BrowserTabStripModelDelegate>(this)),
// tab标签模型
465 tab_strip_model_(std::make_unique<TabStripModel>(
466 tab_strip_model_delegate_.get(),
467 params.profile,
468 params.are_tab_groups_enabled ? TabGroupModelFactory::GetInstance()
469 : nullptr)),
// tab menu 模型
470 tab_menu_model_delegate_(
471 std::make_unique<chrome::BrowserTabMenuModelDelegate>(this)),
472 app_name_(params.app_name),
473 is_trusted_source_(params.trusted_source),
474 session_id_(SessionID::NewUnique()),
475 omit_from_session_restore_(params.omit_from_session_restore),
476 should_trigger_session_restore_(params.should_trigger_session_restore),
477 cancel_download_confirmation_state_(NOT_PROMPTED),
478 override_bounds_(params.initial_bounds),
479 initial_show_state_(params.initial_show_state),
480 initial_workspace_(params.initial_workspace),
481 initial_visible_on_all_workspaces_state_(
482 params.initial_visible_on_all_workspaces_state),
483 creation_source_(params.creation_source),
484 unload_controller_(this),
485 content_setting_bubble_model_delegate_(
486 new BrowserContentSettingBubbleModelDelegate(this)),
487 location_bar_model_delegate_(new BrowserLocationBarModelDelegate(this)),
488 live_tab_context_(new BrowserLiveTabContext(this)),
489 synced_window_delegate_(new BrowserSyncedWindowDelegate(this)),
490 app_controller_(web_app::MaybeCreateAppBrowserController(this)),
491 bookmark_bar_state_(BookmarkBar::HIDDEN),
492 command_controller_(new chrome::BrowserCommandController(this)),
493 window_has_shown_(false),
494 user_title_(params.user_title),
495 signin_view_controller_(this),
496 breadcrumb_manager_browser_agent_(
497 breadcrumbs::IsEnabled()
498 ? std::make_unique<BreadcrumbManagerBrowserAgent>(this)
499 : nullptr)
500 #if BUILDFLAG(ENABLE_EXTENSIONS)
501 ,
502 extension_browser_window_helper_(
503 std::make_unique<extensions::ExtensionBrowserWindowHelper>(this))
504 #endif
505 {
506 if (!profile_->IsOffTheRecord()) {
507 profile_keep_alive_ = std::make_unique<ScopedProfileKeepAlive>(
508 params.profile->GetOriginalProfile(),
509 ProfileKeepAliveOrigin::kBrowserWindow);
510 }
511
512 tab_strip_model_->AddObserver(this);
513
// 导航栏模型
514 location_bar_model_ = std::make_unique<LocationBarModelImpl>(
515 location_bar_model_delegate_.get(), content::kMaxURLDisplayChars);
.......
// 书签状态
533 UpdateBookmarkBarState(BOOKMARK_BAR_STATE_CHANGE_INIT);
534
535 ProfileMetrics::LogProfileLaunch(profile_);
536
537 if (params.skip_window_init_for_testing)
538 return;
539
// 创建浏览器窗口
540 window_ = params.window ? params.window.get()
541 : CreateBrowserWindow(std::unique_ptr<Browser>(this),
542 params.user_gesture,
543 params.in_tab_dragging);
544
......
552 SessionServiceBase* service =
553 GetAppropriateSessionServiceForSessionRestore(this);
554
555 if (service)
556 service->WindowOpened(this);
557
.....
// 添加到浏览器列表。(可以打开多个窗口)
567 BrowserList::AddBrowser(this);
568 }
函数初始化了tab标签模型、menu模型、导航栏模型、和书签条。 并在540-544行创建浏览器窗口。 我们接下来分析窗口的创建。
chrome/browser/ui/browser.cc
BrowserWindow* CreateBrowserWindow(std::unique_ptr<Browser> browser,
bool user_gesture,
bool in_tab_dragging) {
return BrowserWindow::CreateBrowserWindow(std::move(browser), user_gesture,
in_tab_dragging);
}
函数调用CreateBrowserWindow创建BrowserWindow。
chrome/browser/ui/views/frame/browser_window_factory.cc
35 // static
36 BrowserWindow* BrowserWindow::CreateBrowserWindow(
37 std::unique_ptr<Browser> browser,
38 bool user_gesture,
39 bool in_tab_dragging) {
......
47 // Create the view and the frame. The frame will attach itself via the view
48 // so we don't need to do anything with the pointer.
49 BrowserView* view = new BrowserView(std::move(browser));
50 BrowserFrame* browser_frame = nullptr;
......
58 if (!browser_frame)
59 browser_frame = new BrowserFrame(view);
60 if (in_tab_dragging)
61 browser_frame->SetTabDragKind(TabDragKind::kAllTabs);
62 browser_frame->InitBrowserFrame();
63
......
86
87 return view;
88 }
函数49行创建BrowserView, BrowserView 用于描述供浏览器窗口的内容,包括标签栏(TabStrip)、工具栏、下载架(download shelves)、内容区域等。50-59行创建BrowserFrame,BrowserFrame是真正负责ui管理的类。
我们先看BrowserView的创建。
796 BrowserView::BrowserView(std::unique_ptr<Browser> browser)
797 : views::ClientView(nullptr, nullptr),
798 browser_(std::move(browser)),
799 accessibility_mode_observer_(
800 std::make_unique<AccessibilityModeObserver>(this)) {
// 设置显示的icon
801 SetShowIcon(
802 ::ShouldShowWindowIcon(browser_.get(), AppUsesWindowControlsOverlay()));
803
......
// 创建顶部容器view
865 top_container_ = AddChildView(std::make_unique<TopContainerView>(this));
866
.....
// 向顶部容器增加tab栏view
876 tab_strip_region_view_ = top_container_->AddChildView(
877 std::make_unique<TabStripRegionView>(std::move(tabstrip)));
878
879 ColorProviderBrowserHelper::CreateForBrowser(browser_.get());
880
// 设置开发者调试窗口
881 // Create WebViews early so |webui_tab_strip_| can observe their size.
882 auto devtools_web_view =
883 std::make_unique<views::WebView>(browser_->profile());
884 devtools_web_view->SetID(VIEW_ID_DEV_TOOLS_DOCKED);
885 devtools_web_view->SetVisible(false);
886
// 创建网页内容窗口
887 auto contents_web_view =
888 std::make_unique<ContentsWebView>(browser_->profile());
889 contents_web_view->SetID(VIEW_ID_TAB_CONTAINER);
890
// 调试窗口添加到内容窗口中
891 auto contents_container = std::make_unique<views::View>();
892 devtools_web_view_ =
893 contents_container->AddChildView(std::move(devtools_web_view));
// 添加网页内容窗口到内容窗口
894 contents_web_view_ =
895 contents_container->AddChildView(std::move(contents_web_view));
896 contents_web_view_->set_is_primary_web_contents_for_window(true);
// 设置布局管理器
897 contents_container->SetLayoutManager(std::make_unique<ContentsLayoutManager>(
898 devtools_web_view_, contents_web_view_));
899
// 添加工具栏到顶部窗口
900 toolbar_ = top_container_->AddChildView(
901 std::make_unique<ToolbarView>(browser_.get(), this));
902
// 添加内容分割线,用于分割顶部栏和内容栏
903 contents_separator_ =
904 top_container_->AddChildView(std::make_unique<ContentsSeparator>());
905
906 web_contents_close_handler_ =
907 std::make_unique<WebContentsCloseHandler>(contents_web_view_);
908
// 添加内容容器到当前顶部布局
909 contents_container_ = AddChildView(std::move(contents_container));
910 set_contents_view(contents_container_);
911
// 添加分割线
912 right_aligned_side_panel_separator_ =
913 AddChildView(std::make_unique<ContentsSeparator>());
914
915 const bool is_right_aligned = GetProfile()->GetPrefs()->GetBoolean(
......
954 }
BrowserView的构造主要初始化窗口内容和布局。
我们再看BrowserFrame的 构造。
chrome/browser/ui/views/frame/browser_frame.cc
BrowserFrame::BrowserFrame(BrowserView* browser_view)
: native_browser_frame_(nullptr),
root_view_(nullptr),
browser_frame_view_(nullptr),
browser_view_(browser_view) {
browser_view_->set_frame(this);
set_is_secondary_widget(false);
// Don't focus anything on creation, selecting a tab will set the focus.
set_focus_on_creation(false);
}
非常简单。 再来看一下BrowserFrame的初始化,InitBrowserFrame函数。
chrome/browser/ui/views/frame/browser_view.h
91 void BrowserFrame::InitBrowserFrame() {
92 native_browser_frame_ =
93 NativeBrowserFrameFactory::CreateNativeBrowserFrame(this, browser_view_);
94 views::Widget::InitParams params = native_browser_frame_->GetWidgetParams();
95 params.name = "BrowserFrame";
96 params.delegate = browser_view_;
97 params.headless_mode = headless::IsHeadlessMode();
98
.....
139
140 Init(std::move(params));
141 SelectNativeTheme();
142
143 if (!native_browser_frame_->UsesNativeSystemMenu()) {
144 DCHECK(non_client_view());
145 non_client_view()->set_context_menu_controller(this);
146 }
147 }
函数创建NativeBrowserFrame,也就是和本地平台相关的Frame。然后调用Init函数进行初始化。 141行选择本地主题。 143-146行 设置menu。NativeBrowserFrame 实际上创建的是 DesktopBrowserFrameAuraLinux。我们直接看初始化过程Init。Widget 是NativeBrowserFrame的父类, Init函数是Widget实现的。
345 void Widget::Init(InitParams params) {
// 创建根View
434 root_view_.reset(CreateRootView());
435
436 // Copy the elements of params that will be used after it is moved.
437 const InitParams::Type type = params.type;
438 const gfx::Rect bounds = params.bounds;
439 const ui::WindowShowState show_state = params.show_state;
440 WidgetDelegate* delegate = params.delegate;
441
// 初始化Widget
442 native_widget_->InitNativeWidget(std::move(params));
443 if (type == InitParams::TYPE_MENU)
444 is_mouse_button_pressed_ = native_widget_->IsMouseButtonDown();
445 if (RequiresNonClientView(type)) {
// 设置内容视图
446 non_client_view_ =
447 new NonClientView(widget_delegate_->CreateClientView(this));
448 non_client_view_->SetFrameView(CreateNonClientFrameView());
449 non_client_view_->SetOverlayView(widget_delegate_->CreateOverlayView());
450
451 // Bypass the Layout() that happens in Widget::SetContentsView(). Layout()
452 // will occur after setting the initial bounds below. The RootView's size is
453 // not valid until that happens.
454 root_view_->SetContentsView(non_client_view_);
455
456 // Initialize the window's icon and title before setting the window's
457 // initial bounds; the frame view's preferred height may depend on the
458 // presence of an icon or a title.
// 更新icon、title等
459 UpdateWindowIcon();
460 UpdateWindowTitle();
461 non_client_view_->ResetWindowControls();
462 SetInitialBounds(bounds);
463
464 // Perform the initial layout. This handles the case where the size might
465 // not actually change when setting the initial bounds. If it did, child
466 // views won't have a dirty Layout state, so won't do any work.
// 执行布局
467 root_view_->Layout();
468
469 if (show_state == ui::SHOW_STATE_MAXIMIZED) {
470 Maximize();
471 } else if (show_state == ui::SHOW_STATE_MINIMIZED) {
472 Minimize();
473 saved_show_state_ = ui::SHOW_STATE_MINIMIZED;
474 }
475 } else if (delegate) {
476 SetContentsView(delegate->TransferOwnershipOfContentsView());
477 SetInitialBoundsForFramelessWindow(bounds);
478 }
479
......
494 }
函数创建RootView, 初始化widget, 并设置内容视图, 然后更新icon和ttitle。 最后执行布局。
我们先看RootView的创建。
internal::RootView* Widget::CreateRootView() {
return new internal::RootView(this);
}
RootView::RootView(Widget* widget)
: widget_(widget),
pre_dispatch_handler_(
std::make_unique<internal::PreEventDispatchHandler>(this)),
post_dispatch_handler_(
std::make_unique<internal::PostEventDispatchHandler>()) {
AddPostTargetHandler(post_dispatch_handler_.get());
SetEventTargeter(
std::unique_ptr<ViewTargeter>(new RootViewTargeter(this, this)));
}
函数实现比较简单。 不不要展开分析。 再来看看Widget的初始化. native_widget_ 指向DesktopBrowserFrameAuraLinux, DesktopBrowserFrameAuraLinux的父类是DesktopBrowserFrameAura, DesktopBrowserFrameAura 实现了InitNativeWidget。 我们来看一下它的实现。
chrome/browser/ui/views/frame/desktop_browser_frame_aura.cc
548
549 // DesktopNativeWidgetAura, internal::NativeWidgetPrivate implementation:
550 void DesktopNativeWidgetAura::InitNativeWidget(Widget::InitParams params) {
551 ownership_ = params.ownership;
552 widget_type_ = params.type;
553 name_ = params.name;
554
......
// 初始化桌面窗口树
576 desktop_window_tree_host_->Init(params);
577
// 增加子Window
578 host_->window()->AddChild(content_window_);
579 host_->window()->SetProperty(kDesktopNativeWidgetAuraKey, this);
580
581 host_->window()->AddObserver(new RootWindowDestructionObserver(this));
582
......
626
627 host_->window()->SetName(params.name);
628 content_window_->SetName("DesktopNativeWidgetAura - content window");
629 desktop_window_tree_host_->OnNativeWidgetCreated(params);
630
......
// 事件处理
683 event_client_ = std::make_unique<DesktopEventClient>();
684 aura::client::SetEventClient(host_->window(), event_client_.get());
685
686 shadow_controller_ = std::make_unique<wm::ShadowController>(
687 wm::GetActivationClient(host_->window()), nullptr);
688
689 OnSizeConstraintsChanged();
690
691 window_reorderer_ = std::make_unique<WindowReorderer>(
692 content_window_, GetWidget()->GetRootView());
693 }
576行初始化桌面window 树。 578行将内容串口添加到window树中 683 创建事件处理相关结构。
我们继续看desktop_window_tree_host_的初始化。desktop_window_tree_host_是BrowserDesktopWindowTreeHostLinux的实例子。
chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc
void BrowserDesktopWindowTreeHostLinux::Init(
const views::Widget::InitParams& params) {
DesktopWindowTreeHostLinux::Init(std::move(params));
......
}
它调用了父类DesktopWindowTreeHostLinux的Init方法
void DesktopWindowTreeHostLinux::Init(const Widget::InitParams& params) {
DesktopWindowTreeHostPlatform::Init(params);
......
}
DesktopWindowTreeHostLinux又调用了父类DesktopWindowTreeHostPlatform的Init方法。
ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
258 void DesktopWindowTreeHostPlatform::Init(const Widget::InitParams& params) {
.....
293
// 创建平台window
294 CreateAndSetPlatformWindow(std::move(properties));
295
296 // Disable compositing on tooltips as a workaround for
297 // https://crbug.com/442111.
// 创建合成器
298 CreateCompositor(params.force_software_compositing ||
299 params.type == Widget::InitParams::TYPE_TOOLTIP);
300
301 WindowTreeHost::OnAcceleratedWidgetAvailable();
// 初始化
302 InitHost();
// 显示widnow
303 window()->Show();
304 }
我们看一下平台窗口的创建。最终图像都要渲染在平台窗口。
ui/aura/window_tree_host_platform.cc
void WindowTreeHostPlatform::CreateAndSetPlatformWindow(
ui::PlatformWindowInitProperties properties) {
// Cache initial size used to create |platform_window_| so that it does not
// end up propagating unneeded bounds change event when it is first notified
// through OnBoundsChanged, which may lead to unneeded re-layouts, etc.
size_in_pixels_ = properties.bounds.size();
#if BUILDFLAG(IS_OZONE)
platform_window_ = ui::OzonePlatform::GetInstance()->CreatePlatformWindow(
this, std::move(properties));
......
}
Ozone 是chrome用于实现gui的库, 它抽象了各平台gui管理给上层使用。在linux平台上OzonePlatform为OzonePlatformX11。
ui/ozone/platform/x11/ozone_platform_x11.cc
std::unique_ptr<PlatformWindow> CreatePlatformWindow(
PlatformWindowDelegate* delegate,
PlatformWindowInitProperties properties) override {
auto window = std::make_unique<X11Window>(delegate);
window->Initialize(std::move(properties));
return std::move(window);
}
这里创建了X11Window,并执行了初始化, 我们先来看下X11Window的创建。
ui/ozone/platform/x11/x11_window.cc
X11Window::X11Window(PlatformWindowDelegate* platform_window_delegate)
: platform_window_delegate_(platform_window_delegate),
connection_(x11::Connection::Get()),
x_root_window_(GetX11RootWindow()) {
......
}
再来看一下X11Window的初始化过程我们就不深入分析了,它主要请求创建X-Server创建window。
好了我们下面看一下怎么通过layout 更新布局
ui/views/view.cc
void View::Layout() {
needs_layout_ = false;
// If we have a layout manager, let it handle the layout for us.
if (HasLayoutManager())
GetLayoutManager()->Layout(this);
// Make sure to propagate the Layout() call to any children that haven't
// received it yet through the layout manager and need to be laid out. This
// is needed for the case when the child requires a layout but its bounds
// weren't changed by the layout manager. If there is no layout manager, we
// just propagate the Layout() call down the hierarchy, so whoever receives
// the call can take appropriate action.
internal::ScopedChildrenLock lock(this);
for (auto* child : children_) {
if (child->needs_layout_ || !HasLayoutManager()) {
TRACE_EVENT1("views", "View::LayoutChildren", "class",
child->GetClassName());
child->needs_layout_ = false;
child->Layout();
}
}
}
函数比较简单,先调用LyoutManager->Layout() 方法,再调用每一个子View的Layout方法, layout方法主要确定view的大小位置,这里就不具体分析了。 我们继续看view是怎么绘制的。 回到StartupBrowserCreatorImpl::OpenTabsInBrowser()函数,最后一行是调用browser->window()->Show();
chrome/browser/ui/views/frame/browser_view.cc
1226 void BrowserView::Show() {
......
1248 frame_->Show();
1249
1250 browser()->OnWindowDidShow();
1251
......
1268 }
这里调用了frame->Show() 方法。
ui/views/widget/widget.cc
795 void Widget::Show() {
796 if (!native_widget_)
797 return;
798 const ui::Layer* layer = GetLayer();
799 TRACE_EVENT1("views", "Widget::Show", "layer",
800 layer ? layer->name() : "none");
801 ui::WindowShowState preferred_show_state =
802 CanActivate() ? ui::SHOW_STATE_NORMAL : ui::SHOW_STATE_INACTIVE;
803 if (non_client_view_) {
804 // While initializing, the kiosk mode will go to full screen before the
805 // widget gets shown. In that case we stay in full screen mode, regardless
806 // of the |saved_show_state_| member.
807 if (saved_show_state_ == ui::SHOW_STATE_MAXIMIZED &&
808 !initial_restored_bounds_.IsEmpty() && !IsFullscreen()) {
.....
810 } else {
811 native_widget_->Show(
812 IsFullscreen() ? ui::SHOW_STATE_FULLSCREEN : saved_show_state_,
813 gfx::Rect());
814 }
815 // |saved_show_state_| only applies the first time the window is shown.
816 // If we don't reset the value the window may be shown maximized every time
817 // it is subsequently shown after being hidden.
818 saved_show_state_ = preferred_show_state;
819 } else {
820 native_widget_->Show(preferred_show_state, gfx::Rect());
821 }
822
823 HandleShowRequested();
824 }
函数调用native_widget_->Show方法去显示页面.
ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
void DesktopNativeWidgetAura::Show(ui::WindowShowState show_state,
const gfx::Rect& restore_bounds) {
if (!desktop_window_tree_host_)
return;
desktop_window_tree_host_->Show(show_state, restore_bounds);
}
native_widget_->Show 方法调用desktop_window_tree_host_->Show 方法。desktop_window_tree_host_类型为BrowserDesktopWindowTreeHostLinux, Show方法如下:
chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc
void BrowserDesktopWindowTreeHostLinux::Show(ui::WindowShowState show_state,
const gfx::Rect& restore_bounds) {
DesktopWindowTreeHostLinux::Show(show_state, restore_bounds);
......
}
函数调用父类DesktopWindowTreeHostLinux::Show 方法。
ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
413 void DesktopWindowTreeHostPlatform::Show(ui::WindowShowState show_state,
414 const gfx::Rect& restore_bounds) {
415 if (compositor())
416 SetVisible(true);
417
// 请求x-server 显示window
418 platform_window()->Show(DetermineInactivity(show_state));
419
......
439 if (native_widget_delegate_->CanActivate()) {
440 if (show_state != ui::SHOW_STATE_INACTIVE)
// 激活窗口
441 Activate();
442
443 // SetInitialFocus() should be always be called, even for
444 // SHOW_STATE_INACTIVE. If the window has to stay inactive, the method will
445 // do the right thing.
446 // Activate() might fail if the window is non-activatable. In this case, we
447 // should pass SHOW_STATE_INACTIVE to SetInitialFocus() to stop the initial
448 // focused view from getting focused. See https://crbug.com/515594 for
449 // example.
450 native_widget_delegate_->SetInitialFocus(
451 IsActive() ? show_state : ui::SHOW_STATE_INACTIVE);
452 }
453
// 显示内容
454 GetContentWindow()->Show();
455 }
函数先请求x-server 显示窗口,然后激活窗口, 最后调用GetContentWindow()->Show() 绘制窗口内容。
ui/aura/window.cc
void Window::Show() {
......
SetVisibleInternal(true);
}
Window::Show 方法调用SetVisibleInternal
985 void Window::SetVisibleInternal(bool visible) {
......
// 请求绘制
1001 SchedulePaint();
......
1009 }
函数调用SchedulePaint() 请求绘制
void Window::SchedulePaint() {
SchedulePaintInRect(gfx::Rect(0, 0, bounds().width(), bounds().height()));
}
void Window::SchedulePaintInRect(const gfx::Rect& rect) {
layer()->SchedulePaint(rect);
}
函数调用layer()->SchedulePaint() 方法完成绘制。我们查看SchedulePaint方法。
ui/compositor/layer.cc
bool Layer::SchedulePaint(const gfx::Rect& invalid_rect) {
.....
if (!content_layer_ || !deferred_paint_requests_)
ScheduleDraw();
return true;
}
如果需要绘制,调用ScheduleDraw 方法
void Layer::ScheduleDraw() {
// Do not schedule draw if this layer does not contribute the content.
if (type_ == LAYER_NOT_DRAWN && children_.size() == 0) {
return;
}
Compositor* compositor = GetCompositor();
if (compositor) {
compositor->ScheduleDraw();
}
}
调用compositor->ScheduleDraw() 进行绘制。
void Compositor::ScheduleDraw() {
host_->SetNeedsCommit();
}
void LayerTreeHost::SetNeedsCommit() {
DCHECK(IsMainThread());
proxy_->SetNeedsCommit();
......
}
void SingleThreadProxy::SetNeedsCommit() {
DCHECK(task_runner_provider_->IsMainThread());
......
if (scheduler_on_impl_thread_)
scheduler_on_impl_thread_->SetNeedsBeginMainFrame();
}
void Scheduler::SetNeedsBeginMainFrame() {
state_machine_.SetNeedsBeginMainFrame();
ProcessScheduledActions();
}
函数最终调用到Scheduler::SetNeedsBeginMainFrame() 方法,设置了状态机状态, 然后调用ProcessScheduledActions()处理新状态。
Scheduler类是ui渲染的调度器。
关于schedule 调度器比较复杂,这里不进行深入分析。
void Scheduler::ProcessScheduledActions() {
......
do {
action = state_machine_.NextAction();
TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
"SchedulerStateMachine", [this](perfetto::EventContext ctx) {
this->AsProtozeroInto(ctx,
ctx.event()->set_cc_scheduler_state());
});
base::AutoReset<SchedulerStateMachine::Action> mark_inside_action(
&inside_action_, action);
switch (action) {
case SchedulerStateMachine::Action::NONE:
break;
case SchedulerStateMachine::Action::SEND_BEGIN_MAIN_FRAME:
compositor_timing_history_->WillBeginMainFrame(begin_main_frame_args_);
compositor_frame_reporting_controller_->WillBeginMainFrame(
begin_main_frame_args_);
state_machine_.WillSendBeginMainFrame();
client_->ScheduledActionSendBeginMainFrame(begin_main_frame_args_);
last_dispatched_begin_main_frame_args_ = begin_main_frame_args_;
break;
case SchedulerStateMachine::Action::
......
}
SEND_BEGIN_MAIN_FRAME 这个action 用于请求新的一帧绘制,client_->ScheduledActionSendBeginMainFrame(begin_main_frame_args_) 函数调度绘制。
cc/trees/single_thread_proxy.cc
965 void SingleThreadProxy::ScheduledActionSendBeginMainFrame(
966 const viz::BeginFrameArgs& begin_frame_args) {
967 DebugScopedSetImplThread impl(task_runner_provider_);
......
980
981 host_impl_->WillSendBeginMainFrame();
982 task_runner_provider_->MainThreadTaskRunner()->PostTask(
983 FROM_HERE, base::BindOnce(&SingleThreadProxy::BeginMainFrame,
984 weak_factory_.GetWeakPtr(), begin_frame_args));
985 host_impl_->DidSendBeginMainFrame(begin_frame_args);
986 }
异步调用BeginMainFrame
1010 void SingleThreadProxy::BeginMainFrame(
......
// 新的一帧开始之前的准备工作
1062 DoBeginMainFrame(begin_frame_args);
1063
1064 // New commits requested inside UpdateLayers should be respected.
1065 commit_requested_ = false;
1066
.....
// 执行绘制
1081 DoPainting(begin_frame_args);
1082 layer_tree_host_->RecordEndOfFrameMetrics(frame_start_time,
1083 /* trackers */ 0u);
1084 }
我们看DoPainting 绘制页面。
void SingleThreadProxy::DoPainting(const viz::BeginFrameArgs& commit_args) {
layer_tree_host_->UpdateLayers();
......
}
函数调用layer_tree_host->UpdateLayers()
bool LayerTreeHost::UpdateLayers() {
......
client_->WillUpdateLayers();
bool result = DoUpdateLayers();
client_->DidUpdateLayers();
micro_benchmark_controller_.DidUpdateLayers();
.......
return result;
}
DoUpdateLayers更新layer。
cc/trees/layer_tree_host.cc
bool LayerTreeHost::DoUpdateLayers() {
......
LayerList update_layer_list;
draw_property_utils::FindLayersThatNeedUpdates(this, &update_layer_list);
// 执行绘制
bool did_paint_content = PaintContent(update_layer_list);
return did_paint_content;
}
调用PainContent绘制。
bool LayerTreeHost::PaintContent(const LayerList& update_layer_list) {
DCHECK(IsMainThread());
base::AutoReset<bool> painting(&in_paint_layer_contents_, true);
bool did_paint_content = false;
for (const auto& layer : update_layer_list) {
did_paint_content |= layer->Update();
}
return did_paint_content;
}
绘制没一个Layer。
cc/layers/picture_layer.cc
130 bool PictureLayer::Update() {
......
159 if (updated) {
160 {
161 auto old_display_list = std::move(picture_layer_inputs_.display_list);
//绘制内容到displayList
162 picture_layer_inputs_.display_list =
163 picture_layer_inputs_.client->PaintContentsToDisplayList();
164 if (old_display_list &&
165 picture_layer_inputs_.display_list
166 ->NeedsAdditionalInvalidationForLCDText(*old_display_list)) {
167 last_updated_invalidation_.Write(*this) = gfx::Rect(bounds());
168 }
169 }
170
.....
188 recording_source->UpdateDisplayItemList(
189 picture_layer_inputs_.display_list,
190 layer_tree_host()->recording_scale_factor());
191
192 SetNeedsPushProperties();
193 IncreasePaintCount();
194 } else {
195 // If this invalidation did not affect the recording source, then it can be
196 // cleared as an optimization.
197 last_updated_invalidation_.Write(*this).Clear();
198 }
199
200 return updated;
201 }
162-163行更新displaylist。
ui/compositor/layer.cc
1433 scoped_refptr<cc::DisplayItemList> Layer::PaintContentsToDisplayList() {
1434 TRACE_EVENT1("ui", "Layer::PaintContentsToDisplayList", "name", name_);
1435 gfx::Rect local_bounds(bounds().size());
1436 gfx::Rect invalidation(
1437 gfx::IntersectRects(paint_region_.bounds(), local_bounds));
1438 paint_region_.Clear();
1439 auto display_list = base::MakeRefCounted<cc::DisplayItemList>();
1440 if (delegate_) {
1441 delegate_->OnPaintLayer(PaintContext(display_list.get(),
1442 device_scale_factor_, invalidation,
1443 GetCompositor()->is_pixel_canvas()));
1444 }
1445 display_list->Finalize();
1446 // TODO(domlaskowski): Move mirror invalidation to Layer::SchedulePaint.
1447 for (const auto& mirror : mirrors_)
1448 mirror->dest()->SchedulePaint(invalidation);
1449 return display_list;
1450 }
1441 行执行真正的绘制
ui/aura/window.cc
void Window::OnPaintLayer(const ui::PaintContext& context) {
Paint(context);
}
void Window::Paint(const ui::PaintContext& context) {
if (delegate_)
delegate_->OnPaint(context);
}
最终调用到Widget绘制
ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
void DesktopNativeWidgetAura::OnPaint(const ui::PaintContext& context) {
if (native_widget_delegate_) {
desktop_window_tree_host_->UpdateWindowShapeIfNeeded(context);
native_widget_delegate_->OnNativeWidgetPaint(context);
}
}
调用到Widget::OnNativeWidgetPaint
ui/views/widget/widget.cc
void Widget::OnNativeWidgetPaint(const ui::PaintContext& context) {
// On Linux Aura, we can get here during Init() because of the
// SetInitialBounds call.
if (!native_widget_initialized_)
return;
GetRootView()->PaintFromPaintRoot(context);
}
到用到rootview的PaintFromPaintRoot方法,表示从根节点开始绘制
ui/views/view.cc
void View::PaintFromPaintRoot(const ui::PaintContext& parent_context) {
PaintInfo paint_info = PaintInfo::CreateRootPaintInfo(
parent_context, layer() ? layer()->size() : size());
Paint(paint_info);
......
}
查看Paint方法。
void View::Paint(const PaintInfo& parent_paint_info) {
CHECK_EQ(life_cycle_state_, LifeCycleState::kAlive);
if (!ShouldPaint())
return;
.....
ui::ClipRecorder clip_recorder(parent_paint_info.context());
if (!layer()) {
......
OnPaint(canvas);
}
CHECK_EQ(life_cycle_state_, LifeCycleState::kAlive);
// View::Paint() recursion over the subtree.
PaintChildren(paint_info);
}
函数先调用OnPaint绘制自身,再调用PaintChildren 绘制子view。
void View::OnPaint(gfx::Canvas* canvas) {
TRACE_EVENT1("views", "View::OnPaint", "class", GetClassName());
OnPaintBackground(canvas);
OnPaintBorder(canvas);
}
对于普通view, OnPaint绘制背景和边框即可。
void View::PaintChildren(const PaintInfo& paint_info) {
TRACE_EVENT1("views", "View::PaintChildren", "class", GetClassName());
RecursivePaintHelper(&View::Paint, paint_info);
}
绘制子view调用RecursivePaintHelper 方法绘制。
void View::RecursivePaintHelper(void (View::*func)(const PaintInfo&),
const PaintInfo& info) {
View::Views children = GetChildrenInZOrder();
DCHECK_EQ(children_.size(), children.size());
for (auto* child : children) {
if (!child->layer())
(child->*func)(info);
}
}
按照zorder 绘制子view。