크리에이티브 커먼즈 라이선스
Creative Commons License

웹킷CSS 파서(Webkit CSS Parser)

웹킷은 CSS 문법 파일들에서 자동적으로 파서 생성을 위해 Flex and Bison 구문 분석기를 사용한다. 파서 소개를 다시 읽으면 Bison은 bottom-up shift reduce(번역자: 밑에서 부터 위로 범위를 줄여가면서 구문을 분석하는 분석기) 파서를 생성한다. 파이어폭스는 매뉴얼하게 작성된 top down 파서를 사용한다. 양쪽의 케이스에서 각각의 CSS 파일은 CSS 규칙을 포함하는 각각의 객체 즉 스타일쉬트 객체로 파서된다. CSS 규칙 객체는 선택자와 선언을 포함하는 객체 그리고  CSS 문법을 포함하는 객체를 포함한다.

 


그림 7: 파싱 CSS

파싱 스크립트(Parsing Script)

이 챕터에서는 자바스크립트에 대해 다룰 것이다.



스크립트와 스타일쉬트 처리하는 명령(The order of processing scripts and style sheets)


스크립트(Scripts)

웹 모델은 동기방식(synchronous)이다. 웹 저자는 <script> 테그에 도달할 때 즉시 실행될 것인지 구문 분석이 될 것인지를 생각한다. 문서 파싱은 스크립트가 실행될 때 까지 멈춘다.

만일 스크립트가 외부에 있다면 네트워크를 통해 가져와야만 한다. 이것 역시 동기방식으로 리소스를 가져올 때 까지 파싱을 멈춘다. 이것은 수년간 하나의 모델이었고 HTML4와 HTML5 정의서 안에 정의되어져 있다. 스크립트에 “defer” 속성을 사용하면 문서 파싱은 멈추지 않고 파서가 끝난 후에 실행될 것이다. HTML5는 asynchronous를 선택적으로 스크립트에 표시할 수 있는데 이것은 다른 쓰레드에 의해 실행되고 구문분석이 될 것이다.


순리적인 파싱(Speculative parsing)

웹킷과 파이어폭스는 이것에 최적화 되어져 있다. 스크립트가 실행될 때 또 다른 쓰레드가 문서를 구문분석하고 뭔가 다른 리소스가 필요하다면 그것들을 로드하거나 네트워크로 읽어올 것을 찾는다. 이러한 방법을 통해 리소스를 병렬 연결로 읽을 수 있고 전체적인 성능이 더 나아지게 되는 것이다. 주의 - 이 파서는 DOM 트리를 변경할 수 없다. 단지 외부에 존재하는 스크립트, 스타일쉬트, 이미지 등과 같은 외부 리소스를 참조 분석한다.


스타일쉬트(Style sheets)

한편으로 스타일쉬트는 다른 모델을 가진다. 개념상 이것은 DOM 트리를 변경하지 않는다. 이러한 이유로 이것은 문서 파싱이 완료될 때 까지 기다릴 이유가 없다. 이슈가 하나 있다면 문서 파싱 단계 동안 스크립트가 스타일 정보에 대해 묻는 것이다. 만일 스타일이 아직 로드되고 분석되어 있지 않다면 스크립트는 잘못된 답을 가져오거나 문제가 발생하게 될 것이다. 이것은 공통적인 문제이다. 파이어폭스에서는 스타일쉬트가 로드되거나 구문 분석되는 동안 모든 스크립트를 차단한다. 웹킷은 스타일속성에 접근할 때만 스크립트를 차단한다.



렌더 트리 구조(Render tree construction)

DOM 트리가 구조화 될 때 브라우저는 또 다른 트리인 렌더트리를 구조화 하는데 이것을 렌더트리(Render Tree)라 한다. 이것은 보여주기 위한 하나의 트리로서 문서에 시각적 요소를 재표현한다. 이 트리의 목적은 올바른 명령내에서 컨텐츠를 그려준다.


파이어폭스는 렌더 트리 “frames” 내에서 엘리먼트를 호출한다. 웹킷은 렌더러나 렌더 객체를 사용한다. 하나의 렌더러는 레이아웃에 어떻게 페인트 하는지를 안다. 렌더러들의 기본 클래스인 웹킷의 RenderObject 클래스는 다음의 정의를 가진다.




class RenderObject{
virtual void layout();
virtual void paint(PaintInfo);
virtual void rect repaintRect();
Node* node;  //the DOM node
RenderStyle* style;  // the computed style
RenderLayer* containgLayer; //the containing z-index layer
}


각 렌더러는 css2 스팩에 기술된 노드의 css 상자에 상응하는 사각형 영역을 재표현한다. 이것은 넓이, 높이, 포지션과 같은 기하학적인 정보를 포함한다.


box 타입은 style 어트리뷰트의 “display”에 영향을 받는다. 여기에 DOM을 생성하고 display 어트리뷰트에 따라  렌더러의 어떤 타입을 결정하는 웹킷 코드가 있다.



RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)
{
   Document* doc = node->document();
   RenderArena* arena = doc->renderArena();
   ...
   RenderObject* o = 0;

   switch (style->display()) {
       case NONE:
           break;
       case INLINE:
           o = new (arena) RenderInline(node);
           break;
       case BLOCK:
           o = new (arena) RenderBlock(node);
           break;
       case INLINE_BLOCK:
           o = new (arena) RenderBlock(node);
           break;
       case LIST_ITEM:
           o = new (arena) RenderListItem(node);
           break;
      ...
   }

   return o;
}


엘리먼트 타입은 특별한 프레임을 가지는 form 컨트롤과 테이블을 고려해야 한다.

웹킷 내에서 만일 하나의 엘리먼트가 특별한 렌더러를 생성하기를 원한다면 이것은 createRenderer를 오버라이드 할 것이다. 렌더러의 중요한 점은 스타일 객체는 기하학적인 정보를 포함하고 있지 않다는 것이다.



렌더 트리와 DOM 트리 관계 (The render tree relation to the DOM tree)


렌더러는 DOM 엘리먼트에 상응하지만 1:1의 관계는 아니다. 비주얼 DOM 엘리먼트가 아닌 것은 렌더 트리 안에 삽입되지 않는다. 예를 들어서 “head” 엘리먼트가 그것이다. 또한 display 어트리뷰트에 “none”을 할당한 것도 트리에 나타나지 않는다.(엘리먼트에 visibility 어트리뷰트를 “hidden”으로 한 것은 트리에 나타난다.)


DOM 엘리먼트는 몇가지 비주얼 객체들에 상응한다. 보통 엘리먼트는 복합적 구조 이기 때문에 하나의 싱글 사각형에 기술되어 질 수 없다. 예를 들어서 “select”는 3개의 렌더러를 가진다.-display 영역에 대해 하나, 드롭다운 리스트 상자에 대해 하나 그리고 버튼에 대해 하나이다. 또한 텍스트가 멀티 라인일 경우 인데 왜냐하면 하나의 라인으로 충분하지 않기 때문이다. 새로운 라인은 여분의 렌더러로 추가될 것이다.

또 다른 몇몇 렌더러의 예는 HTML이 깨졌을 때 이다. CSS 정의에 따라 인라인(inline) 엘리먼트는 또 다른 블럭(block) 엘리먼트나 오직 인라인 엘리먼트만 포함해야 한다. 혼합된 컨텐트 케이스에서 익명블럭 렌더러는 인라인 엘리먼트를 포장하기 위해 생성될 것이다.


어떤 렌더 객체는 DOM 노드에 상응하지만 트리 내 공간에 위치하지는 않는다. 플롯(Float)과 절대적으로 포지셔닝된 엘리먼트는 흐름의 외부에 있다. 트리 안에 다른 위치에 위치하게 되고 실질적인 프레임에 지정된다. 하나의 플레이스 홀더(placeholder) 프레임은 어디에서든 그것들을 소유하게 될 것이다.


 


(그림11: 렌더트리와 상응하는 DOM 트리. “Viewport”는 블럭(block)을 포함하는 초기화(initial) 이다.웹킷에서 이것은 “RenderView” 객체가 될 것이다.)

트리가 구조화 하는 흐름(The flow of the constructing the tree)


파이어폭스에서 표현은 DOM 업데이트를 위해 하나의 리스너로 등록된다. 표현 위임 프레임(Presentation delegates frame)은 “FrameConstructor”를 생성하고 컨스트럭터는 스타일과 하나의 프레임을 생성하기 위한 대책을 마련한다.


웹킷에서 스타일을 해결하고 하나의 렌더러를 생성하는 것을 “attachment”라 부른다. 모든 DOM 노드는 “attach” 메서드를 가진다. Attachment(첨부)는 동기적으로 DOM 트리에 노드가 삽입될 때 “attach” 메서드는 새로운 노드를 호출한다.


렌더 트리

html과 body 테그 프로세싱은 렌더 트리 루트(render tree root) 구조내 결과이다. 루트 렌더 객체는 containing block이라 부르는 CSS 스펙에 상응한다. - 윗단 대부분의 블럭(block)은 모든 다른 블럭들을 포함한다. 이것의 범위는 Viewport 이다. 브라우저 윈도우는 영역 범위를 보여준다. 파이어폭스는 ViewPortFrame을 호출하고 웹킷은 Render View를 호출한다. 이 토픽에 대한 자세한 내용은 CSS2에 있다.(http://www.w3.org/TR/CSS21/intro.html#processing-model)



스타일 계(Style Computation)

렌더 트리가 만들어 질 때 각각의 렌더 객체의 시각 속성은 계산하는 것을 요구한다. 이것은 각각의 엘리먼트의 스타일 속성을 계산하는 것에 관한 것이다.

스타일은 인라인 스타일 엘리먼트들과 “bgcolor” 속성과 같은 HTML내 시각적 속성과 같은  다양한 기원의 스타일들을 포함한다. 이것은 나중에 CSS 스타일과 매칭시키기 위해 변환한다.


스타일 쉬트의 기원은 브라우저의 기본 스타일 쉬트 이다. 스타일 쉬트들은 페이지 제작자나 사용자의 스타일 쉬트에 제공된다.(브라우저는 여러분이 즐겨 사용하는 스타일을 정의한다. (파이어폭스는 “Firefox Profile” 폴더 안에 위치하고 있다.)


스타일을 사용함에 있어 몇 가지 어려움이 있을 수 있다.

1. 스타일 데이터 는 매우 큰 구조 이다. 다양한 스타일 속성은 메모리에 문제를 일으킬 수 있다.

2. 선택자는 복잡한 구조를 가질 수 있다. 각 요소에 대해 규칙이 일치하지 않는다면 문제를 일으킬 수도 있다. 일치하는 항목을 찾기 위해 각 요소에 대한 전체 규칙 목록을 통과해야 하는 것은 큰 작업이기도 하다. 선택자는 일치하는 경로를 찾기 위해 쓸데없이 입증을 해야 한다.

예를 들어 다음과 같은 구조가 이에 해당한다.


div div div div{
...
}



div 밑으로 3개 더 아래에 있는 div에 매치하라는 의미이다. 만일 하나의 div에 규칙을 적용하고 싶다면 중점이 될 패스를 선택해야 한다. 만일 div 아래에 2개의 div 만 존재하다면 규칙을 적용할 엘리먼트를 찾지 못할 것이다.

3. 규칙의 상속을 정의할 때 복잡한 캐스케이팅을 피해야 한다.



공유 스타일 데이터(Sharing style data)

웹킷의 노드를 참조하는 스타일 객체(렌더 스타일)는 어떤 조건하에서 노드에 의해 공유될 수 있다.

- 엘리먼트는 :hover와 같은 마우스 상태를 가질 수 있다.

- 어떤 엘리먼트는 id를 가져야 한다.

- 테그 이름은 일치되어야 한다.

- 클래스 속성과 일치되야 합니다.

- 매핑된 속성의 집합이 동일해야 합니다.

- 연결 상태는 일치해야 합니다.

- 초점 상태는 일치해야 합니다.

- 요소는 속성에 있는 선택자의 영향을 받거나 모든 선택자내 어떤 위치에 있는 속성 선택자를 사용하는 것과 일치하는 어떤 선택자를 가짐으로써 정의된 것에 영향을 받는다.

- 요소에는 인라인 스타일 속성이 없어야 합니다.


파이어 폭스 규칙 트리(Firefox rule tree)

파이어폭스는 쉬운 스타일 계산을 위해 2개의 여분의 트리를 가진다. 이것은 규칙 트리와 스타일 문맥 트리이다. 웹킷은 또한 스타일 객체를 가진다. 그러나 스타일 문맥 트리 처럼 하나의 트리안에 저장되지 않는다. 단지 DOM 노드는 이것과 관련있는 스타일이라는게 요점이다.

그림 13. 파이어폭스 스타일 컨택스트 트리

스타일 컨택스트는 종료값을 포함한다. 값은 올바른 순서로 일치하는 모든 규칙을 적용하 구체적인 값을 논리에서 변환 조작을 수행하여 계산한다. 예를 들어서 논리값이 화면 비율에 대한 값이라면 이것은 절대값으로 변환되고 계산되어질 것이다. 규칙 트리 아이디어는 정말로 영리하다. 이것은 그것들을 다시 계산하는 것을 피하기 위해 노드 사이의 값을 공유한다. 이것은 또한 공간을 절약하기도 한다.


모든 일치하는 규칙은 하나의 트리에 저장된다. 경로에서 제일 아래에 위치한 노드는 더 높은 우선 순위를 가집니다. 트리는 규칙과 일치하는 모든 경로를 포함합니다. 규칙을 저장하는 것이 느릴 수도 있다. 트리는 매번 노드를 시작하기 위해 계산을 할 필요는 없다. 그러나 노드 스타일은 계산할 때 마다 계산된 경로를 트리에 추가해야 합니다.


아이디어는 사전에 단어로 트리 경로를 확인하는 것입니다.

컨텐트 트리 내 또 다른 엘리먼트에 대한 일치하는 규칙이 필요하다고 가정하자. 그리고 B-E-I와 일치하는 규칙을 찾아라. 우리는 이미 이 규칙을 트리에 가지고 있다. 왜냐하면 이미  A-B-E-I-L의 계산된 경로를 가지고 있다.

이것이 어떻게 일하는지 보도록 하자.


구조체 분할(Division into structs)

스타일 컨텍스트들은 구조체로 부터  구분 되어진다. 이러한 구조체는 테두리 또는 색상과 같은 특정 범주 대한 스타일 정보를 포함한다. 하나의 구조체 내의 모든 속성들은 상속되거나 상속되지 않거나 한다. 상속되는 속성은 부모에서 상속된 것들이나 엘리먼트에 정의된 것들이 상속되고 상속되지 않는 속성은 “reset”이라고 부르는데 보통 엘리먼트에 아무것도 정의되지 않았을 때 사용되는 기본값을 말한다. 트리는 트리 내 전체 구조를 캐싱하여 우리를 도와줍니다. 그bottom 노드가 구조체에 대한 정의를 지원하지 않는다면 upper 노드가 캐시된 구조체를 사용할 수 있다는 것이 아이디어 입니다.



규칙트리를 사용한 스타일 컨텍스트 계산(Computing the style contexts using the rule tree)

특정 요소의 스타일 컨텍스트를 계산할 때 먼저 규칙 트리에서 경로를 계산하거나 기존의 것중 하나를 사용한다. 우리는 새로운 스타일 컨텍스트에서 구조체를 채우기 위해 경로 규칙을 적용하기 시작합니다. 경로의 바닥 노드에서 시작한다. - 우선 순위가 가장 높은 것(보통 지정된 선택자들)들 중 하나를 이용해 구조체가 가득 찰 때 까지 트리를 여행하게 됩니다. 해당 규칙 노드에 구조체에 대한 지정이 없을 경우 광범위한 최적화를 진행합니다. 광범위한 최적화란 전체 구조체를 공유하는 것을 말합니다. 이것은 마지막 값까지 계산이 되면 그것을 메모리에 저장하게 됩니다. 만약 우리가 부분 정의를 찾는다면 구조체가 채워질 때 까지 트리를 돌아다닙니다.


만약에 우리가 우리의 구조체에서 어떤 정의된 것도 찾을 수 없다면 그 구조체는 “inherited” 타입 케이스 입니다. 이 케이스에서는 컨텍스트 구조내 부모 구조체가 핵심 입니다. 이것이 “reset” 구조체 라면 기본값(default values)을 사용할 것입니다.


대부분의 특정 노드에 값이 추가되어 있지 않다면 이것을 실제적인 값으로 변환하는데 있어 몇 가지 추가 계산을 수행해야 합니다. 이것은 트리 노드 안에 결과를 캐시하고 자식 노드에 의해 사용될 것입니다.


하나의 엘리먼트가 형제 또는 동일한 트리 노드를 가지는 케이스에서 같은 트리 노드의 포인트는 전체 스타일 컨텍스트를 그들 사이에서 공유하게 됩니다

하나의 HTML이 있다고 가정해 봅니다. 이것의 예는 아래와 같습니다.


<html>

   <body>

       <div class=”err” id=”div1”>

           <p>이 <span class=”big”>big error</span>

               <span>very big error</span>

           </p>

       </div>

       <div class=”err” id=”div2”>another error</div>

   </body>

</html>


그리고 다음의 규칙을 가집니다.

  1. div {margin:5px;color:black;}

  2. .err{color:red}

  3. .big{margin-top:3px;}

  4. div span{margin-bottom:4px;}

  5. #div1 {color:blue;}

  6. #div2 {color:green;}

간단히 말해 우리는 단지 2개의 구조체(color, margin)를 작성해야 한다고 가정했는데 color 구조체는 단지 색상을 포함하고 있고, margin 구조체는 4개의 면을 포함하고 있다. 이것은 포인트 규칙인 ‘#’을 이용해 표시됩니다.


 

(그림 12: 규칙 트리)


컨텍스트 트리(context tree)에서는 이것처럼 보일 것이다.

 

(그림 13. The Context tree)


우리가 HTML 구문을 분석하과 두번째 div 테그에 도착한다고 가정한다. 우리는 이 노드의 스타일 구조체에 대한 스타일 컨텍스트를 생성해야 한다. 우리는 <div>에 대해 일치하는 규칙 1,2,6을 발견할 것이다. 이것은 이미 우리의 엘리먼트를 사용하기 위한 트리 안에 패스가 존재한다는 것이고 우리는 단지 규칙6(규칙 트리 내 노드 F)에 대한 또 다른 노드를 추가할 필요만 있을 뿐이라는 것을 의미한다.

우리는 컨텍스트 트리 안에 이것을 놓기 위해 하나의 스타일 컨텍스트를 생성할 것이다. 새로운 스타일 컨텍스트는 규칙 트리(rule tree)내 노드 F를 가리킬 것이다.


우리는 지금 스타일 구조체를 작성할 필요가 있다. 우리는 margin 구조체 작성을 시작할 것이다. 마지막 규칙 노드(F) 는 margin 구조체에 추가할 수 없기 때문에  이전 노드 삽입에서 계산된 캐시 구조체를 발견하고 이것을 사용할 때 까지 트리 위로 갈 수 있다. 지정된 margin 규칙은 최상위 노드인 B에서 발견하게 될 것이다. 색상은 하나의 속성을 가지고 있기 때문에 다른 특성을 채우기 위해 트리를 갈 필요가 없다. 우리는 RGB등의 문자열을 변환하여 최종값을 계산할 것이고 노드에 계신된 구조체를 캐시할 것이다.


두번째 <span> 요소에 대한 작업은 더 쉽다. 우리는 이전의 span 처럼 규칙 G를 가리키는 결론에 도달하는 규칙을 일치시킬 것이다. 우리는 같은 노드를 가리키는 형제를 쭉 가져왔기에 전체의 스타일 컨텍스트를 공유할 수 있다. 그리고 이전 span의 컨텍스트를 단지 가리킨다.


부모로 부터 상속되는 규칙을 포함하는 구조체에 대해 컨텍스트 트리에 캐싱된다.(색상 속성은 실제로 상속되지만 파이어 폭스에서는 재설정 처리하고 규칙 트리에 캐시한다.)

만일 우리가 paragraph 안에 폰트에 대한 규칙을 추가한 경우:

p{font-family:Verdana; font-size:10px;font-weight:bold;}

컨텍스트 트리 안에 있는 paragraph의 자식인 div 엘리먼트는 그의 부모와 같은 font 구조체를 공유할 수 있다. 이것은 div에 지정된 font 규칙이 없을 경우에 대한 것이다.


규칙 트리가 없는 웹킷에서 일치된 선언은 4번 통과하게 되는데 먼저 important가 아닌 높은 우선 순위 속성을 적용(display와 같은 다른 것들과 의존하는 것을 먼저 적용한다.)하고 그 다음 높은 우선 순위를 가지는 important를 적용하고 important가 아닌 보통의 우선순위를 가지는 것을 적용하고 마지막으로 important인 보통의 우선 순위를 가지는 것을 적용하게 된다. 그래서 요약하자면 스타일 규칙을 공유하는 것은 이슈1과 3을 해결한다. 파이어폭스의 규칙 트리는 올바은 명령을 속성에 적용하는 것을 돕는다.


쉽게 일치시키기 위해 규칙을 조작하는 것(Manipulating the rules for an easy match)


스타일 규칙에 대한 몇 가지 소스가 있다.

  • CSS 규칙들, 외부 스타일 쉬트나 스타일 요소 중 하나

    • p {color:blue;}

  • 어트리뷰트와 같은 인라인 스타일

    • <p style=”color:blue”>

  • HTML 시각적 어트리뷰트(해당하는 스타일 규칙에 매칭된다.)

    • <p bgcolor=”blue” />


마지막 2개는 엘리먼트에 쉽게 매치할 수 있다. 왜냐하면 스타일 어트리뷰트와 HTML 어트리뷰트를 매칭하는데 있어 하나의 키로 사용할 수 있기 때문이다.

이전에 언급한 바와 같이 CSS 매칭 규칙은 까다롭다. 이러한 어려움을 해결하기 위해 규칙들은 쉬운 접근을 위해 조작되어 질 수 있다.

스타일 쉬트를 구문 분석 한 후 선택자를 따르는 몇몇의 해시맵 중의 하나가 추가될 수 있다. 이들 카테고리와 잘 맞지 않는 무엇인가에 대한 id, class name, tag name 그리고 일반적인 맵 같은 맵들이 있다. 만일 선택자가 id 라면 id map에 추가될 것이다. 만일 그것이 하나의 class 라면 class map 등과 같은 것에 추가될 것이다. 이러한 조작은 일치하는 규칙을 더 쉽게 만들수 있다. 매번 선언에서 보여질 필요는 없다. 우리는 맵들로 부터 엘리먼트에 대한 연관된 규칙을 추출할 수 있다. 이런 최적화를 통해 규칙의 95+%를 제거할 수 있다. 그래서 그것들은 매칭 프로세스에서 고려되어야 하는 것들이 필요하지 않다.


다음의 스타일 규칙의 예를 보자:

p.error {color:red}

#messageDiv {height:50px}

Div {margin:5px}


위의 규칙에서 첫번째 것은 클래스 맵에 삽입됩니다. 두번째는 id 맵으로 그리고 세번째는 tag 맵으로 삽입됩니다. 다음의 HTML 조각에 대해;

<p class=”error”>an error occurred</p>

<div id=”messageDiv”>this is a message</div>


우리는 먼저 p 엘리먼트에 대한 규칙 찾기를 시도할 것이다. class map은 “p.error”를 찾은 것에 대한 규칙인 “error” 키를 포함할 것입니다. div 엘리먼트는 id map(키는 id이다.)과 tag map내에서 연관된 규칙을 가지게 될 것이다. 남은 작업은 실제 매치된 키에 의해 추출된 규칙을 찾는 것입니다.

예를 들어서 div의 규칙인 경우

table div {margin: 5px}

이것은 여전히 tag 맵에서 추출되어질 것입니다. 왜냐하면 key는 대부분 오른쪽 선택자 이다. 그러나 이것은 table 조상을 가지고 있지 않은 우리의 div와 일치하지 않기 때문이다.

웹킷과 파이어폭스가 이러한 조작을 수행합니다.


올바른 캐스케이드 순서를 따르는 것(Applying the rules in the correct cascade order)


스타일 객체는 모든 시각적 어트리뷰트에 상응하는 속성을 가진다.(모든 CSS 어트리뷰트는 보다 더 일반적이다.) 만일 속성이 일치되는 규칙 중 어느 것에도 정의되지 않았다면 - 그런 후 몇몇의 프로퍼티들은 부모 엘리먼트 스타일 객체에 의해 상속될 수 있다. 다른 속성들은 기본값들을 가진다.

하나의 정의 보다는 그 이상일 때 문제는 시작된다. 여기에 문제를 해결하기 위한 케스케이드 순서가 있다.


스타일시트 케스케이드 순서

스타일 속성에 대한 하나의 선언은 여러 개의 스타일시트 안에서 나타날 수 있고 하나의 스타일시트 대신에 여러 시간에 걸쳐 나타날 수도 있다. 이것의 의미는 적용하는 규칙의 순서가 매우 중요하다는 것을 의미한다. 이것은 “cascade”(작은폭포) 순서라고 불린다. CSS2 스펙에 따라 cascade는 높은 곳에서 낮은 곳으로 갑니다.

  1. 브라우저 선언(Browser Declarations)

  2. 사용자 정상 선언 (User normal declarations)

  3. 저자정상선언(Author normal declarations)

  4. 사용자중요선언(User important declarations)


브라우저 선언이 가장 중요하다. 그리고 사용자는 important로 표시된 선언이 있다면 저자를 무시한다. 같은 순서로써의 선언은 특별하게 정렬이 되고 그런 후 순서가 지정된다.

HTML 시각적 속성은 CSS 선언과 일치하는 것으로 변환된다. 그들은 낮은 우선순위로써 저자 규칙을 처리한다.



특이성(Specifity)

CSS의 특이성은 CSS2 명세서에 정의되어 있는데 아래와 같다.

  • style 어트리뷰트가 선언되면 1 아니면 0

  • 선택자(selector)로 ID 어트리뷰트 수를 카운트(=b)

  • 선택자 내에서 다른 어트리뷰트와 가상 클래스의 수를 카운트(=c)

  • 선택자 내에서 엘리먼트 이름들과 가상의 엘리먼트들의 수를 카운트(=d)

기본 숫자 시스템인 a-b-c-d 로 된 4개의 숫자를 제공한다.


이러한 숫자 시스템은 우선 순위를 잡을 때 사용한다. 만약 a=14라면 16진수를 사용할 수 있다. 다른 케이스로 a=17이라면 17 숫자 기반이 필요할 것이다. 마지막으로 선택자는 :html body div div p와 같을 수도 있다.


아래는 몇 가지 예 입니다.

* {}   /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
li {}   /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
li:first-line {}   /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul li {}   /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul ol+li {}   /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
h1 + *[rel=up] {}  /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
ul ol li.red  {}   /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
li.red.level {}   /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
#x34y {}   /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
style=""           /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */



정렬 규칙(Sorting Rule)

규칙이 일치한 후 그것들은 케스케이드 규칙에 따라 정렬됩니다. 웹킷은 큰 거 하나에 대해선 병합을 하고 작은 리스트들에 대해선 버블 정렬을 사용합니다. 웹킷은 규칙에 대한 연산자 “>”를 재지정 하여 정렬을 구현합니다.

static bool operator > (CSSRuleData& r1, CSSRuleData& r2)

{

int spec1 = r1.spector()->specificity();

int spec2 = r2.selector()->specificity();

return (spec1 == spec2) : r1.position() > r2.position() : spec1 > spec2;

}



점진적인 과정(Gradual process)

웹킷은 @important를 포함하는 상위 레벨 스타일 시트라면 마크된 하나의 플래그를 사용하여 로드되어 지게 된다. 스타일을 부착할 때 완전하게 로드되지 않았다면 플레이스 홀더가 사용되고 이것은 문서에 표시된다. 그리고 다시 스타일 시트를 로그하고 재계산할 것이다.



레이아웃(layout)

렌더러가 생성되고 트리에 추가될 때 이것은 포지션과 크기를 가지고 있지 않다. 이러한 값들을 계산하는 것을 레이아웃(layout) 또는 reflow라고 부른다.


HTML은 단일 패스내에서 기하학적인 것을 계산하는 것을 가능하게 하는 시간의 대부분을 의미하는 레이아웃에 기반한 하나의 플로우를 사용한다. 엘리먼트는 플로우 안에서 일반적으로 엘리먼트의 기하학적인 부분에 영향을 주지 않는다. 그래서 레이아웃은 문서 내에서 왼쪽에서 오른쪽으로 위에서 아래로 진행할 수 있다. 예외가 있다면 Table의 경우 단일 패스 이상의 것을 요구하기 때문이다.

좌표 시스템은 루트 프레임에 대해 상대적이다. 왼쪽과 위쪽 좌표가 사용된다.

레이아웃은 순환과정이다. HTML 문서의 요소에 해당하는 루트 렌더러에서 시작된다.

레이아웃은 이것을 필요로하는 각 렌더러를 위해 기하학적 정보를 계산하여 프레임 계층의 모두 또는 일부를 재귀적으로 계속 진행합니다.

루트 렌더러의 포지션은 0,0 이고 크기는 viewport 입니다.(뷰포트는 브라우저 윈도우의 시각적 부분)

모든 렌더러는 “레이아웃” 또는 “리플로우” 메서드를 가집니다. 각 렌더러는 레이아웃을 필요로 하는 children은 이 메서드를 호출합니다.



더티 비트 시스템(dirty bit system)

작은 변화에 대한 명령을 받았을 때 마다 매번 전체 레이아웃을 다시 수행할 수는 없습니다. 브라우저는 “dirty bit” 시스템을 사용합니다. 이것은 레이아웃을 필요로 하는 “dirty”로서 그 자신이나 자식에 표식이 추가된 또는 변경된 렌더러 입니다. 이것은 2개의 플래그가 있는데 “dirty”와 “children are dirty” 이다. 자식은 자신이 OK하거나 레이아웃이 필요로 하는 자식을 적어도 하나를 가지는 렌더러인 더티를 의미한다.


글로벌 및 증분 레이아웃(global and incremental layout)

레이아웃은 전체 렌더 트리에 트리거 될 수 있습니다. 이것이 “글로벌” 레이아웃인데 이것의 결과는 다음과 같습니다.

1. 글꼴 크기 변경과 같은 모든 렌더러에 영향을 미치는 전역 스타일 변화.

2. 크기가 조정된 화면의 결과로써..


레이아웃이 증가할 수 있지만 더티 렌더러 밖으로 누워진 것이다.렌더러가 더티인 경우 증분 레이아웃이 트리거 됩니다. 추가 컨텐츠가 네트워크에 와서 DOM 트리에 추가된 후 새로운 렌더러가 렌더링 트리에 추가 됩니다.


예를 들면 다음과 같습니다.

 


그림 20. 증분 레이아웃 -오직 더티 렌더러들과 그들의 자식은 밖에 있다.



동기및 비동기 레이아웃(Asynchronous and Synchronous layout)


증분 레이아웃은 비동기적으로 수행된다. 증분 레이아웃과 명령 스케줄러 트리거 배치 실행을 위해 파이어폭스는 “리플로우 명령”을 하며 웹킷은 증분 레이아웃을 실행하는 타이머를 가지고 트리를 순회하기 위해 더티 렌더러 외부에 배치되어 있습니다. 스타일 정보를 요청하는 스크립트 “offsightHeight”와 같은 기적 증분 레이아웃을 트리거 할 수 있습니다. 글로벌 레이아웃은 일반적으로 동시에 트리거 됩니다. 가끔 레이아웃의 일부는 스크롤 위치와 함께 속성이 변경되기 때문에 초기 배치후 콜백 으로 트리거 됩니다.



최적화(Optimizations)

레이아웃의 위치나 크기가 변경될 때 렌더 크기는 다시 계산하지 않고 캐시로 부터 가져옵니다. 어떤 케이스에서는 루트에서 부터 시작하지 않고 서브 트리에서만 수정됩니다. 이 케이스가 발생할 때는 로컬과 주변에 영향을 미치지 않습니다.(텍스트 필드에 텍스트를 입력하는 것 처럼)



레이아웃 프로세스(The layout process)

레이아웃은 일반적으로 다음의 패턴을 가집니다.

1. 부모 렌더러는 자신의 너비를 결정합니다.

2. 부모는 자식을 덮어 씌웁니다.

1. 자식 렌더(x 및 y로 설정) 놓습니다.

2. 만일 자식 레이아웃의 호출이 필요하다면 자식의 높이를 계산합니다.

3. 부모는 축적된 높이와 마진과 패딩의 높이를 자신의 높이를 설정하는데 사용합니다.

4. false로 더티비트를 설정합니다.


파이어폭스는 레이아웃 파라미터로 “state” 객체(nsHTMLReflowState)를 사용합니다. state사이에서 부모는 벏이를 포함합니다. 파이어폭스 레이아웃은 “metrics” 객체(nsHTMLReflowMetrics)를 출력합니다. 이것은 계산된 높이를 가진 렌더러를 사용합니다.



폭 계산(Width calculation)

렌더러의 넓이는 블럭의 넓이, 렌더러의 스타일인 넓이 속성, 마진과 보더 컨테이너를 사용하여 계산되는 것이다. 다음의 div 예를 들어…

<div style=”width:30%”>는  class RenderBox method calcWidth로 웹킷에 의해 계산된다.

width 컨테이너는 이용가능한 넓이와 0 컨테이너의 최대이다. 이 경우 이용가능한넓이는 다음과 같이 계산된다.

clientWidth - paddingLeft() - paddingRight()


clientWidth와 clientHeight는 테두리와 스크롤 막대를 제외한 객체의 내부를 나타냅니다.

엘리먼트 width는 “width” 스타일 속성이다. 이것은 컨테이너 넓이의 퍼센트를 계산하여 절대값으로 계산한다.

수평 테두리와 보더들은 지금 추가된다.


여기까지가 기본 넓이 계산이다. 이제 최대 넓이와 최소 넓이를 계산한다. 적절한 넓이가 최대 넓이보다 더 넓다면 최대 넓를 사용한다. 최소 넓이보다 더 적은 넓이라면 최소 넓이를 사용한다.


레이아웃이 필요한 경우 값은 캐시하지만 폭은 변경되지 않습니다.

저작자 표시 비영리 변경 금지
Posted by -세티- 트랙백 0 : 댓글 0