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定义注释掉,留点引子下次继续介绍。
一、不得利用本站危害国家安全、泄露国家秘密,不得侵犯国家社会集体的和公民的合法权益,不得利用本站制作、复制和传播不法有害信息!
二、互相尊重,对自己的言论和行为负责。