浏览器探究——webkit部分——HTMLinput标签

来源:互联网 发布:linux中sort命令 编辑:程序博客网 时间:2024/05/17 07:29
http://blog.csdn.net/hxwwf/article/details/7386164

浏览器探究——webkit部分——HTMLinput标签

测试页面

<html>

<body>

First name: <input type="text"name="fname" />

Last name: <input type="text"name="lname" />

</body>

</html>

这里不看DOM的构建以及layout和Rander的处理等,这里只关注下input标签的一些基本的处理情况。

 

HTMLInputElement

HTML的Input标签的类结构。

class HTMLInputElement : publicHTMLTextFormControlElement, public InputElement

看下集成关系

Node

ContainerNode

Element

StyledElement

HTMLElement    FormAssociateElement

HTMLFormControlElement

HTMLFormControlElementWithState     InputElement

HTMLInputElement

 

HTMLInputElement的创建

#0WebCore::HTMLInputElement::create

#1 inputConstructor

#2WebCore::HTMLElementFactory::createHTMLElement

#3WebCore::HTMLConstructionSite::createHTMLElement

#4WebCore::HTMLConstructionSite::insertSelfClosingHTMLElement

#5WebCore::HTMLTreeBuilder::processStartTagForInBody

#6WebCore::HTMLTreeBuilder::processStartTag

#7WebCore::HTMLTreeBuilder::processToken

#8WebCore::HTMLTreeBuilder::constructTreeFromAtomicToken

#9WebCore::HTMLTreeBuilder::constructTreeFromToken

#10WebCore::HTMLDocumentParser::pumpTokenizer

#11WebCore::HTMLDocumentParser::pumpTokenizerIfPossible

#12WebCore::HTMLDocumentParser::append

#13WebCore::DecodedDataDocumentParser::appendBytes

#14WebCore::DocumentWriter::addData

#15WebCore::DocumentWriter::endIfNotLoadingMainResource

#16WebCore::DocumentWriter::end

#17WebCore::DocumentLoader::finishedLoading

#18WebCore::FrameLoader::finishedLoading

#19WebCore::MainResourceLoader::didFinishLoading

#20WebCore::ResourceLoader::didFinishLoading

#21 android::WebUrlLoaderClient::didFinishLoading

这一长串基本上是构建DOM的一个主体流程,当前对构建DOM的过程先不看,先看下结果是什么。在HTMLElementFactory::createHTMLElement中会根据参数QualifiedNameqname创建一个相关类型的Element,当前qname的toString()方法可以返回它的值,这里是”input”。根据qName的值,HTMLElementFactory::createHTMLElement会从一个FunctionMap中找到合适的创建方法,来创建相应的HTMLElement,即这个是个工厂类,从中找出需要创建那种产品的HTMLElement类的构造器,然后通过构造器创建出具体的产品HTMLElement。

这样经过了HTMLElementFactory::createHTMLElement后就找到了构造器inputConstructor,通过该构造器又可以调用到HTMLInputElement::create方法,该方法就是自己创建一个自己的实例的方法了。

那么经过HTMLInputElement::create的操作,一个HTMLInputElement实例被new出来了。

 

属性的设置

在构建完HTMLInputElement后,HTMLConstructionSite::createHTMLElement会向这个Element中设置相应的属性,通过Element::setAttributeMap函数。当期间有属性添加,修改,删除时会调用Element::attributeChanged来通知并更新该属性情况。

 

RenderTextControlSingleLine的创建

在完成了上述的Element的创建之后,就是与该Element对应的Renderer的创建了

#0WebCore::TextFieldInputType::createRenderer

#1WebCore::HTMLInputElement::createRenderer

#2WebCore::Node::createRendererAndStyle

#3WebCore::Node::createRendererIfNeeded

#4 WebCore::Element::attach

#5WebCore::HTMLFormControlElement::attach

#6WebCore::HTMLInputElement::attach

#7WebCore::HTMLConstructionSite::attach

#8WebCore::HTMLConstructionSite::attachToCurrent

#9WebCore::HTMLConstructionSite::insertSelfClosingHTMLElement

#10WebCore::HTMLTreeBuilder::processStartTagForInBody

#11WebCore::HTMLTreeBuilder::processStartTag

#12WebCore::HTMLTreeBuilder::processToken

在HTMLConstructionSite::insertSelfClosingHTMLElement创建完Element之后,紧跟着就会执行HTMLConstructionSite::attachToCurrent的操作来创建与该Element对应的Renderer。

对于Element的祖先Node,提供了一个虚函数attach

// Attaches this node to the rendering tree. This calculates thestyle to be applied to the node and creates an

   // appropriate RenderObject which will be inserted into the tree (exceptwhen the style has display: none). This

   // makes the node visible in the FrameView.

virtual voidattach();

该函数是个重要的函数,Element的继承子类中,大多实现了该attach函数,用来创建与自己类型对应的Renderer。

另外Node还有个虚函数virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);

该函数用于创建与该Node对应的RenderObject的。

这里HTMLInputElement也实现了自己的attach方法和createRenderer方法,最终创建了与之对应的RenderTextControlSingleLine。

看下RenderTextControlSingleLine的继承体系:

CachedResourceClient

RenderObject

RenderBoxModelObject

RenderBox

RenderBlock

RenderTextControl        PopupMenuClient

RenderTextControlSingleLine

其实这套继承体系主要就是按照css规范做的。

 

在RenderObject类中有成员Node* m_node;在创建相应的RenderObject子类时,会设置该m_node为RenderObject对应的Node。

在Node类中有成员RenderObject* m_renderer;在Node::createRendererAndStyle中,创建完RenderObject后,就会通过Node::setRenderer把新的Renderer设置给m_renderer

这样Node和RenderObject就相互关联上了。

 

设置RenderStyle

这里有一个类RenderStyle,在Node:: createRenderer方法中,会传入这个参数。这个类包含了一些Style用于构造对应的RenderObject时用,但是RenderTextControlSingleLine的构造时没用到。

在RenderObject中还有函数setStyle,可以用来设置RenderStyle的。另外有个setAnimatableStyle函数不知道跟setStyle有什么区别,但是setAnimatableStyle最终还是调用setStyle来设置RenderStyle。

在RenderObject构造完并设置给Node后,就会调用这个RenderObject::setAnimatableStyle来为RenderObject设置RenderStyle。

 

RenderTextControlSingleLine::layoutRenderTextControlSingleLine::paint

对于RenderObject来说RenderObject::layout和RenderObject:: paint是最重要的两个函数了。这里只看下调用栈

#0WebCore::RenderTextControlSingleLine::layout

#1WebCore::RenderObject::layoutIfNeeded

#2WebCore::RenderBlock::layoutInlineChildren

#3WebCore::RenderBlock::layoutBlock

#4 WebCore::RenderBlock::layout

#5WebCore::RenderBlock::layoutBlockChild

#6WebCore::RenderBlock::layoutBlockChildren

#7WebCore::RenderBlock::layoutBlock

#8WebCore::RenderBlock::layout

#9WebCore::RenderBlock::layoutBlockChild

#10WebCore::RenderBlock::layoutBlockChildren

#11WebCore::RenderBlock::layoutBlock

#12WebCore::RenderBlock::layout

#13WebCore::RenderView::layout

#14WebCore::FrameView::layout

#15WebCore::Document::implicitClose

#16WebCore::FrameLoader::checkCallImplicitClose

#17WebCore::FrameLoader::checkCompleted

#18WebCore::FrameLoader::finishedParsing

#19WebCore::Document::finishedParsing

#20WebCore::HTMLTreeBuilder::finished

#21WebCore::HTMLDocumentParser::end

#22WebCore::HTMLDocumentParser::attemptToRunDeferredScriptsAndEnd

#23 WebCore::HTMLDocumentParser::prepareToStopParsing

#24WebCore::HTMLDocumentParser::attemptToEnd

#25WebCore::HTMLDocumentParser::finish

#26WebCore::Document::finishParsing

#27WebCore::DocumentWriter::endIfNotLoadingMainResource

#28WebCore::DocumentWriter::end

#29 WebCore::DocumentLoader::finishedLoading

#30WebCore::FrameLoader::finishedLoading

#31WebCore::MainResourceLoader::didFinishLoading

#32WebCore::ResourceLoader::didFinishLoading

#33android::WebUrlLoaderClient::didFinishLoading

layout的细节不去看了,很麻烦,以后专门去研究下。这里有个印象就好。

#0WebCore::RenderTextControlSingleLine::paint

#1WebCore::InlineBox::paint

#2WebCore::InlineFlowBox::paint

#3WebCore::RootInlineBox::paint

#4WebCore::RenderLineBoxList::paint

#5WebCore::RenderBlock::paintContents

#6WebCore::RenderBlock::paintObject

#7WebCore::RenderBlock::paint

#8WebCore::RenderBlock::paintChildren

#9WebCore::RenderBlock::paintContents

#10WebCore::RenderBlock::paintObject

#11WebCore::RenderBlock::paint

#12WebCore::RenderLayer::paintLayer

#13WebCore::RenderLayer::paintList

#14WebCore::RenderLayer::paintLayer

#15WebCore::RenderLayer::paint

#16WebCore::FrameView::paintContents

#17android::WebFrameView::draw

#18android::WebViewCore::rebuildPicture

#19android::WebViewCore::rebuildPictureSet

#20android::WebViewCore::recordPictureSet

#21android::WebViewCore::recordContent

#22 RecordContent

#23 dvmPlatformInvoke()

paint的细节不去看了,很麻烦,以后专门去研究下。这里有个印象就好。

 

点击事件

当点击输入框时,java层会受到该事件,该事件最终会在WebViewCore.java的handleMessage中通过nativeMoveMouseIfLatest这个jni函数,把事件传递到c层。

C层的WebViewCore会通过Frame找到EventHandler。

EventHandler

EventHandler是c层事件的总入口,提供了对各种时间的处理接口,当前是点击事件,则实际上先收到的是moveMouse事件,因为手机上没有鼠标,而点击时,会先认为讲鼠标移动到该位置了,所以调用了moveMouse事件。对应的EventHandle的处理就是handleMouseMoveEvent函数。

EventHandle::handleMouseMoveEvent

该函数做了一定的处理后,执行了EventHandler::dispatchMouseEvent,即将这个事件转发出去。handleMouseMoveEvent最主要的处理是,找出哪个Node会被接收并处理事件。即当前找出鼠标移动到哪个Node上了。然后在EventHandler::dispatchMouseEvent中即可把事件转发给该Node去处理了,这里调用的是Node::dispatchMouseEvent。可见EventHandler就是事件的汇总处,接收到事件后,判断要让哪个Node来处理,然后就会调用该Node的相应的事件处理接口。

Node::dispatchMouseEvent

到了这里,事件已经通过EventHandler到了某个具体的Node上了。但是Node直接把处理交给了EventDispatcher::dispatchEvent这个静态函数,让它负责做转发的处理。

EventDispatcher

这个类有一些静态函数,用于做事件的分发,其实这个就是对事件做一下包装和转换,做一些额外的处理,最终还是会用某种策略把事件传递给具体需要处理的Node上。调用具体Node的处理函数。也就是它作为了一个中转层,在它处理之前已经知道是什么事件,并作用在什么Node上了。但是它做了一些额外的处理后,在根据一个策略来调用Node的处理函数。

最终该事件到达HTMLInputElement::defaultEventHandler(Event* evt)中。

看下调用栈:

#0 WebCore::HTMLInputElement::defaultEventHandler

#1 WebCore::EventDispatcher::dispatchEvent

#2WebCore::MouseEventDispatchMediator::dispatchEvent

#3WebCore::EventDispatcher::dispatchEvent

#4WebCore::Node::dispatchMouseEvent

#5 WebCore::EventHandler::dispatchMouseEvent

#6 WebCore::EventHandler::handleMouseMoveEvent

#7 android::WebViewCore::moveMouse

#8 android::WebViewCore::moveMouseIfLatest

#9 MoveMouseIfLatest

 

 

 

输入字符

当输入字符时,同样会触发HTMLInputElement::defaultEventHandler,此时栈情况为:

#0WebCore::InputElementData::setValue

#1 WebCore::InputElement::setValueFromRenderer

#2WebCore::HTMLInputElement::setValueFromRenderer

#3 WebCore::RenderTextControlSingleLine::subtreeHasChanged

#4WebCore::HTMLFormControlElementWithState::defaultEventHandler

#5WebCore::HTMLInputElement::defaultEventHandler

 

可见,在HTMLInputElement的defaultEventHandelr处理中的最后会调用基类的defaultEventHandelr。

这个基类HTMLFormControlElementWithState的处理中会做个如下的判断

if (event->type() ==eventNames().webkitEditableContentChangedEvent && renderer() &&renderer()->isTextControl())

即判断了event的类型和renderer的类型后,执行了RenderTextControlSingleLine::subtreeHasChanged的操作。

RenderTextControlSingleLine::subtreeHasChanged

这个函数中,会通过基类RenderTextControl获取到当前输入的text值,然后把这个值赋给与它对应的Node中,即通过InputElement::setValueFromRenderer这个函数。

这样HTMLInputElement就获取并记录了用户输入的字符串的信息。

 

当前就简单的了解下HTMLInputElement的这点基本流程,具体的细节先不看了,有个大概的印象就行了。