chromium的源码非常大,选择合适的点入手能省不少力气。在
win7下编译chromium中我曾提到学习chromium源码的一个小工程test_shell,代码目录在src/webkit/tools/test_shell下,
打开src/webkit下的webkit.sln工程,在webkit下就能看到test_shell。test_shell是一个测试程序,代码和流程都很简单,
这篇笔记主要理清test_shell中的主流程和逻辑,为后面学习webkit打好基础,同时,我们也研究一下google的桌面程序都有哪些特点。
打开test_shell_main.cc文件,找到main函数逐行分析。(windows环境)
base::debug::EnableInProcessStackDumping();
这句使程序生成crash dump,并将标准输出attach到控制台。具体参考 SetUnhandledExceptionFilter API函数。
base::EnableTerminationOnHeapCorruption();
当堆越界或出错时终止进程,winxp sp3以上版本才支持。具体参考 HeapSetInformation API函数。
base::AtExitManager at_exit_manager;
AtExitManager目的是执行类似atexit的动作,它采用后进先出的栈结构运行注册一系列的任务,当对象析构时会执行任务列表,最常用的是执行base::Singleton类型对象的销毁动作,可以看到Singleton的实现中如果有kRegisterAtExit属性,则自动将OnExit函数作为任务添加到AtExitManager中,这样,我们不但能在程序结束时指定Singleton对象的销毁顺序,还能定义更多动作来让AtExitManager帮助管理执行。
TestShellPlatformDelegate::PreflightArgs(&argc, &argv);
根据注释这一句使程序有机会提前根据OS处理一些命令行,并过滤掉一些特有命令。不深究。
CommandLine::Init(argc, argv);
const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
这两句建立一个命令行解析对象,将命令行解释为一个命令列表。后续很多处理都根据parsed_command_line对象的解释结果,配置不同的功能和支持。关于后续不同配置的执行,不再一一详述。
TestShellPlatformDelegate platform(parsed_command_line);
为跨平台做的一些逻辑的代理,比如后续的 platform.CheckLayoutTestSystemDependencies(),platform.SuppressErrorReporting(),platform.InitializeGUI()和 platform.SelectUnifiedTheme()等调用,会检查、收集系统默认字体、字号、主题风格等UI相关的信息,并完成窗口注册等工作。多数为方便测试用,不深究。
MessageLoopForUI main_message_loop;
这行代码看似简单,却包含了chromium中线程和消息循环机制的精髓。test_shell工程中只是用来初始化一些状态,并未真正用到。简单来说,MessageLoop是为不同平台线程事件处理做的封装,MessageLoopForUI面向UI线程,对于windows来说,它是对Win32窗口的消息队列和循环机制做的封装。关于MessageLoop后续会有专门一篇来研究它。
后面的数行代码全都是根据平台环境和命令行确定程序细节功能点,比如v8错误是否abort、窗口风格、加速渲染、url默认加载次数、http cache模式、html5支持等,先略过。
TestShellWebKitInit test_shell_webkit_init(layout_test_mode);
初始化webkit,配置webkit的各种参数。先略过。
icu_util::Initialize();
初始化icu,icu是一个为C/C++和Java语言提供Unicode和国际化支持的库,参考 ICU - International Components for Unicode。chromium将它做为第三方库来支持Unicode。
net::NetModule::SetResourceProvider(TestShell::ResourceProvider);
为network模块设置资源句柄。由于浏览器对本地资源的访问是有限制的,chromium通过NetModule来管理network对本地资源的访问。以后再详细研究。
platform.InitializeGUI();
还记得前面提到的platform对象吗,这句为GUI初始化环境。对于windows来说,内部调用 InitCommonControlsEx初始化标准控件风格,并完成窗口类注册,终于找到熟悉的代码了,呵呵。
TestShell::InitializeTestShell(layout_test_mode, allow_external_pages);
初始化test_shell程序。看代码主要初始化了ole、window list、resource等,同时根据command line设置crash dump,这里用到了google的crash开源处理框架breakpad,之前关于程序崩溃处理的博文中有介绍。
GURL starting_url;
GURL是对url的封装类,test_shell启动时会加载该url,其值在我这里为:“file:///D:/chromium/home/src_tarball/tarball/chromium/src/webkit/data/test_shell/index.htm”。
std::string stats_filename = kStatsFilePrefix +
base::Uint64ToString(base::RandUint64() & 0xFFFFFFFFL);
RemoveSharedMemoryFile(stats_filename);
base::StatsTable *table = new base::StatsTable(stats_filename,
kStatsFileThreads,
kStatsFileCounters);
base::StatsTable::set_current(table);
设置用于统计的stats table。stats table生成一个共享内存的随机名来保证不同实例的唯一性,内部通过一个hash表对刚兴趣的项进行计数。
下面这几行代码就是窗口创建和运行的代码了:
TestShell* shell;
if (TestShell::CreateNewWindow(starting_url, &shell)) {
shell->Show(WebKit::WebNavigationPolicyNewWindow);
if (parsed_command_line.HasSwitch(test_shell::kDumpStatsTable))
shell->DumpStatsTableOnExit();
webkit_glue::SetJavaScriptFlags(TestShell::GetJSFlagsForLoad(0));
MessageLoop::current()->Run();
}
TestShell类是对test_shell app的封装,前面已经调用过很多TestShell的静态函数了,
比如一系列的config设置、 TestShell::InitLogging、 TestShell::InitializeTestShell等。通过调用 TestShell::CreateNewWindow函数,shell变量被实例化,
实际上内部是通过调用 Initialize()函数完成,在windows下主要是创建主窗口m_mainWnd以及初始化主界面,包括“Back”、“Forward”、“Reload”、“Stop”四个按钮和URL编辑框,之后创建一个webview窗口并load url显示内容。
MessageLoop::current()->Run();一句封装了主消息循环,内部通过 base::RunLoop的run函数完成,
最终将调用到 MessagePumpForUI::DoRunLoop中,DoRunLoop基本就是对消息循环的封装。
当收到WM_QUIT消息时,消息循环结束,Run函数返回。经过几个函数的清理工作后,main函数结束。
以上就是整个test_shell的主流程,看上去代码逻辑还是非常清晰的。 真正的chromium逻辑比test_shell肯定要复杂,但大致类似。
可以看到chromium的代码面向对象的封装做的非常好,比如AtExitManager对退出逻辑的封装、MessageLoop对消息循环以及跨平台的包装等。另
外可以看到对command line的封装和处理占了很多语句,还有config、log、crash、statistic等的支持,这些是所有application都需要的部分,值得研究和学习。
从对主流程的学习可以看到,涉及webkit的代码并不多,主要由TestShellWebKitInit和webkit_glue两个封装完成。另外比较重要的部分就是MessageLoop、跨平台以及UI和控件系统了,这几点将是我们后面研究的重点。