1. 程式人生 > >WebKit 核心原始碼分析 ( 四 )

WebKit 核心原始碼分析 ( 四 )

WebKit 核心原始碼分析 ( 四 )

摘要:本文介紹 WebCore  Loader 模組是如何載入資源的,分主資源和派生資源分析 loader 模組的類關係。

關鍵詞: WebKit,Loader,Network,ResouceLoader,SubresourceLoader

一、類結構及介面

Loader 模組是 Network 模組的客戶。 Network 模組提供指定資源的獲取和上傳功能,獲取的資源可能來自網路、本地檔案或者快取。對不同 HTTP 實現的適配會在 Network 層完成,所以 Loader 接觸到的基本上是同 OS  HTTP 實現無關的 Network

 層介面。


如上是 Loader  NetWork 之間的類關係圖, ResourceHandleClient ResourceHandle 的客戶,它定義一系列虛擬函式,這些虛擬函式是 ResouceHandle的回撥,繼承類實現這些介面。

ResouceHandleClient 的介面同網路傳輸過程息息相關,一般為某一個網路事件對應的回撥。下面是其中的一些介面。

// 一般情況下,在發起網路請求前呼叫,可以設定特定的 Http

頭部,比如 user agent 等,在重定向請求的時候,也會自動調

void willSendRequest(ResourceHandle*, ResourceRequest&, const

ResourceResponse&)

// 上傳資料的時候,在 TCP wrtie 事件的時候,向對方傳送資料的

時候呼叫, loader 可以根據這個回撥顯示上傳進度。

void didSendData(ResourceHandle*, unsigned long long

/*bytesSent*/, unsigned long long /*totalBytesToBeSent*/)

// 收到第一個響應包,此時至少 http 的部分頭部已經解析(如

status code ), loader 根據響應的頭部資訊判斷請求是否成功

等。

void didReceiveResponse(ResourceHandle*,

const   ResourceResponse&)

// 收到 HTTP 響應資料,類似 tcp  read 事件,來 http 響應資料

了, Network 的設計機制是來一段資料上傳一段資料。

void didReceiveData(ResourceHandle*, const char*, int,

  int /*lengthReceived*/)

    // 載入完成,資料來齊。

void didFinishLoading(ResourceHandle*, double /*finishTime*/)

// 載入失敗

void didFail(ResourceHandle*, const ResourceError&)

// 要求使用者鑑權

void didReceiveAuthenticationChallenge(ResourceHandle*,

const AuthenticationChallenge&)

WebCore 把要載入的資源分成兩類,一類是主資源,比如 HTML 頁面,或者下載項,一類是派生資源,比如 HTML 頁面中內嵌的圖片或者指令碼連結。這兩類資源對於回撥的處理有很大的不同,比如,同樣是下載失敗,主資源可能需要向用戶報錯,派生資源比如頁面中的一張圖下載失敗,可能就是圖不顯示或者顯示代替說明文字而已,不向使用者報錯。因此有了 MainResourceLoader SubresourceLoader 之分。它們的公共基類 ResourceLoader 則完成一些兩種資源下載都需要完成的操作,比如通過回撥將載入程序告知上層應用。

ResourceLoader 通過 ResourceNotifier 類將回調傳導到FrameLoaderClient 類。

主資源的載入是立刻發起的,而派生資源則可能會為了優化網路,在佇列中等待( 這裡的立刻發起是 loader 層面的,不是 Network 層面的 ) ResourceScheduler 這個類就是用來管理資源載入的排程。主要排程物件就是派生資源,會根據 host 來影響資源載入的先後順序。

主資源和派生資源的載入還有一個區別,主資源目前是沒有快取的,而派生資源是有快取機制的。這裡的快取指的是 Resouce Cache ,用於儲存原始資料(比如 CSS  JS 等),以及解碼過的圖片資料,通過 Resource Cache 可以節省網路請求和圖片解碼的時候。不同於 Page Cache  Page Cache 存的是 DOM 樹和Render 樹的資料結構,用來在前進後退的時候快速顯示頁面。

二、載入流程

下圖是載入 html 頁面時,一個正常的載入流程。

三、主資源載入過程

1. DocumentLoader 呼叫 MainResourceLoader::load  loader 發起請求

2. 呼叫 MainResourceLoader::loadNow

3. 呼叫 MainResourceLoader::willSendRequest

4. 呼叫 ResourceLoader::willSendRequest,  callback 通過ResourceNotifier 傳導給 FrameLoaderClient  Client 可以在回撥中操作 ResourceRequest ,比如設定請求頭部。

5. 呼叫 PolicyChecker::checkNavigationPolicy 過濾掉重複請求等

6. loader 呼叫 ResourceHandle::create  Network 發起載入請求

7. 收到第一個 HTTP 響應資料包 ,Network 回撥MainResourceLoader::didReceiveResponse ,主要處理 HTTP 頭部。

8. 呼叫 PolicyChecker:: checkContentPolicy, 並最終通過FrameLoaderClient  dispatchDecidePolicyForMIMEType 判斷是否為下載請求(存在 "Content-Disposition"http 頭部)

9. 呼叫 MainResourceLoader::continueAfterContentPolicy ,根據ResourceResponse 檢測是否發生錯誤。

10. 呼叫 ResourceLoader::didReceiveResponse ,將 callback 通過ResourceNotifier 傳導給 FrameLoaderClient 

11. 收到 HTTP 體部資料,呼叫 MainResourceLoader::didReceiveData

12. 呼叫 ResourceLoader::didReceiveData ,將 callback 通過ResourceNotifier 傳導給 FrameLoaderClient

13. 呼叫 MainResourceLoader::addData

14. 呼叫 DocumentLoader::receivedData

15. 呼叫 DocumentLoader::commitLoad

16. 呼叫 FrameLoader::commitProvisionalLoad  FrameLoader provisional 狀態躍遷到 Committed 狀態

17. 呼叫 FrameLoaderClientQt::committedLoad

18. 呼叫 DocumentLoader::commitData ,啟動 Writer 物件來處理資料(DocumentWriter::setEncoding  DocumentWriter::addData 

19. 呼叫 DocumentWriter::addData

20. 呼叫 DocumentParser::appendByte

21. 呼叫 DecodedDataDocumentParser::appendBytes 對文字編碼進行解碼

22. 呼叫 HTMLDocumentParser::append ,進行 HTML 解析

23. 資料來齊,呼叫 MainResourceLoader::didFinishLoading

24. 呼叫 FrameLoader::finishedLoading

25. 呼叫 DocumentLoader::finishedLoading

26. 呼叫 FrameLoader::finishedLoadingDocument ,啟動 writer 物件接收剩餘資料,重複 19-22 進行解析

27. 呼叫 DocumentWriter::end 結束接收資料(呼叫Document::finishParsing 

28. 呼叫 HTMLDocumentParser::finish

四、派生資源載入流程

在派生資源的載入中, SubresourceLoader 更多起到的是一個轉發的作用,通過它的 client  SubresourceLoaderClient 類)來完成操作。

 

 各個載入階段的處理在 SubresourceLoaderClient 的派生類CachedResourceRequest,Loader,IconLoader 中完成。 Client 會建立SubresourceLoader 

請求發起階段, ResourceLoadScheduler 負責對 SubresourceLoader 進行排程。

Document 類會建立 CachedResourceLoader 類的物件m_cachedResourceLoader, 這個類 ( 物件 ) 提供了對 Document 的派生資源的訪問介面 requestImage  requestCSSStyleSheet requestUserCSSStyleSheet  requestScript  requestFont requestXSLStyleSheet  requestLinkPrefetch 。為了實現這些介面,CachedResourceLoader 需要建立 CachedResourceRequest 物件來發起請求。

一般情況下,一個 Document 擁有一個 CachedResourceLoader 類例項。

MemoryCache 類則對提供快取條目的管理,可以方便地進行 add  remove,快取淘汰等。具體的快取條目則是通過 CachedResource 類儲存, MemoryCache類維護了一個 HashMap 儲存所有快取條目。

HashMap <String,CachedResource> m_resources;

CachedResourceRequest 依賴於 CachedResource, CacheResourceRequest 的建構函式中,會傳入 CachedResource 物件作為引數。CachedResource 既儲存響應體部,也儲存同 cache 相關的頭部。在發起請求前,會檢查是否有 cache  validator ,在收到響應的時候,則需要更新對應的頭部。 CachedResource 類實現了 RFC2616 中的快取一節。實際上 CachedResource類真正完成了同網路的通訊。 CachedResource 類根據申請的資源型別派生出不同的子類。


     CachedResource 類的使用者必須是 CachedResourceClient, 在這個類中維護了 CachedResourceClient 類的集合 m_clients 。每一個 Client 通過addClient  removeClient 將自己加入到該類的 Client 集合中。CachedResourceClientWalker 則提供了 CachedResouceClient 的一個遍歷介面。當資料來齊的時候, CachedResource 類會通過CachedResouceClient::notifyFinished 介面通知使用者。

下圖是 Image 元素對應的幾個類關係。


下面以 image 為例分析其載入過程

1. 解析 html 頁面的時候,解析到 img 標籤,呼叫HTMLImageElement::create 建立 HTMLImageElement 物件,該物件包含HTMLImageLoader 物件 m_imageLoader

2. 解析到 img  href 屬性,呼叫ImageLoader::updateFromElementIgnoringPreviousError

3. 呼叫 ImageLoader::updateFromElement

4. 呼叫 CachedResourceLoader::requestImage

5. 呼叫 CachedResourceLoader::requestResource( 根據快取的情況確定是否可以從快取獲取,或者需要 revalidate ,或者需要直接從網路獲取 )

6. 呼叫 CachedResourceLoader::loadResource

7. 根據 Resource 的型別呼叫 createResource 建立對應的CachedResource

8. 呼叫 MemoryCache::add  cache 中查詢是否有對應的 cache 條目,如果沒有建立之

9. 呼叫 CachedImage::load

10. 呼叫 CachedResource::load

11. 呼叫 CachedResourceLoader::load

12. 呼叫 CachedResourceRequest::load

13. 建立 CachedResourceRequest 物件,它將作為 SubresourceLoader client

14. 呼叫 ResourceLoaderScheduler::scheduleSubresourceLoad

15. 呼叫 SubresourceLoader::create

16. ResourceLoadScheduler::requestTimerFired

17. 呼叫 ResourceLoader::start

18. 呼叫 ResourceHandle::create 發起請求

19.