이번 달의 Ladybird – 2026년 4월
1 hour ago
1
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 페인팅
Linux Vulkan 빌드에서 WebContent는 GPU-backed Skia surface에 페인트했지만 UI 프로세스와 공유하는 버퍼가 CPU 비트맵이라 매 flush마다 GPU-to-CPU readback이 발생했음
SharedImage가 Linux dmabuf 핸들을 담을 수 있게 되어 front/back buffer가 UI 프로세스까지 GPU에 상주함(#8917 , #8920 )
JavaScript 엔진 성능과 호환성
JS-to-JS 호출 최적화
Call, Return, End 명령이 일반적인 경우 AsmInt 어셈블리 인터프리터 안에 머물도록 여러 PR이 적용됨(#8891 , #8909 , #8912 )
레지스터 저장/복구에는 수작업으로 조정된 ARM64 paired load/store(ldp/stp)를 사용함
native function 호출도 AK::Function 대신 일반 함수 포인터를 담는 새 RawNativeFunction variant를 통해 AsmInt에서 직접 디스패치됨(#8922 )
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::Function closure를 가진 PromiseResolvingFunction 셀 2개를 할당하던 구조에서, Kind enum으로 디스패치되는 static function으로 바뀌어 resolver별 할당이 제거됨(#9188 )
non-dictionary shape의 property-table marking을 건너뛰어 maptiler.com 로딩 중 GC 시간이 1.3초 줄어듦(#9044 )
packed array의 Array.prototype.indexOf fast 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>가 align presentational attribute를 반영해 bricklink.com 의 버튼 배치가 수정됨(#9177 )
stroke-dasharray 보간으로 SVG dash가 부드럽게 애니메이션됨(#9133 )
autofocus attribute가 있는 요소가 페이지 로드 시 실제로 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와 AAAA query를 병렬로 발행하며, 같은 이름에 대한 동시 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 )
이미지 요청의 Accept header에 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 )
메모리 할당과 빌드 체계
mimalloc 기본 할당기
C++와 Rust 코드가 각각 system allocator를 거치지 않고 mimalloc v2 단일 allocator instance를 공유함(#8752 )
malloc()을 시스템 전체로 override하지 않으므로 third-party library는 자체 allocator contract를 유지함
JS benchmark가 전반적으로 개선됨
Rust 필수화와 빌드 시스템 정리
ENABLE_RUST 빌드 옵션이 제거되어 Rust가 필수가 됨(#8742 )
GN build system이 완전히 제거되어 CMake가 단일 기준으로 남음(#8931 )
GC와 메모리 관련 변경
-ftrivial-auto-var-init=zero로 컴파일해 함수 진입 시 오래된 GC pointer를 0으로 덮고, conservative stack scanner가 이를 덜 찾도록 함(#9171 )
드물게 쓰이는 UsedValues property가 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
Reddit 이미지 갤러리 캐러셀이 동작하며, ::slotted() matching과 split inline의 absolutely positioned descendant 주변의 서로 무관한 layout bug 2개가 수정됨(#9148 )
TextDecoderStream 덕분에 SPA가 링크 클릭을 삼키지 않아 댓글을 열 수 있음
infinite scroll도 structural invalidation 작업의 영향을 받음
YouTube
YouTube 는 off-thread top-level JS compile, off-thread WOFF2 decompression, @font-face fetch fanout 감소, RequestServer memory churn 수정, zero-copy TransferArrayBuffer의 영향을 받음
off-thread WOFF2 decompression은 Gmail에서도 약 170ms 를 절약함(#8976 )
초기 로드에서 @font-face fetch fanout이 177 → 약 9개 로 줄어듦(#9032 )
기타 사이트
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 HTMLFormElement property 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 )
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 )
Homepage
개발자
이번 달의 Ladybird – 2026년 4월
🔉 볼륨 줄이기
🔊 볼륨 키우기
🔇 음소거
⏭️ 다음 곡