chromium網路資源載入分析(一) 主資源載入邏輯分析 ---chromium39
最近花了點時間看了看chromium載入網頁的邏輯。由於這段內容較為複雜,現在只看了一部分。現將主資源的載入記錄下來。
注:下面提到的檔案,如果沒有指明目錄,則在third_party/WebKit目錄下
1. ContentViewCore執行loadUrl之後,經過一些邏輯(這些邏輯比較簡單,這裡不做介紹),最終會走到:render_frame_impl.cc的方法:RenderFrameImpl::OnNavigate
該方法中,有程式碼:frame->loadRequest(request);
2. 上面方法在thirdparty/WebKit目錄下,WebLocalFrameImpl.cpp檔案中:WebFrameImpl::loadRequest
3. 接著走到:FrameLoader.cpp檔案中,FrameLoader::load
4. 接著走到:同文件中方法:FrameLoader::loadWithNavigationAction
在該方法中,建立了DocumentLoader物件。且該物件的狀態從:m_policyDocumentLoader變化為m_provisionalDocumentLoader
注:DocumentLoader物件的狀態的再次改變是在方法:FrameLoader::commitProvisionalLoad()中:
m_documentLoader = m_provisionalDocumentLoader.release();
m_policyDocumentLoader = m_client->createDocumentLoader(m_frame, request, substituteData.isValid() ? substituteData : defaultSubstituteDataForURL(request.url()));
之後該DocumentLoader物件狀態發生變化:
m_provisionalDocumentLoader = m_policyDocumentLoader.release();
5. 接著走到:DocumentLoader.cpp檔案中方法: DocumentLoader::startLoadingMainResource()
該方法中有程式碼:m_mainResource = m_fetcher->fetchMainResource(cachedResourceRequest, m_substituteData);
6. 該程式碼是檔案ResourceFetcher.cpp檔案中方法:ResourceFetcher::fetchMainResource
7. 接著走到:同文件中的方法:ResourceFetcher::requestResource
該方法中有程式碼:resource = loadResource(type, request, request.charset()); 會建立Resouce物件。
緊接著有程式碼:resource->load(this, request.options());
8.上面方法是在檔案:Resource.cpp中,Resource::load。
該方法中有程式碼:m_loader = ResourceLoader::create(fetcher, this, request, options);
m_loader->start();
建立了ResourceLoader物件。
9. 我們接著看 m_loader->start();。該程式碼是執行的ResourceLoader.cpp檔案中方法:ResourceLoader::start()。
在該方法中有程式碼:m_host->willStartLoadingResource(m_request);
執行的是ResourceFetcher物件的方法:willStartLoadingResource
上面方法會呼叫檔案ApplicationCacheHost.cpp中方法:willStartLoadingResource。
上面方法會呼叫content目錄下檔案:web_application_cache_host_impl.cc檔案中方法:
WebApplicationCacheHostImpl::willStartSubResourceRequest。
繼續看方法:ResourceLoader::start()。
該方法中有程式碼:m_loader = adoptPtr(blink::Platform::current()->createURLLoader());
這裡建立了平臺化的WebURLLoader物件。
createURLLoader()方法在檔案content/下的blink_platform_impl.cc.
10. 是面建立的WebURLLoader物件是檔案: .../src/content/下的檔案web_url_loader_impl.cc
我們繼續看該檔案中的方法:loadAsynchronously
11. WebURLLoaderImpl::loadAsynchronously方法中有程式碼:context_->Start(request, NULL);
該Start方法在同文件中:void WebURLLoaderImpl::Context::Start
該方法中有程式碼:bridge_.reset(resource_dispatcher_->CreateBridge(request_info));
12. 上面方法會呼叫:content目錄下:resource_dispatcher.cc檔案中的方法:ResourceDispatcher::CreateBridge.
該方法會建立物件:IPCResourceLoaderBridge
13. 我們繼續看10中提到的方法:WebURLLoaderImpl::Context::Start。該方法中有程式碼:bridge_->Start(this)
14. 通過11-12的解釋,我們來看content目錄下:IPCResourceLoaderBridge物件的Start方法。該方法是在content目錄下檔案:resource_dispatcher.cc中:IPCResourceLoaderBridge::Start(RequestPeer* peer)
15. 上面方法會發送訊息,該訊息處理是在content目錄下檔案:resource_dispatcher_host_impl.cc中方法:ResourceDispatcherHostImpl::OnRequestResource
16. 上面方法會執行同檔案的方法:ResourceDispatcherHostImpl::BeginRequest(方法。
17. 上面方法會執行同檔案的方法:ResourceDispatcherHostImpl::BeginRequestInternal方法。
18. 上面方法會執行同檔案的方法:ResourceDispatcherHostImpl::StartLoading方法
19. 上面方法會呼叫content目錄下檔案:resource_loader.cc中方法:ResourceLoader::StartRequest()
20. 上面方法會呼叫同文件的方法:ResourceLoader::StartRequestInternal()。該方法中有程式碼:request_->Start();
該request_是.../src/net目錄下檔案url_request.cc物件。
21. 我們來看.../src/net目錄下檔案url_request.cc中方法: URLRequest::Start()
22. 上面方法會呼叫同文件中的方法:URLRequest::StartJob.該函式最後一行:job_->Start();
該job_是.../src/content目錄下url_request_job.cc物件。
23. 上面方法又會呼叫同文件的方法: MaybeBeginDelivery();
24. 上面方法會呼叫同文件的方法:BeginDelivery
25. 上面方法會呼叫.../src/net/目錄下檔案:url_request_job.cc中的方法:URLRequestJob::NotifyRestartRequired
26. 上面方法呼叫.../src/net/目錄下檔案:url_request.cc中方法:URLRequest::Restart
27. 上面方法會呼叫同文件的方法:URLRequest::RestartWithJob
28. 上面方法呼叫同文件的方法:呼叫同文件方法:PrepareToRestart()將當前的job kill掉。
之後會呼叫:URLRequest::StartJob()
這次該檔案中方法:job_->Start();
這次建立的job是url_request_job.cc物件。
關於這裡,過程有些羅嗦。job建立和restart過程好幾次。最後建立的是url_request_http_job.cc.我們這裡看看其具體過程。
I/chromium( 2598): [url_request_job_manager.cc(50)] URLRequestJobManager::CreateJob
I/xxx ( 2598): Tab.java @@@@@@@@@@@@@@@ onPageStarted url is http://www.baidu.com/
I/chromium( 2598): [url_request_job_manager.cc(87)] vnbo URLRequestJobManager::CreateJob 000 job is 0x00485e60
I/chromium( 2598): [url_request_job.cc(506)] URLRequestJob::NotifyRestartRequired()
I/chromium( 2598): [url_request.cc(715)] URLRequest::Restart()
I/chromium( 2598): [url_request_job_manager.cc(50)] URLRequestJobManager::CreateJob
I/chromium( 2598): [url_request_job_manager.cc(96)] URLRequestJobManager::CreateJob job is 0x004977d8
I/chromium( 2598): [url_request_job.cc(53)] URLRequestJob::Kill() and job is 0x00485e60
I/chromium( 2598): [service_worker_url_request_job.cc(59)] ServiceWorkerURLRequestJob::Start()
I/chromium( 2598): [service_worker_url_request_job.cc(203)] ServiceWorkerURLRequestJob::MaybeStartRequest()
I/chromium( 2598): [service_worker_url_request_job.cc(203)] ServiceWorkerURLRequestJob::MaybeStartRequest()
I/chromium( 2598): [service_worker_url_request_job.cc(214)] ServiceWorkerURLRequestJob::StartRequest()
I/chromium( 2598): [service_worker_url_request_job.cc(224)] ServiceWorkerURLRequestJob::StartRequest() 1
I/chromium( 2598): [url_request_job.cc(506)] URLRequestJob::NotifyRestartRequired()
I/chromium( 2598): [url_request.cc(715)] URLRequest::Restart()
I/chromium( 2598): [url_request_job_manager.cc(50)] URLRequestJobManager::CreateJob
I/chromium( 2598): [url_request_job_manager.cc(87)] URLRequestJobManager::CreateJob 000 job is 0x00497a00
I/chromium( 2598): [url_request_job.cc(53)] URLRequestJob::Kill() and job is 0x004977d8
I/chromium( 2598): [url_request_job.cc(482)] URLRequestJob::CompleteNotifyDone()
I/chromium( 2598): [url_request_job.cc(506)] URLRequestJob::NotifyRestartRequired()
I/chromium( 2598): [url_request.cc(715)] URLRequest::Restart()
I/chromium( 2598): [url_request_job_manager.cc(50)] URLRequestJobManager::CreateJob
I/chromium( 2598): [url_request_job_manager.cc(96)] URLRequestJobManager::CreateJob job is 0x00000000
I/chromium( 2598): [url_request_http_job.cc(156)] URLRequestJob* URLRequestHttpJob::Factory
I/chromium( 2598): [url_request_job_manager.cc(106)] URLRequestJobManager::CreateJob job 2 is 0x00498848
I/chromium( 2598): [url_request_job.cc(53)] URLRequestJob::Kill() and job is 0x00497a00
為了檢視該建立Job的過程,將jobManager中CreateJob方法貼出來
URLRequestJob* URLRequestJobManager::CreateJob(
URLRequest* request, NetworkDelegate* network_delegate) const {
DCHECK(IsAllowedThread());
LOG(INFO)<<" URLRequestJobManager::CreateJob";
// If we are given an invalid URL, then don't even try to inspect the scheme.
if (!request->url().is_valid()){
URLRequestJob* job = new URLRequestErrorJob(request, network_delegate, ERR_INVALID_URL);
LOG(INFO)<<" URLRequestJobManager::CreateJob 0 job is "<<job;
return job;
}
// return new URLRequestErrorJob(request, network_delegate, ERR_INVALID_URL);
// We do this here to avoid asking interceptors about unsupported schemes.
const URLRequestJobFactory* job_factory = NULL;
job_factory = request->context()->job_factory();
const std::string& scheme = request->url().scheme(); // already lowercase
if (!job_factory->IsHandledProtocol(scheme)) {
URLRequestJob* job = new URLRequestErrorJob(
request, network_delegate, ERR_UNKNOWN_URL_SCHEME);
LOG(INFO)<<" URLRequestJobManager::CreateJob 00 job is "<<job;
return job;
//return new URLRequestErrorJob(
// request, network_delegate, ERR_UNKNOWN_URL_SCHEME);
}
// THREAD-SAFETY NOTICE:
// We do not need to acquire the lock here since we are only reading our
// data structures. They should only be modified on the current thread.
// See if the request should be intercepted.
//
// TODO(pauljensen): Remove this when AppCacheInterceptor is a
// ProtocolHandler, see crbug.com/161547.
if (!(request->load_flags() & LOAD_DISABLE_INTERCEPT)) {
InterceptorList::const_iterator i;
for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
URLRequestJob* job = (*i)->MaybeIntercept(request, network_delegate);
if (job){
<strong><span style="color:#FF0000;">// note : the first job</span></strong>
LOG(INFO)<<"vnbo URLRequestJobManager::CreateJob 000 job is "<<job;
return job;
}
}
}
URLRequestJob* job = job_factory->MaybeCreateJobWithProtocolHandler(
scheme, request, network_delegate);
<span style="color:#FF0000;"><strong>// note : this second job</strong></span>
LOG(INFO)<<" URLRequestJobManager::CreateJob job is "<<job;
if (job)
return job;
// See if the request should be handled by a built-in protocol factory.
for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i) {
if (scheme == kBuiltinFactories[i].scheme) {
URLRequestJob* job = (kBuiltinFactories[i].factory)(
request, network_delegate, scheme);
DCHECK(job); // The built-in factories are not expected to fail!
<span style="color:#FF0000;"><strong>// note : the third job</strong></span>
LOG(INFO)<<" URLRequestJobManager::CreateJob job 2 is "<<job;
return job;
}
}
// If we reached here, then it means that a registered protocol factory
// wasn't interested in handling the URL. That is fairly unexpected, and we
// don't have a specific error to report here :-(
LOG(WARNING) << "Failed to map: " << request->url().spec();
// return new URLRequestErrorJob(request, network_delegate, ERR_FAILED);
job = new URLRequestErrorJob(request, network_delegate, ERR_FAILED);
LOG(INFO)<<"vnbo URLRequestJobManager::CreateJob job 3 is "<<job;
return job;
}
29. 我們接著看:
URLRequestHttpJob::AddCookieHeaderAndStart()
30. 上面方法呼叫同文件的方法:URLRequestHttpJob::CheckCookiePolicyAndLoad
31. 上面方法呼叫同文件的方法:URLRequestHttpJob::DoStartTransaction()
32. 上面方法呼叫同文件的方法:URLRequestHttpJob::StartTransaction()。
33 . 上面方法呼叫同文件的方法:URLRequestHttpJob::StartTransactionInternal()
該方法中有程式碼:rv = transaction_->Start(
這裡建立的是HttpCache::Transaction::Transaction物件。
注:在資源的載入的過程中,會有兩類Transaction物件;一個是HttpCache::Transaction::Transaction物件,還有一個HttpNetworkTransaction::HttpNetworkTransaction物件。兩者相互配合,才使的網頁內容download下來。載入過程中,現將網頁的header部分載入,然後再載入body部分。
我們繼續看:rv = transaction_->Start(,該程式碼執行的是:src/net目錄下,http_cache_transaction.cc檔案中方法:HttpCache::Transaction::Start
在該方法中有程式碼:int rv = DoLoop(OK);
在這裡會執行一系列操作,這裡先不進行分析,我們先去找到如何建立和啟動HttpNetworkTransaction::HttpNetworkTransaction物件。因為這個物件才是真正去載入網頁的Transaction。
34. 在上面提到的方法HttpCache::Transaction::DoLoop中,有程式碼:
case STATE_SEND_REQUEST:
DCHECK_EQ(OK, rv);
rv = DoSendRequest();
break;
我們繼續看程式碼:DoSendRequest()。在該方法中,有程式碼:
int rv = cache_->network_layer_->CreateTransaction(priority_,
&network_trans_); 這是建立HttpNetworkTransaction::HttpNetworkTransaction物件。
在該方法的後面有程式碼: rv = network_trans_->Start(request_, io_callback_, net_log_);
這是啟動HttpNetworkTransaction::HttpNetworkTransaction物件。
35. 在物件HttpNetworkTransaction::HttpNetworkTransaction中Start方法中,會呼叫同文件的方法:
int HttpNetworkTransaction::DoLoop
這裡面,會進行一些列呼叫。 會建立HttpStreamRequest和HttpStreamBase物件。
並呼叫方法DoReadHeaders、DoReadBody分別來下載網頁的header部分和body部分。
36. 我們先看看DoReadHeaders方法:該方法中有程式碼:stream_->ReadResponseHeaders(io_callback_);
37. 該方法呼叫websocket_basic_handshake_stream.cc檔案中方法:ReadResponseHeaders.
該方法中有程式碼:int rv = parser()->ReadResponseHeaders
38. 上面呼叫的是檔案:http_stream_parser.cc中,方法:ReadResponseHeaders。這裡有通過一些列呼叫,執行本檔案中方法:
DoReadHeaders()。通過Socket,將網頁的header部分讀取出來:
connection_->socket()
->Read(read_buf_.get(), read_buf_->RemainingCapacity(), io_callback_);