WebKit阅读起步

首先文笔不好, 其次精力关系,理解错误肯定很多,欢迎指正,随时修改.

       WebKit代码非常的多,现在的WebKit源代码包括jscore部分大概有90万代码,去除js部分有70万左右(WebKit组成部分加移植部分很多,各版本差异可能大概在几万左右.这里的代码统计是我自己写代码读取的行数).大家都称WebKit相比FireFox代码上要清晰很多,但里面还是有着臃肿混乱的部分,相信除了WebKit的代码维护者,一般人对着源代码去了解其运行逻辑还是痛苦的,外加代码庞大,"物理意义"上仅仅是代码行数乘以1秒换算成时间,也是让人望而却步的(感觉就像你不吃不喝拿自己的工资乘以时间去参照北京房价一样),所以一个好的,熟悉的调试工具,代码阅读工具是很重要的.

      Qt是一个很完善的开源平台(很多代码看上去比WebKit友好清晰,更关键是Qt在各个平台上的编译及初期工作都很温柔及人性化.),Qt对WebKit有一个很完善的移植,借助Qt这个外壳,能让我们跳过无意义的时间耗费,更有效和快速接近WebKit的内部.所以我主要是通过Qt中对WebKit部分对WebKit进行阅读的.
      首先你的CPU必须是双核的,以便编译过程太卡时,你还能干点代码阅读的事情,此外CPU速度和硬盘速度也不能太古董,否则说不定光按下F5调试就足以你泡杯咖啡并喝完它.软件工具: VisualStudio,Source Insigh,其中VS需要搭配VS assistant, Source Insigh需要搭配标签页工具(我使用的是TabSiPlus),以更人性化的使用VS和source insigh.
      a.按照网络上的教程使用qmake命令生成Qt的Visual studio工程文件,手工编辑sln文件,删除其它的Qt工程,仅保留Browser,jscore,QtWebKit
     编译.
      b.新建Source Insigh的工程,添加Qt/thirdpartys/Webkit中所有文件到工程中.然后使用vs和source Insigh进行代码的调试运行和阅读.
   
VS打开:
    找到browsermainwindow.cpp文件,里面的void BrowserMainWindow::slotHome()函数,修改
    QString home = settings.value(QLatin1String("home"), QLatin1String("file:///C:/Users/moon/Desktop/save/17.html")).toString();
    这行代码 指向自己想加载的主页.

    A. 找到RenderObject::addChild函数添加断点.VS调试运行工程,WebKit在加载网页的开始加载了一个空白页(参看FrameLoader::Init的第一行注释.),所以可能有前1,2次进入RenderObject::addChild是无意义的.
    命中断点时,打开调用堆栈界面,
   
    这是在收到网络数据后,从网络移植部分调用到RenderObject::addChild函数的过程.

    WebCore::RenderObject::addChild
    WebCore::RenderBlock::addChild
    WebCore::Node::createRendererIfNeeded
    WebCore::HTMLParser::insertNode
    WebCore::HTMLParser::insertNodeAfterLimitBlockDepth
    WebCore::HTMLParser::parseToken
    WebCore::HTMLTokenizer::processToken
    WebCore::HTMLTokenizer::parseTag
    WebCore::HTMLTokenizer::write
    WebCore::FrameLoader::write
    WebCore::FrameLoader::addData
    WebCore::FrameLoaderClientQt::committedLoad
    WebCore::FrameLoader::committedLoad
    WebCore::DocumentLoader::commitLoad
    WebCore::DocumentLoader::receivedData
    WebCore::FrameLoader::receivedData
    WebCore::MainResourceLoader::addData
    WebCore::ResourceLoader::didReceiveData
    WebCore::MainResourceLoader::didReceiveData
    WebCore::ResourceLoader::didReceiveData
    WebCore::QNetworkReplyHandler::forwardData

   
    B.给ResourceHandleQt.cpp文件中的ResourceHandle::start加上断点,加载网页:
        WebCore::ResourceHandle::start(WebCore::Frame * frame=0x035c5810)
        WebCore::ResourceHandle::create(const WebCore::ResourceRequest & request={...}, WebCore::ResourceHandleClient * client=0x035feb40, WebCore::Frame * frame=0x035c5810, bool defersLoading=false, bool shouldContentSniff=false)
        WebCore::MainResourceLoader::loadNow(WebCore::ResourceRequest & r={...})
        WebCore::MainResourceLoader::load(const WebCore::ResourceRequest & r={...}, const WebCore::SubstituteData & substituteData={...})
        WebCore::DocumentLoader::startLoadingMainResource(unsigned long identifier=1)
        WebCore::FrameLoader::continueLoadAfterWillSubmitForm()
        WebCore::FrameLoader::continueLoadAfterNavigationPolicy(const WebCore::ResourceRequest & __formal={...}, WTF::PassRefPtr<WebCore::FormState> formState={...}, bool shouldContinue=true)
        WebCore::FrameLoader::callContinueLoadAfterNavigationPolicy(void * argument=0x035c5840, const WebCore::ResourceRequest & request={...}, WTF::PassRefPtr<WebCore::FormState> formState={...}, bool shouldContinue=true)
        WebCore::PolicyCallback::call(bool shouldContinue=true)
        WebCore::PolicyChecker::continueAfterNavigationPolicy(WebCore::PolicyAction policy=PolicyUse)
        WebCore::FrameLoaderClientQt::callPolicyFunction(void (WebCore::PolicyAction)* function=0x1076f340, WebCore::PolicyAction action=PolicyUse)
        WebCore::FrameLoaderClientQt::dispatchDecidePolicyForNavigationAction(void (WebCore::PolicyAction)* function=0x1076f340, const WebCore::NavigationAction & action={...}, const WebCore::ResourceRequest & request={...}, WTF::PassRefPtr<WebCore::FormState> __formal={...})
        WebCore::PolicyChecker::checkNavigationPolicy(const WebCore::ResourceRequest & request={...}, WebCore::DocumentLoader * loader=0x035fe080, WTF::PassRefPtr<WebCore::FormState> formState={...}, void (void *, const WebCore::ResourceRequest &, WTF::PassRefPtr<WebCore::FormState>, bool)* function=0x102f0540, void * argument=0x035c5840)
        WebCore::FrameLoader::loadWithDocumentLoader(WebCore::DocumentLoader * loader=0x035fe080, WebCore::FrameLoadType type=FrameLoadTypeStandard, WTF::PassRefPtr<WebCore::FormState> prpFormState={...})
        WebCore::FrameLoader::load(WebCore::DocumentLoader * newDocumentLoader=0x035fe080)
        WebCore::FrameLoader::load(const WebCore::ResourceRequest & request={...}, const WebCore::SubstituteData & substituteData={...}, bool lockHistory=false)
        WebCore::FrameLoader::load(const WebCore::ResourceRequest & request={...}, bool lockHistory=false)
        QWebFrame::load(const QNetworkRequest & req={...}, QNetworkAccessManager::Operation operation=GetOperation, const QByteArray & body={...})
        QWebFrame::load(const QUrl & url={...})
        QWebView::load(const QUrl & url={...})
        WebView::loadUrl(const QUrl & url={...})
        TabWidget::loadUrlInCurrentTab(const QUrl & url={...})
        BrowserMainWindow::loadUrl(const QUrl & url={...})
        BrowserMainWindow::loadPage(const QString & page={...})
        BrowserMainWindow::slotHome()
        BrowserApplication::postLaunch()
    这是程序启动到发起一次网络下载操作的流程,ResourceHandle包含了一个QNetworkReplyHandler对象,并在有data可read的时候执行QNetworkReplyHandler::forwardData. A和B在这里联系起来.

    C. 给drawTextCommon加上断点,加载一个网页:
        WebCore::drawTextCommon()
        WebCore::Font::drawComplexText()
        WebCore::Font::drawText()
        WebCore::GraphicsContext::drawText()
        WebCore::paintTextWithShadows()
        WebCore::InlineTextBox::paint()
        WebCore::InlineFlowBox::paint()
        WebCore::RootInlineBox::paint()
        WebCore::RenderLineBoxList::paint()
        WebCore::RenderBlock::paintContents()
        WebCore::RenderBlock::paintObject()
        WebCore::RenderBlock::paint()
        WebCore::RenderLayer::paintLayer()
        WebCore::RenderLayer::paint()
        WebCore::FrameView::paintContents()
        QWebFramePrivate::renderRelativeCoords()
        QWebFrame::render(QPainter * painter=0x0012cb40, const QRegion & clip={...})
        QWebView::paintEvent(QPaintEvent * ev=0x0012d364)

    这是一次绘画的流程,从重画事件一直到读取保存在RenderObject,RenderStyle(它们是webkit中对CSS部分的实现)中的信息进行paint.

    D.给JsObject.cpp里的jsobject::put加上断点,给jscore里的parser.cpp的didfinishParsing加上断点, 加载一个有js代码的网页.
        
        (1) jsobject::put
        JSC::JSObject::put(JSC::ExecState * exec=0x03d00048, const JSC::Identifier & propertyName={...}, JSC::JSValue value={...}, JSC::PutPropertySlot & slot={...})
        JSC::JSValue::put(JSC::ExecState * exec=0x03d00048, const JSC::Identifier & propertyName={...}, JSC::JSValue value={...}, JSC::PutPropertySlot & slot={...})
        cti_op_put_by_id(void * * args=0x0012cf60)
        cti_op_convert_this@4()
        JSC::JITCode::execute(JSC::RegisterFile * registerFile=0x03501cac, JSC::ExecState * callFrame=0x03d00048, JSC::JSGlobalData * globalData=0x034dd0e0, JSC::JSValue * exception=0x0012d094)
        JSC::Interpreter::execute(JSC::ProgramExecutable * program=0x0356b508, JSC::ExecState * callFrame=0x0354b6f8, JSC::ScopeChainNode *         JSC::JSObject * thisObj=0x03bc0000, JSC::JSValue * exception=0x0012d094)
       
        (2) parser::didfinishparsing
        JSC::Parser::didFinishParsing(JSC::SourceElements * sourceElements=0x03545c58, JSC::ParserArenaData<WTF::Vector<std::pair<JSC::Identifier const *,unsigned int>,0> > * varStack=0x03545ac8, JSC::ParserArenaData<WTF::Vector<JSC::FunctionBodyNode *,0> > * funcStack=0x00000000, unsigned int features=6, int lastLine=36, int numConstants=0)
        jscyyparse(void * globalPtr=0x034bd150)
        JSC::Parser::parse(JSC::JSGlobalData * globalData=0x034bd150, int * errLine=0x0012d034, JSC::UString * errMsg=0x0012d03c)
        JSC::Parser::parse<JSC::ProgramNode>(JSC::JSGlobalData * globalData=0x034bd150, JSC::Debugger * debugger=0x00000000, JSC::ExecState * debuggerExecState=0x03542c80, const JSC::SourceCode & source={...}, int * errLine=0x0012d034, JSC::UString * errMsg=0x0012d03c)
        JSC::ProgramExecutable::compile(JSC::ExecState * exec=0x03542c80, JSC::ScopeChainNode * scopeChainNode=0x03542de0)
       
        (3) 2者共同部分
        JSC::evaluate(JSC::ExecState * exec=0x0354b6f8, JSC::ScopeChain & scopeChain={...}, const JSC::SourceCode & source={...}, JSC::JSValue thisValue={...})
        WebCore::ScriptController::evaluateInWorld(const WebCore::ScriptSourceCode & sourceCode={...}, WebCore::DOMWrapperWorld * world=0x03501f98)
        WebCore::ScriptController::evaluate(const WebCore::ScriptSourceCode & sourceCode={...})
        WebCore::ScriptController::executeScript(const WebCore::ScriptSourceCode & sourceCode={...})
        WebCore::HTMLTokenizer::scriptExecution(const WebCore::ScriptSourceCode & sourceCode={...}, WebCore::HTMLTokenizer::State state={...})
        WebCore::HTMLTokenizer::executeExternalScriptsIfReady()
        WebCore::HTMLTokenizer::notifyFinished(WebCore::CachedResource * __formal=0x0353fa08)
        WebCore::CachedScript::checkNotify()
        WebCore::CachedScript::data(WTF::PassRefPtr<WebCore::SharedBuffer> data={...}, bool allDataReceived=true)
        WebCore::Loader::Host::didFinishLoading(WebCore::SubresourceLoader * loader=0x03540640)
        WebCore::SubresourceLoader::didFinishLoading()
        WebCore::ResourceLoader::didFinishLoading(WebCore::ResourceHandle * __formal=0x0353ffd8)
        WebCore::QNetworkReplyHandler::finish()
    这是一个网页加载完毕到javascript解析逻辑,以及在js核心里的流程.

    我们可以参考一下A,B,C,D这样的总体流程,以更直观的理解逻辑,当然我这里的断点选择仅仅是示意,并不是最佳的展示.
   
    我第一次看到WebKit代码中did,will前缀有点困惑,看多了才熟悉了其含义.will/did相当于某些项目里的before/after,也就是在某件事情发生之间和发生之后要进行的处理(有些地方并不正确,但大体如此),did类似callback的含义,像win平台上常见的OnKeyDown里的On. will,did,callback,after,before在webkit的函数命名中都有使用.
    其命名惯例还有client和private,  AAAClient表示的是需要关注AAA处理的过程中一些逻辑点的类,***client一般是个接口类,继承它,实现自己的逻辑,这样***在进行一个操作时会调用client类的逻辑,实现通知or处理or拦截.
    private就是pimpl手法,多用于外围逻辑和移植的部分,AAA类会有一个AAAprivate成员,AAA的方法就是简单的调用AAAprivate的同名方法.在WebKit里有些地方也用impl这个标缀,但不多.
   
    阅读过程可能很累很疲倦,涉及方方面面细节很多,最好找至少30分钟的块状时间来进行阅读.看5分钟就被打断了,回过头很可能就忘记了,也就白浪费了5分钟.
    个人觉得多注意几点:
   
    0. 选择性阅读,注意每个类实现了什么功能,这个功能的关键程度.
       我们的时间只够看有意义的类,不可能犄角旮旯的代码也去仔细阅读.
   
    1. 对于 类,可以通过查看类名的引用关注它是在何处何时构造的,以清晰它的重要程度.
      
    2. 阅读一个类的方法时,多使用"查看所有引用"功能.
       以:
         a.防止自己在阅读无太大意义的代码.
         b.清晰这个方法调用时的运行环境/逻辑环境,以更好的体会方法中的代码要注意的地方.
 
    3. VS与Source Insigh交换着使用,2者各有各的优缺点.阅读时应该注意自己此时使用的是哪样工具从事哪样事情,争取提高每分每秒的效率.
       我使用的是VS2005,其代码上下文窗口响应极其缓慢,查看定义和声明时常有一些bug,"智能感应"功能耗CPU很高却耗时很久,而source insigh在这方面相当的完美.
       VS内置标签页功能,而且可以建立垂直选项卡组,在宽屏上可以很方便进行并排阅读.安装visual assistant后,有更多快捷的功能.
       总而言之,我们要灵活使用2者来最高效率的完成夸张的工作量.
 
    4. 阅读时可以加些断点,加载一个网页,执行一些操作,跟踪下流程,加深理解.
 
    5. 多做记录,因为逻辑繁杂,很容易看一些忘一些(我去年就看过一小部分webkit代码,今年几乎就想不起来了,幸亏留了些笔记.), 做些记录,以便查看时能把以前的理解很快的捡起来.

    6. 对一些有趣优秀的地方,一些臃肿的地方进行总结反思学习,多总结才能更好的帮助自己代码设计的更好,写的更好.

    7. 对自己要严酷,要有规划,若仅仅是非常肤浅的读webkit一部分代码,个人强烈建议节约时间看一些网络上的webkit代码阅读总结大体了解好了.毕竟不深刻的记忆总是很容易忘记的,而人的时间是最昂贵的,做一件不能成为回忆的事情是对生命极大的浪费.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值