[Browser] How browser works (5) - Layout

김기완·2021년 2월 18일
0

How Browser Works?

목록 보기
6/7

이 글은 Tali Garsiel의 "How Browsers Work: Behind the scenes of modern web browsers"를 번역한 글입니다.

Layout

Renderer 가 생성되어 트리에 추가될 때는 위치나 크기에 대한 정보를 가지고 있지 않습니다. 위치, 크기 값을 계산하는 과정을 Layout 혹은 Reflow 라고 부릅니다.

HTML은 위치, 크기 값을 단 하나의 경로를 통해 계산할 수 있는 Flow based layout model 을 사용합니다. 나중에 배치되는 요소가 flow에 있다면, 이미 배치된 요소에 영향을 미치지 않기 때문에, 글을 읽는 것과 마찬가지로 단 하나의 경로를 통해 계산할 수 있습니다. 그러나, table 과 같은 요소는 하나 이상의 경로가 필요합니다.

좌표계는 좌측 상단을 기준으로 하여 좌표를 계산합니다.

Layout<html> 에 해당하는 Root renderer 부터 시작해서, 프레임에서 renderer 가 필요한 좌표를 재귀적으로 계산합니다.

Root renderere 의 좌표는 (0, 0) 이고, 크기는 viewport 입니다. viewport 는 브라우저에서 보이는 부분의 크기를 의미합니다.

모든 rendererlayout 혹은 refelow 메소드를 가지고 있고, 배치해야 할 자식의 layout 메소드를 호출합니다.

Drity bit system

변화가 생길 때마다 모든 elementlayout 을 방지하기 위해서, 브라우저는 dirty bit 를 사용합니다. 추가되거나 변화가 있는 renderer 와 자식들은 dirty 로 표시합니다.

dirty, 그리고 rendererlayout은 필요하지 않지만 자식의 layout 필요한 children are dirty 플래그가 있습니다.

Global and incremental layout

Render tree 전체에 layout 이 발생하는 것을 Global layout 이라고 부릅니다. 이 현상은 아래의 이유로 발생합니다.

  1. 폰트 크기 변화와 같은 모든 Renderer 에 영향을 주는 스타일의 변화
  2. 스크린 resize 이벤트

Incremental layoutrendererdirty 할 때 비동기적으로 발생합니다. 예를 들어, 새로운 element 가 네트워크로부터 로드되어 DOM 트리에 추가된 후, rendererrender tree 에 추가 될 때 발생합니다.모
Incremental layout

Incremental layout

Asynchronous and Synchronous layout

Incremental layout 은 비동기적으로 일어납니다. Firefox는 reflow commands 를 모으고, 스케쥴러 이를 한 번에 실행합니다. Webkit은 incremental layout 을 실행시키는 타이머가 존재합니다. 타이머가 종료되면, 트리를 순회하면서 dirty rendererlayout 합니다.

"offsetHeight" 와 같은 스타일 정보가 필요한 스크립트는 incremental layout 을 동기적으로 실행시킬 수 있습니다.

Global layout 은 대부분 동기적으로 실행됩니다.

layout 은 스크롤 위치와 같은 속성의 변화로 인해서 첫 layout 이후 콜백으로 다시 실행될 수 있습니다.

Optimizations

resizerenderer 의 위치 변화에 의해 layout 이 발생하면, renderer 의 크기는 캐싱된 값을 사용합니다.

텍스트 필드에 텍스트가 추가된 상황처러머 변화가 주변에 영향을 미치지 않는 경우에는 하위 트리만 수정되고 루트로부터 layout 이 발생하지 않습니다.

The layout process

layout 은 아래와 같은 형태로 진행합니다.

  1. 부모 renderer 는 자신의 width 를 결정합니다.
  2. 부모가 자식을 검토하고
    1. 자식 renderer 의 좌표를 계산합니다.
    2. dirty 하거나 Global layout 인 경우, 자식의 layout 메소드를 호출합니다.
  3. 부모는 자식들의 height, margin 그리고 padding 값을 합쳐 자신의 height 로 사용합니다.
  4. dirty bit 플래그를 제거합니다.

Firefox는 layout 의 파라미터로 state object(nsHTMLReflowState) 를 사용합니다. 이 객체는 부모의 width 값을 가지고 있습니다. Firefox에서 layout 메소도의 리턴값은 metrics object(nsHTMLReflowMetrics) 입니다. 이 객체는 rendererheight 를 가지고 있습니다.

Width calculation

Rendererwidthwidth 프로퍼티, 그리고 margin, border 갑을 사용해서 계산합니다.
아래와 같은 divwidth 를 계산해 보겠습니다.

<div style="width: 30%"/>

Webkit은 아래의 과정으로 계산합니다. RenderBox 클래스의 calcWidth 메소드를 사용합니다.

  • 컨테이너의 widthmax(availableWidth, 0) 입니다. availableWidth 는 아래와 같이 계산합니다.
	clientWidth() - paddingLeft() - paddingRight()

clientWidthclientHeightborderscrollbar 를 제외한 값을 의미합니다.

  • element의 widthwidth 속성이 결정합니다. 컨테이너 width 의 백분율 값을 절대 값으로 변환합니다.
  • 좌우측borderpadding 값을 추가합니다.

위의 과정을 통해서 preferred width 를 계산했습니다. 이제 width 최대값과 최소값과 비교해야 합니다.
preferred width 가 최대값보다 크면 최대값을 사용합니다. 마찬가지로, 최소값보다 작으면 최소값을 사용합니다.

width 가 고정된 경우 캐싱해서 사용합니다.

Linke Breaking

Rendererlayout 중간에 줄을 바꿔야한다면, 이를 부모에 전달합니다. 부모는 renderer 를 추가적으로 만들어 layout 을 호출합니다.




참고 자료
How Browsers Work: Behind the scenes of modern web browsers
브라우저는 어떻게 동작하는가?

0개의 댓글