谷歌chrome浏览器的源码分析(七)

来源:互联网 发布:pdf文件修改软件 编辑:程序博客网 时间:2024/05/18 02:46

上一次说到通过管道把接收到的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是怎么样显示的呢?这个会比上面的过程更加复杂,后面再仔细地分析它。下一次,主要仔细地看看这些过程里的一些类功能。


0 0