Introduction and summary of WebKit (2)

5. Call process

Knowing the general structure of WebKit, we can go deeper and see how the browser engine works. First introduce a few basic and important classes:

  1. Page : Open the page.h header file, we don't seem to see anything related to the "page" in our concept, yes, the Page here is not the simple web page in our impression, we found a lot about history in the header file Things, goBack(), goForward(), etc., about the setting of the theme, about the description of the Frame, etc. Therefore, the Page here is more like the browser we see, abstractly, it should be regarded as our access to the website A browsing session;
    in the page.cpp file, there is also an important global pointer variable: static HashSet<Page*>* allPages; This variable contains all page instances, yes! Just like FireFox, we can start several browsers, and it is in one process;
    allPages adds each new Page object to the Page constructor; every time a new window is started, a new Page object will be created , and trigger PageGroup::addPage();

  2. Frame: Compared with Page, Frame is more like a web page in our impression. It focuses on the display of the page (FrameView), the loading of page data (FrameLoader), and various controllers (Editor, EventHandler, ScriptController, etc.) and so on, it can be said that this structure indicates that the browser has begun to shift from external control to the specific description of a page;

  3. Document: The grandpa class of this class is Node, which is the base class of each element of the DOM tree; Document has a subclass HTMLDocument, which is the root node of the entire document DOM tree, so it is clear: the original Document is to describe the specific document Code, look at its header file, it will be more clear, its attributes and methods are around various nodes: Text , Comment , CDATASection , Element... Of course,
    Document not only describes HTML-related nodes , and other types of pages such as XHTML, SVG, WML, etc.;
    in addition, one of the parent classes of Node is EventTarget, which means that all elements have the ability to respond to events. Since the Document class is the location of the DOM root node, it is in When a window event occurs, it is the first to receive the event and find the target element where the event occurs, and then process the event sequentially from the target element up the tree path.

  4. RenderObject: When forming a DOM tree node, Node will call the RenderObject method as needed to generate a Render node and finally form a Render tree, so this class is the base class of various node classes of the Render tree, and a grandchild of it —— RenderView is the root node of the entire Render tree.

For the calling process, here is a passage, which I think describes a scenario more clearly:
"A classic application scenario of a browser is that the user gives a URL (directly input or click on a link or JavaScript parsing, etc.). Then the browser shell Call FrameLoader to load the page. FrameLoader first checks some conditions (policyCheck()), such as whether the URL is not empty, whether the URL is reachable, whether the user cancels, etc. Then start a MainResourceLoader through DocumentLoader to load the page. MainResourceLoader calls the network module Interface to download page content ( ResourceHandle ), in fact, the Resourcehandle here is already platform-related content, for example, in Qt, there will be ResourceHandleQt to control, and then call QtNetworkReplyHandler to handle HTTP requests ( GET , POST , etc.). Receive data In the future, there will be a callback function to tell MainResourceLoader that the data has been received. Then return all the way to FrameLoader and start calling HTMLTokenizer to parse the HTML text. During the parsing process, if you encounter a Javascript script, it will also call the Javascript engine (JavascriptCore in Webkit, in chrome V8) to parse. After the data is parsed, it becomes a node one by one, generates a DOM tree and a Render tree, and then calls an external shell through the FrameLoaderClient to display the content.” Let’s take the Qt platform as an example to see
this The specific function call relationship in the scenario:

The first is to collate and send the client request to the server :

WebCore:: FrameLoader::load()

WebCore:: FrameLoader::loadWithDocumentLoader() →WebCore:: FrameLoader::continueLoadAfterNavigationPolicy() →WebCore:: FrameLoader::continueLoadAfterWillSubmitForm() →WebCore:: DocumentLoader::startLoadingMainResource()

WebCore:: MainResourceLoader::load()

WebCore:: MainResourceLoader::loadNow()

Here, notice that when this function calls ResourceHandle::create(), MainResourceLoader passes itself in as the second parameter of create(). This parameter is ResourceHandleClient, the grandparent class of MainResourceLoader, so that it is convenient to call the virtual function of the grandparent class below. didReceiveData(), the actual call is the didReceiveData() method of MainResourceLoader.

continue:

WebCore:: ResourceHandle::create()

WebCore:: ResourceHandleQt::start()

WebCore:: QnetworkReplyHandler::start()

As of this function, the user request will be finally sent out , and then use the connect() method to mount several signal callback functions, such as the finish() function for the finished() signal, and the forwardData() function for the readyRead() signal , the sendQueuedItems() function for the processQueuedItems() signal, the most important of which is the forwardData() function, which is to receive and process the data returned by the server .

Let's take a look at the processing of the returned data :

WebCore:: QnetworkReplyHandler::forwardData()

WebCore:: (QNetworkReply)QIODevice::read(),ResourceHandleClient:: didReceiveData()

It can be seen that first, the forwardData() function will use the read() method of QIODevice to read the received data from the network data buffer, and then call the didReceiveData() method. In the class ResourceHandleClient, this method is actually a virtual function, so the actual call Is the function of the same name of its subclass 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 。

 

Guess you like

Origin blog.csdn.net/spacetiller/article/details/5784475