1. 程式人生 > >chromium網路資源載入分析(一) 主資源載入邏輯分析 ---chromium39

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_);