一 . WebKit 简介
Webkit 是一个开放源代码的浏览器引擎 (web browser engine) ,最初的代码来自 KDE 的 KHTML 和 KJS( 均开放源代码 ) 。苹果公司在 Webkit 的基础上做了大量优化改进工作 ,此时的 Apple Webkit 已经和 Webkit 有了不少差别,最后开发出了著名的 Safari ,可以说 Safari 是一个相当成功的产品,但是 Safari 却不是开放源代码的。
基于 WebKit 的浏览器产品有:苹果的 Safari 和 iPhone , Google 的 Chrome 和 Android , Nokia 的 S60 ,傲游 3(Maxthon3) ;
WebKit 目前支持 HTML4/5 , CSS1/2 , DOM1/2 , HTTP/FILE , GIF/JPEG/PNG , XML ,SVG , RSS2.0 等;
同类的浏览器引擎有: Trident ( IE 内核), Gechko ( Netscape, Mozilla 和 Firefox 内核), Presto ( Opera 内核,不免费), Tasman ( IE for MAC 内核),等等,而 WebKit 因为其功能强大、速度快而且免费备受欢迎。
二 . 用到的库:
除了平台相关的库, WebKit 需要用到的一些主要的后台库有:
-
ICU : International Components for Unicode , 一个成熟,广泛使用的一套为 C / C + + 和Java 库提供 Unicode 的 全球化支持软件;
-
XSLT : eXtensible Stylesheet Language Transformation, W3C 定义的用于 XML 文档转换的规范;
-
Curl : 一个利用 URL 语法的命令行数据传输工具,基于 libcurl 。
-
Sqlite : SQLite 是实现了 SQL92 标准的 SQL 数据库引擎,它能在一个库里组合数据库引擎和接口 , 将所有数据存储于单个文件 ;
-
Gperf :一个很完美的哈希函数生成器;
-
Flex : Fast Lex, 快速词法分析生成器;
-
Bison :语法分析生成器,可以将一段带注释的上下文无关语法转化成 LALR 或 GLR 语法;
-
Enchant :一个拼写检查库,提供单词的拼写检查、纠错等功能;
三 . 代码目录结构
-
WebKitTools
一些测试 WebKit 实现功能的程序; -
WebKitLibraries
WebKit 用到的库以及系统调用接口定义; -
WebKitExamplePlugins
一些来自 Netscape 的插件,比如输入法、动画和 Cocoa 环境等; -
WebKitSite: 保存了 www.webkit.org 网站的
-
WebKit
此目录位于 WebKit 的最上层,定义了与应用相关的一些接口,因此它是平台相关的,每个子目录都是对应平台的完整实现: -
cf : Core Foundation, MAC OS X 上的系统级 C 语言 API 接口;
-
win : Windows ;
-
mac : MAC OS X ;
-
qt : Q Toolkit ,其公司已被 Nokia 收购;
-
gtk : Gimp Toolkit ;
-
scripts :一些脚本,目前只有一个关于 WebKit 版本的脚本程序;
-
chromium : Google Chrome 开发平台;
-
wx : wxWindows ,一种可移植的 C++ 和 Python GUI 工具箱, by Julian Smart ;
-
haiku :一种开源 OS ,从 BeOS 而来, 2001 开始, 2009 发布首版;
-
efl: Enlightenment Foundation Libraries, Enlightenment 平台;
WebCore
WebKit 的核心部分,定义了浏览相关的数据 IO 、页面加载、脚本分析、 UI 组织、事件处理、网络分析、平台相关的具体实现等内容。
- xml :提供 xml 相关的内容;
- html :提供 html 相关的内容;其下的 Canvas 目录定义了 3D 画布以及 WebGL 库相关的内容;
- wml: Wireless Markup Language ;
- css :提供 css 相关的内容;
- dom :提供 dom 相关的内容;
- editing :编辑相关的功能;
- page :浏览相关内容,并非是我们看到的一个页面,在一次浏览会话中,它只有一个实例;
- rendering :页面渲染相关的内容,在对页面脚本进行 DOM 树分析之后,需要对这些元素进行渲染和显示;
- notification :内部模块间的事件通信;
- history :页面浏览历史记录相关的内容;
- svg :矢量图形功能,有选项, --svg ;
- mathml : W3C 为网页中的数学表达式制定的规范;有编译选项, --mathml ;
- loader : 加载资源及 Cache ;
- workers :“ Web Workers 为 WEB 前端网页上的脚本提供了一种能在后台进程中运行的方法。一旦它被创建, Web Workers 就可以通过 postMessage() 向任务池发送任务请求,执行完之后再通过 postMessage() 返回消息给创建者指定的事件处理 程序 ( 通过 onmessage 进行捕获 )。
Web Workers 进程能够在不影响用户界面的情况下处理任务,并且,它还可以使用XMLHttpRequest 来处理 I/O ,无论 responseXML 和 channel 属性是否为 null 。” - storage : Web Storage 相关的内容,保存页面的数据,可以看成是 Cookie 的升级;
- websockets :与网络连接相关的内容;
- bridge: 主要包含 NPPlugin(Netscape Plugin) 方面的接口访问等内容;
- binding : Dom 与 JavaScriptCore 绑定的功能;
- accessibility :提供控件的可用性相关的内容, accessibility 常用来形容对一些特殊人群的功能支持,比如残障者、老人等;
- icu :里面放了专门为 Mac OS X 10.4 编译的 icu 相关头文件 ;
- platform :提供了平台相关的具体实现,如事件响应、本地化、网络连接等;
- plugins :插件相关内容;
- ForwardingHeaders :头文件;
- inspector : Inspector 是 WebKit 提供的查看网页源代码, DOM 树,以及调试脚本的工具,本目录包含了实现此功能的内容;
- Configurations : X Code 环境相关的配置文件;
- English.lproj :本地化文件;
- manual-tests :测试用的 html 文件;
- Resources :资源,图标;
- WebCore.gyp :工程文件。 GYP ( Generate Youre Project )是 google 自己开发了一个脚本工具,这个工具也 是采用 python 编写的。它采用了自定义的一套规则,用于生成各种工程文件;
- WebCore.vcproj : VC 工程文件;
- WebCore.xcodeproj : X Code 工程文件;
- WTF : Apple 的 C++ 库,可以看作精简的 STL ; SunSpider
- 一个 JavaScript 的检测程序,它不检测 DOM 或者浏览器其他的 API ,只用来检测 Javascript 。
-
JavaScriptGlue
-
JavaScriptCore :有关 JavaScript 的相关内容,包括了脚本解释器、分析器以及执行程序;
-
PlanetWebkit: 一个比较灵活的 RSS 阅读器; Webkit 网站上的 Planet :一站式的 Webkit 开发与动态信息;
四 . 体系结构
WebKit 主要包括三部分: WebKit , WebCore ,以及 JavaScriptCore ,加上所使用的库,依托的平台,其基本的体系结构 (Architecture) 如下所示:
注意有的模块相对于下面的模块有突出,这是因为此模块与下面几个模块直接相关,比如 WebCore 模块就与JavaScriptCore 、 Libraries 和 Platforms 模块直接相关。
五 . 调用过程
知道了 WebKit 的大体结构,我们就可以深究下去,看看这个浏览器引擎具体是怎么工作的。首先介绍几个基本且重要的类:
-
Page :打开 page.h 头文件,我们似乎看不到我们概念中的“页面”相关的东西,没错,这里的 Page 并非就是我们印象中的简单网页,在头文件中我们发现很多关于 history 的东西, goBack(),goForward(), 等等,关于主题的设定,关于Frame 的描述等等,因此,这里的 Page 更像是我们见到的浏览器,抽象起来,应该算是我们访问网站的一次浏览会话;
在 page.cpp 文件里,还有个重要的全局指针变量: static HashSet<Page*>* allPages; 这个变量包含了所有的page 实例,没错!就像 FireFox 一样,我们可以启动几个浏览器,而且就是在一个进程里;
allPages 在 Page 的构造函数里将每次新产生的 Page 对象加入;每次启动新的 window ,才会新建一个 Page 对象,并触发 PageGroup::addPage() ; -
Frame :与 Page 相比, Frame 更像我们印象中的一个网页,它关注的是页面的显示 (FrameView) 、页面数据的加载(FrameLoader) 、页面内的各种控制器 (Editor, EventHandler, ScriptController, etc.) 等等,可以说,这个结构表示浏览器开始从外部控制转向关注一个页面的具体描述了;
-
Document :这个类的爷爷类是 Node ,它是 DOM 树各元素的基类; Document 有个子类是 HTMLDocument ,它是整个文档 DOM 树的根结点,这样就明白了:原来 Document 就是描述具体文档的代码,看一下它的头文件,就更明白了,它的属性与方法就是围绕着各种各样的结点: Text , Comment , CDATASection , Element……
当然, Document 不止描述了 HTML 相关的结点,还有 XHTML 、 SVG 、 WML 等等其他类型的页面;
另外, Node 的父类之一是 EventTarget ,也就是所有元素都有事件响应的能力,由于 Document 类作为 DOM 根结点的位置,因此在窗口事件发生时,它第一个接收到事件,并寻找到事件发生的目标元素,然后从目标元素开始以树的路径向上依次处理事件。 -
RenderObject :在形成 DOM 树结点的时候, Node 会根据需要调用 RenderObject 的方法,生成 Render 结点并最终形成 Render 树,因此此类是 Render 树各种结点类的基类,它的一个孙子类—— RenderView ,就是整个 Render 树的根结点。
对于调用过程,这里有一段话,我认为比较明确地描述了一个场景:
“ 浏览器的一个经典应用场景是用户给出一个 URL (直接输入或者点击链接或者 JavaScript 解析等方式)。然后浏览器外壳调用 FrameLoader 来装载页面。 FrameLoader 首先检查一些条件 (policyCheck()) ,如 URL 是否非空、 URL 是否可达,用户是否取消等等。然后通过 DocumentLoader 启动一个 MainResourceLoader 来装载页面。MainResourceLoader 调用 network 模块中的接口来下载页面内容( ResourceHandle ),实际上这里的Resourcehandle 已 经是平台相关的内容了,比如在 Qt 里面,会有 ResourceHandleQt 来控制,然后调用QtNetworkReplyHandler 来处理 HTTP 请求( GET , POST 等)。接收到数据以后,会有回调函数,告诉MainResourceLoader 数据已经接收到了。然后一路返回到 FrameLoader 开始调用 HTMLTokenizer 解析 HTML 文本。解析过程中,如果遇到 Javascript 脚本的话,也会调用 Javascript 引擎( Webkit 中的 JavascriptCore , chrome 中的 V8 )来解析。数据被解析完了以后,成了一个一个的 node ,生成 DOM 树和 Render 树,然后通过 FrameLoaderClient 调用外部的壳把内容显示出来。”
下面我们以 Qt 平台为例,看看这个场景下的具体函数调用关系:
首先是 整理并向服务器发送客户请求 :
WebCore:: FrameLoader::load()
→ WebCore:: FrameLoader::loadWithDocumentLoader() →WebCore::FrameLoader::continueLoadAfterNavigationPolicy() →WebCore::FrameLoader::continueLoadAfterWillSubmitForm() →WebCore::DocumentLoader::startLoadingMainResource()
→ WebCore:: MainResourceLoader::load()
→ WebCore:: MainResourceLoader::loadNow()
这里,注意到本函数在调用 ResourceHandle::create() 时, MainResourceLoader 把自己作为 create() 的第二个参数传入,这个参数是 MainResourceLoader 的祖父类 ResourceHandleClient ,这样便于下面当调用祖父类的虚函数 didReceiveData() 时,实际调用的是 MainResourceLoader 的 didReceiveData() 方法。
继续:
→ WebCore:: ResourceHandle::create()
→ WebCore:: ResourceHandleQt::start()
→ WebCore:: QnetworkReplyHandler::start()
截至本函数,用户请求才会被最终发送出去 ,然后用 connect() 方法挂载了几个信号回调函数,比如针对 finished() 信号的 finish() 函数,针对 readyRead() 信号的 forwardData() 函数,针对 processQueuedItems() 信号的sendQueuedItems() 函数,其中最重要的就是 forwardData() 函数,此函数就是 对服务器返回数据的接收与处理 。
让我们来看看 返回数据的处理过程 :
WebCore:: QnetworkReplyHandler::forwardData()
→ WebCore:: (QNetworkReply)QIODevice::read(),ResourceHandleClient:: didReceiveData()
可见,首先 forwardData() 函数会利用 QIODevice 的 read() 方法从网络数据缓冲区中读取接收数据,然后调用didReceiveData() 方法,在类 ResourceHandleClient 中,这个方法实际上是个虚函数,因此实际调用的是其子类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
六 . 编译与调试
在 ubuntu-10.04 上编译 Webkit ,所用的版本 r60742 ,基于 Qt :
- 安装 sudo apt-get install libxslt-dev gperf bison libsqlite3-dev flex libqt4-dev build-essential subversion libenchant-dev libXt-dev ;如果有其他库没有安装,根据提示,用 apt-get install 安装,如果不知道包的名称,可以用 apt-get search NAME 搜索。
- 进到 WebKit 目录下,开始编译: ( 以下默认为此目录 )
QTDIR=/usr/share/qt4/ ./WebKitTools/Scripts/build-webkit --qt --debug
如果你只使用 WebKit 而不管内部结构,可以不用 --debug 选项;
如果是第一次编译,而且你用的机器性能又一般,那么时间会长一点;
中间可能出现错误,比如文件找不到,那么你要看看是哪个包的文件,要将这个包安装一下,诸如此类。 - 编译成功之后,会有提示信息
==================================================
WebKit is now built (1h:04m:28s).
To run QtLauncher with this newly-built code, use the
"WebKit/WebKitTools/Scripts/run-launcher" script.
==================================================
然后可用脚本 run-launcher 运行。 - 调试来查看 WebKit 的工作过程:
脚本 run-launcher 实际的运行程序为: ./WebKitBuild/Debug/bin/QtTestBrowser ;
程序源代码在: ./WebKitTools/QtTestBrowser/ ; - 以 www.baidu.com 为例,查看调试过程:
对 WebCore 内部的断点设置,需要在程序运行过程中设定,因为 so 库的符号表是运行时加载的(不然就不叫“动态链接”了),比如先用 break main 在 main 程序停住,然后设置WebCore::FrameLoader::load(...) ,可以设置的标志是:当你输入部分断点函数名,连续按 2下 Tab 键,会有提示;
在某个断点停住之后,可以用 bt 查看此函数之前的调用关系,比如调用过程中的函数。 - 另外,如果编译 GTK 版本的 WebKit, 首先确保 Gtk+-2.0 安装完毕,然后用类似的方法:./WebKitTools/Scripts/build-webkit --qt --debug 即可。
七 . 主要概念类图
WebKit 中的类有几千个之多,这里只是将 WebKit 特别是 WebCore 中主要的一些类及其关系勾画出来,希望能给大家以借鉴。
八 . 参考文献及资源链接
对 webKit 探讨比较全面和深入的,首推侯炯的《 WebKit 研究报告》,分 I 、 II 两部分,在网上可以轻易搜到;
另外, Jelly's Blog 中也对 WebKit 有比较好的探讨,包括基础介绍、架构以及几个重要的专题,地址是: http://www.jjos.org/tag/webkit ;
还有一些正宗的文章,来自 WebKit 官方网站, www.webkit.org 的 Surfin 博客:(英文,另外注意发布时间)
-
WebKit 页面缓存 I: http://webkit.org/blog/427/webkit-page-cache-i-the-basics/
-
样式滚动条: http://webkit.org/blog/363/styling-scrollbars/
-
3D 变换 : http://webkit.org/blog/386/3d-transforms/
-
CSS 单元:http://webkit.org/blog/57/css-units/
-
CSS 动画I :http://webkit.org/blog/138/css-animation/
-
CSS 动画II:http://webkit.org/blog/324/css-animation-2
-
CSS 变换:http://webkit.org/blog/130/css-transforms/
-
CSS 反射:http://webkit.org/blog/182/css-reflections/
-
CSS Mask : http://webkit.org/blog/181/css-masks/
-
CSS 画布: http://webkit.org/blog/176/css-canvas-drawing/
-
CSS 梯度: http://webkit.org/blog/175/introducing-css-gradients/
-
CSS 背景裁剪: http://webkit.org/blog/164/background-clip-text/
-
Inspector 介绍: http://webkit.org/blog/41/introducing-the-web-inspector/
-
Inspector 重新设计: http://webkit.org/blog/197/web-inspector-redesign/
-
完全通过 Acid3 测试: http://webkit.org/blog/280/full-pass-of-acid-3/
-
介绍新的 JavaScript 引擎—— SquirrelFish :
http://webkit.org/blog/189/announcing-squirrelfish/ -
再次探讨 SquirrelFish : http://webkit.org/blog/214/introducing-squirrelfish-extreme/
-
Windows 系统的 GDI 文字: http://webkit.org/blog/168/gdi-text-on-windows/
-
RefPtr 与 PassRefPtr 基础: http://webkit.org/coding/RefPtr.html
-
webkit 页面加载过程 http://webkit.org/blog/1188/how-webkit-loads-a-web-page/
-
客户端数据存储: http://webkit.org/blog/126/webkit-does-html5-client-side-database-storage/
-
Rendering : http://webkit.org/blog/page/19/
-
背景音乐: http://webkit.org/blog/96/background-music/
-
Text Stroke : http://webkit.org/blog/85/introducing-text-stroke/
-
理解 html,xml,xhtml:http://webkit.org/blog/68/understanding-html-xml-and-xhtml/
-
Strange Medium ,关于 Text : http://webkit.org/blog/67/strange-medium/
-
高 DPI1 : http://webkit.org/blog/55/high-dpi-web-sites/
-
高 DPI2 : http://webkit.org/blog/56/high-dpi-part-2/
-
Webcore 目录结构: http://webkit.org/blog/42/webcore-directory-structure/
-
按钮: http://webkit.org/blog/28/buttons/
-
Javascript 与 DOM 的兼容: http://webkit.org/blog/27/javascript-and-dom-compatibility/
-
Webkit 代码风格: http://webkit.org/blog/25/webkit-coding-style-guidelines/
-
Safari 内存泄漏的改进: http://webkit.org/blog/24/update-on-memory-leaks/
-
Safari 内存泄漏的检查: http://webkit.org/blog/20/were-hunting-memory-leaks/
-
哈希表 1 : http://webkit.org/blog/6/hashtables-part-1/
-
哈希表 2: http://webkit.org/blog/8/hashtables-part-2/