转载:http://blog.csdn.net/lihui130135 作者:李东辉 邮件:lihui40319@gmail.com
1.下载一个webkit的版本,目前我使用的版本是webkit-r54749,这个版本正好是android2.2版本对应webkit版本
2.进行编译,这个过程根据自己机器环境qt,gtk,win之类都是可以编译,在这里简单说一下怎么进行编译
切换到WebKit-r54749目录下
./WebKitTools/Scripts/build-webkit --help
下面罗列出很多的细节:
--help Show this help message
--clean Cleanup the build directory
--debug Compile in debug mode
--cairo-win32 Build using Cairo (rather than CoreGraphics) on Windows
--chromium Build the Chromium port on Mac/Win/Linux
--gtk Build the GTK+ port
--qt Build the Qt port
--inspector-frontend Copy changes to the inspector front-end files to the build directory
--makeargs=<arguments> Optional Makefile flags
--minimal No optional features, unless explicitly enabled.
--[no-]3d-canvas Toggle 3D canvas support (default: 0)
--[no-]3d-rendering Toggle 3D rendering support (default: 0)
--[no-]channel-messaging Toggle MessageChannel and MessagePort support (default: 1)
--[no-]client-based-geolocation Toggle client-based Geolocation support (default: 1)
--[no-]coverage Toggle code coverage support (default: 0)
--[no-]database Toggle Database Support (default: 1)
--[no-]datagrid Toggle Datagrid Support (default: 0)
--[no-]datalist Toggle HTML5 datalist support (default: 1)
--[no-]dom-storage Toggle DOM Storage Support (default: 1)
--[no-]eventsource Toggle server-sent events support (default: 1)
--[no-]filters Toggle Filters support (default: 1)
--[no-]geolocation Toggle Geolocation support (default: 1)
--[no-]icon-database Toggle Icon database support (default: 1)
--[no-]indexed-database Toggle Indexed Database API support (default: 0)
--[no-]javascript-debugger Toggle JavaScript Debugger/Profiler support (default: 1)
--[no-]mathml Toggle MathML support (default: 0)
--[no-]notifications Toggle Desktop Notifications Support (default: 0)
--[no-]offline-web-applications Toggle Offline Web Application Support (default: 1)
--[no-]ruby Toggle HTML5 Ruby support (default: 1)
--[no-]shared-workers Toggle SharedWorkers support (default: 1)
--[no-]svg Toggle SVG support (default: 1)
--[no-]svg-animation Toggle SVG animation support (implies SVG support) (default: 1)
--[no-]svg-as-image Toggle SVG as Image support (implies SVG support) (default: 1)
--[no-]svg-dom-objc-bindings Toggle SVG DOM Objective-C bindings support (implies SVG support) (default: 0)
--[no-]svg-fonts Toggle SVG fonts support (imples SVG support) (default: 1)
--[no-]svg-foreign-object Toggle SVG foreign object support (implies SVG support) (default: 1)
--[no-]svg-use Toggle SVG use element support (implies SVG support) (default: 1)
--[no-]video Toggle Video support (default: 1)
--[no-]web-sockets Toggle Web Sockets support (default: 1)
--[no-]wml Toggle WML support (default: 0)
--[no-]xhtmlmp Toggle XHTML-MP support (default: 0)
--[no-]wcss Toggle WCSS support (default: 0)
--[no-]workers Toggle Web Workers support (default: 1)
--[no-]xpath Toggle XPath support (default: 1)
--[no-]xslt Toggle XSLT support (default: 1)
这个时候就可以根据自己环境选择
./WebKitTools/Scripts/build-webkit --gtk --debug
这样就可以编译一个基于gtk的debug版本
我们发现在WebKit-r54749目录下会生成一个WebKitBuild目录
有个Debug目录存在
当然在编译的过程中会有很多的错误,比如缺少某个库,之类的错误,这个时候要根据缺失的进行安装,在这里就不进行详细介绍了
当然最新的webkit版本较之这个版本已经有太多的变化了,但是核心部门的大致逻辑不会有太大的变化,只不过分工更加明细了,结构更加合理了
闲话少说接着来
在Debug目录下有个Programs,然后我们看下里面存在一个
DumpRenderTree GtkLauncher jsc minidom unittests
这么几个文件,GtkLauncher就是webkit的入口程序
DumpRenderTree这就是我们这节说的重点所在,这个可执行程序对应的源文件就是DumpRenderTree.cpp文件中
我们直接执行
./DumpRenderTree http://192.168.1.68/index.html
就可以看到下面的打印
layer at (0,0) size 76x92
RenderView at (0,0) size 76x92
layer at (0,0) size 76x92
RenderBlock {HTML} at (0,0) size 76x92
RenderBody {BODY} at (0,0) size 76x92
RenderTable {TABLE} at (0,0) size 1x1
RenderTableSection {TBODY} at (0,0) size 1x1
RenderTableRow {TR} at (0,0) size 1x1
RenderTableCell {TD} at (0,0) size 1x1 [r=0 c=0 rs=1 cs=1]
RenderImage {IMG} at (0,0) size 1x1
如果是第一次运行有可能会出现这样的错误
** ERROR **: WEBKIT_TESTFONTS environment variable is not set, but it should point to the directory containing the fonts you can clone from git://gitorious.org/qtwebkit/testfonts.git
这个是需要设置一个fonts的路径
我们只需要这样做下:export WEBKIT_TESTFONTS=/opt/raul/sources/browser/WebKit-r54749/WebKitTools/DumpRenderTree/fonts
就可以了
我们接着看忙活半天的成果
有个这个好工具就非常有助于我们学习webkit的render过程
我们简单的分析一下是如何打印出这个罗列顺序的,熟悉浏览器用法的人都知道浏览器有个最基本的功能,右键查看页面源代码。
这个功能和DumpRenderTree功能很相近,就是把内部Dom tree中的结构给提炼出来
现在简单分析下提炼的过程
int main(int argc, char* argv[])
{
g_thread_init(NULL);
gtk_init(&argc, &argv);
#if PLATFORM(X11)
FcInit();
initializeFonts();
#endif
struct option options[] = {
{"notree", no_argument, &dumpTree, false},
{"pixel-tests", no_argument, &dumpPixels, true},
{"tree", no_argument, &dumpTree, true},
{NULL, 0, NULL, 0}
};
int option;
while ((option = getopt_long(argc, (char* const*)argv, "", options, NULL)) != -1)
switch (option) {
case '?': // unknown or ambiguous option
case ':': // missing argument
exit(1);
break;
}
window = gtk_window_new(GTK_WINDOW_POPUP);
container = GTK_WIDGET(gtk_scrolled_window_new(NULL, NULL));
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(container), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_container_add(GTK_CONTAINER(window), container);
gtk_widget_show_all(window);
webView = createWebView();
gtk_container_add(GTK_CONTAINER(container), GTK_WIDGET(webView));
gtk_widget_realize(GTK_WIDGET(webView));
gtk_widget_show_all(container);
gtk_widget_grab_focus(GTK_WIDGET(webView));
mainFrame = webkit_web_view_get_main_frame(webView);
setDefaultsToConsistentStateValuesForTesting();
gcController = new GCController();
axController = new AccessibilityController();
if (argc == optind+1 && strcmp(argv[optind], "-") == 0) {
char filenameBuffer[2048];
printSeparators = true;
while (fgets(filenameBuffer, sizeof(filenameBuffer), stdin)) {
char* newLineCharacter = strchr(filenameBuffer, '\n');
if (newLineCharacter)
*newLineCharacter = '\0';
if (strlen(filenameBuffer) == 0)
continue;
runTest(filenameBuffer);
}
} else {
printSeparators = (optind < argc-1 || (dumpPixels && dumpTree));
for (int i = optind; i != argc; ++i)
runTest(argv[i]);
}
delete gcController;
gcController = 0;
delete axController;
axController = 0;
gtk_widget_destroy(window);
return 0;
}
下面是基本的main函数,initializeFonts();这个函数的实现包含的就是上面提高的找不到WEBKIT_TESTFONTS环境变量的问题
在这个过程中做的基本的事件就是加载一个页面,然后把这个页面的基本的render tree的结构打印出来,顺便运行一下几个单元测试
我们只是研究下关键代码
webView = createWebView();
这个函数的实现如下:
static WebKitWebView* createWebView()
{
WebKitWebView* view = WEBKIT_WEB_VIEW(webkit_web_view_new());
// From bug 11756: Use a frame group name for all WebViews created by
// DumpRenderTree to allow testing of cross-page frame lookup.
webkit_web_view_set_group_name(view, "org.webkit.gtk.DumpRenderTree");
g_object_connect(G_OBJECT(view),
"signal::load-started", webViewLoadStarted, 0,
"signal::load-finished", webViewLoadFinished, 0,
"signal::window-object-cleared", webViewWindowObjectCleared, 0,
"signal::console-message", webViewConsoleMessage, 0,
"signal::script-alert", webViewScriptAlert, 0,
"signal::script-prompt", webViewScriptPrompt, 0,
"signal::script-confirm", webViewScriptConfirm, 0,
"signal::title-changed", webViewTitleChanged, 0,
"signal::navigation-policy-decision-requested", webViewNavigationPolicyDecisionRequested, 0,
"signal::status-bar-text-changed", webViewStatusBarTextChanged, 0,
"signal::create-web-view", webViewCreate, 0,
"signal::close-web-view", webViewClose, 0,
"signal::database-quota-exceeded", databaseQuotaExceeded, 0,
NULL);
WebKitWebInspector* inspector = webkit_web_view_get_inspector(view);
g_object_connect(G_OBJECT(inspector),
"signal::inspect-web-view", webInspectorInspectWebView, 0,
"signal::show-window", webInspectorShowWindow, 0,
"signal::close-window", webInspectorCloseWindow, 0,
NULL);
if (webView) {
WebKitWebSettings* settings = webkit_web_view_get_settings(webView);
webkit_web_view_set_settings(view, settings);
}
return view;
}
最关键的部位是在
"signal::load-finished", webViewLoadFinished, 0,
我们看webViewLoadFinished函数的实现
static void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*)
{
if (!done && !gLayoutTestController->dumpFrameLoadCallbacks()) {
guint pendingFrameUnloadEvents = webkit_web_frame_get_pending_unload_event_count(frame);
if (pendingFrameUnloadEvents) {
char* frameName = getFrameNameSuitableForTestResult(view, frame);
printf("%s - has %u onunload handler(s)\n", frameName, pendingFrameUnloadEvents);
g_free(frameName);
}
}
if (frame != topLoadingFrame)
return;
topLoadingFrame = 0;
WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
if (gLayoutTestController->waitToDump())
return;
if (WorkQueue::shared()->count())
g_timeout_add(0, processWork, 0);
else
dump();
}
在最后一行找到他的小尾巴了,那么看看到底如何提取出来的那?
void dump()
{
invalidateAnyPreviousWaitToDumpWatchdog();
bool dumpAsText = gLayoutTestController->dumpAsText();
if (dumpTree) {
char* result = 0;
gchar* responseMimeType = webkit_web_frame_get_response_mime_type(mainFrame);
dumpAsText = g_str_equal(responseMimeType, "text/plain");
g_free(responseMimeType);
// Test can request controller to be dumped as text even
// while test's response mime type is not text/plain.
// Overriding this behavior with dumpAsText being false is a bad idea.
if (dumpAsText)
gLayoutTestController->setDumpAsText(dumpAsText);
if (gLayoutTestController->dumpAsText())
result = dumpFramesAsText(mainFrame);
else
result = webkit_web_frame_dump_render_tree(mainFrame);
if (!result) {
const char* errorMessage;
if (gLayoutTestController->dumpAsText())
errorMessage = "[documentElement innerText]";
else if (gLayoutTestController->dumpDOMAsWebArchive())
errorMessage = "[[mainFrame DOMDocument] webArchive]";
else if (gLayoutTestController->dumpSourceAsWebArchive())
errorMessage = "[[mainFrame dataSource] webArchive]";
else
errorMessage = "[mainFrame renderTreeAsExternalRepresentation]";
printf("ERROR: nil result from %s", errorMessage);
} else {
printf("%s", result);
g_free(result);
if (!gLayoutTestController->dumpAsText() && !gLayoutTestController->dumpDOMAsWebArchive() && !gLayoutTestController->dumpSourceAsWebArchive())
dumpFrameScrollPosition(mainFrame);
if (gLayoutTestController->dumpBackForwardList())
dumpBackForwardListForAllWebViews();
}
if (printSeparators) {
puts("#EOF"); // terminate the content block
fputs("#EOF\n", stderr);
fflush(stdout);
fflush(stderr);
}
}
if (dumpPixels) {
if (!gLayoutTestController->dumpAsText() && !gLayoutTestController->dumpDOMAsWebArchive() && !gLayoutTestController->dumpSourceAsWebArchive()) {
// FIXME: Add support for dumping pixels
}
}
// FIXME: call displayWebView here when we support --paint
done = true;
gtk_main_quit();
}
这个函数太长,我们还是截取最关键的代码
result = webkit_web_frame_dump_render_tree(mainFrame);
这个函数执行返回的结果就是我们上面执行打印的结果
看来还需要继续追踪
gchar* webkit_web_frame_dump_render_tree(WebKitWebFrame* frame)
{
g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);
Frame* coreFrame = core(frame);
if (!coreFrame)
return g_strdup("");
FrameView* view = coreFrame->view();
if (view && view->layoutPending())
view->layout();
String string = externalRepresentation(coreFrame);
return g_strdup(string.utf8().data());
}
String string = externalRepresentation(coreFrame);这个函数返回的,这位大仙何许人阿,追踪了下,发现是在
RenderTreeAsText.cpp中进行定义的
这个类是render模块专门拿出来处理收集信息
String externalRepresentation(Frame* frame, RenderAsTextBehavior behavior)
{
frame->document()->updateLayout();
RenderObject* o = frame->contentRenderer();
if (!o)
return String();
TextStream ts;
#if ENABLE(SVG)
writeRenderResources(ts, o->document());
#endif
if (o->hasLayer()) {
RenderLayer* l = toRenderBox(o)->layer();
writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), 0, behavior);
writeSelection(ts, o);
}
return ts.release();
}
好了从逻辑上来说,就弄到这边吧,留点自己思考的时间
在android平台上如果要打印render tree 直接通过webview.dumpDomTree(false)
很是方便,前提需要把NDEBUG定义注释掉,留点引子下次继续介绍。