从Chrome源码看浏览器如何构建DOM树

Posted 会编程的银猪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从Chrome源码看浏览器如何构建DOM树相关的知识,希望对你有一定的参考价值。

最近下了Chrome的源码,安装了一个debug版的Chromium研究了一下,虽然很多地方都一知半解,但是还是有一点收获,将在这篇文章介绍DOM树是如何构建的,看了本文应该可以回答以下问题:

  1. IE用的是Trident内核,Safari用的是Webkit,Chrome用的是Blink,到底什么是内核,它们的区别是什么?
  2. 如果没有声明<!DOCTYPE html>会造成什么影响?
  3. 浏览器如何处理自定义的标签,如写一个<data></data>?
  4. 查DOM的过程是怎么样的?

先说一下,怎么安装一个可以debug的Chrome

1. 从源码安装Chrome

为了可以打断点debug,必须得从头编译(编译的时候带上debug参数)。所以要下载源码,Chrome把最新的代码更新到了Chromium的工程,是完全开源的,你可以把它整一个git工程下载下来。Chromium的下载安装可参考它的文档, 这里把一些关键点说一下,以Mac为例。你需要先下载它的安装脚本工具,然后下载源码:

–no-history的作用是不把整个git工程下载下来,那个实在是太大了。或者是直接执行git clone:

这个就是整一个git工程,下载下来有6.48GB(那时)。博主就是用的这样的方式,如果下载到最后提示出错了:

可以这样解决:

就不用重头开始clone,因为实在太大、太耗时了。

下载好之后生成build的文件:

–ide=xcode是为了能够使用苹果的XCode进行可视化进行调试。gn命令要下载Chrome的devtools包,文档里面有说明。

装备就绪之后就可以进行编译了:

在笔者的电脑上编译了3个小时,firfox的源码需要编译7、8个小时,所以相对来说已经快了很多,同时没报错,一次就过,相当顺利。编译组装好了之后,会在out/gn目录生成Chromium的可执行文件,具体路径是在:

运行这个就可以打开Chromium了:

那么怎么在可视化的XCode里面进行debug呢?

2. 在XCode里面进行Debug

在上面生成build文件的同时,会生成XCode的工程文件:sources.xcodeproj,具体路径是在:

双击这个文件,打开XCode,在上面的菜单栏里面点击Debug -> AttachToProcess -> Chromium,要先打开Chrome,才能在列表里面看到Chrome的进程。然后小试牛刀,打个断点试试,看会不会跑进来:

在左边的目录树,打开chrome/browser/devtools/devtools_protocol.cc这个文件,然后在这个文件的ParseCommand函数里面打一个断点,按照字面理解这个函数应该是解析控制台的命令。打开Chrome的控制台,输入一条命令,例如:new Date(),按回车可以看到断点生效了:

通过观察变量值,可以看到刚刚敲进去的命令。这就说明了我们安装成功,并且可以通过可视化的方式进行调试。

但是我们要debug页面渲染过程,Chrome的blink框架使用多进程技术,每打开一个tab都会新开一个进程,按上面的方式是debug不了构建DOM过程的,从Chromium的文档可以查到,需要在启动的时候带上一个参数:

Chrom的启动进程就会绪塞,并且提示它的渲染进程ID:

[7339:775:0102/210122.254760:ERROR:child_process.cc(145)] Renderer (7339) paused waiting for debugger to attach. Send SIGUSR1 to unpause.

7339就是它的渲染进程id,在XCode里面点 Debug -> AttachToProcess By Id or Name -> 填入id -> 确定,attach之后,Chrome进程就会恢复,然后就可以开始调试渲染页面的过程了。

content/renderer/render_view_impl.cc这个文件的1093行RenderViewImpl::Create函数里面打个断点,按照上面的方式,重新启动Chrome,在命令行带上某个html文件的路径,为了打开Chrome的时候就会同时打开这个文件,方便调试。执行完之后就可以看到断点生效了。可以说render_view_impl.cc这个文件是第一个具体开始渲染页面的文件——它会初始化页面的一些默认设置,如字体大小、默认的viewport等,响应关闭页面、OrientationChange等事件,而在它再往上的层主要是一些负责通信的类。

3. Chrome建DOM源码分析

先画出构建DOM的几个关键的类的UML图,如下所示:

第一个类HTMLDocumentParser负责解析html文本为tokens,一个token就是一个标签文本的序列化,并借助HTMLTreeBuilder对这些tokens分类处理,根据不同的标签类型、在文档不同位置,调用HTMLConstructionSite不同的函数构建DOM树。而HTMLConstructionSite借助一个工厂类对不同类型的标签创建不同的html元素,并建立起它们的父子兄弟关系,其中它有一个m_document的成员变量,这个变量就是这棵树的根结点,也是js里面的window.document对象。

为作说明,用一个简单的html文件一步步看这个DOM树是如何建立起来的:

然后按照上面第2点提到debug的方法,打开Chromium并开始debug:

我们先来研究一下Chrome的加载和解析机制

1. 加载机制

以发http请求去加载html文本做为我们分析的第一步,在此之前的一些初始化就不考虑了。Chrome是在DocumentLoader这个类里面的startLoadingMainResource函数里去加载url返回的数据,如访问一个网站则返回html文本: