webkit Dump Render Tree 工作流程

长平狐 发布于 2013/04/25 10:57
阅读 549
收藏 0

转载: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定义注释掉,留点引子下次继续介绍。




原文链接:http://blog.csdn.net/lichwei1983/article/details/8188620
加载中
返回顶部
顶部