WebKit 源碼分析 -- loader

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

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

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

1、類結構及接口數據結構

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

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

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

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

通常狀況下,在發起網絡請求前調用,能夠設置特定的 Http頭部,好比 user agent 等,在重定向請求的時候,也會自動調用fetch

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 樹的數據結構,用來在前進後退的時候快速顯示頁面。

2、加載流程

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

 

 

 

3、主資源加載過程

 

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

 

4、主資源加載過程

      在派生資源的加載中, 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及其子類類完成的,看一會兒類繼承關係

相關文章
相關標籤/搜索