谷歌chrome浏览器的源码分析(七)
上一次說到通過管道把接收到的HTTP數(shù)據(jù)通知另一個線程處理,它不是直接發(fā)送數(shù)據(jù)過去,而是把數(shù)據(jù)在共享內存里的句柄發(fā)送過去,達到高效通訊的目的。下面就來分析資源處理進程里,接收到這個消息之后,做些什么處理。這個消息的處理代碼如下:
?
#001??void ResourceDispatcher::OnReceivedData(int request_id,
#002?????????????????????????????????????????SharedMemoryHandle shm_handle,
#003?????????????????????????????????????????int data_len) {
#004????// Acknowlegde the reception of this data.
回應這個消息,說已經收到數(shù)據(jù)了。
#005????IPC::Message::Sender* sender = message_sender();
#006????if (sender)
#007??????sender->Send(
#008??????????new ViewHostMsg_DataReceived_ACK(MSG_ROUTING_NONE, request_id));
#009?
#010????DCHECK((shm_handle && data_len > 0) || (!shm_handle && !data_len));
?
打開共享內存文件,使用只讀的方式。
#011????SharedMemory shared_mem(shm_handle, true);??// read only
#012?
?
查找到請求下載的資源的請求標識號。
#013????PendingRequestList::iterator it = pending_requests_.find(request_id);
?
如果沒有找到相應的請求標識號,就直接返回,不用處理這些數(shù)據(jù)。
#014????if (it == pending_requests_.end()) {
#015??????// this might happen for kill()ed requests on the webkit end, so perhaps
#016??????// it shouldn't be a warning...
#017??????DLOG(WARNING) << "Got data for a nonexistant or finished request";
#018??????return;
#019????}
#020?
?
這里找到相應的請求標識號,就把數(shù)據(jù)放到請求信息里處理。
#021????PendingRequestInfo& request_info = it->second;
#022?
#023????if (data_len > 0 && shared_mem.Map(data_len)) {
#024??????RESOURCE_LOG("Dispatching " << data_len << " bytes for " <<
#025???????????????????request_info.peer->GetURLForDebugging());
#026??????const char* data = static_cast<char*>(shared_mem.memory());
#027??????request_info.peer->OnReceivedData(data, data_len);
#028????}
#029??}
?
上面這個函數(shù)實現(xiàn)接收到HTTP數(shù)據(jù),并且把數(shù)據(jù)放到請求的緩沖區(qū)里,但它沒有知道什么時候接收數(shù)據(jù)完成,顯然有另外一個消息來做這些的工作,就是下面類ResourceDispatcherHost的函數(shù):
?
#001????bool OnResponseCompleted(int request_id, const URLRequestStatus& status) {
#002??????receiver_->Send(new ViewMsg_Resource_RequestComplete(
#003??????????routing_id_, request_id, status));
#004?
#005??????// If we still have a read buffer, then see about caching it for later...
#006??????if (spare_read_buffer_) {
#007????????read_buffer_.reset();
#008??????} else if (read_buffer_.get() && read_buffer_->memory()) {
#009????????spare_read_buffer_ = read_buffer_.release();
#010??????}
#011??????return true;
#012????}
?
這個函數(shù)里通過發(fā)送消息ViewMsg_Resource_RequestComplete來通知資源進程已經把網絡的數(shù)據(jù)接收完成了,可以進入下一步處理。然后在資源進程里就會處理這個消息,下一次再來分析這方面的代碼。
上一次說到在類ResourceDispatcher會收到接收HTTP數(shù)據(jù)消息,并進一步處理數(shù)據(jù)。那么ResourceDispatcher類又把接收到的數(shù)據(jù)發(fā)往何處呢?這是需要我們去搞懂它的。通過進一步的跟蹤,會發(fā)現(xiàn)在ResourceDispatcher::OnReceivedData函數(shù)調用WebCore::ResourceHandleInternal類來處理,也就是把接收到的數(shù)據(jù)拋給WebCore來處理了。如下面的代碼:
#001??void ResourceDispatcher::OnReceivedData(int request_id,
#002?????????????????????????????????????????SharedMemoryHandle shm_handle,
#003?????????????????????????????????????????int data_len) {
#004????// Acknowlegde the reception of this data.
#005????IPC::Message::Sender* sender = message_sender();
......
#023????if (data_len > 0 && shared_mem.Map(data_len)) {
#024??????RESOURCE_LOG("Dispatching " << data_len << " bytes for " <<
#025???????????????????request_info.peer->GetURLForDebugging());
#026??????const char* data = static_cast<char*>(shared_mem.memory());
#027??????request_info.peer->OnReceivedData(data, data_len);
#028????}
#029??}
?
上面第27行代碼就是調用webcore里類ResourceHandleInternal::OnReceivedData函數(shù),這樣就把數(shù)據(jù)保存到webcore里面,也就是webkit里面了。經過如下面的調用過程:
1)??WebCore::ResourceLoader::didReceiveData
2)??WebCore::SubresourceLoader::didReceiveData
3)??WebCore::Loader::didReceiveData
4)??WebCore::CachedImage::data
?
這里就是把圖像的數(shù)據(jù)緩存起來,以便后面調用渲染引擎來顯示。這一次就分析到這里,總算把數(shù)據(jù)怎么樣放到webkit里搞清楚了,下一次再來看看webkit是怎么樣把數(shù)據(jù)顯示出來的。
上一次說到圖像緩存起來,其實很多情況下是文本的顯示,也就是HTML的解釋。要把網頁顯示出來,肯定是先從HTTP里收到網頁數(shù)據(jù),然后再使用HTML分析器來解釋HTML語言,最后根據(jù)HTML來生成所有可以顯示的元素,再由于這些元素生成BMP位圖,這樣只需要把BMP位置顯示到窗口里就萬事大吉了。這個過程看起來簡單,其實是一個非常復雜的過程,現(xiàn)在就帶你去深入地分析這個過程,就基本把Webkit的過程搞清楚了,同時也把chrome分析網頁的過程搞清楚了。這個過程如下:
1)??ResourceDispatcher::OnReceivedData()??資源分派類接收到網頁數(shù)據(jù)。
2)??WebCore::ResourceHandleInternal::OnReceivedData()??WebCore::ResourceHandleInternal類接收到數(shù)據(jù)。
3)??WebCore::ResourceLoader::didReceiveData()??資源加載類接收到數(shù)據(jù)。
4)??WebCore::MainResourceLoader::didReceiveData()??主資源類接收到數(shù)據(jù)。
5)??WebCore::MainResourceLoader::addData()?主資源類保存數(shù)據(jù)。
6)??WebCore::FrameLoader::receivedData()?框架加載類保存數(shù)據(jù)。
7)??WebCore::DocumentLoader::receivedData()?文檔加載類保存數(shù)據(jù)。
8)??WebCore::DocumentLoader::commitLoad()?文檔加載類提交所有接收的數(shù)據(jù)。
9)??WebCore::FrameLoader::committedLoad()?框架加載類提交數(shù)據(jù)。
10)????WebFrameLoaderClient::committedLoad()?網頁框架加載類提交數(shù)據(jù)。
11)????WebFrameImpl::DidReceiveData()?網頁框架實現(xiàn)類保存提交的數(shù)據(jù)。
12)????WebCore::FrameLoader::addData()?框架加載類保存數(shù)據(jù)。
13)????WebCore::FrameLoader::write()?把網頁數(shù)據(jù)寫入HTML緩沖。
14)????WebCore::HTMLTokenizer::write()??HTML終結符分析器進行保存。
15)????WebCore::HTMLTokenizer::processToken()??HTML終結符分析器分析HTML數(shù)據(jù)。
16)????WebCore::HTMLParser::parseToken()??HTML分析器分析網頁數(shù)據(jù)。
17)????WebCore::HTMLParser::insertNode()?分析到一個網頁里的節(jié)點,開始插入。
18)????WebCore::Text::attach()??發(fā)現(xiàn)一個文本節(jié)點并保存。
19)????WebCore::Node::createRendererIfNeeded()??創(chuàng)建可以渲染的節(jié)點。
20)????WebCore::Text::createRenderer()?開始創(chuàng)建文本渲染對象。
21)????WebCore::RenderText::RenderText()?創(chuàng)建文本渲染對象RenderText。
?
從上面的過程,可以看到分析過程是比較復雜的,不過,總算把分析網頁數(shù)據(jù)這個主線抓住了,其它的東西,都是為了這條主線而進行的。只要跟著這條主線,把相應的類再進一步分析,就可以把整個程序搞得一清二楚了。在最后一步里,就會生成RenderObject對象,而所有的RenderObject對象是根據(jù)分析HMTL生成一棵樹來保存起來。當界面上要顯示出來時,其實就是去遍歷整個RenderObject對象樹。下一次再來分析界面怎么樣顯示這些對象的。
通過上一次的分析,我們看到所有網頁數(shù)據(jù)經過HTML分析器之后,都會變成一個一個RenderObject對象,那么這些RenderObject對象又是怎么樣顯示到界面上面的呢?現(xiàn)在就帶著這個疑問來分析下面的代碼,這樣肯定會找到解決方法的。怎么樣找到入口呢?其實可以先從界面顯示的類開始,可以看到顯示界面的窗口類名稱叫做Chrome_RenderWidgetHostHWND,有了這個類名稱,就可以到代碼里查看它在那里了。
#001??class RenderWidgetHost;
#002??class WebMouseEvent;
#003??class WebCursor;
#004?
#005??typedef CWinTraits<WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0>
#006??????RenderWidgetHostHWNDTraits;
#007?
#008??static const wchar_t* const kRenderWidgetHostHWNDClass =
#009??????L"Chrome_RenderWidgetHostHWND";
?
可看到這個窗口類名稱是定義在這里,再跟著kRenderWidgetHostHWNDClass來查找,就會找到顯示窗口,如下:
#001??class RenderWidgetHostHWND :
#002????public CWindowImpl<RenderWidgetHostHWND,
#003???????????????????????CWindow,
#004???????????????????????RenderWidgetHostHWNDTraits>,
#005????public RenderWidgetHostView {
#006???public:
#007????RenderWidgetHostHWND(RenderWidgetHost* render_widget_host);
#008????virtual ~RenderWidgetHostHWND();
#009?
#010????void set_close_on_deactivate(bool close_on_deactivate) {
#011??????close_on_deactivate_ = close_on_deactivate;
#012????}
#013?
#014????void set_parent_hwnd(HWND parent) { parent_hwnd_ = parent; }
#015?
#016????DECLARE_WND_CLASS_EX(kRenderWidgetHostHWNDClass, CS_DBLCLKS, 0);
?
通過上面的分析,就可以找到顯示網頁的窗口類RenderWidgetHostHWND,在這個類里,主要顯示的位置是在void RenderWidgetHostHWND::OnPaint(HDC dc)函數(shù)里面,它的代碼如下:
#001??void RenderWidgetHostHWND::OnPaint(HDC dc) {
#002????DCHECK(render_widget_host_->process()->channel());
#003?
#004????CPaintDC paint_dc(m_hWnd);
#005????HBRUSH white_brush = reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
#006?
#007????RenderWidgetHost::BackingStore* backing_store =
#008????????render_widget_host_->GetBackingStore();
#009?
#010????if (backing_store) {
#011??????gfx::Rect damaged_rect(paint_dc.m_ps.rcPaint);
#012?
#013??????gfx::Rect bitmap_rect(
#014??????????0, 0, backing_store->size().width(), backing_store->size().height());
#015?
#016??????gfx::Rect paint_rect = bitmap_rect.Intersect(damaged_rect);
#017??????if (!paint_rect.IsEmpty()) {
#018????????BitBlt(paint_dc.m_hDC,
#019???????????????paint_rect.x(),
#020???????????????paint_rect.y(),
#021???????????????paint_rect.width(),
#022???????????????paint_rect.height(),
#023???????????????backing_store->dc(),
#024???????????????paint_rect.x(),
#025???????????????paint_rect.y(),
#026???????????????SRCCOPY);
#027??????}
......
#058??}
?
其實這個函數(shù)是通過如下發(fā)送消息給另一個進程進行渲染成BMP的圖片,
Send(new ViewMsg_Repaint(routing_id_, view_size));
?
那么誰來接收ViewMsg_Repaint消息呢?繼續(xù)細心地查找,就到在如下類函數(shù)里處理:
void RenderWidget::OnMsgRepaint(const gfx::Size& size_to_paint)
在這個函數(shù),并不是最終的結果,它又會調用其它線程來處理渲染,以便達到異步的結果。它的調用過程如下:
1)??RenderWidget::DoDeferredPaint()??線程里開始渲染網頁顯示
2)??RenderWidget::PaintRect()?窗口里開始進行顯示
3)??WebViewImpl::Paint() web視類開始顯示。
4)??WebFrameImpl::Paint() web框架類開始顯示。
5)??WebCore::ScrollView::paint()?滾動窗口顯示。
6)??WebCore::Frame::paint() WebCore里的框架顯示。
7)??WebCore::RenderLayer::paint()?分層顯示。
8)??WebCore::RenderLayer::paintLayer()
9)??WebCore::RenderBlock::paint()??在每一層里顯示每一塊區(qū)域。
10)????WebCore::RenderBlock::paintObject()?顯示這一區(qū)域的對象。
11)????WebCore::RenderBlock::paintContents()?顯示需要顯示的內容。
12)????WebCore::RenderFlow::paintLines()?這里需要顯示文字。
13)????WebCore::RootInlineBox::paint()?開始顯示一行文字。
14)????WebCore::InlineFlowBox::paint()?進行一行文字排列。
15)????WebCore::InlineTextBox::paint()?
16)????WebCore::GraphicsContext::drawText()??進行一個一個文字顯示。
17)????WebCore::Font::drawText()??這里調用字體類來把文字的編碼變成位圖。
18)????WebCore::Font::drawSimpleText()??這里把位圖顯示到界面內存里。
?
通過上面的分析,可以看到顯示一串文字的過程是如此復雜的過程。其它圖片顯示的過程也是一樣,都把它們變成位圖,然后再分層顯示出來。那么JavaScript是怎么樣顯示的呢?這個會比上面的過程更加復雜,后面再仔細地分析它。下一次,主要仔細地看看這些過程里的一些類功能。
總結
以上是生活随笔為你收集整理的谷歌chrome浏览器的源码分析(七)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 谷歌chrome浏览器的源码分析(六)
- 下一篇: 前端技术-开发工具