Table of Contents
Welcome to the second Shadow devlog! If you don’t know it, Shadow is my novel browser engine made almost entirely in JS. Shadow is 2 weeks old as of this post! 🎂
Shadow was (accidentally) in the news again! This time in JavaScript Weekly #661.
Comparisons
(left/top: now, right/bottom: last post)
Ladybird
It was completely busted before, but now is quite good! The header looks broken as we do not have flex support yet.
SunSpider Results
The results did not display before as it used unsupported DOM APIs, but we now implement them so it shows fine now. Also looks a bit better due to some layout fixes.
WHATWG
Before it was completely broken, but now it no longer crashes! It doesn’t look great but progress is progress :)
Performance
I did a lot of work on performance and debug tools to help investigate it. I mostly cared about load time performance but did a bit of work on rendering too.
New Debug Overlay
Shadow has a new debug overlay, showing frame time and delta time (accessed via Shift+Z).
Page Load Profiler
Additionally, we now profile page loads to see what takes up the time.
~3x Faster Page Load
Compared to before, page loads are now ~3x faster! This is mostly thanks to rewriting script execution and removing dead text nodes.
~2x Faster Rendering
Rendering the page is also ~2x faster than before, thanks to adding offscreen culling (not rendering elements outside of the viewport).
Layout
Pseudo-classes
Shadow is now beginning to implement pseudo-classes. We currently implement :link
(+ :any-link
) and :root
.
var()
Shadow now supports the var()
CSS function, including a default value (var(foo, 2px)
).
CSS Parser
The CSS parser now also supports psuedo-classes, and can now parse at-rules so no longer crashes when encountering them (although they are just ignored in layout for now). It also now bails instead of crashing the entire tab when it errors.
~8x Faster Dead Text Node Removal
Removing dead text nodes is a pass layout does after assembling the layout tree to simplify it for us later. It removes unneeded text nodes (empty, or pure whitespace which can be appended to others).
I rewrote it to be ~8x faster which noticably helps page load times with big pages.
Many Fixes
- Fixed some CSS selector matching bugs
- Fixed % values for position absolute
- Rewrote computing element’s height
JavaScript
~5x Faster Worker Execution Loop
The worker execution loop (how the worker loops into getting JS to eval) is now async, so it no longer sits there constantly using CPU when not executing JS. This also makes execution ~5x faster due to not having to wait a loop cycle.
New APIs
appendChild
, createElement
, and createTextNode
are now implemented, also some of performance
, plus queueMicrotask
.
HTML Parser
Attributes Without Value
The HTML parser can now parse attributes without a value, eg <div foo>
.
Autofix Mismatching Closing Tags
The HTML parser now also tries to automatically fix mismatching closing tags. For example:
<main>
<ul>
<li>item
</main>
<article>
</ul>
</li>
</article>
Gets transformed into:
<main>
<ul>
<li>item</li>
</ul>
</main>
<article>
</article>
Thanks for reading! Hope you found it interesting. Tune in next week (🤞) for this week’s progress, or watch my Twitter for “live” updates.