WebKit 源碼分析 -- loader


原文地址: http://peirenlei.iteye.com/blog/1718569

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

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

一、類結構及接口

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

如上是 Loader 和 NetWork 之間的類關系圖

ResourceHandleClient 是ResourceHandle 的客戶,它定義一系列虛函數,這些虛函數是 ResouceHandle 的回調,繼承類實現這些接口,ResourceHandleClient更多的繼承關系如下圖:

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

一般情況下,在發起網絡請求前調用,可以設置特定的 Http頭部,比如 user agent 等,在重定向請求的時候,也會自動調用

virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& /*redirectResponse*/) { }

上傳數據的時候,在 TCP wrtie 事件的時候,向對方發送數據的時候調用, loader 可以根據這個回調顯示上傳進度。

virtual void didSendData(ResourceHandle*, unsigned long long /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/) { }

收到第一個響應包,此時至少 http 的部分頭部已經解析(如status code ), loader 根據響應的頭部信息判斷請求是否成功等

virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&) { }

收到 HTTP 響應數據,類似 tcp 的 read 事件,來 http 響應數據了, Network 的設計機制是來一段數據上傳一段數據

virtual void didReceiveData(ResourceHandle*, const char*, int, int /*encodedDataLength*/) { }

加載完成,數據來齊

virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/) { }

加載失敗

virtual void didFail(ResourceHandle*, const ResourceError&) { }

要求用戶鑒權

virtual 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 類根據申請的資源類型派生出不同的子類

 

 對於資源的加載客戶端CacheResourceClient及其子類的關系如圖

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. 收到 HTTP 響應頭部,調用 ResourceLoader::didReceiveResponse 20. 調用 SubresourceLoader::didiReceiveResponse 21.  調用 CachedResourceRequest::didReceiveResponse 處理響應頭部,特別是同緩存相關的頭部,比如 304 的 status code 22. 調用 ResourceLoader::didReceiveResponse 23. 收到體部數據,調用 ResourceLoader::didReceiveData 24. 調用 SubresourceLoader::didReceiveData 25. 調用 ResourceLoader::didReceiveData 26. 調用 ResourceLoader::addData 將數據存儲到 SharedBuffer 里面 27. 調用 CachedResourceRequest::didReceiveData 28. 數據來齊 , 調用 ResourceLoader::didFinishLoading 29. 調用 SubresourceLoader::didFinishLoading 30. 調用 CachedResourceRequest::didFinishLoading 31. 調用 CachedResource::finish 32. 調用 CachedResourceLoader::loadDone 33.  調用 CachedImage::data ,創建對應的 Image 對象,解碼

 

對於文檔的解析是通過DocumentParse及其子類類完成的,看一下子類繼承關系


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM