chromium中的URL加载过程解析
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()
今天先列一个大纲,详细叙述需要慢慢填充,望大家谅解!
————————————————
版权声明:本文为CSDN博主「跳出地球」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/camike/article/details/85230513
从render_view_impl.cc开始说起。
1. 方法RenderViewImpl::Initialize中有:
WebLocalFrame* web_frame = WebLocalFrame::create(main_render_frame_.get());
这里会创建WebLocalFrame对象。
之后有webwidget_ = WebView::create(this); 这里会创建WebViewImpl对象。且后面将两者关联起来。
2. 我们来先来看看WebLocalFrame::create
1. WebLocalFrameImpl的构造函数中,创建FrameLoaderClientImpl对象。
3. WebViewImpl的创建
WebViewImpl在创建中,会创建Page对象。一个WebViewImpl对应一个Page。
4.WebViewImpl的setMainFrame
这里的调用逻辑如下:
WebViewImpl::setMainFrame
WebLocalFrameImpl::initializeCoreFrame
LocalFrame::LocalFrame
在LocalFrame的构造函数中,会创建FrameLoader对象和ScriptController对象
4. Document对象及相关逻辑
FrameLoader::init()和 FrameLoader::startLoad函数中,创建了多个DocumentLoader对象。
RawResource::didAddClient
DocumentLoader::dataReceived
DocumentLoader::commitData
DocumentLoader::ensureWriter
DocumentLoader::createWriterFor
(LocalDOMWindow::create)
LocalDOMWindow::installNewDocument
LocalDOMWindow::createDocument
Document::Document
5.归纳
1. Local Frame主要处理逻辑,含有FrameView对象,用于处理内容显示。FrameView继承ScrollViewArea类,故,FrameView内容可超过屏幕。
(未完待续)
————————————————
版权声明:本文为CSDN博主「u011882998」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u011882998/article/details/26150415