谷歌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是怎么样显示的呢?这个会比上面的过程更加复杂,后面再仔细地分析它。下一次,主要仔细地看看这些过程里的一些类功能。
- 谷歌chrome浏览器的源码分析(七)
- 谷歌chrome浏览器的源码分析(二)
- 谷歌chrome浏览器的源码分析(三)
- 谷歌chrome浏览器的源码分析(四)
- 谷歌chrome浏览器的源码分析(五)
- 谷歌chrome浏览器的源码分析(六)
- 谷歌chrome浏览器源码分析
- 谷歌chrome浏览器源码分析
- 谷歌chrome浏览器的源码分析(一)
- 谷歌浏览器的源码分析(作者:蔡军生)&&Chrome源码剖析(作者:duguguiyu)
- chrome 源码分析
- chrome 源码分析
- chrome 源码分析
- 基于.net开发chrome核心浏览器【七】
- 使用Python分析谷歌浏览器Chrome的历史记录
- 对于谷歌浏览器(chrome)容易上瘾的分析
- 谷歌chrome浏览器的使用体验
- 谷歌Chrome 操作系统基于浏览器的OS
- 转行程序员2 机器学习 线性回归 Linear Regression II 纯属敦促自己学习
- PAT:二分查找
- Java源码解析-BlockingQueue
- 有用的链接
- android监听SD卡挂载并获取路径
- 谷歌chrome浏览器的源码分析(七)
- Vmstat 2 详解
- Redis+Spring缓存实例(windows环境,附实例源码及详解)
- c语言学习之数组指针
- 通过打"patch"简单升级Android Studio
- POJ 3090 Visible Lattice Points
- OC 单例设计
- c++第四次实验
- 服务插件如何抛出交互信息