디지털 장인정신

블로그 이미지
WebKit개발자의 혼자쓰는 블로그
스페로

Article Category

All (71)
Technology (47)
Creativity (19)
Private (4)

Recent Comment

Recent Trackback

Calendar

«   2018/01   »
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      

Archive

My Link

  • Total313,516
  • Today12
  • Yesterday68
  1. 2012.10.15
    WebKit2 Injected Bundle (1)
  2. 2012.01.28
    WebKit2 Rendering 분석 (only if using Texture Mapper) (1)

Refer to

http://lists.webkit.org/pipermail/webkit-gtk/2012-May/001078.html


WebProcess의 addon을 달 수 있는 기능. 현재는 거의 WebKitTestRunner때문에 만들어졌다고 봐도 과언이 아니다. 
일반적인 plugin mechanism과 달리 오직 하나의 InjectedBundle만 WebProcess에 inject할 수 있다.

일반적인 plugin mechanism이란? (recommend to read  http://www.apibook.com/blog/)
API를 plugin을 통해 extending하는 방법. 크롬이 NPAPI API를 통해 author가 작성한 플러그인을 사용하거나 3D Max에서 서드파티가 제공하는 addon을 붙이는 방법이다. author는 plugin API를 구현한뒤 shared library로 만든다. 어플리케이션이 런타임에 shared library를 로드하고 해당 API를 콜하게 된다.
보통 plugin manager를 만들어 여러 shared library를 동시에 로드할 수 있게끔 구현한다. (크롬이 flash plugin과 pdf reader plugin을 동시에 쓸수 있는것 처럼)
근데 Injected Bundle은 오직 하나의 shared library만 로드할 수 있도록 심플하게 구현되어있다. (가장위의 refer 참조)

Call Flow
1. initialize
in Source/WebKit2/WebProcess/WebProcess.cpp

void WebProcess::postInjectedBundleMessage(const CoreIPC::DataReference& messageData)

{

    InjectedBundle* injectedBundle = WebProcess::shared().injectedBundle();

    ...

}


in Tools/WebKitTestRunner/InjectedBundle/InjectedBundleMain.cpp

void WKBundleInitialize(WKBundleRef bundle, WKTypeRef initializationUserData)

{

    WTR::InjectedBundle::shared().initialize(bundle, initializationUserData);

}


in Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp

void InjectedBundle::initialize(WKBundleRef bundle, WKTypeRef initializationUserData)

{

    WKBundleClient client = {


    };

    WKBundleSetClient(m_bundle, &client);

}


in Source/WebKit2/WebProcess/InjectedBundle/InjectedBundle.cpp

void InjectedBundle::initializeClient(WKBundleClient* client)

{

    m_client.initialize(client);

}


2. create page

in Source/WebKit2/WebProcess/WebPage/WebPage/WebPage.cpp

PassRefPtr<WebPage> WebPage::create(uint64_t pageID, const WebPageCreationParameters& parameters)

{

    ....

    if (page->pageGroup()->isVisibleToInjectedBundle() && WebProcess::shared().injectedBundle())

        WebProcess::shared().injectedBundle()->didCreatePage(page.get());

    ....

}


in Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp

void InjectedBundle::didCreatePage(WKBundlePageRef page)

{

    m_pages.append(adoptPtr(new InjectedBundlePage(page)));

}


in Tools/WebKitTestRunner/InjectedBundle/InjectedBundlePage.cpp

InjectedBundlePage::InjectedBundlePage(WKBundlePageRef page)

    : m_page(page)

    , m_world(AdoptWK, WKBundleScriptWorldCreateWorld())

{

    WKBundlePageLoaderClient loaderClient = {    };

    WKBundlePageSetPageLoaderClient(m_page, &loaderClient);


    WKBundlePageResourceLoadClient resourceLoadClient = {    };

    WKBundlePageSetResourceLoadClient(m_page, &resourceLoadClient);


    WKBundlePagePolicyClient policyClient = {    };

    WKBundlePageSetPolicyClient(m_page, &policyClient);


    WKBundlePageUIClient uiClient = {    };

    WKBundlePageSetUIClient(m_page, &uiClient);


    WKBundlePageEditorClient editorClient = {    };

    WKBundlePageSetEditorClient(m_page, &editorClient);


#if ENABLE(FULLSCREEN_API)

    WKBundlePageFullScreenClient fullScreenClient = {    };

    WKBundlePageSetFullScreenClient(m_page, &fullScreenClient);

#endif

}


in Source/WebKit2/WebProcess/WebPage/WebPage/WebPage.cpp

void WebPage::initializeInjectedBundleLoaderClient(WKBundlePageLoaderClient* client)

{

    ....

    m_loaderClient.initialize(client);

}


3. loader callback

in Source/WebKit2/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp

void WebFrameLoaderClient::postProgressFinishedNotification()

{

    if (WebPage* webPage = m_frame->page()) {

        if (m_frame->isMainFrame()) {

            // Notify the bundle client.

            webPage->injectedBundleLoaderClient().didFinishProgress(webPage);


            webPage->send(Messages::WebPageProxy::DidFinishProgress());

        }

    }

}


in Tools/WebKitTestRunner/InjectedBundle/InjectedBundlePage.cpp

void InjectedBundlePage::didFinishProgress(WKBundlePageRef, const void *clientInfo)

{

    static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishProgress();

}



Build

InjectedBundle은 shared library로 따로 빌드된다. WebKitTestRunner를 실행히 env로 shared library의 path가 세팅된다.


in  Tools/WebKitTestRunner/GNUmakefile.am

# The InjectedBundle library allows the render process to load harness code.

if ENABLE_WEBKIT2

noinst_LTLIBRARIES += Libraries/libTestRunnerInjectedBundle.la

endif



How to send InjectedBundle path from UI Process to Web Process

위에서 서술하다 싶이 env로 UI Process의 TestController가 알아낸후 WebContext에게 알려준다.

in Tools/WebKitTestRunner/gtk/main.cpp

int main(int argc, char** argv)

{

    gtk_init(&argc, &argv);

    // Prefer the not installed web and plugin processes.

    WTR::TestController controller(argc, const_cast<const char**>(argv));

    return 0;

}


in Tools/WebKitTestRunner/TestController.cpp

void TestController::initialize(int argc, const char* argv[])

{

    ...

    initializeInjectedBundlePath();


    ...

    m_context.adopt(WKContextCreateWithInjectedBundlePath(injectedBundlePath()));

    ...

}


in Source/WebKit2/UIrocess/WebContext.cpp

PassRefPtr<WebContext> WebContext::create(const String& injectedBundlePath)

{

    JSC::initializeThreading();

    WTF::initializeMainThread();

    RunLoop::initializeMainRunLoop();

    return adoptRef(new WebContext(ProcessModelSharedSecondaryProcess, injectedBundlePath));

}


What is InjectedBundle in UI Process

UI Process에도 callback을 inject하는 듯한 코드들이 있다. (밑에처럼)

이것은 shared library를 ld하여 callback을 등록한다는 개념의 injected bundle보다는, 그냥 callback을 등록하는 코드이다.

코드를 reuse하기위해 콜백등록 패턴을 그냥 쓰는데, 코드를 읽는데 굉장한 혼란을 준다.


in Tools/WebKitTestRunner/TestController.cpp

void TestController::initialize(int argc, const char* argv[])

{

    ...

    WKContextInjectedBundleClient injectedBundleClient = {

        kWKContextInjectedBundleClientCurrentVersion,

        this,

        didReceiveMessageFromInjectedBundle,

        didReceiveSynchronousMessageFromInjectedBundle,

        0 // getInjectedBundleInitializationUserData

    };

    WKContextSetInjectedBundleClient(m_context.get(), &injectedBundleClient);

    ...

}


'Technology > Webkit' 카테고리의 다른 글

WebKit css3 animations on Accelerated Compositing  (1) 2012.10.23
WebKit Filters Animation Internal  (1) 2012.10.20
WebKit2 Injected Bundle  (1) 2012.10.15
oprofile  (2) 2012.06.02
massif 메모리 사용량을 측정하자  (0) 2012.05.31
cachegrind in valgrind  (5) 2012.05.31
Trackback 1 and Comment 1
WebKit2의 IPC중 가장 복잡한 부분이 Rendering이다. ShareableBitmap과 scene graph정보를 적절히 massage로 주고 받아야 하기 때문이다. 이번글에서, Qt의 Accelerated Compositing인 Texture Mapper가 어떻게 IPC를 통해 브라우저에 그림을 그리는지 살펴보겠다.

특별히 Qt의 TexMap을 분석하는 이유는 WebKit에 TextureMapper의 구현코드가 모두 있기 때문이다. (OpenGL를 써서 매우 아름답게 구현되어 있다.) Chrome과 Mac은 그들의 2D Vector Graphics Library (i.e. skia, core graphics)를 써서 Accelerated Compositing을 구현했기 때문에 분석이 쉽지 않다. 그 때문에 GTK와 EFL 포트역시 TexMap을 자신들의 AC(Accelerated Compositing)로 포팅을 하고 있다.

WebKit2에서 TexMap을 enable하려면 무조건 TBS를 enable해야 한다.
TexMap을 disable하고 TBS만 enable할 수도 있다. 그러면 그림을 그리는 통신의 주체가 TiledDrawingArea가 된다. 이 글에서는 다루지 않는다.
TexMap을 enable하고 TBS를 disable할 수는 없다.
TexMap을 disable하고 TBS를 disable할 수 있다.  그러면 그림을 그리는 통신의 주체가 DrawingArea가 된다. 이 글에서는 다루지 않는다.
AC(TexMap뿐만 아니라)를 enable했을 때 그림을 그리는 통신의 주체는 LayerTreeHost이다.

이글은 TexMap과 TBS를 알고 있어야 읽을수 있다. 모르시는 분은 다음 링크부터 읽으세요.
TBS : http://ariya.ofilabs.com/2011/06/progressive-rendering-via-tiled-backing-store.html
TexMap :
http://www.sencha.com/blog/understanding-hardware-acceleration-on-mobile-browsers/
http://ariya.ofilabs.com/2011/07/fluid-animation-with-accelerated-composition.html
http://qtwebkit.blogspot.com/2010/10/texture-mapper-accelerated-compositing.html
WebKit2 : http://trac.webkit.org/wiki/WebKit2
Qt WebKit2 : http://ariya.ofilabs.com/2011/08/first-look-at-nokia-n950-web-browser.html

우선 Class Diagram부터 살펴보자
1. WebProcess
http://www.diagrammr.com/edit?key=dl7okJw6Skf
LayerTreeHostQt : UIProcess에 drawing message를 send & receive한다. WebLayerInfo struct에 ShareableBitmap과 Scene Graph 정보를 담아서 send한다.
 Receive Msg
      SetVisibleContentRectTrajectoryVectorID, : scroll
      SetVisibleContentRectAndScaleID, : scale
      RenderNextFrameID, : invalidate
      PurgeBackingStoresID, 
 Send Msg
      SyncCompositingLayerStateID, 
      SetRootCompositingLayerID,
      DeleteCompositingLayerID,
      CreateTileForLayerID, : TBS tile creation
      UpdateTileForLayerID,
      RemoveTileForLayerID,
      CreateDirectlyCompositedImageID, : Image elemene같은거는 TBS를 사용하지 않는다
      DestroyDirectlyCompositedImageID,
      DidRenderFrameID, 

Send Message의 struct
UpdateInfo : Tile 정보를 보낼때 사용
  tile의 size, scroll, updateRect 등의 정보
  ShareableBitmap::Handle

WebLayerInfo : GraphicsLayer의 정보를 보낼때 사용
  scene graph 정보
  ShareableBitmap::Handle : image element만 사용 (TBS 를 사용하지 않기 때문에)

WebGraphicsLayer
  1. LayerTreeHostQt가 AC를 시작하는 entry point가 되는 rootLayer : LayerTreeHostQt가 UI Process로 부터 받는 Msg를 수행하는 entry point
  2. 모든 RenderLayer의 GraphicsLayer : 모든 RenderLayer의 변화 사항을 UI Process로 보내는 곳 (m_client 이용)
  3. TBS의 tile의 client : Tile의 변화 사항을 UI Process로 보내는 곳 (m_layerTreeTileClient 이용)

2. UIProcess
http://www.diagrammr.com/edit?key=dkTm7Ld2Ndi
LayerTreeHostProxy :
 1. WebProcess의 LayerTreeHostQt와 IPC 통신을 한다. 어떤 Msg를 주고 받는지는 위에 기술한거의 반대이다.
 2. WebView로 부터 paintToCurrentGLContext call를 받았을때 m_rootLayer인 GraphicsLayerTextureMapper에게 AC를 시작 시킴

GraphicsLayerTextureMapper
 1. LayerTreeHostProxy가 AC를 시작하는 entry point가 되는 rootLayer : Web Process로 부터 받는 Msg를 수행하는 entry point
 2. LayerTreeHostProxy의 rootLayer를 head로 GraphicsLayer tree의 가 node들



시퀀스 다이어그램을 살펴보기전 TexMap구현의 몇가지 특이점을 집고 넘어가자.

GraphicsLayer의 구현체 : GraphicsLayerTextureMapper in UI Process,  WebGraphicsLayer in Web Process.

TexMap의 위치 : UI Process

WebProcess가 UI Process로 그림을 넘기는 방법
--- WebProcess
 1. WebProcess에서 WebGraphicsLayer들은 모두 TBS(Tiled Backing Store)를 갖는다. (TBS가 여러개란 의미이다.)
 2. SetNeedsDisplay 타이밍에 각각의 WebGraphicsLayer들이 자신의 TBS에 Contents를 update한다.
--- UIProcess
 3. UIProcess로 정보를 WebLayerInfo struct로 잘 packaging하여 메세지를 전송한다.
 4. 메세지를 잘 해석하여 GraphicsLayer의 PlatformLayer구현체인 TextureMapperNode를 업데이트한다.

UI Process에서 WebView가 그림을 그리는 방법
 1. LayerTreeHostProxyQt가 WebProcess로 부터 화면갱신에 관한 메세지를 받으면 m_messagesToRenderer에 메세지를 큐잉하고 webView에 updateViewport(이것이 invalidate)를 콜한다.
 2. 일정시간후 webView가 LayerTreeHostProxyQt에 paintToCurrentGLContext를 콜한다.
 3. m_messagesToRenderer에 쌓인 메세지를 모두 처리하고 TexMap을 이용하여 그림을 그린다.

WebGraphicsLayer의 생성 시점 (WebProcess) :
 1. 각 GraphicsLayer들이 만들어 질때.  (WebGraphicsLayer는 매우 여러개가 만들어진다.)
 2. LayerTreeHost가 만들어 질때. LayerTreeHost가 own하는 WebGraphicsLayer가 rootLayer이다.
GraphicsLayerTextureMapper의 생성 시점 (UIProcess) :
 1. root GraphicsLayer를 attach할 때.
 2. LayerTreeHostProxy가 만들어 질때.  LayerTreeHostProxy가 own하는 GraphicsLayerTextureMapper가 rootLayer이다.

SharableBitmap을 IPC하는 방법:
 1. SharableBitmap는 data (shared memory + image size) + method (GraphicsContext)이다.
 2. 질문의 답은 결국 shared memory를 IPC 어떻게 하는가이다.
     - win32경우 shared memory의 file handle과 pid를 같이 다른 process에 msg를 넘긴다.
     - 다른 process에서 handle이 만들어진 pid를 알면 같은 kernel object를 가르키는 file handle을 만들수 있다.


setNeedsDisplay부터 브라우저 화면에 그림이 그려지기 까지의 시퀀스 다이어그램

1. Invalidate
http://www.diagrammr.com/edit?key=dEJUawO7FFj
 1. RenderTree에서 자기가 소속된 RenderLayer에 setNeedsDisplay를 콜한다.
 2. 각 WebGraphicsLayer는 자신이 가진 TBS에 invalidate를 콜한다.
 3. TBS는 각 Tile에 updateBackBuffer를 콜한다.
 4. 각 Tile은 TileClient(WebGraphicsLayer)에 tiledBackingStorePaint를 콜한다.
 5. WebGraphicsLayer는 자신에게 소속된 RenderTree를 traversal하면서 Tile에 그림을 그린다.
 6. 각 Tile은 그림을 완성했으므로 UI Process에 그림과 세부정보를 UpdateInfo struct로 싸서 메세지를 보낸다.
 7. UI Process의 LayerTreeHostProxyQt는 m_messagesToRenderer에 메세지를 큐잉하고 webView에 updateViewport를 콜한다.

2. Update
http://www.diagrammr.com/edit?key=d0udlR9XYKS
 1. 여러가지 이유(i.e. UI Process의 여러 화면 바뀜 MSG들, RenderTree의 뭔가 바뀌었다는 notify들) 때문에 LayerTreeHostQt::scheduleLayerFlush()가 콜된다.
 2. LayerTreeHostQt는 자신이 가지고 있는 rootLayer와 nonCompositedContentLayer에 syncCompositingState를 콜한다.
 3. WebGraphicsLayer는 TBS를 업데이드 해야하면 업데이트하고 모든 pendingInfo를 LayerInfo로 만든후 UI Process에 메세지를 보낸다.
 4. UI Process의 LayerTreeHostProxyQt는 m_messagesToRenderer에 메세지를 큐잉하고 webView에 updateViewport를 콜한다.

3. Paint
http://www.diagrammr.com/edit?key=du1XRpcWn01
 1. WebView가 LayerTreeHostProxyQt에 paintToCurrentGLContext를 콜한다.
 2. m_messagesToRenderer에 쌓인 메세지를 처리하기 위해 GraphicsLayerTextureMapper::setSomething과 TextureMapperNode::syncCompositingState를 콜한다.
 3. 이렇게 TexMap Tree과 Web Process의 RenderLayer Tree가 sync되었다.
 4. LayerTreeHostProxyQt가 자신이 가진 rootNode(TextureMapperNode)에 paint를 콜한다.
 5. TexMap이 그림을 GLContext에 그린다.


'Technology > Webkit' 카테고리의 다른 글

WebKit Filters Animation Internal  (1) 2012.10.20
WebKit2 Injected Bundle  (1) 2012.10.15
oprofile  (2) 2012.06.02
massif 메모리 사용량을 측정하자  (0) 2012.05.31
cachegrind in valgrind  (5) 2012.05.31
WebKit2 Rendering 분석 (only if using Texture Mapper)  (1) 2012.01.28
Trackback 3 and Comment 1