- Ladybird는 4월에 35명 기여자의 333개 PR을 병합했고, Human Rights Foundation의 50,000달러와 Jakub Stęplowski의 1,000달러 후원을 새로 받음
- 인라인 PDF 뷰어, 방문 기록 기반 주소창 자동완성, GTK4/libadwaita Linux 프런트엔드,
about:bookmarks관리 UI가 추가되어 브라우징 기능과 데스크톱 UI가 확장됨 - HTML 파서는 응답 본문을 점진적으로 파싱하고 추측 파서가 리소스를 미리 가져오며, JavaScript 최상위 컴파일은 백그라운드 스레드로 이동해 YouTube 로딩에서 약 200ms의 메인 스레드 시간을 줄임
- JavaScript 엔진은 for-in 캐시, O(1) 레지스터 할당기, zero-copy 식별자 공유, lazy
JS::Substring, typed-array view cache 개선 등으로 Speedometer와 실제 사이트 로딩 성능이 개선됨 Cache/CacheStorage,image-set(), CSS anchor positioning, 비동기 DNS, dmabuf 기반 GPU 페인팅, mimalloc 기본화, Rust 필수화가 포함됐고 WPT 점수는 2,003,537에서 2,067,263으로 상승함
개발 규모와 후원
- 4월 Ladybird는 35명 기여자의 333개 PR을 병합했고, 이 중 7명은 Ladybird에 처음 커밋함
- 신규 후원자로 Human Rights Foundation이 “AI for Individual Rights” 프로그램을 통해 50,000달러, Jakub Stęplowski가 1,000달러를 후원함
- Ladybird는 오픈 웹을 지지하는 기업과 개인의 후원으로 전액 운영됨
브라우징 기능과 프런트엔드
-
인라인 PDF 뷰어
- PDF가 번들된 pdf.js 뷰어를 통해 인라인 렌더링됨(#9132)
- pdf.js는 JavaScript, HTML, CSS만으로 작성된 PDF 뷰어이며 페이지 탐색, 텍스트 선택, 확대/축소, 문서 내 검색을 제공함
- Intel ISA Manual을 pdf.js로 로딩하는 과정에서 typed-array view cache와
:has()무효화 개선 지점이 드러남
-
방문 기록과 주소창 자동완성
- 주소창 입력 시 방문 기록 기반의 풍부한 추천이 표시되며, 이전 방문 페이지의 파비콘과 제목, 검색 엔진 바로가기, 일반 URL 완성을 포함함(#8933)
- SQLite 기반
HistoryStore가 모든 탐색의 제목, 파비콘, 방문 횟수, 마지막 방문 시각을 저장함 - 개인정보 설정 페이지에 “Clear browsing history”가 연결됐고, Qt와 AppKit UI 모두 새 리치 행을 렌더링함
-
GTK4 / libadwaita 프런트엔드
- Ladybird에 GTK4와 libadwaita 기반의 새 Linux 프런트엔드가 추가됐으며 기존 Qt 프런트엔드와 나란히 제공됨(#8691)
- GNOME Web(Epiphany)에서 영감을 받았고 GNOME 설계 지침에 따라 메뉴바 없이 햄버거 메뉴와
AdwTabView탭을 사용함 - URL 바 자동완성 및 보안 아이콘, 페이지 내 찾기, 전체화면, 콘텍스트 메뉴, alert/confirm/prompt/color/file 대화상자, 클립보드, 다중 창, 라이트/다크 테마, DPR 스케일링을 제공함
- 아직 초기 단계라 Qt 및 AppKit 프런트엔드와 기능 동등성에는 도달하지 못함
-
북마크
- 지난달 추가된 북마크에 관리 UI가 더해짐
about:bookmarks페이지에서 북마크와 폴더를 관리할 수 있음(#8825)- 새 페이지에서 북마크 가져오기와 내보내기를 지원함(#8938)
- 북마크와 폴더 편집용 콘텍스트 메뉴가 추가됨(#8715)
- 모든 북마크와 폴더에
date_added타임스탬프가 추가됨(#8867) - 북마크 바에서 새 탭 열기, URL 복사, 가운데 클릭 및 Ctrl/Cmd+클릭 새 탭 열기를 지원함(#8758)
- HTML5 드래그 앤 드롭 API가 연결됐고
about:bookmarks는 이를 재정렬에 사용하며 일반 웹 페이지에서도 동작함(#8783)
HTML 파싱, 스크립트 실행, 렌더링 파이프라인
-
추측 및 점진적 HTML 파싱
- HTML 파서가 응답 본문을 점진적으로 소비하도록 바뀜(#9151)
- 바이트는 스트리밍 텍스트 디코더를 지나 토크나이저에 청크 단위로 들어가며, 입력이 부족하면 토크나이저가 멈췄다가 더 도착하면 재개함
- 전체 본문 수신을 기다린 뒤 파싱을 시작하던 기존 모델을 대체함
- 추측 HTML 파서도 구현됨(#9114)
- 메인 파서가 동기 외부 스크립트에서 막히면 별도 토크나이저가 아직 파싱되지 않은 입력을 앞서 스캔하고
<script src>,<link rel=stylesheet|preload>,<img src>리소스의 추측 가져오기를 발행함 <base href>를 추적하고 템플릿 및 foreign content 내부를 올바르게 건너뜀- 추측 파서가 문서의 preload map에 연결되어 추측으로 발견된 리소스가 일반 파서의 이후 가져오기와 중복 제거됨(#9164)
-
오프스레드 JavaScript 컴파일
- 가져온 스크립트의 최상위 코드 바이트코드 생성이 백그라운드 스레드 풀에서 실행됨(#9118)
- 워커 스레드는 바이트코드와
Executable생성에 필요한 데이터를 만들고, VM 또는 GC 힙을 건드리는 작업은 메인 스레드에 남김 - classic script, module, 최상위 IIFE를 처리하며 YouTube 로딩만으로도 약 200ms의 메인 스레드 시간이 백그라운드 스레드로 이동함
-
Navigable별 래스터화
- 각 Navigable이 자체 스레드에서 독립적으로 래스터화됨(#8793)
- 이전에는 iframe이 부모 display list 내부의 중첩 display list로 동기 페인트되어 최상위 traversable의 렌더링 스레드만 활성화됐음
- 부모 display list는 이제
ExternalContentSource를 통해 각 iframe의 래스터화된 출력을 참조하므로 iframe 무효화가 부모 재기록을 요구하지 않음 - 병렬성 외에도 iframe을 별도 샌드박스 프로세스로 옮기기 위한 준비 작업임
-
Linux dmabuf 기반 GPU 페인팅
JavaScript 엔진 성능과 호환성
-
JS-to-JS 호출 최적화
-
O(1) 바이트코드 레지스터 할당기
Generator::allocate_register는 사용 가능한 풀을 스캔해 가장 낮은 번호의 레지스터를 찾던 구조였고, x.com 로딩 중 이 함수만 약 800ms를 소비했음- C++/Rust 파이프라인 동등성 기간이 끝난 뒤 할당기는 단순 LIFO 스택으로 바뀜(#9007)
-
캐시된 for-in 반복
for (key in obj)위치가 평탄화된 enumerable key 스냅샷을 캐시하고, receiver의 shape, indexed storage, prototype chain이 유지되는 동안 재사용함(#8856)- Speedometer 2는 67.7 → 73.6, Speedometer 3는 4.11 → 4.22로 상승함
-
기타 엔진 개선
- 파서가 lexer, parser, scope collector 전반에서 식별자 이름을 zero-copy로 공유해 웹사이트 JS 코퍼스에서 파싱이 1.14배 빨라지고 RSS가 282MB 감소함(#8801)
- 짧은 문자열 연결은 결과가 어차피 flat string으로 관찰될 때 rope 표현을 건너뛰며, 촘촘한
a + b루프에서 2.13배 빨라짐(#9184) - lexical-this arrow function은 호출마다 function environment를 할당하지 않아 마이크로벤치마크에서 2.13배 개선됨(#9192)
- sparse array는 hole에 대한 즉시 비용을 내지 않으며
Array(20_000_000)이 2천만 개의 가상 요소에 비례하는 작업 대신 대부분 메타데이터로 유지됨(#8847) - 새 lazy
JS::Substring타입이 regexp capture와slice,split, indexed access 같은 string builtin을 뒷받침하며 Octane regexp 벤치마크에서 1.066배 개선됨(#8863) - 바이트코드 source map에서 source position이 end-to-end로 보존되어 x.com에서 약 250ms를 절약함(#9027)
- zero-copy
TransferArrayBuffer가 YouTube 로딩에서 약 130ms를 절약함(#9088) - cached typed-array view가
WeakHashSet에서 intrusive list로 바뀌어 pdf.js에서 Intel ISA PDF를 로딩할 때 약 250ms를 절약함(#9180) - 모든
Promise가 capture하지 않는AK::Functionclosure를 가진PromiseResolvingFunction셀 2개를 할당하던 구조에서,Kindenum으로 디스패치되는 static function으로 바뀌어 resolver별 할당이 제거됨(#9188) - non-dictionary shape의 property-table marking을 건너뛰어 maptiler.com 로딩 중 GC 시간이 1.3초 줄어듦(#9044)
- packed array의
Array.prototype.indexOffast path가 추가됨(#9123) Array.prototype.sort가 비교마다 재트랜스코딩하지 않고 캐시된 UTF-16을 재사용함(#9036)- WASM, JSON, CSS modules import가 추가됨(#6029)
ShadowRealm제안이 표준화 과정에서 정체되어 지원이 제거됨(#8753)
웹 플랫폼 API와 CSS
-
Cache와 CacheStorage
Cache와CacheStorage가 end-to-end로 구현됨(#8745)open,has,delete,keys,match,matchAll,add,addAll,put등 9개 메서드가 일시적 인메모리 저장소를 기반으로 동작함
-
CSS 기능과 렌더링 수정
image-set()의 표준 및-webkit-prefix 형태에 대한 기본 지원이 추가됐고, 페인트 시점에 device pixel ratio와 가장 잘 맞는 해상도 후보를 선택하며 지원하지 않는 MIME 타입은 건너뜀(#9090)image-set()지원으로 gocomics.com의 헤더 이미지가 표시됨position-anchor와 CSS anchor positioning 초기 지원이 추가되어 cssdoom.wtf의 손과 총 위치가 수정됨(#8686)- 색상 보간이 css-color-4에 맞춰 재작성되어
u8대신 float에서 보간하고, missing/powerless component, out-of-gamut sRGB, alpha multiplier를 일관되게 처리함(#8934) align,bgcolor같은 legacy presentational HTML attribute가 직접 cascaded properties를 쓰지 않고 일반 author declaration처럼 cascade를 통과해var()치환과 invalid-at-computed-value-time fallback이 올바르게 동작함(#9176)- presentational hint cascade 변경으로 html.spec.whatwg.org의 crash가 수정됨
<thead>,<tbody>,<tfoot>,<tr>가alignpresentational attribute를 반영해 bricklink.com의 버튼 배치가 수정됨(#9177)stroke-dasharray보간으로 SVG dash가 부드럽게 애니메이션됨(#9133)autofocusattribute가 있는 요소가 페이지 로드 시 실제로 focus를 받음(#9016)- RTL 텍스트의 list marker가 오른쪽에 배치되어 Arabic Wikipedia의 목록 렌더링이 수정됨(#9099)
- inline flex/grid container의 baseline이 마지막 wrapped line이 아니라 child의 first line box에서 파생되어 nos.nl의 링크 텍스트와 아이콘 정렬이 수정됨(#9183)
네트워킹과 스타일 무효화
-
네트워킹
getaddrinfo가 더 이상 event loop를 막지 않음- LibDNS는 lookup을 thread pool에서 실행하고
A와AAAAquery를 병렬로 발행하며, 같은 이름에 대한 동시 lookup을 병합함(#9109) - RequestServer의 preconnect 경로가 resolver를 우회해 libcurl의 threaded resolver가 main thread에서
pthread_join을 유발하던 문제가 같은 DNS pool 경로로 라우팅되도록 수정됨(#9109) - WebContent가 네트워크보다 느릴 때 RequestServer의 queued response data drain이 O(n²)였고, YouTube 비디오를 열 때
memcpy에 약 30초,Vector::remove에 3초를 소비했음 AllocatingMemoryStream이 singly-linked chunk list로 바뀌어 소비가 O(1)이 됨(#9028)- 이미지 요청의
Acceptheader에 AVIF와 WebP를 광고해 다른 엔진과 맞췄고, 일부 CDN은 이 헤더로 최신 포맷 제공 여부나 JPEG fallback을 결정함(#9046)
-
스타일 무효화
- 기존 selector invalidation은 selector가 아래 방향만 본다는 전제에서 단순했지만,
:host와:has()때문에 descendant 변경이 ancestor의:has()결과를 바꿀 수 있어 위로 올라가는 walk가 필요해짐 - stylesheet mutation이 하나의 scope만 바꿨을 때 모든 style scope cache를 재구성하지 않도록 하여 Reddit rule cache rebuild가 13.2초 → 3.2초로 줄어듦(#9138)
- sibling structural invalidation이 position을 관찰하지 않는 descendant로 퍼지지 않게 되어 Reddit infinite scroll에서 불필요한 recompute가 11% 감소함(#9155)
:has()mutation invalidation이 영향받지 않는 anchor를 건너뛰며 azure.com에서 큰 감소가 측정됨(#9168)- Intel ISA PDF에서
:has()child-list visit이 71k → 1.6k로 줄었고, pdf.js 로딩에서 약 650ms를 절약함(#9179) - 새 structural-invalidation 테스트 묶음이 여러 무효화 누락을 드러냈고 이를 수정함(#9095)
- hover, stylesheet mutation scope, custom-property map, computed-style diffing 주변의 작은 개선도 포함됨(#9077, #9049, #9079, #9080, #9141)
- 기존 selector invalidation은 selector가 아래 방향만 본다는 전제에서 단순했지만,
메모리 할당과 빌드 체계
-
mimalloc 기본 할당기
- C++와 Rust 코드가 각각 system allocator를 거치지 않고 mimalloc v2 단일 allocator instance를 공유함(#8752)
malloc()을 시스템 전체로 override하지 않으므로 third-party library는 자체 allocator contract를 유지함- JS benchmark가 전반적으로 개선됨
-
Rust 필수화와 빌드 시스템 정리
-
GC와 메모리 관련 변경
-ftrivial-auto-var-init=zero로 컴파일해 함수 진입 시 오래된 GC pointer를 0으로 덮고, conservative stack scanner가 이를 덜 찾도록 함(#9171)- 드물게 쓰이는
UsedValuesproperty가 lazy pointer 뒤로 이동해 struct가 424바이트 → 176바이트로 줄었고, sainsburys.co.uk 로딩 중LayoutState::populate_node_from()가 139ms → 65ms로 줄어듦(#9104) - fetch body chunk가 pull-promise 경로를 거치며 chunk당 GC object 7개를 할당하던 구조에서 byte stream controller로 직접 들어가도록 바뀜(#9169)
개선된 사이트 동작
-
Reddit
-
YouTube
-
기타 사이트
- gocomics.com은
image-set()덕분에 헤더 이미지가 표시됨 - yandex.com/maps는
WEBGL_debug_renderer_info확장을 포함한 WebGL 수정으로 vector-tile WebGL 렌더링이 동작함(#9043) - strava.com는
Navigator.getBattery가 자체 오류가 아니라 spec-mandated error type을 던지게 되어 로그인이 동작함(#8770) - GitHub Insights는
Element.matches()와.closest()selector cache 덕분에 약 100ms 빠르게 로드됨(#8987) - tweakers.net의 노트북 비교 페이지는 indexed
HTMLFormElementproperty name lookup으로 약 31% 빨라짐(#9009) - neon.com은 더 이상 crash하지 않음(#8812)
- channel4.com은 flex auto-margin resolution 수정으로 category text의 세로 정렬 문제가 해결됨(#9050)
- Cloudflare Turnstile은 아직 통과하지 못하지만, auth-scheme handling,
Array.prototype.shift()최적화,<input>range 및 number element의 UA event handler hardening 덕분에 훨씬 빠르게 실패함(#9063)
- gocomics.com은
WPT와 기타 플랫폼 변화
-
Web Platform Tests
- WPT 점수가 2,003,537 → 2,067,263으로 올라 이번 달 63,726개 subtest 증가를 기록함
- 다만 WPT가 공식 ECMAScript conformance suite인 test262를 upstream으로 가져오며 JavaScript subtest 53,207개가 추가됨
- Ladybird는 test262를 수년간 별도로 실행해 왔고 LibJS conformance가 좋은 상태라 이 중 52,045개, 즉 97.8% 를 통과함
- 63.7k 증가분 중 약 52k는 test262 import에서 왔고, 나머지 약 11.7k가 실제 새 브라우저 플랫폼 진전임
- test262 import로 WPT가 JavaScript conformance를 나머지 플랫폼과 함께 측정하게 됨
-
텍스트, 레이아웃, 프로세스, UI
- ligature가 있는 텍스트의 selection과 hit testing이 한 glyph당 한 code unit을 가정하던 구조에서 grapheme cluster를 순회하고 glyph advance를 해당 grapheme들에 나누는 방식으로 바뀜(#8829)
- shadow root에
innerHTML을 설정해도 전체 document layout tree를 무효화하지 않으며, pomax.github.io/bezierinfo에서 layout-and-paint 시간이 21% 줄어듦(#9191) - popup tab을 다른 site로 탐색해도 parent의 WebContent process가 종료되지 않음(#8730)
- Qt UI에서
Ctrl+Tab과Ctrl+Shift+Tab으로 열린 탭을 순환할 수 있음(#8704) - 가운데 마우스 버튼을 누른 채 드래그해 스크롤하거나, 제자리 클릭으로 autoscroll mode에 들어갈 수 있음(#8881, #8928)
- 주소창 입력이 URL이나 검색 질의로 sanitize될 수 없을 때 입력을 조용히 버리는 대신 적절한 error page가 표시됨(#9072)
TextDecoder의 streaming counterpart인TextDecoderStream이 구현됐고, chunk 경계에서 partial UTF-8을 hold-back해 Reddit 댓글 수정이 가능해짐(#9143)- cross-process
BroadcastChannel메시지가 WebContent와 WebWorker process 사이에서 IPC로 라우팅되어 listener가 어느 process에 있든 다른 브라우저와 같은 방식으로 동작함(#8865)