2012년 6월 30일 토요일

JavaScript Web Applications (한빛미디어)




이 책은 자바스크립트 애플리케이션 개발 시 유용한 아키텍쳐 패턴인 MVC 패턴을 소개하고, 이 패턴을 애플리케이션에 적용하는 데 도움을 주는 여러 모듈의 사용 방법 및 테스트를 위한 프레임워크 그리고 HTML5의 파일API, 웹소켓에 대해서 다루고 있습니다.

이제는 웹 애플리케이션이 단순히 서버에서 제공하는 정보를 보여주는 것이 아니라 클라이언트에서 복잡한 연산과 로컬 데이터 생성 및 관리가 필요하게 되었습니다. 곧 네이티브 어플리케이션이 하는 작업을 웹 애플리케이션에서도 수행을 할 수 있게 된것인데, 이는 웹 프로그램도 네이티브 프로그램처럼 복잡해진다는 것을 의미합니다. 따라서 구조화된 개발이 필요하게 되었고, 이 책은 MVC 패턴으로 웹 애플리케이션을 구조화를 할 수 있도록 모델, 컨트롤러, 뷰 컴포넌트에 대해 아주 자세히 설명하고 있습니다. 책 후반부에서는 이 패턴 사용에 필요한 라이브러리인 스파인, 백본, 자바스크립트MVC 라이브러리를 다루고, 간단한 애플리케이션 예제를 통해 라이브러리를 어떻게 활용하는지 설명하고 있습니다. 또한 프로그램이 커져가면서 자연히 의존하는 모듈이 늘어가게 되는데 이러한 의존성을 관리하는 방법을 다루고 있습니다.

이 책의 서두에서 대상독자는 자바스크립트 초보자가 대상이 아니라고 말하고 있지만, 정말 쌩초보(?)가 아니라면 이책을 권하고 싶습니다. 본격적인 개발경험을 하기 전에 이 책을 통해 유용한 자바스크립트 아키텍쳐 패턴인 MVC 패턴에 대해 알고 익힐 수 있다면 실제 개발에 큰 도움이 될 것입니다. 
자바스크립트를 쓸 줄 아는 개발자에서 자바스크립트를 잘! 쓸 수 있는 개발자로 변신하고 싶다면 꼭 이책을 읽기를 바랍니다.

2012년 6월 23일 토요일

About Renderer process


How browser process manages the number of renderer process?

As you may know, chromium has multi process architecture. In this architecture many renderer processes are running. You can see many renderer processes running by ps command(in linux) or task manager(in Windows) When we use chromium browser, many renderer processes are created and destroyed. It lives long or very short depends on the situation.
In this document, I’ll cover two topics. First one is when new renderer process is created. Second is how the number of renderer processes are controlled.

Topic 1. When renderers are created and destroyed?

1. At start time

When user execute chromium, new renderer is created with new tab. If only one tab is opened, one renderer is created. If many tabs are opened, many renderers are created. The number of renderer process depends on installed system memory and --renderer-process-limit option. The number of renderer process will be covered second section.

2.  Type new url to omnibox of opened tab

When user types new url, whether new renderer process is created or not is depends on below two options.
*process-per-site-instance
This is a current default process model. If user don’t add either process-per-site or process-per-tab, this mode is used. When you type new url to current tab, renderer is replaced.
I’m curious about separate visits to the same site on the same tab. Sometime, renderer process is replaced in this case.
In this mode, different tabs have different renderer regardless of its domain.
* process-per-site
If you type url that have different domain to current tab, new renderer is created for current tab. And old renderer is destroyed. Otherwise(typed url that have same domain), existing renderer is used for rendering new url.
  ex1) Current tab is in www.google.com and you type gmail.google.com, current renderer is
           used because two url have same domain(google.com).
  ex2) Current tab is in www.google.co.kr and you type gmail.google.com, current renderer is
          destroyed and new renderer process is launched because two url has different domain.
In this mode,  many tabs that have same domains are grouped in a renderer.
* process-per-tab
When user add this switch value, one renderer process is used before the tab is closed. I found that this mode has some bug when I enabled low-memory-observer.

3. Add new tab

When user clicks new tab button, NTP(New Tab Page) that show most visited pages or installed webapp is created.  Opening many NTP sequentially don’t  create renderer process. When user insert url on the NTP, new renderer is also created if renderer limit is not exceeded. If user types new url or click page snap, new renderer is created and existed renderer is destroyed. This behavior also depends --process-per-tab or --process-per-site.

4. OOM Handler (chromeos)

When browser process receive oom event from kernel’s low-mem notifier, browser process choose least recently used tab’s renderer process. And that renderer is discarded.

Topic 2. How many renderers are created?


As I mentioned earlier, you can see many renderers. The number of tabs is not equals the number of renderer process. In this topic, I’ll explain what determines the number of renderer process

1.  --renderer-process-limit=N

Browser process only creates renderer process up to N. When new tab is created, RenderProcessHost checks whether this switch is used.

2. --single-process

Browser process don’t create renderer process if this is on.

3. Installed system memory

If above switches are not used, max number of renderer process is calculated based on installed system memory not available memory.
You can see the formula that calculates the max number of renderer process in the RenderProcessHost::GetMaxRendererProcessCount().

4. The number of Extension service process

If there is a room to create another renderer process, chromium considers running extension service. Browser process don’t create renderer if extension service process number is bigger than max number of render process * 0.3.
Refer to ChromeContentBrowserClient::ShouldTryToUseExistingProcessHost().

5. Click the link

When user clicks url link with target attribute ‘_blank’, new tab is created for that link. But, browser process don’t create new renderer process and share its parent’s renderer process. When I click many links from same webpage, only one renderer is used for all created tabs.

2012년 5월 30일 수요일

읽기 좋은 코드가 좋은 코드다 (한빛미디어)



"읽기 쉬운 100줄의 코드는 읽기 어려운 50줄의 코드에 비해서 훨씬 낫다"

회사의 프로젝트를 수행하면서 직접 개발을 하는 경우가 많지만, 이에 못지않게 이미 개발된 소스코드를 분석하는 일도 많다.
남의 소스코드를 분석하다보면 잘 이해가 되는 소스가 있는 반면, 여러번 봐야하고, 심지어 필기를 해가며 분석을 해가야 이해가 되는 소스가 있다. 함수가 어떤 동작을 수행하는지 알기 위해 코드 한줄 한줄을 다 읽어야 이 함수가 무슨 일을 하는 지 알수 있다면 이 코드의 분석은 정말 끔찍하고 피하고 싶은 일이 될것이다.
이 책을 읽고 난 뒤, "이 코드는 이 부분 때문에 가독성이 참 떨어지네" 라고 잡아낼 수 있는 감이 생긴 것 같다. 물론 직접 코딩을 할 때도 많은 도움이 될 것이다.

이 책은 얇다. 삽화, 소스코드, 글 들이 잘 배치가 되어있어서 지루하지않고 재미있게 읽을 수 있고, 250페이지 정도여서 짧은 시간에 읽을 수 있는 분량이다.
저자들은 예제를 통해서 이 코드는 왜 나쁜지, 어떻게 하면 읽기 좋은 코드가 되는지를 설명하고 있다. 저자들이 직접 개발했던 코드들, 오픈소스 코드들(크로미움 프로젝트 등)에서 어떠한 부분이 읽기 좋지 않았고, 어떻게 개선이 되서 읽기 좋은 코드가 되었는지 잘 설명을 해주고 있다. 설명을 위해 만든 코드가 아닌, 문제가 있었던 코드들을 찾아서 사용하고 있어서 그런지 예제들이 참 좋았던 것 같다.

이 책은 총 네 파트로 구성 되어있다.
첫번째 파트 (표면적 수준에서의 개선) - 파트 이름대로 표면적 수준에서의 개선할 수 있는 내용들을 설명하고 있다. 변수명, 함수명, 주석등을 통해 코드를 접했을 때 바로 보이는 부분들을 다루고 있다.
두번째 파트 (루프와 논리를 단순화하기) - 조건문, 루프 (do/while, goto)를 분석하기 쉽게 구성하는 방법, 큰 논리를 단순한 작은 논리로 단순화하여, 읽는 사람이 쉽게 소화할 수 있는 방법을 제시하고 있다.
세번째 파트 (코드 재작성하기) - 큰 흐름과 관계없는 작은 문제들로 쪼갠다음 분리하고, 작업은 한번에 하나씩이라는 원리를 토대로 작업을 재정리하는 방법을 다루고 있다.
네번째 파트 (선택된 주제들) - 이 파트에서는 테스트 코드의 가독성에 대해 다루고, 분/시간 카운터 설계 및 구현과정을 진행하면서 어떻게 가독성을 높여가는지 보여주고 있다.

소프트웨어 엔지니어라는 직업을 가지고도 변수 명, 변수 선언 위치 등에 대해 고민하고 구현을 했었던 적이 과연있었나 싶고, 부끄럽기까지하다. 이 책의 내용들을 통째로 머릿속에 집어넣고 다니고 싶다. 코드를 작성하는 사람이 아닌 코드를 읽는 사람의 입장에서 구현을 하라는 말을 명심한다면 읽기 좋은 코드를 작성할 수 있을 것이다.

자신의 코드를 남에게 보이기 부끄러웠다면, 당장 이 책을 사서 보길 바란다.

2012년 5월 16일 수요일

Chromium - Notification Service

Overview

Notification Service provides notification delivery mechanism from producer to observer in the same thread. This class is created and used in browser process’s sub threads.
Refer to src/content/browser/browser_process_sub_thread.cc






Components

a. NotificationService
It handles notification delivery and manages observer list.
This service provides method that can add or remove notification for observer.
Also, Notify method is provided for notification provider.
To get the NotificationService object, content::NotificationService::current() is used.
It could be used in that thread only. It means calling content::NotifcationService::current()
returns different object in UI thread and IO thread.

b. Notification Observer:
This component is responsible for registration its preferred notification type to
NotificationService.
For receiving notification, Observer must implement NotificationObserver class.

c. Notification Provider:
It is a notification producer. This component sends notification to NotificationService.


How To Register Notification to NotificationService

a. Add Notification
For registration, NotificationRegistrar class is used instead of using NotificationService
directly. It is a helper class for registering notification and ensures that all registered
notifications are unregistered when the class is destroyed.
Refer to src/content/public/browser/notification_registrar.h

b. Implement Notification Handler
For implementing handler, Observer should implement NotificationObserver class as a base
class. It has only one function, observe(). When registered notification is occured,
NotificationService calls this function.
Refer to src/content/public/browser/notification_observer.h


How to Notify to NotificationService

NotificationService provides Notify() for synchronous notify mechanism. Notification provider should deliver notification type, source that originates this notification and details that contains additional information to NotificationService via Notify().
Refer to src/content/browser/notification_service_impl.cc 

2012년 5월 8일 화요일

Chromium Bug - 82276 (3)

이제 FindBarController가 마우스 클릭 이벤트를 구독하도록 하고, 이벤트 핸들러를 구현해보자.

이전 포스트에서 언급했듯이 Find Bar의 life cycle은 FindBarController 의 Show()/EndFindSession() 에서 시작되고 종료된다.

Show() 함수에서 다음과 같이 마우스 클릭 이벤트를 등록했다.


registrar_ (content::NotificationRegistrar) 의 Add() 를 이용해서 Noti를 받고싶은 원하는 notification type을 등록한다.
Add()의 첫번째 파라미터로 NotificationObserver를 구현한 객체를 전달하는데,
Notificaiton Service (NotificationServiceImpl) 에게 이 Noti가 발생했을때, 이 객체의 Observe()를 호출하라는 의미이다.
두번째 파라미터는 Noti의 이름이고, 세번째 파라미터는 받고싶은 Noti의 Source를 설정하는 부분이다.
Noti의 Source란 이 Noti를 생성하는 부분인데, 두번째 파라미터의 Noti를 Observer 가 특정 객체로부터 생성된 것만 받거나 모든 객체에서 생성되것을 받을 수 있다.
다음과 깉이 설정하면 WebContents의 객체에서 Noti만 받을 수 있다.
content::Source(tab_contents_->web_contents())
위 그림에서 처럼 AllSources() 로 설정하면, 모든 객체에서 생성되는 INPUT_EVENT_ACK Noti를 받을 수 있다.


다음은 Observe() 함수에 추가한 부분이다.



Observe() 함수의 파라미터를 통해 type, source, details를 받을 수 있다.
details는 이 Noti가 전달해주는 추가 데이터이다. INPUT_EVENT_ACK의 경우 인풋 이벤트의 종류이다.
여기서는 Mouse Up 이벤트만 관심있으므로 datails 를 통해 인풋 이벤트중 원하는 이벤트를 선택할 수 있다.

마지막으로 이벤트 등록을 삭제하는 부분이다.


삭제도 물론 NotificationService 로부터 이루어지므로 registrar_의 Remove() 를 통해 이루어지고, 파라미터는 Add() 와 같다.


이것으로 Find Refresh 에 필요한 이벤트 등록이 완료됐다.
다음 포스트에서는 어떻게 Refresh를 할지 고민해보자.

2012년 5월 4일 금요일

Chromium Bug - 82276 (2)

Chromium - Notification Registration/Handler

이 버그를 어떻게 풀어나갈지 고민 중이다.

우선 Find Bar를 언제 Refresh 해야할까? (여기서 Refresh란 다시 find 동작을 수행하는 것이다.)
웹페이지의 Re-Paint 이벤트는 이 상황에서 적당한 이벤트 일까?
Re-Paint 이벤트는 웹페이지 내 커서만 깜빡여도 발생하므로 Refresh 가 쉴새 없이 진행될 것이다.
따라서 Refresh 이벤트로 사용하기에는 부적절한 것 같다.
이 경우에 쓸만할 적당한 이벤트가 무엇이 있을까?
흠... 이 버그를 보이게 한 시작점은 폴딩된 메일의 클릭이므로, 마우스 클릭 이벤트를 Refresh 이벤트로 사용해보자!

소스코드를 뒤진 결과 다음과 같은 이벤트가 있다.
content::NOTIFICATION_RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK
이 이벤트는 RenderWidgetHostImpl 객체에서 인풋 이벤트 처리가 끝난 후, Notify 하고 있다.
인풋 이벤트 처리 후가 Refresh 시점에 적절한 지는 좀 더 생각을 해봐야하겠지만, 우선은
인풋 관련 해서 현재 Noti 하고있는 이벤트는 이것뿐인 것 같으므로 사용해보자.

그렇다면 이 이벤트는 언제 받아보면 될까?
브라우저가 실행되고 있다고 해서 이 인풋 이벤트를 받아볼 필요는 없다.
Find Bar 가 떠 있는 상태에서만 인풋 이벤트를 받아서 처리하면 될 것이다.
그렇다면 Find Bar의 보이기/사라지기 가 수행되는 시점을 찾아서 보이기 시점에 인풋 이벤트를 구독하고
사라지는 시점에 구독하던 이벤트를 제거하면 될 것 같다.
Find Bar는 FindBarController::Show()에서 FindBar를 보이게 하고, FindBarController::EndFindSession()에서 사라지게 하고 있다.
따라서 인풋 이벤트는 Show() 호출 시 이벤트와 핸들러를 등록하고, EndFindSession() 불릴 때, 제거하면 될 것같다.

그렇다면...
Chromium은 이벤트 등록/핸들러를 어떻게 할까?

원하는 이벤트의 등록을 위해 NotificationRegistrar 클래스를 제공하고 있다.
이 클래스는 content/public/browser/notification_registrar.h 에 정의되어 있다.
이벤트 핸들러구현을 위해서는 NotificationObserver 클래스를 제공하고 있고,
content/public/browser/notification_observer.h 에 정의되어 있다.

원하는 이벤트 등록을 위해서는 NotificationRegistrar 클래스를 사용해야한다.
아래 그림은 content::NotificationRegistrar 클래스 이다.
public 함수들을 보면 Add(), Remove(), RemoveAll() 등등 이벤트 등록에 필요한 함수들이
구현되어 있음을 알 수 있다.



이벤트 핸들러 등록을 처리하는 NotificationObserver 에 대해서 알아보자.
아래 소스를 보면 NotifiationObserver 는 추상클래스 이다.


핸들러 등록을 원하는 클래스에서 이 추상 클래스를 구현하면 등록한 이벤트가 발생했을때,
이 클래스의 Observe() 함수가 호출이 됨으로써 등록한 핸들러가 실행이 된다.

다음 포스트에서는 FindBarController 클래스의 Show()/EndFindSession()에서 이벤트의 등록/핸들러 구현을 해보겠다.

2012년 4월 29일 일요일

웹 표준으로 만드는 안드로이드 웹앱 (한빛미디어)



이 책의 제목을 처음 접했을 때, 웹 앱을 만드는 방법을 소개하는 책이구나 하는 생각이
들면서, 과연 안드로이드 용 웹앱은 무엇을 말하는 것인가 하는 궁금증이 생겼습니다.
그리고 큰 제목 아래 깨알같이 많은 기술을 나열하고 있습니다.
HTML, CSS, 자바스크립트, 제이쿼리, 제이큐터치, 폰갭 등의 기술들이 제목 아래 쓰여
있는데, 이렇게 200페이지도 안 되는 얇은 책이 이런 기술을 다 커버할 수 있나 하는 의구심이
들었고, 맛보기용 도서일 수도 있겠다 하는 생각이 들었습니다.
책을 읽고 나서 안드로이드 용 웹 앱은 이렇게 만드는 겁니다 라고 쉬운 말로 이야기를
들었다는 느낌이었습니다.

이 책은 웹 앱 중에서도 안드로이드(모바일) 용 웹 앱에 대한 설명을 하고 있습니다.

모바일 환경은 작은 화면, 터치 인터페이스 등등 PC에서의 웹 앱 사용 시나리오와는
많은 차이가 있습니다.
이 책은 이 차이를 어떻게 웹 앱에서 줄여줄 것인가를 가이드하고 있습니다.
물론 책 제목에 쓰인 많은 기술들(HTML, CSS, 자바스크립트, 제이쿼리, 제이큐터치, 폰갭)을
설명하고 있지만 이 기술들은 맛보기로 언급하는 수준일 뿐입니다.
물론 이러한 기술들을 잘 모르는 독자들은 이 책으로 위 기술들이 무엇인가 감을 잡을 수가
있을 것입니다.

이 책은 모바일용 웹앱 제작을 어떻게 하는것인가가 핵심으로써 위의 기술들을 잘
아는 전문가라고 하더라도 모바일 환경에서의 웹 앱 경험이없는 개발자라면 이 책을
추천하고 싶습은니다.

개정판 답게 철자 오류 등도 다른 책에 비해 작고, 번역도 상당히 매끄럽습니다.

모바일용(안드로이드) 웹 앱을 개발하고 싶다면 이 책부터 읽기를 추천드립니다!

2012년 4월 14일 토요일

Chromium Bug - 82276 (1)

소스분석을 하면서 사소한 패치들을 올려왔다.
지금까지 여러개의 패치를 올렸지만 모두 사소한 것들이라 패치라고 하기에도 민망하긴 하다.

이젠 알려진 버그 해결을 시도해볼 계획이다.
지금까진, Chromium의 구조를 다 이해하지 못해서 망설이고 있었지만,
Chromium committer가 목표를 높게 가져보라는 말을 듣고, 시도를 해볼 계획이다.

http://code.google.com/p/chromium/issues 로 가면 현재 Chromium의 버그 리포팅들을 볼 수 있고, 진행 상태를 알 수 있다.

쭈욱 살펴보니 대부분은 내용을 알수없는 버그들이다...
무엇을 골라야할지 고민하다, 그래도 버그 내용이 이해가 되는 것을 골랐다.


브라우저에서 웹페이지 내의 문자열을 찾을때, Ctrl+F 를 통해 찾게된다.
위 버그는 gmail 에서 발견된 버그인데, gmail의 경우 엮인 메일들이 많을경우,
엮인 메일중 특정 메일의 내용을 접을수가 있다.
접힌상태의 메일에 포함된 문자열을 검색하면, 접힌 상태이므로 검색이 안된다.
그런데, 접힌 메일을 풀게되도 Find Bar에 입력된 문자열이 자동으로 강조(highlight)되지 않는다.

이 버그를 잡아보려한다.

어디서 시작해야할지는 전혀 감이 안오지만, 멘땅에 헤딩해보자!

Thank you Peter & Carlos.

2012년 4월 12일 목요일

Content Shell - 분석 방향 고민..

지난 포스트에서는 ShellBrowserMain()에서 content::BrowserMainRunner 객체를 생성하고, 이
객체의 초기화(Initialize()), 실행(Run())을 통해 Content Shell이 구동되는 것을 보았다.
ShellBrowserMain()의 마지막에 실행되는 Run()이고 이 함수가 종료되면 브라우저가 종료된다.
BrowserMainRunnerImpl 객체는 BrowserMainLoop 객체를 통해 실제 브라우저의 구동을 수행하고 있다.

이제 이 단계에서 Content Shell 분석의 방향이 어디로 진행되어야할 지 고민이다.
브라우저의 실행 코드를 따라가며 분석을 해야할 지, 아니면 src/content/shell 아래의 내용에
대한 분석을 이어나가야 할 지 말이다.

고민결과, 브라우저 코드를 따라가면서(BrowserMainLoop) 분석을 하는 것이 맞을 것 같다.
Content Shell만의 내용 분석은 조금 늦어지겠지만, 브라우저 코드를 분석하다보면 브라우저의 전체 실행 흐름에 대해서도 알 수 있을 것이다.
Content Shell 부분은 Content Module에서 ContentClient를 참조하는 부분을 통해 언제 어느 부분에서 Shell이 참조되는지 알게 될 것이다.

따라서 다음 포스트부터 BrowserMainLoop 클래스에 대해 분석을 시작하겠다.

2012년 4월 4일 수요일

Content Shell - ShellBrowserMain()

앞 포스트에서 ContentMainRunner::Run()에 대해 알아보았다.

Content Shell의 main()에서 ShellMainDelegate 객체를 생성하고, ContentMain()은 파라미터로
전달된 이 객체 레퍼런스를 이용해서 Content Module의 핵심 클래스인 ContentMainRunner 객체를
생성한다.
ContentMainRunner::Initialize()에서 Content Module의 초기화가 진행되고, ContentMainRunner::Run()에서는 process_type을 ContentMainRunner::RunNamedProcessTypeMain()에 전달을 한다.


이 RunNamedProcessTypeMain()에는 전달받은 type에 맞는 함수를 호출하게되는데,  호출 전에 delegate(ShellMainDelegate) 가 있으면 delegate의 RunProcess()를 먼저 호출하고,
delegate의 RunProcess()에서 에러를 리턴하면 process_type에 매칭되는 함수를 Content Module에서 kMainFunctions[]에서 매칭되는 함수를 호출한다.

이 번 포스트에서는 delegate(ShellMainDelegate)의 RunProcess()의 동작에 대해 살펴보자.

ShellMainDelegate::RunProcess()는 type이 빈 문자열("")인 경우에만 ShellBrowseMain()을 호출한다. ShellBrowseMain()은 src/content/shell/shell_browser_main.cc 에 구현되어 있다.

이 함수는 크게 세 부분으로 나뉜다.
content::BrowserMainRunner 객체 생성(Create()), 객체 초기화(Initialize()), 객체 실행(Run()).
ContentMain()에서의 ContentMainRunner 객체와 비슷한 동일한 패턴으로 실행이 된다.
클래스 이름을 보니 ContentMainRunner 클래스가 주 Runner이고 BrowserMainRunner 가 서브 Runner 인 것같다.

BrowserMainRunner 객체 생성할 때는 delegate(ShellMainDelegate)가 전달되지는 않는다.
그렇다면 BrowserMainRunner 에서는 ShellMainDelegate를 전혀 참조하지 않을까?

ShellMainDelegate::PreSandboxStartup()에서 content::SetContentClient()를 통해 Content Module에 process_type에 해당되는 Client 를 셋팅하였다. 지금의 process_type은 빈 문자열 이므로 Content Module의 browser client 에 ShellContentBrowserClient 가 셋팅되어 있다.

BrowserMainRunner 객체가 Client(Content Shell)의 참조가 필요할 때, ShellContentBrowserClient 객체 포인터가 참조가 될 것이다.

그렇다면 언제 참조가 될까?

먼저 ShellContentBrowserClient 객체에 대해 살펴보면 이 Client 객체가 Content Module에서 어떤일을 할 것인지 알 수 있을것이다.

다음 포스트에서는 ShellContentBrowserClient 에 대해 알아보자.

2012년 4월 3일 화요일

ContentMain - ContentMainRunner (2)


지난 포스트에서는 ContentMain()에서 ShellMainDelegate 가 처음 참조되어 사용도는 부분인
ContentMainRunner 의 Initialize()를 살펴보았다.

이번 포스트에서는 ContentMain()의 핵심 호출인 ContentMainRunner::Run()에 대해 살펴보자.

아래의 ContentMain()를 보면 Run()은 Chromium 브라우저가 실행되는 동안 호출되는 함수이다.
Run()이 리턴되면 Chromium도 종료가 된다.


Run() 구현부분을 보자. 아래 소스코드에서 처럼 process type 값과, 생성한 content::MainFunctionParams 객체, 그리고 delegate_ (ShellMainDelegate)를 RunNamedProcessTypeMain()의 매개변수로 전달한다. Run()이 하는일은 이것이 전부다.


process type이 무엇일까? CommandLine(src/base/command_line.cc) 객체에서 가져오는것을 보니 command line에서 전달되는 값인것 같 Initialize()다. switches::kProcessType(src/content/public/common/content_switches.cc)은 "type" 문자열이다.
content_shell을 실행시키고 ps 를 통해 content_shell 관련 정보를 보면 다음과 같다.


총 다섯개의 프로세스가 떠있고, 두개는 type이 없으며 나머지 세개의 프로세스의 type은 각각 zygote, renderer, gpu-process 이다.

Run()에서 가져오는 type이 위의 type switch의 값이다.
사용자가 content_shell을 실행할 때는 파리미터 전달없이 실행시키므로 아래의 복잡한 파라미터들이 설정되어 실행되는 content_shell 프로세스들은 런타임이 fork()가 된것들일 것이다.

이렇게 process type이 설정이 된다음, MainFunctionParams 객체가 생성이 되는데, 이 객체는 Platform 의존적인 부분이 포함된 ComandLine의 Wrapper 객체이다.

이제 Run()에서 마지막으로 호출되는 RunNamedProcessTypeMain()에 대해 살펴보자.


RunNamedProcessTypeMain()은 process_type 으로 전달된 문자열을 가지고 kMainFunctions 배열에서 실행시킬 함수포인터를 찾는다. 지금은 빈 문자열("")이 전달되었기때문에 BrowserMain이 실행될 함수포인터에 해당된다.
이 함수가 실행되기전!!! 드디어 우리의 관심사인 delegate(ShellMainDelegate)가 참조되고 있는 것을 볼수 있다.
ShellMainDelegate::RunProcess()이 호출되고 kMainFunction[] 배열에서 process_type("")에 해당하는 BrowserMain이 호출이됨으로써 content_shell 이 실행이 되고 있다.

이번 포스트에서는 ContentMainRunner::Run() 에 대해 살펴보았다.
다음 포스트에서는 ShellMainDelegate::RunProcess()가 호출하는 ShellBrowserMain()에 대해 알아보자.

2012년 3월 28일 수요일

ContentMain - ContentMainRunner (1)

Content Shell main()의 두번째 라인이자 마지막 라인인 ContentMain() 호출이다.

int ContentMain(int argc,
                const char** argv,
                ContentMainDelegate* delegate);

ShellMainDelegate 객체를 생성한 뒤, ContentMain()를 호출하면서 Content Shell을 실행하고 있다.

이제 ContentMain()에 전달한 ShellMainDelegate 객체가 어디서 어떻게 쓰이는지 살펴보고,
클라이언트들(ShellContentBrowserClient, RendererClient, UtilityClient 등) 가
언제 어디서 참조되는지 알아보자.

ContentMain()... 긴 여정이 될 것 같은 느낌이다.

ContentMain()의 구현은 다음과 같다.



ContentMain()은 ContentMainRunner 클래스를 통해 content module의 초기화, 실행, 종료를
처리하고 있다.

사실 ContentMainRunner 는 추상클래스이고, ContentMainRunner::Create()를 통해 추상클래스를 구현한 ContentMainRunnerImpl 객체가 생성된다.

Chromium 코드를 보다보면 많은 패턴들을 만나게 되는데, 여기서는 factory method 패턴을 만난것 같다. :)


ContentMainDelegate  포인터는 Content State 를 초기화하는 Initialize()의 파라미터로 전달된다.

class ContentMainRunner {
virtual int Initialize(int argc,
                           const char** argv,
                           ContentMainDelegate* delegate) = 0;
};

Initialize()의 실제 구현부분으로 가보자.
Initialize()는 src/content/app/content_main_runner.cc 에 구현되어있다.

Initialize()에서 delegate를 참조하여 ShellMainDelegate 의 멤버함수를 호출하는 첫번째 부분이다.


BasicStartupComplete()를 호출하는 부분이다. ContentMainRunner의 아주 초기 시점에 호출이 되는 것을 알 수 있다. (ShellMainDelegate 클래스의 위 함수는 빈 함수임)

몇몇 함수들이 수행된 후 PreSandboxStartup()이 호출된다.


ShellMainDeleage 의 PreSandboxStartup()에서는 ShellContentClient 객체를 content module의 content client 등록하고 (content::SetContentClient()), process_type 에 맞는 sub client(browser client, renderer client 등)를 초기화한다.

다음으로 SandboxInitialized() 가 호출된다.



ShellMainDelegate의 SandboxInitialized()는 아무일도 하지 않는다.

ContentMainRunner의 Initialize() 에서 ShellMainDelegate 의 역할을 간단히 정리해보면,
content module에 content client 등록(ShellContentClient)과 sub client 등록인것 같다.

2012년 3월 27일 화요일

Content Shell 의 구조 - ShellMainDelegate 클래스(3)

Content Shell의 ShellMainDelegate 클래스의 구현에 대해 살펴보자.

shell_main_delegate.cc 파일을 보면 ContentMainDelegate의 많은 추상함수들이
빈 함수로 구현되어 있음을 알 수 있다.

아래는 ShellMainDelegate 클래스에서 실제로 구현한 함수들이다.
PreSandboxStartup()
RunProcess()
ZygoteForked()
InitializeShellContentClient()
InitializeResourceBundle()

PreSandboxStartup() 함수를 살펴보자.


content::SetContentClient() 함수가 이 Content Shell에서 중요한 역할을 하고 있는것 같다.
이전에 언급한 적이 있는것 같은데, Content Module이 Server가 되고, Content Module을 이용하여
구현한 브라우저가 Client가 되는형태라고 말이다.
ShellMainDelegate 클래스를 가볍게 보고나서, ContentMain() 함수를 알아볼 때
이 client 가 언제 어떻게 사용되는지 알 수 있을 것이다.

다음으로 InitializeShellContentClient() 함수를 호출하고 있다.
process_type을 파라미터로 전달해 주는데, 이 type이 무엇일까?
아래의 InitializeShellContentClient() 함수를 보면 process_type을 비교하여 type에 맞는 client 를
설정하고 있다. Content Client 도 세부적으로 browser client, renderer client, plugin client, utility client들이 필요한 것 같다.


type에 맞는 세부 클라이언트들은 content::GetContentClient()->set_XXX 함수를 이용하여 셋팅하고 있다. PreSandboxStartup() 에서 content::SetContentClient()를 이용하여 ShellContentClient 객체를 셋팅하고, content::GetContentClient()는 이 때 설정한 ShellContentClient를 리턴한다.
Content Module은 SetContentClient()를 통해 셋팅된 각각의 포인터를 통해 Embedder의 Client 객체들을 참조하게 될것이다. 여기서 Embedder가 무엇인가? 이제는 바로 답을 할 수 있어야 할 것이다!
다음으로 InitializeResourceBundle() 함수를 호출하고 있다.
이 함수는 content_shell.pak 이라는 pak 파일을 이용하여 ui::ResourceBundle에 초기화를 요청하는것 같다. 이 pak 파일은 content_shell 빌드시 생성된다.

PreSandboxStartup() 함수에 대해 알아보았다. PreSandboxStartup()은 Content Module 이 Embedder에게 Sandbox 초기화전에 할일하라고 알려줄 때 사용되는 추상함수이다. Content Module은 이 추상함수를 호출하고 Sandbox 관련 내용(?)을 초기화 할 것이다. 아마 이 부분은 content::ContentMain() 함수에서 수행될 것이다.

SandboxInitialized() 함수는 빈 함수로 보아 Content Shell 에서는 Sandbox 에 대한부분은 없는것 같다.

RunProcess() 함수를 살펴보자.
이 함수는 process type과 MainFunctionParams 라는 구조체를 파라미터로 전달받고 있고 파라미터 타입이 빈 문자열일 경우에 ShellBrowserMain() 함수를 호출하고 있다.
ShellBrowserMain() 함수는 src/content/shell/shell_browser_main.cc 에 정의되어 있고, 주석을 보면 이 함수가 Content Shell의 Main routine 임을 알 수있다. 이번 포스트는 ShellMainDelegate 클래스에 대한 이야기이므로 ShellBrowserMain은 다음에 알아보자.

마지막으로 ZygoteForked() 함수를 살펴보자. 프로세스가 fork() 된 후,  process type(switchs::kProcessType)을 보고 이 타입에 맞는 Content Client 를 InitializeShellContentClient() 함수에서 초기화하고 있다. 여기서 초기화되는 각각의 클라이언트들은 Content Module 내 프로세스들이 embedder에게 무언가 요청할 때 사용될 것이다.

지금까지 ShellMainDelegate 클래스에 대해 알아보았다. 이 클래스가 하는일은 크게 두가지로 볼 수 있겠다. 첫번째는 Content Module에서 사용되는 프로세스 타입에 맞는 Content Client 들을 초기화하는 일이고 두번째는 ShellBrowserMain을 통해 브라우저 메인 루틴을 실행하는 일이다. 이 두가지 모두 ContentMain() 이 호출되면서 그 안에서 참조되는 것들이다. 이것들의 자세한 내용은 ContentMain()에서 사용될 때 알아보자.

2012년 3월 22일 목요일

Content Shell 의 구조 - ShellMainDelegate 클래스(2)

이전 포스트에서는 ContentMainDelegate 인터페이스 클래스에 대해 알아보았다.
이제 ShellMainDelegate 클래스에 대해 살펴볼 시간이다.

ShellMainDelegate 클래스는 생성자, 소멸자, ContentMainDelegate 인터페이스 클래스의
멤버함수들, 그리고 두개의 private 멤버함수, 다섯개의 private 멤버함수로 구성되어 있다.

private 멤버함수는 다음과 같다.

InitializeShellContentClient()
InitializeResourceBundle()

private 멤버 변수는 다음과 같다.

content::ShellContentBrowserClient          browser_client_
content::ShellContentRendererClient         renderer_client_
content::ShellContentPluginClient             plugin_client_
content::ShellContentUtilityClient             utility_client_
content::ShellContentClient                     content_client_

위 변수들의 이름을 보면 ShellContentXXXClient 인 것을 알 수 있다.
여기서 잠깐,,, Client 이긴 한데 무엇의 Client 일까? Client가 있으면 Server도 있어야 하는데
여기서 Server는 무엇일까?

잠시 Content Module로 돌아가서 여기에서 말하는 Client - Server 가 무엇인지 알아보자.

Content Shell에 대한 포스트 에서 Content Shell은 Content Module을 이용한 레퍼런스
브라우저라고 잠깐 이야기를 했었다.

Chrome 이나 Content Shell 은 각각의 ContentMainDelegate 인터페이스 클래스를 구현하고
그 객체를 ContentMain() 함수에 매개변수로 전달하는것으로 브라우저가 시작이된다.

이 구조로 본다면, Content Module이 Server 가 되고, Delegate 가 Client 가 되겠다.
Content Shell 이라는 Client 가 Content Module 이라는 Server 를  이용해서 브라우저를 구성하고 있는것이다.

Content Module이 동작을 하면서 자신을 구동시킨 Client (Chrome or Content Shell)를
호출(?)하고 싶을때, 이 Delegate 내 Client 를 이용하여 함수들을 호출을 하는것이다.

Client 들이 무슨 일을 하는지, Content Module에서는 언제 Client 를 호출해서 이용하는지 궁금해진다.
차차 알아보자~^^;

다시 ShellMainDelegate 클래스로 돌아가서,,,

InitializeShellContentClient() 멤버 함수가 있다. 이름으로 미루어 이 함수를 이용해서
위에서 선언된 여러 Client 들을 초기화하지 않을까 싶다.

이것은 다음 포스트에서 실제 ShellMainDelegate 함수들의 구현을 살펴보면서 확인해보자.

2012년 3월 21일 수요일

Content Shell 의 구조 - ShellMainDelegate 클래스(1)

앞 포스트에서 Content Shell의 메인함수를 살펴보았다.
메인함수는 ShellMainDelegate 객체를 생성하고, ContentMain() 함수는 이 객체를 가지고
Content Shell을 실행하고 있다

계속 Content에 대한 이야기를 하고 있는데, 과연 Content가 무엇인가?
아래 그림은 Chromium Developer Site 에 있는 다이어그램이다.


위 다이어그램은 Chrome 에서 Content의 위치를 보여주고 있다.
(Content Shell이 아님, Chrome임)
Chrome은 Content Module이 제공하는 Content API를 이용하여 구현된 것처럼 보인다.

Developer 페이지에서 Content Module은 페이지 렌더링을 위한 Core Module이라고 기술하고 있다.
다이어그램을 보면 Content Module이 WebKit을 이용하고 있는 것을 알 수 있다.
아마도 Chrome은 페이지 렌더링은 Content API를 이용하여 페이지 렌더링을 하고
그외 다른 특징들(ex, Extension, NaCl)은 Chrome 계층에서 구현을 하고 있는 듯 하다.

Content Module은 크롬 브라우저를 구성하는 엔진(WebKit을 포함하는)이라고 보는게 맞는듯 하다.
그리고, Content Shell은 Content Module을 이용한 레퍼런스 브라우저 정도가 될 것 같다.

ShellMainDelegate 클래스 분석에 앞서 간단히 Content Module의 위치(?)가 어느쯤인지 알아보았다.

자 그럼 ShellMainDelegate 클래스를 알아보자.

src/content/shell/shell_main_delegate.h 파일을 보면, ShellMainDelegate 클래스는
content::ContentMainDelegate 인터페이스 클래스를 구현하고 있는 것을 알 수 있다.

Content Module이 정의해 놓은 ContentMainDelegate 인터페이스에 맞게 Delegate 클래스를
구현하면, Content Module을 이용한 브라우저를 구현할 수 있는 것 같다.

ContentMainDelegate 인터페이스가 정의하고 있는 함수들은 다음과 같다.

* BasicStartupComplete()
* PreSandboxStartup()
* SandboxInitialized()
* RunProcess()
* ProcessExiting()
* ZygoteStarting()
* ZygoteForked()

Mac 관련 함수를 제외하고 보니 위의 7개의 함수만 구현을 하면 나도 Content Module을 이용하여
브라우저를 만들수가 있을 것 같다~~ :)

함수 프로토타입위에 친절히 주석이 달려있긴하지만, 아직은 내용이 와닿지 않는다.
그래도 한번 살펴보자.
이해가 안가더라도 이후에 ShellMainDelegate 클래스를 구현한 부분을 보면 조금은 이해가 가지 않을까?

1. BasicStartupComplete()
주석에 따르면 이 함수를 통해 embedder에게 아주 기본적인 작업(absolute basic startup)이
끝났음을 말해준다고 한다.
embedder?? 아마 여기서 embedder는 Chrome 모듈 또는 Content Shell이 될 것 같다.
ContentMain() 함수가 호출 되면, Content Module이 초기화 작업을 진행하다 아주 기본 작업이 끝났음을 embedder(chrome, chromium or content shell)에게 알려주는 함수인 것이다.

2. PreSandboxStartup()
Content Module이 Embedder에게 Sandbox가 초기화되기 전에 필요한 일을 당장 해라라고 이 함수를 통해 알려준다.
embedder는 이 함수에서 관련 작업을 수행하면 될 것이다. 그런데 여기서 말하는 sandbox는 무엇일까?
흠... 차차 알게되겠지 ^^;

3. Sandboxinitialized()
이제 embedder는 이 함수를 통해 sandbox 초기화 이후에 수행할 수 있는 작업을 할 수 있게 있다.
sandbox 초기화 이후에 embedder가 할 일이 무엇일까? 궁금 궁금..

4. RunProcess()
Embedder에게 프로세스를 시작하라고 말한다? 이것은 무슨 말일까?
Content Module은 멀티프로세스 렌더링 구조를 가지고 있다고 말하고 있는데,
프로세스는 embedder를 통해 생성된다는 말인가??? 이해가 잘 안된다...

5. ProcessExisting()
프로세스가 끝나기 전에 호출한다고 하는데, 여기서 말하는 프로세스는 RunProcess()를 통해 생성한
프로세스 인가? 점점 궁금해진다. 얼른 ShellMainDelegate 클래스의 구현이 보고 싶어진다.

6. ZygoteStarting()
Zygote 가 나왔다. 안드로이드에서 처음 들은 단어인 Zygote...
Zygote 개념이 Chromium 에서도 사용되나보다.
Embedder에게 Zygote 프로세스가 시작되었다고 알려준단다.
이 함수의 리턴값은 ZygoteForkDelegate* 란 Delegate 로 미루어 ContentModule에서
zygote 관련일을 이 포인터를 통해 embedder에게 요청하는 것을 짐작할 수 있다.

7. ZygoteForked()
Zygote 프로세스가 fork 될 때마다 호출된다고 한다. Zygote는 자신을 자가 복제 세포 처럼 fork() 하여
다른 프로세스로 변신을 하는데, 아마도 zygote가 fork 되었으니 새로운 프로세스로 변신할 준비가 되었다고 embedder 에게 알려주는 함수인것 같은 느낌이다.

앞서 Content Module 이 엔진이라고 비유를 했었는데, 이 엔진 자체로는 브라우저가 될 수 없고,
UI 등등 브라우저 Customizaiton 이 필요한 부분들은 Content Module이 이 Delegate 를 통해
embedder에게 위임(delegate)하고 있는 것을 알 수 있다.

오픈소스를 보며 항상 느끼는 것이지만, 코드가 정말 구조화가 잘 되어 있는 느낌이다.
학생 때 오픈소스를 알았더라면... 하는 생각을 요즘들어 자주 하지만 지금이라도 늦지 않았다!!

다음 포스트에서는 이 ContentMainDeleage 인터페이스 클래스를 ShellMainDelegate 클래스가 어떻게 구현을 하고 있는지
살펴보자.

2012년 3월 20일 화요일

Content Shell의 구조 - main 함수

이번 포스트에서는 Content Shell의 main 함수가 구현되어 있는 shell_main.cc 파일을 살펴보겠다.

파일을 보면 main 함수는 단 10줄로 구현되어있다... 정말 깔끔한 코드!!
그것도 리눅스와 관련이 없는 코드들을 제외하면 단 2줄의 코드가 호출될 뿐이다.. 아름답다...ㅠ


위 코드 이미지를 보면, ShellMainDelegate 객체 인스턴스를 생성하고, ContentMain 이란 함수에
이 인스턴스를 넘겨주는 것으로 브라우저를 실행시키고 있다.

Delegate 이름이 붙은것으로 미루어 delegate 패턴이 사용하고 있음을 짐작하게 한다.
아마 ContentMain() 함수에서 플랫폼관련 초기화 부분(ex, UI)은 이 ShellMainDelegate 인스턴스에 위임(delegate) 하고 있을 것 같다.

ShellMainDelegate 클래스는 content::ContentMainDelegate 를 구현(Implement) 한 클래스이다.
Content Module은 Content Module을 기반으로 브라우저를 구현할 때 필요한 요소들을
ContentMainDelegate 인터페이스 클래스를 구현하여 ContentMain() 함수에 전달하는 방식으로 사용하도록 하고있다.

Chromium(chrome) 브라우저도 마찬가지로 ContentMainDelegate 클래스를 구현하여 구성이되어있을 것이다.

다음 포스트에서는 ShellMainDelegate 클래스에 대해 알아보자.

Content Shell


Chromium은 Content Module을 토대로 페이지 렌더링을 수행하고 있다.
즉, Content Module의 API(Content API)를 이용하여 Chromium이 구현된 것이다.

Content Module은 멀티 프로세스 렌더링이 가능하게 구현되어 있고, GPU 가속 기능도 구현되어 있다고 한다.
그렇다고 하는데, 아직은 어느 부분에서 어떻게 구현이 되어 있는지는 차차 공부해서 알아볼 계획이다.

Content Module의 분석에 앞서 Content Module 테스트를 위한 브라우저인 Content Shell을 통해 브라우저 구현을 위해
Content API 가 어떻게 사용되는지 알아보자.

Chromium은 아주 거대한 프로젝트여서 풀 빌드시 한시간은 훌쩍 넘긴다.
(물론 빌드서버 성능에 따라 다르지만... 내 노트북에서는 그렇다.)

따라서 Chromium feature 를 건드리지 않고 Content Module을 수정& 테스트하고 싶다면,
Content Shell을 통해 빠르게 빌드해서 테스트해볼 수 있다.

Chromium은 Content Shell 뿐만아니라 Test Shell도 제공하고 있다.
Test Shell은 Content Module이 아닌 WebKit을 이용하여 구현된 테스트 브라우저 처럼 보인다.
(src/webkit/tools/test_shell에 있음)
Content Shell 및 Test Shell은 Chromium을 공부하는데 좋은 시작점이 될 것 같다.

Content Shell의 코드는 Chromium의 src/content/shell에 있다.
디렉토리 내 파일들을 쭈욱 보면, 그리 많지 않은 수의 파일들로 구성이 되어 있고,
각각의 파일들의 사이즈도 그리 크지 않는 것으로 보아, Content Module을 이용하여 간단한 브라우저를 구현하는것이
그리 어려운 일은 아닌 것 처럼 보인다.

이번 포스트에서는 content shell을 빌드해서 테스트를 해보자.
Chromium을 빌드하는 방법은 이전 포스트들에서 설명을 해놓았으므로 자세한 설명은 생략하고,
다음의 명령으로 Content Shell을 빌드할 수 있다.

$> make -j4 BUILDTYPE=Release content_shell

BUILDTYPE 은 Release 와 Debug 두 타입의 선택이 가능하다.
아래는 빌드 후 실행한 화면이다. 첫 페이지로 google 홈페이지가 로딩이 되는데, Content Shell 코드 내에서
www.google.com 페이지를 로딩하고 있기 때문이다. 차차 분석해가며 초기페이지를 바꿔보기도 해보자!


다음 포스트에서는 content shell의 main 함수부터 분석을 해보자!!

2012년 3월 16일 금요일

HTML5 Canvas (한빛미디어)



"To Flash" 라는 문구로 시작되는 이 책은 HTML5 Canvas에 대한 내용을 담고 있다.

캔버스(canvas)에 수많은 기능들이 있겠지만 이 책은 그중에서 2D 에 대한 내용을 중점적으로 다루고 있다.
선그리기, 도형그리기, 이미지로딩, 색칠, 문자 출력, 회전변환, 확대 축소 등등의 기본적인 2D 에 대한 기능 설명과 함께,
캔버스에서 비디오와 오디오가 어떻게 사용되는지에 대한 내용도 포함하고 있다.
이러한 기능들을 이용하여 간단한 웹앱 게임을 만들어가면서 설명하고 있어서 지루하지 않게 배울수가 있었다.
책의 뒷부분에서는 맛보기로 WebGL, ElectroServer에 대한 설명을 언급하고 있는데,
독자들이 이 책을 읽고 스스로 더 공부를 하도록 가이드를 주는것 같아서 좋았다.

이 책을 선택한 이유 중 하나는 "어떻게 html과 javascript로 웹앱을 만들지?" 하는 궁금함 이었다.
그만큼 나는 html과 javascript에 대한 지식이 거의 없었다.
하지만 이 책은 캔버스를 사용하는데 필요한 기본 html, javascript에 대한 배경설명으로 시작되기 때문에,
책의 예제코드를 이해하는데 크게 어려움이 없었던 것 같다.
이 책은 예제 소스코드 한줄 한줄에 대해 자세히 설명하고 있고,
매 챕터마다 풀 소스코드를 수록하고있어서 책만 읽더라도
캔버스를 이용하여 html을 어떻게 구현을 하는지에 대한 감을 잡을 수 있었다.

하지만 예제로 사용되는 코드가 중복되는 부분이 많아서 불필요하게 책이 좀 두꺼워진 것 같다.
그렇지만 나와같은 초보자들에게는 더 도움이 되는 부분이다.

번역서는 보통 문맥이 매끄럽지않게 번역되는 부분때문에 개인적으로 원서를 고집하는 경향이 있었지만,
이 책은 깔끔한 번역으로 번역서라고 느껴지지 않을 정도로 글을 읽는데 문제가 없었다.

이 책을 꼼꼼히 읽는다면 웹앱을 만들 채비를 갖출 수 있을것이다.

나만의 멋진 웹앱으로 크롬의 웹앱스토어나 아이튠스 앱스토어에 등록해보자.

http://www.hanb.co.kr/trackback/978-89-7914-898-5