chromium中的URL加载过程解析

2 篇文章 0 订阅
1 篇文章 0 订阅

chromium内核代码一直在更新,最近又有了大动作。尤其是IPC通信部分,因为性能问题,传统的IPC已经被弃用,虽然不是完全舍弃,但除了严重依赖于时序关系的Navigate相关消息外,其他的所有IPC::Channel都被替换成了mojom。这就导致以前的FrameMsg_Navigate、ResourceHostMsg_RequestSource等IPC消息在chromium代码中不再可见了。因为大体的流程没有改变,有兴趣的可以回顾一下老罗的文章,这里只是从头梳理一下chromium69版本内核代码加载一个url的整个过程,以及该过程中涉及到的一些重要的类和方法。

(个人理解得不够深刻,如有问题,请留言指教,不胜感激)

      用户在地址栏输入一个URL,浏览器加载该URL的过程,被称为“Navigate”,所以该过程中会有很多类和方法名中,包含有该单词,如有遇到,可以认为该类或方法很可能和URL加载过程相关。

第一阶段(发送请求):

1. WebContents

      在浏览器中代表一个页面的实体,它是一个抽象类,实现于WebContentsImpl。每个WebContents对象都会有一个NavigationController,它管理着URL的前进/后退列表,负责加载URL到WebContents中。所以,浏览器在导航URL的时候,会首先创建一个WebContentsImpl对象,调用其GetController()方法,获取到其NavigationController成员。获取到与WebContents关联的NavigationController后,浏览器一般会调用它的LoadURLWithParams()方法进行目标URL的加载工作。

2. NavigationController

      每个WebContents里都关联着一个NavigationController对象,且每个NavigationController也只被关联在一个WebContents中,二者一一对应。NavigationController也是一个抽象类,它的实现类的类名,也是在后面加个"Impl",即NavigationControllerImpl。当其成员函数LoadURLWithParams()方法被调用后,它会在进行简单的url判断后,调用成员函数NavigateWithoutEntry()继续处理。该函数实现里主要做了三件事:
1. 确定目标URL所要加载在FrameTree中的哪个node上,即得到一个FrameTreeNode对象;
2. 创建一个NavigationEntry对象,用于组建一个NavigationRequest;
3. 输送NavigationRequest到目标FrameTreeNode的NavigatorImpl对象的Navigate()方法中。

3. Navigator

       该类负责在一棵FrameTree的节点中执行URL导航操作,可以被同一棵FrameTree上的多个FrameTreeNode所共享,但不能被多棵FrameTree的子节点所共享。该类是一个抽象类,实现类为NavigatorImpl。Navigate()方法中,先判断当前指定的FrameTreeNode所代表的网页中是否有悬挂的BeforeUnload事件处理器需要执行,如果有,则先执行BeforeUnload事件处理程序,稍后派发NavigationRequest到FrameTreeNode;如果没有,则立即派发。派发形式如下:
1. 调用FrameTreeNode的CreateNavigationRequest()方法,将NavigationRequest对象存储;
2. 调用FrameTreeNode中NavigationRequest对象的BeginNavigation()方法进行加载。
BeforeUnload事件的判断处理,是在第一步和第二步中间。

4. NavigationRequest

       该类存在于UI线程,确保URL请求会在IO线程中的ResourceDispatcherHost中执行,描述UI线程和IO线程之间的交互。该类先对目标URL进行了内容安全策略检查,以及注册了各种NavigationThrottles对目标URL进行审批,最终会创建一个NavigationURLLoader对象。

5. NavigationURLLoader

       该类实现类NavigationURLLoaderImpl构造函数中,进行线程调度,在IO线程中执行StartWithoutNetworkService()方法,并在该方法中调用ThrottlingURLLoader::CreateLoaderAndStart()方法创建了一个ThrottlingURLLoader对象。

6. ThrottlingURLLoader

       该类继承于network::mojom::URLLoaderClient,可以进行IPC通信,且Render进程和Browser进程都有其实例化对象。类名中带着"Throttling"的,且和URL加载相关的类,通常都会根据某些自定义规则,对网络数据进行拦截过滤处理,就像一个瓶塞一样。它的CreateLoaderAndStart()方法,创建完自身的一个实例对象后,调用其Start()方法。Start方法接收一个SharedURLLoaderFactory类实例(该实例是在NavigationURLLoaderImpl的StartWithNetworkService()方法中创建的),并调用SharedURLLoaderFactory实例的CreateLoaderAndStart()。

7. SingleRequestURLLoaderFactory

       该类继承于network::SharedURLLoaderFactory,而SharedURLLoaderFactory又继承于mojom::URLLoaderFactory。URLLoaderFactory这一系列的近亲类(比如WebUIURLLoaderFactory、FileURLLoaderFactory、CORSURLLoaderFactory等),都可以创建一个mojom::URLLoader对象,既可以跨进程加载url并得到返回数据,又可以同进程加载url,该性质来自于mojom的调用机制。
      SingleRequestURLLoaderFactory的CreateLoaderAndStart方法中执行回调函数,调用堆栈返回到了URLLoaderRequestController::CreateNonNetworkServiceURLLoader(),该方法调用ResourceDispatcherHostImpl类的BeginNavigationRequest()。

8. ResourceDispatcherHostImpl

      ResourceDispatcher和ResourseDispatcherHost分别是Render进程和Borwser进程进行资源分发的接口类。
      在BeginNavigationRequest()方法中创建了一个URLRequest对象,在BeginRequestInternal()方法中创建了一个ResourceLoader对象,然后在StartLoading()方法中,调用ResourceLoader对象的StartRequest()方法开始加载请求。

9. ResourceLoader

      该类集中接收转发URLRequest、SSLErrorHandler、SSLClientAuthHandler、ResourceHandler相关的事件。
      该类StartRequestInternal()方法中,直接调用了URLRequest对象的Start()方法,至此结束了Navigate()的第一个过程。

 

第二阶段(数据响应):

网络模块获取到响应头数据,数据流向及处理方法。

     ResourceLoader类的ResponseCompleted()方法被调用,然后通过ResourceLoader成员变量handler的OnResponseCompleted()方法向上传递数据。主要的handler类有MimeSniffingResourceHandler、CrossSiteDocumentResourceHandler、InterceptingResourceHandler、MojoAsyncResourceHandler等,各个handler都可以对数据进行截获处理,最终NavigationRequest类的OnResponseStarted()方法被调用。该方法最终调用到RenderFrameHostImpl::CommitNavigation(),RenderFrameHostImpl发送了一个IPC消息到Render进程。

 

第三阶段(Render进程发起主要资源(一般指html文件)网络请求):

1. RenderFrameImpl

      CommitNavigation()函数除了携带response_header、request_params等基本信息,还有mojom通信相关接口url_loader_client_endpoints和Browser进程目前所支持的URLLoaderFactory列表subresource_loader_factories。关于mojom接口的绑定过程,参考Converting Legacy Chrome IPC To Mojo一文,这里不详细赘述。参数subresource_loader_factories是一个Bundle,包裹着从Browser进程传递过来的各种URLLoaderFactory,前面说过,URLLoaderFactory可以跨进程进行资源请求,而不同的URLLoaderFactory用来请求不同scheme的资源。比如WebUIURLLoaderFactory用来请求浏览器内置页面,url格式一般类似于chrome://page;再比如FileSystemURLLoaderFactory用来请求本地资源,url格式类似于file:///C:\\test.txt。进行网络请求的时候,Render进程去factory列表里根据url的scheme里查找对应的URLLoaderFactory,调用它的CreateLoaderAndStart()方法进行资源请求。
      RenderFrameImpl类的CommitNavigation()方法被mojom消息调起,它根据消息携带的header信息、request参数信息以及url_loader_client_endpoints创建一个WebURLRequest对象,并调用成员变量frame_的CommitNavigation()将其传递过去。RenderFrameImpl的成员变量frame_指向了WebLocalFrameImpl,WebLocalFrameImpl接收到WebURLRequest对象后,将其转换成FrameLoadRequest类型,然后调用传递给FrameLoader类的CommitNavigation()函数。

2. FrameLoader

       CommitNavigation()接收到FrameLoadRequest后,直接调用了StartLoad()函数,在StartLoad()函数中,创建并用FrameLoadRequest参数初始化了一个DocumentLoader对象,然后调起DocumentLoader对象的StartLoading()方法。

3. DocumentLoader

       StartLoading()函数准备好request、fetcher等参数,调用RawResource类的静态方法FetchMainResource()去请求主要资源。

4. RawResource

      FetchMainResource()函数,根据Resoure::kMainResource类型去创建一个ResourceFactory对象,同FetchParameters对象一起作为参数,调起参数列表中fetcher的RequestResource()函数。

5. ResourceFetcher

    RequestResource()函数创建Resource对象,调用StartLoad()方法。StartLoad()方法创建一个ResourceLoader对象,调用loader的Start()方法。

6. ResourceLoader

   Start()方法调用ResourceLoaderScheduler::Request(),最终回调到ResourceLoader::StartWith()方法中。ResourceLoader的StartWith()调用WebURLLoaderImpl类的LoadAsynchronously()方法。

7. WebURLLoaderImpl

    LoadAsynchronously()通过自己的成员变量context_,对request进行加载。最终从WebURLLoaderImpl::Context::Start()方法中调用了ResourceDispatcher类的StartAsync()方法。

8. ResourceDispatcher

    调用ThrottlingURLLoader类的CreateLoaderAndStart()方法。

9. ThrolltingURLLoader

    Start()函数接收一个SharedURLLoaderFactory和一个ResourceRequest参数,调用factory的CreateLoaderAndStart()方法。

10. ChildURLLoaderFactoryBundle

   CreateLoaderAndStart()方法被调起,参数包含network::ResourceRequest和一个network::mojom::URLLoaderRequest对象,根据request.url获取对应的URLLoaderFactory,然后调用该factory的CreateLoaderAndStart()发送跨进程IPC消息到Browser Process。

 

第四阶段(请求主要资源):

   Browser进程接收CreateLoaderAndStart()方法的跨进程调用的位置,是在ResourceMessageFilter类的同名方法CreateLoaderAndStart()。 该类有一个成员变量url_loader_factory_,指向CORSURLLoaderFactory,且该类的CreateLoaderAndStart()方法被调用。最终传递到URLLoaderFactoryImpl::CreateLoaderAndStart(),该方法获取全局的ResourceDispatcherHostImpl实例,调用其OnRequestResourceWithMojo(),代替以前的ResourceHostMsg_RequestResource消息。ResourceDispatcherHostImpl接收来自Render进程的网络请求相关参数,调用OnRequestResourceInternal()方法开始对该请求进行加载。过程同第一阶段相同。

    当数据请求有结果时,同样是几个Handler类的OnReadCompleted()方法最先被调用,然后通过network::mojom::URLLoaderClientProxy类的OnStartLoadingResponseBody()方法将数据结果跨进程通知回Render进程。

第五阶段(接收处理主要资源,发起子资源请求):

同名方法OnStartLoadingResponseBody()被调用,分别经过以下几个类,最终到达HTMLTreeBuilder:
URLResponseBodyConsumer::OnReadable()
WebURLLoaderImpl::RequestPeerImpl::OnReceivedData()
WebURLLoaderImpl::Context::OnReceivedData()
ResourceLoader::DidReceviedData()
RawResource::AppendData()
DocumentLoader::DataReceived()   ::ProcessData()   ::CommitData()  ::InstallNewDocument()
HTMLDocumentParser::AppendBytes()  ::PumpPendingSpeculations()  ::ProcessTokenizedChunkFromBackgroundParser()
HTMLTreeBuilder::ConstructTree()

在构建DOM树的时候,如果发现一个子节点需要加载资源,比如css文件。则HTMLDocumentParser类的DocumentElementAvailable()方法会被调用,然后调用自身的资源预加载器preloader_的TakeAndPreload()对资源进行加载。之后的调用过程如下:
HTMLResourcePreloader::Preload()
PreloadRequest::Start()
DocumentLoader::StartPreload()  
CSSStyleSheetResource::Fetch() 
ResourceFetcher::RequestResource()
过程同上.....
ChildURLLoaderFactoryBundle::CreateLoaderAndStart()

 

 

以下是堆栈调用的一个大概过程:

content::NavigationControllerImpl::LoadURLWithParams()
content::NavigationControllerImpl::NavigateWithoutEntry()
content::NavigatorImpl::Navigate()
content::NavigationRequest::BeginNavigation()
content::NavigationHandleImpl::WillStartRequest()
content::NavigationRequest::OnStartCehcksComplete()
content::NavigationURLLoader::Create()
content::NavigationURLLoaderImpl::NavigationURLLoaderImpl()
content::NavigationURLLoaderImpl::StartWithoutNetworkService()
content::ThrottlingURLLoader::CreateLoaderAndStart()
content::ThrottlingURLLoader::Start()

content::ThrottlingURLLoader::StartNow()
content::SingleRequestURLLoaderFactory::CreateLoaderAndStart()
    
content::SingleRequestURLLoaderFactory::HandleRequest()
content::NavigationURLLoaderImpl::URLLoaderRequestController::CreateNonNetworkServiceURLLoader();
      
content::ResourceDispatcherHostImpl::BeginNavigationRequest()
content::ResourceDispatcherHostImpl::BeginNavigationRequestInternal()   -->  content::ResourceLoader::ResourceLoader()
content::ResourceDispatcherHostImpl::StartLoading()

content::ResourceLoader::StartRequest()     
content::ResourceLoader::ScopedDeferral::~ScopedDeferral()   //判断状态
content::ResourceLoader::Resume()      
content::ResourceLoader::StartRequestInternal()
net::URLRequest::Start() 


content::NavigationURLLoaderImpl::OnReceiveResponse()
content::NavigationRequest::OnResponseStarted()
content::NavigationHandleImpl::WillProcessResponse()
content::NavigationRequest::OnWillProcessResponseChecksComplete()
content::NavigationRequest::CommitNavigation()      
content::RenderFrameHostImpl::CommitNavigation()
content::mojom::FrameNavigationControlProxy::CommitNavigation()         // Send IPC Message To Render Process


-----------------Render Process-------------------------------------
.........
content::RenderFrameImpl::CommitNavigation()
blink::WebLocalFrameImpl::CommitNavigation()
blink::FrameLoader::CommitNavigation()
blink::FrameLoader::StartLoad()
blink::DocumentLoader::StartLoading()
blink::RawResource::FetchMainResource()
blink::ResourceFetcher::RequestResource()
    
blink::ResourceFetcher::StartLoad()
blink::ResourceLoader::Start()
blink::ResourceLoaderScheduler::Request()
blink::ResourceLoaderScheduler::Run()
blink::ResourceLoader::Run()
blink::ResourceLoader::StartWith(blink::ResourceRequest& request)
content::WebURLLoaderImpl::LoadAsynchronously()
content::WebURLLoaderImpl::Context::Start()
content::ResourceDispatcher::StartAsync()
content::ThrottlingURLLoader::CreateLoaderAndStart()
content::ThrottlingURLLoader::Start()
content::ThrottlingURLLoader::StartNow()
content::ChildURLLoaderFactoryBundle::CreateLoaderAndStart()
network::mojom::URLLoaderFactoryProxy::CreateLoaderAndStart()    
    
    
-------------------------------------------------Browser Process-----------------
content::ResourceMessageFilter::CreateLoaderAndStart()
network::cors::CORSURLLoaderFactory::CreateLoaderAndStart()
content::URLLoaderFactoryImpl::CreateLoaderAndStart()
content::ResourceDispatcherHostImpl::OnRequestResourceWithMojo()
content::ResourceDispatcherHostImpl::OnRequestResourceInternal()
content::ResourceDispatcherHostImpl::BeginRequest()
content::ResourceDispatcherHostImpl::StartLoading()
content::ResourceLoader::StartRequest()      
content::ResourceLoader::ScopedDeferral::~ScopedDeferral()   //判断状态
content::ResourceLoader::Resume()      
content::ResourceLoader::StartRequestInternal()
net::URLRequest::Start()    
    
    
content::LayeredResourceHandler::OnReadCompleted()
content::InterceptingResourceHandler::OnReadCompleted()
maxthon::MxResourceSnifferHandler::OnReadCompleted()
content::MojoAsyncResourceHandler::OnReadCompleted()
network::mojom::URLLoaderClientProxy::OnStartLoadingResponseBody()


-----------------------Renderer Process-------------------------------------       
content::URLLoaderClientImpl::OnStartLoadingResponseBody()
content::URLResponseBodyConsumer::OnReadable()      
content::WebURLLoaderImpl::RequestPeerImpl::OnReceivedData()      
content::WebURLLoaderImpl::Context::OnReceivedData()
blink::ResourceLoader::DidReceiveData()      
blink::RawResource::AppendData()      
blink::Resource::AppendData()      
blink::DocumentLoader::DataReceived()
blink::DocumentLoader::ProcessData()
blink::DocumentLoader::CommitData()      
    blink::HTMLDocumentParser::AppendBytes()  
    blink::DocumentLoader::CommitNavigation()
blink::DocumentLoader::InstallNewDocument()    
    
blink::HTMLDocumentParser::PumpPendingSpeculations()
blink::HTMLDocumentParser::ProcessTokenizedChunkFromBackgroundParser()
blink::HTMLTreeBuilder::ConstructTree()
blink::HTMLTreeBuilder::ProcessToken()
blink::HTMLTreeBuilder::ProcessStartTag()
blink::HTMLConstructionSite::InsertHTMLHtmlStartTagBeforeHTML()
blink::HTMLHtmlElement::InsertedByParser()
blink::HTMLDocumentParser::DocumentElementAvailable()
blink::ResourcePreloader::TakeAndPreload()
blink::HTMLResourcePreloader::Preload()
blink::PreloadRequest::Start()
blink::DocumentLoader::StartPreload()      
blink::CSSStyleSheetResource::Fetch() 
blink::ResourceFetcher::RequestResource()
blink::ResourceFetcher::StartLoad()
blink::ResourceLoader::Start()
blink::ResourceLoaderScheduler::Request()
blink::ResourceLoaderScheduler::Run()
blink::ResourceLoader::Run()
blink::ResourceLoader::StartWith(blink::ResourceRequest& request)
content::WebURLLoaderImpl::LoadAsynchronously()
content::WebURLLoaderImpl::Context::Start()
content::ResourceDispatcher::StartAsync()
content::ThrottlingURLLoader::CreateLoaderAndStart()
content::ThrottlingURLLoader::Start()
content::ThrottlingURLLoader::StartNow()
content::ChildURLLoaderFactoryBundle::CreateLoaderAndStart(n)
network::mojom::URLLoaderFactoryProxy::CreateLoaderAndStart()  

 

今天先列一个大纲,详细叙述需要慢慢填充,望大家谅解!

 

 

 



 
    
    
    
    
    
    

  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
### 回答1: Chromium渲染过程解析过程是一个复杂的过程,它首先会解析HTML文档,以解析的元素,然后会执行JavaScript代码,并将其的代码转换为绘图命令,然后将命令发送到GPU进行渲染,最后将渲染结果显示在屏幕上。 ### 回答2: Chromium的渲染过程是一个复杂但又高效的流程,主要包括以下几个步骤: 1. HTML解析Chromium首先将接收到的HTML代码解析成DOM树,树的每个节点代表一个HTML元素。 2. 样式计算:Chromium会对每个DOM节点进行样式计算,包括继承和层叠等规则,最终确定每个节点的具体样式。 3. 布局计算:根据DOM树和样式信息,Chromium会计算每个节点在页面上的位置和大小,形成布局树。 4. 绘制过程:布局树形成后,Chromium会进行绘制过程,将页面上的各个元素转化为图像,包括文字、图片等。 5. 合成与显示:Chromium将绘制好的图像进行合成,并将最终的结果显示在屏幕上。 在这整个过程Chromium使用多个线程来并行处理不同的任务,提高渲染效率和响应速度。其,主线程负责解析HTML代码、样式计算和布局计算,合成线程负责合成和显示图像。 此外,为了加快渲染速度,Chromium还使用了一些其他的优化技术,如预渲染(Prerendering)、预取(Prefetching)等。预渲染是在用户还没有打开某个链接时,提前将其内容渲染到后台,当用户访问时可以直接展示。预取是在用户打开某个链接后,在后台预取可能的下一个页面,以缩短加载时间。 总的来说,Chromium的渲染过程是一个复杂而又高效的流程,通过并行处理和其他优化技术,能够快速且准确地将HTML代码转化为可见的网页内容。 ### 回答3: Chromium 渲染过程是指浏览器网页内容如何被解析和显示的一系列步骤。下面对其进行详细说明。 1. 解析 HTML:Chromium 首先会将接收到的 HTML 文档解析成一个 DOM 树(文档对象模型),DOM 树是一个表示网页结构的树状结构。解析过程会将标签、属性、文本内容等解析成对应的节点。 2. 构建渲染树:解析 HTML 后,Chromium 会根据样式信息和 DOM 树构建渲染树,渲染树只包含需要显示的节点,如可见的文本、图片、表单等元素。渲染树的构建过程会考虑 CSS 样式、布局和脚本等信息。 3. 布局:渲染树构建完成后,Chromium 开始进行布局计算。布局计算是指确定每个节点在屏幕上的位置、大小等信息。布局计算会考虑 DOM 结构和渲染树的布局属性。 4. 绘制:布局完成后,Chromium 根据渲染树和布局信息开始进行绘制。绘制过程将渲染树转换为图像,绘制出网页的可见部分。 5. 栅格化和合成:绘制完成后,Chromium 还需要将图像分成小块并进行栅格化(rasterization),将图像转换为位图,并将这些位图合成为最终的屏幕图像。 6. 显示:最后,Chromium 将生成的图像发送给显示设备,如屏幕,进行显示。 整个渲染过程是一个复杂的流程,涉及到 HTML 解析、CSS 处理、布局计算、绘制、栅格化和合成等多个步骤。Chromium 通过优化这些步骤,使得网页可以快速、平滑地显示在用户的浏览器上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值