WebKit介绍及总结(二)
Page :打开 page.h 头文件,我们似乎看不到我们概念中的“页面”相关的东西,没错,这里的 Page 并非就是我们印象中的简单网页,在头文件中我们发现很多关于 history 的东西, goBack(),goForward(), 等等,关于主题的设定,关于Frame 的描述等等,因此,这里的 Page 更像是我们见到的浏览器,抽象起来,应该算是我们访问网站的一次浏览会话; Frame :与 Page 相比, Frame 更像我们印象中的一个网页,它关注的是页面的显示 (FrameView) 、页面数据的加载(FrameLoader) 、页面内的各种控制器 (Editor, EventHandler, ScriptController, etc.) 等等,可以说,这个结构表示浏览器开始从外部控制转向关注一个页面的具体描述了; Document :这个类的爷爷类是 Node ,它是 DOM 树各元素的基类; Document 有个子类是 HTMLDocument ,它是整个文档 DOM 树的根结点,这样就明白了:原来 Document 就是描述具体文档的代码,看一下它的头文件,就更明白了,它的属性与方法就是围绕着各种各样的结点: Text , Comment , CDATASection , Element…… RenderObject :在形成 DOM 树结点的时候, Node 会根据需要调用 RenderObject 的方法,生成 Render 结点并最终形成 Render 树,因此此类是 Render 树各种结点类的基类,它的一个孙子类—— RenderView ,就是整个 Render 树的根结点。 对于调用过程,这里有一段话,我认为比较明确地描述了一个场景: 首先是 整理并向服务器发送客户请求 : WebCore:: FrameLoader::load() → WebCore:: FrameLoader::loadWithDocumentLoader() →WebCore:: FrameLoader::continueLoadAfterNavigationPolicy() →WebCore:: FrameLoader::continueLoadAfterWillSubmitForm() →WebCore:: DocumentLoader::startLoadingMainResource() → WebCore:: MainResourceLoader::load() → WebCore:: MainResourceLoader::loadNow() 这里,注意到本函数在调用 ResourceHandle::create() 时, MainResourceLoader 把自己作为 create() 的第二个参数传入,这个参数是 MainResourceLoader 的祖父类 ResourceHandleClient ,这样便于下面当调用祖父类的虚函数 didReceiveData() 时,实际调用的是 MainResourceLoader 的 didReceiveData() 方法。 继续: → WebCore:: ResourceHandle::create() → WebCore:: ResourceHandleQt::start() → WebCore:: QnetworkReplyHandler::start() 截至本函数,用户请求才会被最终发送出去 ,然后用 connect() 方法挂载了几个信号回调函数,比如针对 finished() 信号的 finish() 函数,针对 readyRead() 信号的 forwardData() 函数,针对 processQueuedItems() 信号的sendQueuedItems() 函数,其中最重要的就是 forwardData() 函数,此函数就是 对服务器返回数据的接收与处理 。 让我们来看看 返回数据的处理过程 : WebCore:: QnetworkReplyHandler::forwardData() → WebCore:: (QNetworkReply)QIODevice::read(),ResourceHandleClient:: didReceiveData() 可见,首先 forwardData() 函数会利用 QIODevice 的 read() 方法从网络数据缓冲区中读取接收数据,然后调用didReceiveData() 方法,在类 ResourceHandleClient 中,这个方法实际上是个虚函数,因此实际调用的是其子类ResourceLoader 的同名函数: → WebCore:: ResourceLoader::didReceiveData(ResourceHandle*, const char* data, int length, int lengthReceived) → WebCore:: MainResourceLoader::didReceiveData() → WebCore:: ResourceLoader::didReceiveData(const char* data, int length, long long lengthReceived, bool allAtOnce) 在这个函数中,有个直接调用 addData(data, length, allAtOnce); 虽然这个语句在 ResourceLoader 中,但是实际上调用的并非 ResourceLoader::addData(), 而是 MainResourceLoader::addData() ,又一个虚函数覆盖的例子: → WebCore:: MainResourceLoader::addData() → WebCore:: ResourceLoader::addData(), FrameLoader::receivedData() →WebCore:: DocumentLoader::receivedData() → WebCore:: DocumentLoader::commitLoad() → WebCore:: FrameLoader::committedLoad() → WebCore:: FrameLoaderClient::committedLoad() → WebCore:: FrameLoaderClientQt::committedLoad() → WebCore:: FrameLoader::addData() → WebCore:: DocumentWriter::addData() 至此,一次 URL 请求就完成了初始化设置,请求发送,以及数据接收,接下来就是 HTML / JS 分析 。 → WebCore:: Tokenizer::write() → WebCore:: HTMLTokenizer::write() 在此函数中有个循环,针对每个 Tag 进行分析,下面是对某个 Tag 的分析过程: → WebCore:: HTMLTokenizer::advance() → WebCore:: HTMLTokenizer::parseTag(),HTMLTokenizer::processToken() → WebCore:: HTMLParser::parseToken() → WebCore:: HTMLParser::insertNodeAfterLimitDepth() → WebCore:: HTMLParser::insertNode() → WebCore:: Element::attach() 当分析了一个 Tag ,如果不是 HTML 的 Tag ,则调用相关的 parse 函数解析(如 parseNonHTMLText );如果它是HTML 的 Tag ,就将其加入 Dom 树的一个节点,接下来根据这个节点 生成 Render 树节点 : → WebCore:: Node::createRendererIfNeeded() → WebCore::Text::createRenderer() → WebCore::RenderText::RenderText() 另外,在 HTMLTokenizer::parseTag() 中,也会调用 HTMLTokenizer::parseNonHtmlText, 之后调用: → WebCore::HTMLTokenizer::scriptHandler() → WebCore::HTMLTokenizer::scriptExecution() → WebCore::ScriptController::executeScript() → WebCore::ScriptController::evaluate() → WebCore::ScriptController::evaluateInWorld() → WebCore::JSMainThreadExecState::evaluate() → JSC::evaluate() → JSC::Interpreter::execute() → JSC::JITCode::execute() → JSC::JITThunks::tryCacheGetByID() → cti_op_put_by_id() → JSC::JSValue::put() → WebCore::JSHTMLInputElement::put() → JSC::lookupPut<WebCore::JSHTMLInputElement, WebCore::JSHTMLElement> () → JSC::lookupPut<WebCore::JSHTMLInputElement>() → WebCore::setJSHTMLInputElementSelectionStart() → WebCore::JSHTMLInputElement::setSelectionStart() → WebCore::HTMLTextFormControlElement::setSelectionStart() → WebCore::HTMLTextFormControlElement::textRendererAfterUpdateLayout() → WebCore::Document::updateLayoutIgnorePendingStylesheets() → WebCore::Document::updateLayout() → WebCore::FrameView::layout () 之后有可能会调用 FrameView::adjustViewSize(), FrameView::setContentsSize(), ScrollView::updateScrollbars(), FrameView::visibleContentsResized(), FrameView::endDeferredRepaints(), FrameView::doDeferredRepaints() 等函数, 然后调用: → WebCore::ScrollView::repaintContentRectangle() → WebCore::Chrome::invalidateContentsAndWindow() 在此函数中,有关键的一句: emit m_webPage->repaintRequested(windowRect) ,意思是 将 paint 的信号最终发送出去 。 在 qt 中,函数 QEventLoop::exec() 负责对事件的检测,当检测到事件发生(信号),会调用以下函数进行处理: → QeventLoop::processEvents() → ? → QEventDispatcherGlib::processEvents → g_main_context_iteration() → ? → g_main_context_dispatch() → ? → QCoreApplication::sendPostedEvents() → QCoreApplicationPrivate::sendPostedEvents() → QCoreApplication::notifyInternal() → QApplication::notify() → QApplicationPrivate::notify_helper() → QMainWindow::event(QEvent*) → QWidget::event(QEvent*) → QWidgetPrivate::syncBackingStore() → ? → QWidgetPrivate::drawWidget() → QCoreApplication::notifyInternal() → QApplication::notify() → QApplicationPrivate::notify_helper() → QWebView::event() → Qwidget::event() 以上是 Qt 事件处理的通用过程,从下面的函数开始, Qt 识别出此信号是 paint 信号: → QWebView::paintEvent() → QWebFrame::render() → QWebFramePrivate::renderRelativeCoords() → WebCore::FrameView::paintContents() → WebCore::RenderLayer::paint() → WebCore::RenderLayer::paintLayer() → WebCore::RenderLayer::paintList() → WebCore::RenderLayer::paintLayer() → WebCore::RenderLayer::paintList() → WebCore::RenderLayer::paintLayer() → WebCore::RenderBlock::paint() → WebCore::RenderBlock::paintObject() → WebCore::RenderBlock::paintContents() → WebCore::RenderBlock::paintChildren() → WebCore::RenderBlock::paint() → WebCore::RenderBlock::paintObject() → WebCore::RenderBlock::paintContents() → WebCore::RenderBlock::paintChildren() → WebCore::RenderBlock::paint() → WebCore::RenderBlock::paintObject() → WebCore::RenderBlock::paintContents() → WebCore::RenderLineBoxList::paint() → WebCore::RootInlineBox::paint() → WebCore::InlineFlowBox::paint() → WebCore::InlineFlowBox::paint() → WebCore::InlineTextBox::paint() → paintTextWithShadows() → WebCore::GraphicsContext::drawText() → WebCore::Font::drawText() → WebCore::Font::drawComplexText() → QPainter::drawText() 可以看到,以上有的函数会重复调用,因为现在所展示的只是一个执行流程,于 WebKit 全体只是冰山一角。到此, WebKit最终调用 Qt 的 QPainter 画出文字。 这样,从最初的数据载入到将一个文字最终画出,基本上完成了,其他如图片的过程类似。 之后,在 resize 、 mouse-click 等事件的驱动下, WebKit 仍然会不停地进行 relayout 和 repaint 。
|
|
来自: just_person > 《webkit》