测试页面
<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::layout和RenderTextControlSingleLine::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的这点基本流程,具体的细节先不看了,有个大概的印象就行了。