webkit find功能浅析

来源:互联网 发布:python web监控系统 编辑:程序博客网 时间:2024/05/29 14:03


Webkit的find功能是webkit比较小的一个功能,它在底层的主要接口就是android::WebViewCore::findTextOnPage。这个函数主要干了三件事情:

 

A.     移除已有的标记

B.     搜索符合条件的字符串(需记录字符串的个数和它们的位置),并为它们做上标记

C.     显示新标记

第一步和第三步比较简单,本质上就是向带有标记的node所对应的RenderObject发送一个重绘的请求,剩下的事情由webkit的绘制流程来完成。关键是第2步,要完成查找匹配字符串和确定它们所在node及位置的任务。下面就着重看下Editor::countMatchesForText函数,其中只保留了关键语句,省略了大量非主干的代码。

if (!searchRange)

searchRange= rangeOfContents(m_frame->document()); //将执行搜索操作的frame所对应的DOM树都加入到Range类,对于Range类的理解下面会说到。

。。。

do { //循环搜索,直到上述Range遍历完为止

RefPtr<Range>resultRange(findPlainText(searchRange.get(), target, options &~Backwards)); //以上述Range为目标区域,搜索目标字符串,返回匹配区域range

。。。

// Onlytreat the result as a match if it is visible

if(insideVisibleArea(resultRange.get())) {

    ++matchCount; //计数统计

    if (markMatches)

       m_frame->document()->markers()->addMarker(resultRange.get(),DocumentMarker::TextMatch); //将匹配区域Range加入到Marker列表,即做上标记

    }

       。。。

searchRange->setStart(resultRange->endContainer(exception),resultRange->endOffset(exception), exception); //以匹配区域的末尾为新目标区域的起始点,重新开始搜索

。。。

} while (true);

 

可见,上述代码描述的是一个简单的搜索遍历,我们需要理解一个关键的类Range。这个类描述的是一个范围,确切的说是一个有序的节点组合。那么它当然应该有一个起始节点和结束节点。实际上起始和结束节点还有一个offset整型值,来更详细的记录该节点内部的偏移。对于一个非叶节点来说,offset偏移表示它的第几个孩子,对于一个叶子端的RenderText节点来说,offset偏移表示它的字符数。在解释之后大家应该对上述流程比较清楚了。那么我们先看findPlainText函数怎样来实现查找的,再看addMarker怎样将目标Range加入Marker列表。

首先来看TextIterator::findPlainText函数,该函数中先以目标Range区域为参数创建一个CharacterIterator。CharacterIterator借助TextIterator类来实现其功能,后者顾名思义会包含目标Range区域的Text节点。CharacterIterator即这些节点所包含的字符。

真正的字符串匹配过程是借助searchbuffer类来完成的,将CharacterIterator的内容字符化到searchbuffer中,通过memcmp系统函数来与目标字符串进行比较。具体算法没有进行分析,但字符串匹配应该不出那几个经典算法。最后的匹配结果会在CharacterIterator中记录位置。并进一步转换为在目标Range区域中的位置。具体过程可以见代码,这里不再详述。

实际上webkit的实现还是比较复杂,让我们自己来想一想,如果实现在网页中匹配字符串应该怎么做。首先,在网页中可见的字符串都是Text节点,通过RenderText::text()可以得到字符串。其次,应该按照深度优先搜索遍历这些节点,将节点内容链接起来。通过这两步,就能将网页中的内容提取成一个字符串(当然还有些细节问题,比如两个Text节点的内容是无缝连接,还是需要加换行符等),然后跟目标字符串比较。另外,Text节点的字符长度应该记录,这样得到匹配字符串的位置后,可以计算出相应的Text节点及偏移量。所以,按我的理解,第一步就应该删减节点,只留下Text节点。webkit为什么不这样做,基于什么考虑现在还不是很清楚。

无论如何通过上述步骤,匹配区域Range所在的节点和偏移就确定了,那下一步就是加入到标记列表。DocumentMarkerController类负责将Marker分门别类的收集起来,不同类型的Marker事先定义了不同的显示方式。在节点重绘的时候会根据marker的类型来具体绘制。

具体的参见InlineTextBox::paintDocumentMarkers函数,在该函数中会遍历所有的DocumentMarker,并按Marker的类型调用不同的绘制函数

对于find功能所对应的Marker,它的绘制过程在 InlineTextBox::paintTextMatchMarker中,它的显示方式在RenderTheme类中,这是一个基类,可以由浏览器来定义自己的实现,android下的实现为RenderThemeAndroid.

0 0
原创粉丝点击