站在老罗的肩膀上:https://blog.youkuaiyun.com/luoshengyang/article/details/50615628
每一个HTML标签在DOM Tree中都有一个对应的HTMLElement节点。相应地,在DOM Tree中每一个需要渲染的HTMLElement节点在Layout Object Tree中都有一个对应的Layout Object节点,如图1所示(To Update):
图1 Layout Object Tree与DOM Tree、Render Layer Tree和Graphics Layer Tree的关系
从图1还可以看到,Layout Object Tree创建完成之后,WebKit还会继续根据它的内容创建一个Layout Layer Tree和一个Graphics Layer Tree。本文主要关注Render Object Tree的创建过程。
从前面Chromium DOM Tree创建过程分析一文还可以知道,DOM Tree是在网页内容的下载过程中创建的。一旦网页内容下载完成,DOM Tree就创建完成了。网页的Layout Object Tree与DOM Tree不一样,它是在网页内容下载完成之后才开始创建的。因此,接下来我们就从网页内容下载完成时开始分析网页的Layout Object Tree的创建过程。
从前面Chromium网页URL加载过程分析一文可以知道,WebKit是通过Browser进程下载网页内容的。Browser进程一方面通过Net模块中的URLRequest类去Web服务器请求网页内容,另一方面又通过Content模块中的ResourceLoader类的成员函数OnReadCompleted不断地获得URLRequest类请求回来的网页内容,如下所示:
void ResourceLoader::OnReadCompleted(net::URLRequest* unused, int bytes_read) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"),
"ResourceLoader::OnReadCompleted");
DCHECK_EQ(request_.get(), unused);
DVLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\""
<< " bytes_read = " << bytes_read;
pending_read_ = false;
// bytes_read == -1 always implies an error.
if (bytes_read == -1 || !request_->status().is_success()) {
ResponseCompleted();
return;
}
CompleteRead(bytes_read);
}
src/content/browser/loader/resource_loader.cc
参数bytes_read表示当前这次从URLRequest类中读取回来的网页内容的长度。当这个长度值等于0的时候,就表示所有的网页内容已经读取完毕。这时候ResourceLoader类的成员函数OnReadCompleted就会调用另外一个成员函数ResponseCompleted进行下一步处理。
ResourceLoader类的成员函数ResponseCompleted的实现如下所示:
void ResourceLoader::ResponseCompleted() {
TRACE_EVENT_WITH_FLOW0("loading", "ResourceLoader::ResponseCompleted", this,
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
DVLOG(1) << "ResponseCompleted: " << request_->url().spec();
ScopedDeferral scoped_deferral(this, DEFERRED_FINISH);
handler_->OnResponseCompleted(request_->status(),
std::make_unique<Controller>(this));
}
src/content/browser/loader/mojo_async_resource_handler.cc
在前面Chromium网页URL加载过程分析一文中,我们假设ResourceLoader类的成员变量handler_指向的是一个MojoAsyncResourceHandler对象。ResourceLoader类的成员函数ResponseCompleted调用这个MojoAsyncResourceHandler对象的成员函数OnResponseCompleted进行下一步处理。
MojoAsyncResourceHandler类的成员函数OnResponseCompleted的实现如下所示:
void MojoAsyncResourceHandler::OnResponseCompleted(
const net::URLRequestStatus& request_status,
std::unique_ptr<ResourceController> controller) {
...
url_loader_client_->OnComplete(loader_status);
...
}
src/content/browser/loader/mojo_async_resource_handler.cc
void URLLoaderClientProxy::OnComplete(
const network::URLLoaderCompletionStatus& in_status) {
...
auto message = URLLoaderClientProxy_OnComplete_Message::Build(
kSerialize, kExpectsResponse, kIsSync, std::move(in_status));
...
ignore_result(receiver_->Accept(&message));
}
src/out/Debug/gen/services/network/public/mojom/url_loader.mojom.cc
receiver为renderer进程的AsyncResourceHandler类的成员函数OnResponseCompleted所做的事情是向Render进程发送一个类型为ResourceMsg_RequestComplete的IPC消息,用来通知Render进程它所请求的网页内容已下载完毕。
Render进程是通过ResourceDispatcher类的成员函数DispatchMessage接收类型为ResourceMsg_RequestComplete的IPC消息的,如下所示:
How renderer process accept message from browser
...Missing...
从这里可以看到,ResourceDispatcher类的成员函数DispatchMessage将类型为mojo::Messege消息分发给另外一个成员函数OnRequestComplete处理。
ResourceDispatcher类的成员函数OnRequestComplete的实现如下所示:
void ResourceDispatcher::OnRequestComplete(
int request_id,
const network::URLLoaderCompletionStatus& status) {
TRACE_EVENT0("loader", "ResourceDispatcher::OnRequestComplete");
PendingRequestInfo* request_info = GetPendingRequestInfo(request_id);
...
RequestPeer* peer = request_info->peer.get();
...
peer->OnCompletedRequest(renderer_status);
}
src/content/renderer/loader/resource_dispatcher.cc
从前面Chromium网页URL加载过程分析一文可以知道,Render进程在请求Browser进程下载指定URL对应的网页内容之前,会创建一个PendingRequestInfo对象。这个PendingRequestInfo对象以一个Request ID为键值保存在ResourceDispatcher类的内部。这个Request ID即为参数request_id描述的Request ID。因此,ResourceDispatcher类的成员函数OnRequestComplete可以通过参数request_id获得一个PendingRequestInfo对象。有了这个PendingRequestInfo对象之后,ResourceDispatcher类的成员函数OnSetDataBuffer再通过它的成员变量peer获得一个WebURLLoaderImpl::Context对象,并且调用它的成员函数OnCompletedRequest通知它下载网页内容的请求已完成。
WebURLLoaderImpl::Context类的成员函数OnCompletedRequest的实现如下所示:
void WebURLLoaderImpl::RequestPeerImpl::OnCompletedRequest(
const network::URLLoaderCompletionStatus& status) {
context_->OnCompletedRequest(status);
}
void WebURLLoaderImpl::Context::OnCompletedRequest(
const network::URLLoaderCompletionStatus& status) {
...
if (client_) {
...
client_->DidFail(
status.cors_error_status
? WebURLError(*status.cors_error_status, has_copy_in_cache, url_)
: WebURLError(status.error_code, status.extended_error_code,
has_copy_in_cache,
WebURLError::IsWebSecurityViolation::kFalse, url_),
total_transfer_size, encoded_body_size, status.decoded_body_length);
} else {
client_->DidFinishLoading(status.completion_time, total_transfer_size,
encoded_body_size, status.decoded_body_length,
status.should_report_corb_blocking);
}
}
}
src/content/renderer/loader/web_url_loader_impl.cc
从前面Chromium网页URL加载过程分析一文可以知道,WebURLLoaderImpl::Context类的成员变量client_指向的是WebKit模块中的一个ResourceLoader对象。在成功下载完成网页内容的情况下,WebURLLoaderImpl::Context类的成员函数OnC