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::InitLoggingTestShell::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和控件系统了,这几点将是我们后面研究的重点。