Googlechrome瀏覽器的源碼分析(七)_Google

來源:互聯網
上載者:User

上一次說到通過管道把接收到的HTTP資料通知另一個線程處理,它不是直接發送資料過去,而是把資料在共用記憶體裡的控制代碼發送過去,達到高效通訊的目的。下面就來分析資源處理進程裡,接收到這個訊息之後,做些什麼處理。這個訊息的處理代碼如下:

 

#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();

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

 

如果沒有找到相應的請求標識號,就直接返回,不用處理這些資料。

#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 

 

這裡找到相應的請求標識號,就把資料放到請求資訊裡處理。

#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  }

 

上面這個函數實現接收到HTTP資料,並且把資料放到請求的緩衝區裡,但它沒有知道什麼時候接收資料完成,顯然有另外一個訊息來做這些的工作,就是下面類ResourceDispatcherHost的函數:

 

#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    }

 

這個函數裡通過發送訊息ViewMsg_Resource_RequestComplete來通知資源進程已經把網路的資料接收完成了,可以進入下一步處理。然後在資源進程裡就會處理這個訊息,下一次再來分析這方面的代碼。




上一次說到在類ResourceDispatcher會收到接收HTTP資料訊息,並進一步處理資料。那麼ResourceDispatcher類又把接收到的資料發往何處呢。這是需要我們去搞懂它的。通過進一步的跟蹤,會發現在ResourceDispatcher::OnReceivedData函數調用WebCore::ResourceHandleInternal類來處理,也就是把接收到的資料拋給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函數,這樣就把資料儲存到webcore裡面,也就是webkit裡面了。經過如下面的調用過程:

1)  WebCore::ResourceLoader::didReceiveData

2)  WebCore::SubresourceLoader::didReceiveData

3)  WebCore::Loader::didReceiveData

4)  WebCore::CachedImage::data

 

這裡就是把映像的資料緩衝起來,以便後面調用渲染引擎來顯示。這一次就分析到這裡,總算把資料怎麼樣放到webkit裡搞清楚了,下一次再來看看webkit是怎麼樣把資料顯示出來的。





上一次說到映像緩衝起來,其實很多情況下是文本的顯示,也就是HTML的解釋。要把網頁顯示出來,肯定是先從HTTP裡收到網頁資料,然後再使用HTML分析器來解釋HTML語言,最後根據HTML來產生所有可以顯示的元素,再由於這些元素產生BMP位元影像,這樣只需要把BMP位置顯示到視窗裡就萬事大吉了。這個過程看起來簡單,其實是一個非常複雜的過程,現在就帶你去深入地分析這個過程,就基本把Webkit的過程搞清楚了,同時也把chrome分析網頁的過程搞清楚了。這個過程如下:

1)  ResourceDispatcher::OnReceivedData()  資源指派類接收到網頁資料。

2)  WebCore::ResourceHandleInternal::OnReceivedData()  WebCore::ResourceHandleInternal類接收到資料。

3)  WebCore::ResourceLoader::didReceiveData()  資源載入類接收到資料。

4)  WebCore::MainResourceLoader::didReceiveData()  主資源類接收到資料。

5)  WebCore::MainResourceLoader::addData() 主資源類儲存資料。

6)  WebCore::FrameLoader::receivedData() 架構載入類儲存資料。

7)  WebCore::DocumentLoader::receivedData() 文檔載入類儲存資料。

8)  WebCore::DocumentLoader::commitLoad() 文檔載入類提交所有接收的資料。

9)  WebCore::FrameLoader::committedLoad() 架構載入類提交資料。

10)    WebFrameLoaderClient::committedLoad() 網頁架構載入類提交資料。

11)    WebFrameImpl::DidReceiveData() 網頁架構實作類別儲存提交的資料。

12)    WebCore::FrameLoader::addData() 架構載入類儲存資料。

13)    WebCore::FrameLoader::write() 把網頁資料寫入HTML緩衝。

14)    WebCore::HTMLTokenizer::write()  HTML終結符分析器進行儲存。

15)    WebCore::HTMLTokenizer::processToken()  HTML終結符分析器分析HTML資料。

16)    WebCore::HTMLParser::parseToken()  HTML分析器分析網頁資料。

17)    WebCore::HTMLParser::insertNode() 分析到一個網頁裡的節點,開始插入。

18)    WebCore::Text::attach()  發現一個文本節點並儲存。

19)    WebCore::Node::createRendererIfNeeded()  建立可以渲染的節點。

20)    WebCore::Text::createRenderer() 開始建立文本渲染對象。

21)    WebCore::RenderText::RenderText() 建立文本渲染對象RenderText。

 

從上面的過程,可以看到分析過程是比較複雜的,不過,總算把分析網頁資料這個主線抓住了,其它的東西,都是為了這條主線而進行的。只要跟著這條主線,把相應的類再進一步分析,就可以把整個程式搞得一清二楚了。在最後一步裡,就會產生RenderObject對象,而所有的RenderObject對象是根據分析HMTL產生一棵樹來儲存起來。當介面上要顯示出來時,其實就是去遍曆整個RenderObject對象樹。下一次再來分析介面怎麼樣顯示這些對象的。



通過上一次的分析,我們看到所有網頁資料經過HTML分析器之後,都會變成一個一個RenderObject對象,那麼這些RenderObject對象又是怎麼樣顯示到介面上面的呢。現在就帶著這個疑問來分析下面的代碼,這樣肯定會找到解決方案的。怎麼樣找到入口呢。其實可以先從介面顯示的類開始,可以看到顯示介面的視窗類別名稱叫做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)函數裡面,它的代碼如下:

#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  }

 

其實這個函數是通過如下發送訊息給另一個進程進行渲染成BMP的圖片,

Send(new ViewMsg_Repaint(routing_id_, view_size));

 

那麼誰來接收ViewMsg_Repaint訊息呢。繼續細心地尋找,就到在如下類函數裡處理:

void RenderWidget::OnMsgRepaint(const gfx::Size& size_to_paint)

在這個函數,並不是最終的結果,它又會調用其它線程來處理渲染,以便達到非同步結果。它的調用過程如下:

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()  在每一層裡顯示每一塊地區。

10)    WebCore::RenderBlock::paintObject() 顯示這一地區的對象。

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是怎麼樣顯示的呢。這個會比上面的過程更加複雜,後面再仔細地分析它。下一次,主要仔細地看看這些過程裡的一些類功能。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.