크로미움

EdLee·2023년 7월 2일

빌드

  • 소스 코드 다운

    	- https://chromium.googlesource.com/chromium/src.git
  • 빌드 및 디버그 도구 준비

    	- https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/get_the_code.md
    	- 비주얼 스튜디오 2022
    	- sdk 10.0.22621.0
  • fetch chromium 시 유의사항

    	- --no-history 플래그 줄 것
    		- 과거 히스토리 필요없으므로..
    		- git 다운 시 3시간 걸릴 걸 1시간으로 줄여줌
  • 빌드 시 유의사항

    	- 환경 변수 관련
    		- depot_tools를 python, git보다 상위에 둘 것
    		- 환경 변수에 vs 설치 경로 추가 필요
    			- vs2022_install = C:\Program Files\Microsoft Visual Studio\2022\Professional
    			- 없으면 빌드 시도 시 vs 설치가 안되어 있다며 빌드 실패함
    	- 불필요한 옵션은 끌 것
    		- 예) js 디버깅할 것 아니면 v8 디버그 기능은 끈다
    			- v8_symbol_level = 0
    		- 첫 빌드에 48시간 정도 걸리므로 미리 계획을 잘 세울 것
    			- 재빌드는 코드 수정 범위에 따라 10분 ~ 1시간 정도 걸림
    		- 소스 및 빌드 결과물 용량 : 약 140GB
  • 코드 수정 시 유의사항

    	- 크로미움 전체 코드를 한꺼번에 열었다간 VS 또는 PC가 다운됨
    	- 가급적이면 전체 코드는 다음 경로에서 확인
    		- https://chromium.googlesource.com/chromium/src.git/+/refs/heads/main/third_party/blink/public
    		- 검색하고 싶으면,
    			- https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:third_party/blink/renderer/core/layout/layout_object.h;drc=7c39b16ecfc42db65732d1a011621edd79e2df8f;l=4205
    	- 수정하려면 모듈별로 열어서 수정할 것
    		- ex) 블링크만 열어서 수정

크로미움 소스 구조

전체

폴더내용
android_webviewAndroid 소프트웨어의 web view를 나타내는 부분을 간략화해 interface로 제공해주는 소스들을 모아둔 곳
appsChrome 관련된 packaged app들이 존재
baseChromium 내 모든 프로젝트에서 공유되는 부분
base 디렉토리에는 일반적인 string이나 일반적인 utility들
buildBuild 와 연관된 설정들이 존재
ccChromium 내 compositor를 담당하는 부분
chrome디렉토리 이름과 같이, chrome 브라우저 내 필요한 것들이 존재
componentContent module이 가지고 있는 모듈들이 저장된 곳
가장 최상위에서 사용되는 부분들이 여기에 저장되며, 공용으로 사용되는 것들이 많음
contentsandbox로 구성된 browser process에서 multiprocessing 동작을 수행할 수 있는 핵심 코드
deviceCross-platform
netnetwork 라이브러리
skia + third_party/skiaGoogle에서 개발한 skia graphic library가 존재하는 부분
sql파일기반 데이터 베이스인 SQLite의 wrapper함수들이 존재하는 부분
third_party크고 작은 외부 라이브러리들이 존재하는데, 여기에는 압축, 디코딩, web engine 부분들이 존재(당시, web engine은 webkit에서 fork한 후 third_party directory에 두고 수정한 방식으로 진행해왔다. 특히 blink에 주목, web engine은 blink 부분에 존재)
…/blink/renderer
 - web engine의 핵심.
 - HTML을 화면에 뿌려주는 역할
ui/gfx공유 graphic class 들이 존재한다. Chromium의 UI를 기반의 형태를 갖추고 있음
ui/viewsUI 관련된 모든 것들이 존재
event 처리, UI 개발, rendering을 위한 부분
urlGoogle의 open source인 url parsing 동작을 하는 소스코드
v8Javascript 파싱 및 동작을 위한 부분들을 담당한다. v8엔진도 구글에서 오픈소스로 제공되고 있음

블링크 엔진

폴더설명
third_party/blink/Chromium 소스 코드 내 Blink 렌더링 엔진의 기본 디렉터리
renderer/코어 레이아웃, 렌더링 및 페인팅 등 대부분의 렌더링 코드가 여기 있음
 core/: DOM, CSS 및 레이아웃 개체 등 렌더링 엔진 전체에서 사용되는 클래스들 포함
 platform/: 그래픽, 네트워크 및 입력 등
 modules/: WebRTC, WebUSB 및 WebBluetooth 등의 모듈들
public/Blink 렌더링 엔진용 공개 API. mojom 등
common/렌더러와 브라우저 프로세스 간의 공유 코드.(주로 웹 플랫폼 기능 및 렌더러와 브라우저 간의 통신과 관련된 코드가 있다고 하는데 readme가 충분하지 않아보임..)
controller/입력 처리, 탐색, 스레드 등 컨트롤러에 대한 코드
bindings/웹 API를 V8 JavaScript 엔진 및 웹 IDL(인터페이스 정의 언어)과 같은 기본 C++ 구현에 바인딩하기 위한 코드
gpu/GPU 가속 및 WebGL 렌더링과 관련된 코드
web_tests/레이아웃 테스트 코드

레이아웃 매니저

  • 크로미움의 ui를 그리는데에 사용되는 클래스
  • blink의 layout과 유사한 구조지만, 렌더트리를 만들지 않고 즉시 렌더링한다는 특징이 있음
폴더설명
ui/view/layout/레이아웃 매니저와 렌더트리를 거치지 않는 노드들의 레이아웃
ui/view/layout/layout_manager.hui를 그리기 위해 레이아웃을 관리하는 클래스
ui/view/layout/table_layout.hui 요소를 테이블 형식으로 그리기 위해 사용됨
ui/view/layout/fill_layout.hui 요소를 사용 가능한 공간에 맞게 크기를 조절하여 중앙ㅇ에 배치하는데 사용됨
ui/view/layout/box_layout.h특정 행이나 열 위치에 ui요소를 배치하는데 사용됨
ui/view/layout/animating_layout_manager.h특정 레이아웃의 애니메이션에 사용됨

크로미움의 시작

크로미움의 시작

  • 각 OS 별 시작 함수들

    	- src/chrome/app/chrome_exe_main_win.cc
    	- src/chrome/app/chrome_exe_main_mac.cc
    	- ...등
  • 윈도우 시작함수

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE prev, wchar _t*, int);
  • 함수 정의

    	- APIENTRY : 매크로. 윈도우용으로 빌드된 크로미움이라면 "WINAPI"가 들어감
    	- hInstance : 현재 인스턴스 핸들러
    	- prev : 이전 인스턴스 핸들러. 일반적인 호출 시엔 nullptr로써, 사용되지 않는 변수
    	- _t\* : 커맨드 라인으로 받는 인수. 일반적인 호출 시엔 nullptr로써, 사용되지 않는 변수
    	- int : 항상 0이고, 사용되지 않음. (변수명이 없음)
    		- 단, remote용 진입점 등 다른 곳에 구현된 wWinMain에서는 사용하고 있었음..
      
    • remote 등 특정한 상황에서는 2,3,4번째 인자가 사용되지만, 일반적인 실행 시에는 첫번째 인자 외에는 사용되지 않는 것으로 보인다.
  • 함수 동작

    • base::CommandLine::Init() : 커맨드 라인 초기화하고, 프로세스의 종류를 가져옴. 상단의 코드들을 참고해보면 여기서는 브라우저 프로세스를 가져올 것으로 예상됨
    • GetModuleHandle() : 윈도우라면 이 함수에서 GetModuleHandleW 함수를 호출하며, 현재 프로세스의 관련 라이브러리들의 핸들러를 가져옴
    • 프로세스 유형에 따른 처리
      • CrashpadHandler() : 오류 처리
      • FallbackCrashHandler() : 커맨드 라인 인자로부터 뭔가를 받아서 처리하는데, 현재는 프로그램의 첫 시작이므로 nullptr이 전달되므로, 이 경우는 없을 것
    • AttemptFastNotify() : nullptr 전달하므로, 이 케이스 발생안함
    • RemoveAppCompatFlagsEntry() : 목적이 뭔지는 모르겠지만, 윈도우에 설정된 호환성 레지스트리 항목을 명시적으로 무시하는 함수로 보임.
    • MakeMainDllLoader() : 라이브러리들을 실행시킴. 여기서 부터 본격적으로 프로그램의 실행.
      - MainDllLoader::Launch() 함수가 호출됨

라이브러리 로드

  • MainDllLoader::Launch()
    • 프로세스 타입 체크
      • empty() : 브라우저
      • !kNoSandbox() : 샌드박스
    • 프로세스가 브라우저거나 샌드박스라면, 샌드박스 초기화
    • Load(&file) : DLL 들을 로딩
    • chrome_main() : 각 DLL의 ChromeMain이라는 함수를 호출
      • 프로세스 타입에 따라서 함수명이 달라짐
      • BrowserMain()
      • RendererMain()

브라우저 프로세스

크로미움 프로세스의 종류

  • 브라우저(outer browser process)

    	- 사용자 입력, 브라우저 UI, 다른 프로세스들 총괄
  • GPU

    	- 렌더러가 생성한 skia 속성(그래픽 API)들을 받아서 픽셀을 칠함
    	- GPU 프로세스가 렌더러와 분리된 이유
    		- skia 또는 OpenGL 오류를 렌더러와 분리하여 렌더러를 보호
    		- 렌더러 프로세스는 CPU에 집중, GPU 프로세스는 GPU에 집중해 병렬로 동작하여 성능 향상
  • 유틸리티

  • 탭 프로세스(렌더러 프로세스, tab-specific renderer process)

    	- 해당 페이지 이름을 가진 프로세스
    	- 일반적으로 탭 한 개당 프로세스 1개
    		- 탭 별로 프로세스를 독립시켜 보안 강화(sandbox Model) => 보안, 안정성 강화
    	- Blink 엔진
    		- 페이지 렌더링 담당
  • 예비 렌더러

  • 서브 프레임

BrowserMain

  • 프로그램 시작 시점에 실행되는 프로세스는 브라우저 프로세스이므로, DLL에서 실행하는 함수도 당연히 BrowserMain()
  • 브라우저 프로세스는 크로미움 그 자체이며 outer browser process라고 한다. 반면 렌더링 프로세스는 브라우저 내의 웹 페이지 부분이며, 브라우저 프로세스에 의해 관리됨
  • base::CurrentProcess::GetInstance().SetProcessType(base::CurrentProcessType::PROCESS_BROWSER) : 현재 프로세스를 브라우저 프로세스로 설정
  • std::unique_ptr main_runner(BrowserMainRunnerImpl::Create()) : 프로세스 객체 생성
  • main_runner->Initialize(std::move(parameters)) : 컴포넌트와 시스템 초기화
  • main_runner->Run() : 브라우저 프로세스 실행. shutdown이 true가 될 때까지 무한 반복

main_runner->Initialize

  • initialization_started_ : 브라우저 초기화가 한 번만 수행되도록 확인하는 플래그
  • SkGraphics::Init() : Skia 그래픽 라이브러리 초기화
  • gfx::InitializeFonts() : 시스템 글꼴 초기화
  • BrowserMainLoop : 브라우저의 메인 이벤트 루프를 생성하고 초기화. 메인 루프는 이벤트 처리와 프로세스 간 메시지 전달을 관리. 컴포넌트 들도 이 때 초기화
    • main_loop_->Init()
    • main_loop_->CreateStartupTasks() : 브라우저 시작 시 수행할 작업들 생성. OS 별 설정과 브라우저의 주요 스레드를 생성.
  • ui::InitializeInputMethod() : 사용자의 입력 방법 초기화(키보드, 마우스 관련 인듯)

main_loop_->init()

  • StartupDataImpl startup_data = static_cast<StartupDataImpl>(parameters_.startup_data.get()) : startup 데이터가 있다면, 관련 스레드나 Mojo IPC suppor를 초기화
  • parts_ = GetContentClient()->browser()->CreateBrowserMainParts(!!parameters_.ui_task) : 브라우저의 주요 구성 요소들을 초기화. 메인 이벤트들을 처리하고 ui 작업도 이 다음에 수행
  • ContentBrowserClient 클래스 참고

ContentBrowserClient

  • CreateBrowserMainParts()가 정의된 추상 인터페이스
  • 종류
    • ChromeContentBrowserClient : 브라우저 프로세스 제어
    • ViewsContentBrowserClient : 데스크탑 UI 프레임 워크, 드디어 ui 그리는 부분 나옴.
    • ShellContentBrowserClient : ...모르겠음

렌더링 파이프 라인

렌더링 파이프라인

  • 일반적인 브라우저의 렌더링 파이프라인(간략)
  • 크로미움의 렌더링 파이프라인(상세)

  • 렌더 프로세스

    	- Main Thread
    		- Parse, DOM&Style, Layout, Prepaint, Paint, Layerize
    	- Main Thread Helper
    	- Compositor Thread
    		- Commit, Tiling, Raster, Quad, Activate
    		- + 스크롤, 애니메이션 등 병렬로 처리할 기능들
    	- Compositor Thread Helper
    	- 기타
    		- web Worker
    		- Media, Audio etc..

렌더링 파이프라인 : 메인 스레드

  • DOM을 Blink가 파싱해 GPU에서 처리할 수 있는 명령어로 변환하는 작업
  • 시작 요건 : HTML, 사용자 상호작용, JS, Animation, 스크롤 등
  • Parse

    - 파싱 과정을 거쳐 최종적으로 "DOM" 트리 생성
    - parse 동작 도중 중단이 되는 이유 => https://sia.codes/posts/render-blocking-resources/
  • DOM&Style

    	- DOM : Document Object Model, 트리구조 형태의 HTML Tag 노드 리스트
    • Style : CSS
  • Layout

    	- DOM 노드들의 위치와 크기 정보를 조회해 렌더트리를 만든다
    	- LayoutObject* LayoutObject::CreateObject(Element* element, const ComputedStyle* style, LegacyLayout legacy)
    		- element : Dom node
    		- style : ComputedStyle
    	- Dom node + Css = LayoutObject
  • Prepaint

    - paint 준비, 레이어를 위한 과정
    	- 각 레이어에 적용할 효과들인 Property Tree 생성
    	- Property Tree : 레이어마다 적용할 효과를 나타내는 트리
    		- 참고 : https://developer.chrome.com/articles/renderingng-data-structures/
    - 레이어 : 화면에 보이는 요소들을 그룹 별로 묶은 것. 최종적으로 하나의 레이어로 합쳐짐
  • Paint

    	- 앞서 계산된 위치, 크기 정보(render tree)와 스타일 정보(property tree)로 렌더링 함수 draw를 호출
    	- 각 레이어 별로 있던 property tree는 레이어가 묶이며 displayItems로 병합됨
    	- 참고 : https://debugger.skia.org/
    		- 위 사이트에서 displayItems의 항목으로 결과를 확인해볼 수도 있음
  • Layerize

    	- 앞서 묶인 묶여진 레이어들의 목록 **Composited Layer List** 생성
    	- 렌더링은 각 레이어 단위로 이뤄짐

렌더링 파이프라인 : 합성 스레드(Compositor Thread)

  • 합성

    	- 웹페이지의 각 요소를 레이어 별로 분리하여 각 레이어 별로 픽셀을 그리는 기능
    	- 레이어 단위로 다시 그릴 수 있는 등 성능 상 이점이 있음
    	- 레이어들이 최종적으로 하나의 웹페이지로 합쳐지기 때문에 합성이라 부르는 것으로 보임
    	- translate처럼 합성만 일어나는 애니메이션 css는 성능이 뛰어남
  • commit

    	- composited Layer List가 Property Tree와 함께 합성 스레드로 복사됨
    	- 여기서 blink 엔진의 역할이 끝남
    	- 멀티 버퍼링 패턴
    		- Recyle Tree : 캐싱용 트리
    		- Pending Tree : 작업 중인 트리(현재 프레임)
    		- Active Tree : 작업 완료된 트리(이전 프레임)
    	- 메인스레드에서 HTML Tag를 파싱하고, JS 처리하는 등 바쁘더라도, commit은 메인스레드로부터 렌더링에 필요한 tree를 복사해 별도로 사용하기 때문에 화면에 끊임없이 렌더링을 할 수 있음
  • Tiling

    	- Tile(타일)은 Raster의 작업단위
    	- 레이어를 작은 타일로 쪼개는 과정
    	- 이로써 스크롤 밖에 있는 Tile의 Raster는 생략 가능
  • Raster

    	- Tile별로 draw 명령을 호출 -> GPU 메모리에 비트맵 저장
    	- GPU 하드웨어 가속기능을 사용한다면, Raster 과정에서 GPU가 연산을 대신해주기도 함
  • Quad

    	- Raster된 타일의 위치에 대한 정보를 가짐
    	- Draw Rect, Draw Text -> Draw Quad 변경
  • Activate

    	- Layer list -> (commit) -> pending tree -> (Raster) -> (Quad) -> Active Tree
    • activate된 quad들은 합성 프레임(Compositor Frame)으로 묶여 GPU에 전송됨

렌더링 파이프라인 : GPU 프로세스

  • Aggregate

    	- Compositor Frame, 브라우저 프로세스(UI), 렌더러 프로세스(다른 탭, iframe) 등 화면에 그려야할 모든 프로세스가 집합
  • Draw

    	- GPU 메인 thread에 올려진 skia 라이브러리에서 draw quad 명령이 수행되며 화면에 픽셀이 그려짐
    	- 렌더링 파이프 라인 종료

블링크 엔진 동작 - SPAN 렌더링

관련 소스

  • src/third_party/blink/renderer/core/html/: 문단이나 개체 등을 비롯한 핵심적인 HTML 요소들이 여기 있음

  • src/third_party/blink/renderer/core/layout/: 레이아웃 개체에 대한 코드

    	- layout_text.cc
    	- layout_inline.cc
    	- layout_box.cc
    	...등
  • "레이아웃 객체"를 skia로 전달하여 렌더링하게 된다

    	- 예전에는 블링크 엔진에 다음 파일들이 있었는데, webkit으로부터 탈피하면서 변경됨
    	- HTMLTableElement.cpp -> html_table_element.cc
    	- LayoutTable.cpp -> ng/table/layout_ng_table.cc

블링크 동작의 개요

레이아웃 객체

  • 파싱된 요소가 skia에서 렌더링할 수 있는 모델이 된 것

  • 렌더링 트리를 표현할 수 있는 노드 구조에 필요한 멤버들

    	- Node* node_: 레아아웃 객체의 DOM 노드 포인터
    	- LayoutObject* parent_: 렌더링 트리의 부모 레이아웃 객체에 대한 포인터
    	- LayoutObject* previous_sibling_ 및 LayoutObject* next_sibling_: 렌더링 트리에서 형제를 가리키는 포인터
    	- LayoutObject* first_child_ 및 LayoutObject* last_child_
    		- 렌더링 트리에서 레이아웃 객체의 첫 번째 및 마지막 자식을 가리키는 포인터
    	- scoped_refptr<const ComputedStyle> style_: 참조 횟수가 계산되는 스마트 포인터
  • 레이아웃 위치 관련 멤버들

    - LayoutBoxModelObject* container_: 레이아웃 개체의 위치와 크기를 계산하는데 사용되는 기준이 됨
    	- LayoutUnit x_, y_, width_, height_: 레이아웃 개체의 위치(x 및 y 좌표)와 사이즈(너비 및 높이)
    	- LayoutRect visual_overflow_rect_ 및 LayoutRect layout_overflow_rect_
    		- 레이아웃 객체가 자시느이 영역을 넘어가는 경우를 표현
    		- 비주얼 오버플로는 그림자나 윤곽선 등, 레이아웃 오버플로는 텍스트나 이미지가 경계를 넘어가는 것을 표현
    	- LayoutObject* previous_in_flow_ 및 LayoutObject* next_in_flow_: inline 요소를 표현하는 것처럼 보임
    	- bool is_self_needs_layout_ 및 bool is_child_needs_layout_
    		- 자신 또는 자식 객체에 레이아웃을 업데이트 필요한지 여부

<span>의 처리 과정

1. 태그 파싱

  • 참고 위치 : third_party/blink/renderer/core/html/parser/html_document_parser.cc

    • HTMLDocumentParser 클래스 참고
  • 동작 과정

    • HTMLParserBase : HTMLDocument 문서가 로딩되면 이를 파싱하기 위해 초기화됨
    • HTMLTokenizer : HTML 콘텐츠들의 구조를 식별하고 토큰화
    • HTMLTreeBuilder : 토크나이저로 받은 토큰을 DOM 트리로 구성
    • HTMLScriptRunner : 파싱 중에 자바스크립트를 만나면 실행됨

2. 렌더 트리 구성

  • 참고 위치 : third_party/blink/renderer/core/dom/node.cc

    • HTMLDocumentParser 클래스 참고
  • 동작 과정

    • AttachLayoutTree(AttachContext& context) : 태그 파싱 중 새로운 element를 만나면 이 함수가 실행됨. 함수 마지막에 레이아웃 트리에 요소를 추가하며 종료되므로, 블링크의 시작과 끝을 담당한다고 봐도 무방함
    • CreateLayoutObject() : 인터페이스. 각 element별로 존재하여 자체 처리됨. 처리가 완료되면, LayoutInline, LayoutBlockFlow와 같은 레이아웃 개체를 생성

3. 레이아웃 객체 생성

  • 참고 위치 : third_party/blink/renderer/core/layout/layout_object_factory.cc

  • LayoutObjectFactory 클래스 참고

    • CreateLayoutObject()

    •   LayoutObject* LayoutObject::CreateObject(Element* element,
                                               const ComputedStyle& style,
                                               LegacyLayout legacy) {
        DCHECK(IsAllowedToModifyLayoutTreeStructure(element->GetDocument()));
       
        // Minimal support for content properties replacing an entire element.
        // Works only if we have exactly one piece of content and it's a URL, with
        // some optional alternative text. Otherwise acts as if we didn't support this
        // feature.
        const ContentData* content_data = style.GetContentData();
        if (!element->IsPseudoElement() &&
            ShouldUseContentDataForElement(content_data)) {
          LayoutImage* image = MakeGarbageCollected<LayoutImage>(element);
          // LayoutImageResourceStyleImage requires a style being present on the
          // image but we don't want to trigger a style change now as the node is
          // not fully attached. Moving this code to style change doesn't make sense
          // as it should be run once at layoutObject creation.
          image->SetStyleInternal(const_cast<ComputedStyle*>(&style));
          if (const StyleImage* style_image =
                  To<ImageContentData>(content_data)->GetImage()) {
            image->SetImageResource(
                MakeGarbageCollected<LayoutImageResourceStyleImage>(
                    const_cast<StyleImage*>(style_image)));
            image->SetIsGeneratedContent();
          } else {
            image->SetImageResource(MakeGarbageCollected<LayoutImageResource>());
          }
          image->SetStyleInternal(nullptr);
          return image;
        } else if (element->GetPseudoId() == kPseudoIdMarker) {
          return LayoutObjectFactory::CreateListMarker(*element, style, legacy);
        }
       
        switch (style.Display()) {
          case EDisplay::kNone:
          case EDisplay::kContents:
            return nullptr;
          case EDisplay::kInline:
            return MakeGarbageCollected<LayoutInline>(element);
          case EDisplay::kBlock:
          case EDisplay::kFlowRoot:
          case EDisplay::kInlineBlock:
          case EDisplay::kListItem:
            return LayoutObjectFactory::CreateBlockFlow(*element, style, legacy);
          case EDisplay::kTable:
          case EDisplay::kInlineTable:
            return MakeGarbageCollected<LayoutNGTable>(element);
          case EDisplay::kTableRowGroup:
          case EDisplay::kTableHeaderGroup:
          case EDisplay::kTableFooterGroup:
            return MakeGarbageCollected<LayoutNGTableSection>(element);
          case EDisplay::kTableRow:
            return MakeGarbageCollected<LayoutNGTableRow>(element);
          case EDisplay::kTableColumnGroup:
          case EDisplay::kTableColumn:
            return MakeGarbageCollected<LayoutNGTableColumn>(element);
          case EDisplay::kTableCell:
            return MakeGarbageCollected<LayoutNGTableCell>(element);
          case EDisplay::kTableCaption:
            return MakeGarbageCollected<LayoutNGTableCaption>(element);
          case EDisplay::kWebkitBox:
          case EDisplay::kWebkitInlineBox:
            if (style.IsDeprecatedWebkitBoxWithVerticalLineClamp()) {
              return MakeGarbageCollected<LayoutNGBlockFlow>(element);
            }
            UseCounter::Count(element->GetDocument(),
                              WebFeature::kWebkitBoxWithoutWebkitLineClamp);
            return MakeGarbageCollected<LayoutNGFlexibleBox>(element);
          case EDisplay::kFlex:
          case EDisplay::kInlineFlex:
            UseCounter::Count(element->GetDocument(), WebFeature::kCSSFlexibleBox);
            return MakeGarbageCollected<LayoutNGFlexibleBox>(element);
          case EDisplay::kGrid:
          case EDisplay::kInlineGrid:
            UseCounter::Count(element->GetDocument(), WebFeature::kCSSGridLayout);
            return MakeGarbageCollected<LayoutNGGrid>(element);
          case EDisplay::kMath:
          case EDisplay::kBlockMath:
            return MakeGarbageCollected<LayoutNGMathMLBlock>(element);
          case EDisplay::kLayoutCustom:
          case EDisplay::kInlineLayoutCustom:
            return MakeGarbageCollected<LayoutNGCustom>(element);
        }
       
        NOTREACHED();
        return nullptr;
      }

      - 디스플레이 속성에 따라서 처리됨 (span은 보통 EDisplay::kInline)
      - 스위치 문에서 "kInline"일 경우, MakeGarbageCollected(element) 가 수행

    • MakeGarbageCollected(element)

      • 새로운 LayoutInline 객체를 생성하고 제공된 element로 초기화
      • 생성된 LayoutInline 개체는 가비지 컬렉터에서 관리하여, 불필요할 때 제거됨
    • LayoutInline 개체 생성 과정
      - 생성자 함수

      LayoutInline::LayoutInline(Element* element)
        : LayoutBoxModelObject(element), line_boxes_() {
        SetChildrenInline(true);
      }
      • LayoutInline::Layout()

        - LayoutInline 개체 및 자식에 대한 레이아웃 프로세스 시작
      • LayoutBlockFlow::LayoutInlineChildren()

        - LayoutBlock의 하위 클래스. LayoutInline 개체를 포함하여 인라인 하위 요소의 레이아웃 처리
        	- LayoutBlockFlow::Layout()
        	- LayoutBlockFlow 개체 및 자식의 레이아웃 프로세스 시작
        - LayoutBlockFlow::LayoutBlock()
        	- 하위 블록 수준에 있는 요소의 레이아웃 처리
        - LayoutBlockFlow::LayoutInlineChildren()
        	- LayoutBlockFlow 내에서 줄 바꿈, 플로팅 및 고정된 위치를 고려하여 인라인 요소인 자식들을 배치
      • LineLayoutAlgorithm 클래스

        - 줄에서 현재 가용한 너비 및 스타일 조건에 맞춰서 line에 inline element를 배치하거나 line break 수행
        - LineLayoutAlgorithm::LayoutLine()
        	- 인라인 콘텐츠의 한 줄을 배치
        	- 라인 내에서 인라인 요소의 위치를 결정하고 필요에 따라 line break
        - LineLayoutAlgorithm::ComputeInlinePositions()
        	- 텍스트 정렬, 맞춤 및 기타 스타일 규칙을 가지고 라인 내 인라인 요소의 수평 위치를 계산
        - LineLayoutAlgorithm::BreakLine()
        	- 가용한 너비 및 스타일에 따라 line break 결정

4. 스타일 적용

  • 참고 위치 : third_party/blink/renderer/core/style/computed_style.h

    • ComputedStyle 클래스
  • 동작 과정

    - ComputedStyle::Create() : 스타일 객체의 시작과 끝
    - StyleResolver : 스타일이 결정되는 객체. 일단 이 객체가 초기화되며 스타일 계산이 시작됨
    	- MatchAuthorRules() : 스타일 시트를 읽고 지정된 요소에 일치하는 css 규칙을 찾아 매칭
    	- CascadeAndApply()
    		- 스타일들의 우선순위 적용
    		- !important가 여기서 처리됨
    	- SetComputedStyle() : 계산된 스타일을 dom트리에 저장
    		- 저장 위치는 Element::InlineStyle() 메서드 참조

5. 레이아웃 트리 삽입

  • 다시 node.cc로 돌아와서 AttachLayoutTree() 함수가 종료되며 레이아웃 트리에 삽입 완료
    void Node::AttachLayoutTree(AttachContext& context) {
    DCHECK(GetDocument().InStyleRecalc() || IsDocumentNode());
    DCHECK(!GetDocument().Lifecycle().InDetach());
    DCHECK(!context.performing_reattach ||
           GetDocument().GetStyleEngine().InRebuildLayoutTree());
    
    LayoutObject* layout_object = GetLayoutObject(); // 현재 노드와 관련된 레이아웃 객체를 가져옴
    DCHECK(!layout_object ||
           (layout_object->Style() &&
            (layout_object->Parent() || IsA<LayoutView>(layout_object)))); // 레이아웃 객체가 있으면 스타일이 있는지, 레이아웃의 부모 자식 관계를 확인
    
    ClearNeedsReattachLayoutTree(); //
    
    if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache())
      cache->UpdateCacheAfterNodeIsAttached(this);
    
    if (context.performing_reattach)
      ReattachHookScope::NotifyAttach(*this);
    }

third_party/blink/renderer/core/html/html_table_element.cc
third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h

0개의 댓글