<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Andreas Kling]]></title><description><![CDATA[Thoughts on systems development, browsers, and being human.]]></description><link>https://awesomekling.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!t_mX!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05881c5b-1c63-4bfd-8c93-b93680f4b388_460x460.jpeg</url><title>Andreas Kling</title><link>https://awesomekling.substack.com</link></image><generator>Substack</generator><lastBuildDate>Fri, 22 May 2026 18:12:51 GMT</lastBuildDate><atom:link href="https://awesomekling.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Andreas Kling]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[awesomekling@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[awesomekling@substack.com]]></itunes:email><itunes:name><![CDATA[Andreas Kling]]></itunes:name></itunes:owner><itunes:author><![CDATA[Andreas Kling]]></itunes:author><googleplay:owner><![CDATA[awesomekling@substack.com]]></googleplay:owner><googleplay:email><![CDATA[awesomekling@substack.com]]></googleplay:email><googleplay:author><![CDATA[Andreas Kling]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[I'm forking Ladybird and stepping down as SerenityOS BDFL]]></title><description><![CDATA[In 2018, I created the SerenityOS project after completing a drug rehab program.]]></description><link>https://awesomekling.substack.com/p/forking-ladybird-and-stepping-down-serenityos</link><guid isPermaLink="false">https://awesomekling.substack.com/p/forking-ladybird-and-stepping-down-serenityos</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Mon, 03 Jun 2024 09:15:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!t_mX!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05881c5b-1c63-4bfd-8c93-b93680f4b388_460x460.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In 2018, I created the SerenityOS project after completing a drug rehab program. I needed something to soak up my free time while learning to live a normal life, and it turned out that building a new operating system was a task of just the right proportions.</p><p>After six months of working on it by myself, I posted it online, and invited others to participate.</p><p>Since then, SerenityOS has grown into a large OSS community with over one thousand contributors all over the world. We've built a friendly culture of setting differences aside and focusing on our shared love for programming. Countless people have poured their heart and soul into it, and I like to think it's inspired some to attempt more challenging things in life.</p><p>Personally, for the past two years, I've been almost entirely focused on Ladybird, a new web browser that started as a simple HTML viewer for SerenityOS. When Ladybird became a cross-platform project in 2022, I switched all my attention to the Linux version, as testing on Linux was much easier and didn't require booting into SerenityOS.</p><p>Time flew by, and now I can't remember the last time I worked on something in SerenityOS that wasn't related to Ladybird.</p><h4>Two projects in one space</h4><p>Until now, the project and its community have existed in a state with two groups sharing all resources and infrastructure:</p><ul><li><p>One group who mostly cares about building a desktop OS.</p></li><li><p>Another group who mostly cares about building a cross-platform web browser.</p></li></ul><p>In the past, there was enough overlap between interests that it made sense to share everything, but these interests have slowly grown apart.</p><p>I've decided it's time to make a big change. To streamline development of Ladybird, I'm forking it off from SerenityOS into a new, separate top-level project.</p><p>I love SerenityOS, and I love the community that has formed around it. I don't want to hold it back anymore by being a distracted BDFL. This is why I've also decided to step down. Effective immediately, SerenityOS is now in the hands of its maintainer group. They're a lovely bunch of people, and I trust they'll take good care of it.</p><p>This is a difficult and emotional decision for me, but I believe this will lead to a better future for both projects.</p><h4>What does this mean for Ladybird?</h4><ul><li><p>Ladybird development now happens in a <a href="https://github.com/LadybirdBrowser/ladybird">new repository on GitHub</a>.</p></li><li><p>Day-to-day communication moves to a new <a href="https://discord.gg/nvfjVJ4Svh">Ladybird Discord server</a>.</p></li><li><p>Ladybird now targets Linux and macOS. The SerenityOS target is dropped.</p></li><li><p>Unlike SerenityOS, Ladybird will have a relaxed NIH policy (instead of "no 3rd party code!"), and will leverage the greater OSS ecosystem.</p></li></ul><h4>What does this mean for SerenityOS?</h4><ul><li><p>The extreme focus on building a web browser will disappear from the project.</p></li><li><p>"Hacking on fun software with friends" is once again the main focus.</p></li><li><p>What happens next is up to the community!</p></li></ul><p>Before anyone asks, there is no drama behind this change. It's simply recognizing that there have been two big projects packed uncomfortably into a single space for too long, and I'm doing what I believe will make life better for everyone involved.</p><p>I want to give my heartfelt thanks to everyone who has worked together with me on these projects. You've all helped me get and stay sober, and these have been the best years of my life! Now, I'm handing SerenityOS to you, so that you can make it your own.</p><p>I'm excited about the future of both projects, now that we can all focus better on our main interests! :^)</p>]]></content:encoded></item><item><title><![CDATA[Fuzzing Ladybird with tools from Google Project Zero]]></title><description><![CDATA[While Ladybird does an okay job with well-formed web content, I thought it would be useful to throw some security research tools at it and see what kind of issues it might reveal.]]></description><link>https://awesomekling.substack.com/p/fuzzing-ladybird-with-tools-from</link><guid isPermaLink="false">https://awesomekling.substack.com/p/fuzzing-ladybird-with-tools-from</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Sat, 16 Mar 2024 11:01:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!b2zu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bd74774-78da-4188-ab8b-dc3fbd2622c1_1741x1105.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>While <a href="https://ladybird.dev/">Ladybird</a> does an okay job with well-formed web content, I thought it would be useful to throw some security research tools at it and see what kind of issues it might reveal. So today we&#8217;ll be using &#8220;<strong><a href="https://github.com/googleprojectzero/domato">Domato</a></strong><a href="https://github.com/googleprojectzero/domato"> &#127813;</a>&#8221;, a DOM fuzzer from <a href="https://googleprojectzero.blogspot.com/">Google Project Zero</a>, to stress test Ladybird and fix some issues found along the way.</p><p>The way this works is that Domato generates randomized web pages with lots of mostly-valid but strange HTML, CSS and JavaScript. I then load these pages into a debug build of Ladybird and observe what happens.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!b2zu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bd74774-78da-4188-ab8b-dc3fbd2622c1_1741x1105.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!b2zu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bd74774-78da-4188-ab8b-dc3fbd2622c1_1741x1105.png 424w, https://substackcdn.com/image/fetch/$s_!b2zu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bd74774-78da-4188-ab8b-dc3fbd2622c1_1741x1105.png 848w, https://substackcdn.com/image/fetch/$s_!b2zu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bd74774-78da-4188-ab8b-dc3fbd2622c1_1741x1105.png 1272w, https://substackcdn.com/image/fetch/$s_!b2zu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bd74774-78da-4188-ab8b-dc3fbd2622c1_1741x1105.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!b2zu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bd74774-78da-4188-ab8b-dc3fbd2622c1_1741x1105.png" width="1456" height="924" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9bd74774-78da-4188-ab8b-dc3fbd2622c1_1741x1105.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:924,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:663971,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!b2zu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bd74774-78da-4188-ab8b-dc3fbd2622c1_1741x1105.png 424w, https://substackcdn.com/image/fetch/$s_!b2zu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bd74774-78da-4188-ab8b-dc3fbd2622c1_1741x1105.png 848w, https://substackcdn.com/image/fetch/$s_!b2zu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bd74774-78da-4188-ab8b-dc3fbd2622c1_1741x1105.png 1272w, https://substackcdn.com/image/fetch/$s_!b2zu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bd74774-78da-4188-ab8b-dc3fbd2622c1_1741x1105.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Domato generates HTML pages of roughly 500 KiB in size, filled with &#8220;interesting&#8221; JS, CSS and HTML to surprise and delight your browser engine!</figcaption></figure></div><p>The Domato README boasts a ton of bugs discovered in all major browsers, so I have no doubt it will find some in ours as well. Here we go!</p><h3>Issue #1: &lt;th&gt; inside &lt;mfrac&gt;</h3><p>Unsurprisingly, it took less than a second to find the first issue! The output produced by Domato is actually 562 KiB, but I was able to reduce it to the following:</p><pre><code>&lt;body&gt;
&lt;script&gt;
let mfrac = document.createElement("mfrac");
mfrac.appendChild(document.createElement("th"));
document.body.appendChild(mfrac);
&lt;/script&gt;</code></pre><p>I&#8217;ve compiled Ladybird with UBSAN (Undefined Behavior SANitizer) for this test, and I get the following very helpful trace output:</p><pre><code><strong>LibWeb/HTML/HTMLTableCellElement.cpp:55:36: runtime error: member call on null pointer of type 'Web::DOM::Node'
</strong>#0 0x7e9bfa1610dd in table_containing_cell LibWeb/HTML/HTMLTableCellElement.cpp:55:36
#1 0x7e9bfa1610dd in Web::HTML::HTMLTableCellElement::apply_presentational_hints(Web::CSS::StyleProperties&amp;) const LibWeb/HTML/HTMLTableCellElement.cpp:101:33
#2 0x7e9bf97c22d1 in Web::CSS::StyleComputer::compute_cascaded_values(Web::CSS::StyleProperties&amp;, Web::DOM::Element&amp;, AK::Optional&lt;Web::CSS::Selector::PseudoElement::Type&gt;, bool&amp;, Web::CSS::StyleComputer::ComputeStyleMode) const LibWeb/CSS/StyleComputer.cpp:1448:17
#3 0x7e9bf97e5899 in Web::CSS::StyleComputer::compute_style_impl(Web::DOM::Element&amp;, AK::Optional&lt;Web::CSS::Selector::PseudoElement::Type&gt;, Web::CSS::StyleComputer::ComputeStyleMode) const LibWeb/CSS/StyleComputer.cpp:2231:5
#4 0x7e9bf97e447e in Web::CSS::StyleComputer::compute_style(Web::DOM::Element&amp;, AK::Optional&lt;Web::CSS::Selector::PseudoElement::Type&gt;) const LibWeb/CSS/StyleComputer.cpp:2202:12
#5 0x7e9bf9a7e60c in Web::DOM::Element::recompute_style() LibWeb/DOM/Element.cpp:575:64</code></pre><p>It&#8217;s a good old-fashioned null pointer dereference!</p><p>As it turns out, we&#8217;ve implemented <code>&lt;th&gt;</code> and <code>&lt;td&gt;</code> elements with the assumption that there&#8217;s always a <code>&lt;table&gt;</code> somewhere above it in the DOM tree. We probably believed this because the following is not allowed by the HTML parser:</p><p><code>&lt;mfrac&gt;&lt;th&gt;</code></p><p>If you load the above markup in a spec-compliant browser, it will create a single <code>&lt;mfrac&gt;</code> element with nothing inside it.</p><p>However, when creating DOM nodes manually using JavaScript API, you can break some of the rules that the parser has to follow, and indeed put a <code>&lt;th&gt;</code> inside an <code>&lt;mfrac&gt;</code>!</p><p>Here&#8217;s the buggy function:</p><pre><code>HTMLTableElement const&amp; table_containing_cell(HTMLTableCellElement const&amp; node)
{
    auto parent_node = node.parent();
&nbsp;&nbsp;&nbsp; while (!is&lt;HTML::HTMLTableElement&gt;(parent_node))
  &nbsp;&nbsp;    parent_node = parent_node-&gt;parent();
 &nbsp;&nbsp;&nbsp;return static_cast&lt;HTML::HTMLTableElement const&amp;&gt;(*parent_node);
}</code></pre><p>It&#8217;s used to implement the ancient feature where <code>&lt;table border=3&gt;</code> and <code>&lt;table padding=5&gt;</code> applies CSS border and padding to each table cell, and not just the table box itself.</p><p>The fix is simply to stop assuming that <code>&lt;th&gt;</code> and <code>&lt;td&gt;</code> elements always have a containing <code>&lt;table&gt;</code> in the ancestor chain. We don&#8217;t need the <code>table_containing_cell</code> helper but can instead simply replace this:</p><pre><code>auto const&amp; table_element = table_containing_cell(*this);</code></pre><p>With this:</p><pre><code>auto const table_element = first_ancestor_of_type&lt;HTMLTableElement&gt;();
if (!table_element)
    return;</code></pre><p>And we&#8217;re done with issue #1! <a href="https://github.com/SerenityOS/serenity/commit/b9bacb3ff49ba675f9875434d646fb1b34d2c0df">(Fix committed here.)</a></p><h3>Issue #2: Assigning window event handlers in detached DOM</h3><p>We continue executing the fuzzer and once again, within less than 1 second, we hit a new problem. The Domato output is 472 KiB, but it whittles down to this:</p><pre><code>&lt;script&gt;
&nbsp;&nbsp;&nbsp;&nbsp;var parser = new DOMParser();
&nbsp;&nbsp;&nbsp;&nbsp;var doc = parser.parseFromString("", "text/html");
&nbsp;&nbsp;&nbsp;&nbsp;var body = doc.createElement("body");
&nbsp;&nbsp;&nbsp;&nbsp;body.onblur = null;
&lt;/script&gt;</code></pre><p>When opened in Ladybird, we fail like so:</p><pre><code>VERIFICATION FAILED: m_ptr at LibJS/Heap/GCPtr.h:174
#1&nbsp; JS::GCPtr&lt;Web::HTML::Window&gt;::operator* at LibJS/Heap/GCPtr.h:174
#2&nbsp; Web::DOM::Document::window at LibWeb/DOM/Document.h:320
#3&nbsp; Web::HTML::HTMLBodyElement::global_event_handlers_to_event_target at LibWeb/HTML/HTMLBodyElement.cpp:104
#4&nbsp; Web::HTML::GlobalEventHandlers::set_onblur at LibWeb/HTML/GlobalEventHandlers.cpp:24
#5&nbsp; Web::Bindings::HTMLElementPrototype::onblur_setter at LibWeb/Bindings/HTMLElementPrototype.cpp:1153</code></pre><p>The reason things go wrong here is due to the special behavior of <code>onfoo</code> event handler attributes on the <code>&lt;body&gt;</code> element. For compatibility with ancient web content, assignments to <code>document.body.onfoo</code> must forward to <code>window.onfoo</code>. However, documents created via <code>DOMParser</code> <em>do not have a window object!</em></p><p>It turns out we&#8217;ve misunderstood this detail in our implementation, and structured our internal object model as if every document always has a window. Oops!</p><p>The fix is to make <code>Document::window()</code> return a nullable value, and then handle null in a bajillion places. When assigning <code>document.body.onblur</code> in a window-less document, we now simply do nothing, same as other browsers.</p><p><a href="https://github.com/SerenityOS/serenity/commit/b98a2be96bf03562ec21e80b48d4b3b97ad74cd6">(Fix committed here.)</a></p><h3>Issue #3: Infinite recursion in SVG &lt;linearGradient&gt;</h3><p>Modern browsers have to support SVG both inline in HTML, and also as an external image format. This adds a whole host of new edge cases and interesting interactions.</p><p>For example, SVG allows declaring gradients that reference other gradients to inherit their colors. As it turns out, we hadn&#8217;t considered the case where a gradient references itself:</p><pre><code>&lt;svg&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;linearGradient id="oops" href="#oops"/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;rect fill="url(#oops)" /&gt;
&lt;/svg&gt;</code></pre><p>The SVG above would cause our implementation to loop forever as it attempted to follow the chain of referenced gradients. Oops, indeed!</p><p>The obvious fix is to stop following the chain of linked gradients if the current gradient references itself. However, that doesn&#8217;t cover reference cycles that span multiple steps like so:</p><pre><code>&lt;svg&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;linearGradient id="lol" href="#lmao"/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;linearGradient id="lmao" href="#even"/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;linearGradient id="even" href="#lol"/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;rect fill="url(#lol)" /&gt;
&lt;/svg&gt;</code></pre><p>To properly handle cyclical references, we have to keep track of all the gradients we&#8217;ve visited, and stop following the chain if we encounter a gradient we&#8217;ve already visited before.</p><p>Curiously, Firefox actually complains about these kind of gradients in their developer console:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vRvf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10a9009-633b-453e-ba7f-610dcb2af2f5_842x40.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vRvf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10a9009-633b-453e-ba7f-610dcb2af2f5_842x40.png 424w, https://substackcdn.com/image/fetch/$s_!vRvf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10a9009-633b-453e-ba7f-610dcb2af2f5_842x40.png 848w, https://substackcdn.com/image/fetch/$s_!vRvf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10a9009-633b-453e-ba7f-610dcb2af2f5_842x40.png 1272w, https://substackcdn.com/image/fetch/$s_!vRvf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10a9009-633b-453e-ba7f-610dcb2af2f5_842x40.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vRvf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10a9009-633b-453e-ba7f-610dcb2af2f5_842x40.png" width="842" height="40" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b10a9009-633b-453e-ba7f-610dcb2af2f5_842x40.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:40,&quot;width&quot;:842,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vRvf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10a9009-633b-453e-ba7f-610dcb2af2f5_842x40.png 424w, https://substackcdn.com/image/fetch/$s_!vRvf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10a9009-633b-453e-ba7f-610dcb2af2f5_842x40.png 848w, https://substackcdn.com/image/fetch/$s_!vRvf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10a9009-633b-453e-ba7f-610dcb2af2f5_842x40.png 1272w, https://substackcdn.com/image/fetch/$s_!vRvf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb10a9009-633b-453e-ba7f-610dcb2af2f5_842x40.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><a href="https://github.com/SerenityOS/serenity/commit/2e0297d7035a807d4a18c89c8b310ec06bef005d">(Fix committed here.)</a></p><h3>Issue #4: Accessing the window properties of a removed iframe</h3><p>This one is interesting:</p><pre><code>&lt;iframe&gt;&lt;/iframe&gt;
&lt;script&gt;
&nbsp;&nbsp;&nbsp;&nbsp;window.onload = function() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let iframe = document.querySelector("iframe")
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let iframeWindow = iframe.contentWindow;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iframe.remove();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iframeWindow.getSelection();
&nbsp;&nbsp;&nbsp;&nbsp;}
&lt;/script&gt;</code></pre><p>The above test would crash like this:</p><pre><code><strong>LibWeb/HTML/WindowProxy.cpp:161:136: runtime error: reference binding to null pointer of type 'const BrowsingContext'</strong>
#0 0x77367675cadf in Web::HTML::WindowProxy::internal_get(JS::PropertyKey const&amp;, JS::Value, JS::CacheablePropertyMetadata*) const LibWeb/HTML/WindowProxy.cpp:161:5
#1 0x773670c4fcca in JS::Bytecode::get_by_id(JS::VM&amp;, AK::DeprecatedFlyString const&amp;, JS::Value, JS::Value, JS::Bytecode::PropertyLookupCache&amp;) LibJS/Bytecode/CommonImplementations.h:105:18
#2 0x773670c182d5 in JS::Bytecode::Op::GetById::execute_impl(JS::Bytecode::Interpreter&amp;) const LibJS/Bytecode/Interpreter.cpp:1088:28
#3 0x773670bc8e1f in execute LibJS/Bytecode/Op.h:1898:9
#4 0x773670bc8e1f in JS::Bytecode::Interpreter::run_bytecode() LibJS/Bytecode/Interpreter.cpp:409:38</code></pre><p>As it turns out, this is actually a bug in the HTML spec! When an iframe is removed from the DOM, its content document is detached from its browsing context. However, when getting or setting a property on a window object, we run a spec algorithm called <strong>"check if an access between two browsing contexts should be reported"</strong> which inspects the browsing context of the &#8220;accessor&#8221; and &#8220;accessed&#8221; window. The spec incorrectly assumed that both windows have an associated browsing context at the time of property access.</p><p><a href="https://github.com/whatwg/html/issues/10192">I&#8217;ve opened an issue against the HTML spec</a>, and patched this in Ladybird in the meantime by adding a null check.</p><p>Finding spec bugs is actually one of my favorite things while working on Ladybird. It allows us to improve the specs for everyone by submitting a bug report or fix.</p><p><a href="https://github.com/SerenityOS/serenity/commit/ad843b6e4a4404a4897b587711f9ce97a4504ed0">(Fix committed here.)</a></p><h3>Issue #5: Infinite looping in Element.before()</h3><p>When opening the next page, it just wouldn&#8217;t finish loading. It just sat there chewing 100% CPU.</p><p>Here&#8217;s the reduction:</p><pre><code>&lt;div id="one"&gt;&lt;/div&gt;&lt;div id="two"&gt;&lt;/div&gt;
&lt;script&gt;
&nbsp;&nbsp;&nbsp;&nbsp;two.before(one);
&lt;/script&gt;</code></pre><p>This was a mistake in the implementation of <code>before()</code>, which has to find the first preceding sibling of <code>&lt;div id=&#8221;two&#8221;&gt;</code> that isn&#8217;t one of its arguments (i.e <code>&lt;div id=&#8221;one&#8221;&gt;</code> in this case.) We had the following loop mistake in the implementation:</p><pre><code>while (auto previous_sibling = node-&gt;previous_sibling()) {
&nbsp; &nbsp; // check if previous_sibling is one of the arguments
}</code></pre><p>The problem here is that we kept fetching <code>node-&gt;previous_sibling</code>, instead of continuing to follow the sibling chain via <code>previous_sibling-&gt;previous_sibling</code>.</p><p>Here&#8217;s how I fixed it:</p><pre><code>for (auto sibling = node-&gt;previous_sibling(); sibling; sibling = sibling-&gt;previous_sibling()) {

&nbsp; &nbsp; // check if previous_sibling is one of the arguments

}</code></pre><p><a href="https://github.com/SerenityOS/serenity/commit/35f359c51c787153cd17c7289664ab87146241ea">(Fix committed here.)</a></p><h3>Conclusion</h3><p>This can probably go on for quite some time, so let&#8217;s call it a day. We found five real bugs, one of which was a spec bug, and were able to fix all of them.</p><p>Even though things went basically as I expected, it&#8217;s still quite interesting just how quickly we fall apart when confronted with strange and unexpected inputs. Fuzzers like Domato are an amazing resource for anyone who wants to make their software more robust.</p><p>The next step here will be to get Ladybird to the point where we can handle a sustained onslaught of fuzzed inputs. And once it&#8217;s reasonably stable, we can start running it automatically in the cloud somewhere, and hopefully shake out even more issues.</p>]]></content:encoded></item><item><title><![CDATA[Welcoming Shopify as a Ladybird sponsor]]></title><description><![CDATA[Hello friends!]]></description><link>https://awesomekling.substack.com/p/welcoming-shopify-as-a-ladybird-sponsor</link><guid isPermaLink="false">https://awesomekling.substack.com/p/welcoming-shopify-as-a-ladybird-sponsor</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Wed, 28 Jun 2023 04:45:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!t_mX!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05881c5b-1c63-4bfd-8c93-b93680f4b388_460x460.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello friends!</p><p>I'm super excited to announce that <a href="https://www.shopify.com/">Shopify</a> has stepped up to become the first ever corporate sponsor of the <a href="https://www.ladybird.dev/">Ladybird browser project</a>, generously contributing $100,000 USD!</p><p>Here's a quote from Mike Shaver, Distinguished Engineer at Shopify, on why they decided to back us:</p><blockquote><p>&#8220;Ladybird is a love-letter to the web, and proof that open technologies enable innovation by organizations of all sizes. Shopify grew up on those technologies, and proudly contributes to them. We&#8217;re excited to add Ladybird to the set of projects we support.&#8221;</p></blockquote><p>As you can imagine, I'm pretty hyped! Not just about the financial support, but also about the massive vote of confidence in what we're doing. Of course, this accomplishment isn't mine alone. It's a testament to the dedication and hard work of our entire team who put so much of their time and love into Ladybird. I'm so incredibly proud of the work everyone is doing!</p><p>In a time where big tech companies are giving up on their own browsers and consolidating on Chromium, it's up to hackers &amp; web enthusiasts like us to reintroduce diversity into the browser market. And that's exactly what we hope to do with Ladybird: to create an independent browser from scratch, free of 3rd party code, in classical open-source spirit.</p><p>A massive thank you to Shopify for believing in Ladybird! I can't wait to put the funds to work and see how far we can propel this thing. :)</p><p>Follow <a href="https://twitter.com/ladybirdbrowser">@ladybirdbrowser</a> on Twitter for updates and please reach out if you're interested in getting involved. Thanks for reading!</p>]]></content:encoded></item><item><title><![CDATA[15-minute habit: Magyarul tanulok! (I am learning Hungarian)]]></title><description><![CDATA[4 weeks ago I wrote about taking on a 15 minute daily cleaning habit together with my wife.]]></description><link>https://awesomekling.substack.com/p/15-minute-habit-magyarul-tanulok</link><guid isPermaLink="false">https://awesomekling.substack.com/p/15-minute-habit-magyarul-tanulok</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Sat, 29 Apr 2023 08:21:54 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!B9ct!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895cfafc-e565-4b43-a195-536d3dc9d38c_1125x2436.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>4 weeks ago I wrote about <a href="https://awesomekling.substack.com/p/15-minutes-every-day">taking on a 15 minute daily cleaning habit</a> together with my wife. It has been tremendously effective and our home looks like we just moved into it: everything is clean and sparkly, and it even smells nice. And all it takes is 15 minutes per day!</p><p>In the wake of this astonishing success, I thought I&#8217;d try adding a second 15-minute-per-day habit, to see if I can stack these things.</p><p>The first thing that came to mind was something I&#8217;ve been meaning to do for years, but always kept making excuses about and postponing: I want to learn my wife&#8217;s native language of Hungarian.</p><p><a href="https://twitter.com/KatalinKult/">Katalin</a> and I have been married for over 10 years, and she has learned my language (Swedish), but I haven&#8217;t learned hers. Even though we live in Sweden, and her speaking Swedish has more obvious practical value, this has always felt unfair to me. At the same time I found it hard to take any real steps towards actually doing it.</p><p>For the last 7 days, I&#8217;ve been learning Hungarian for 15 minutes per day. I&#8217;m using the <a href="https://www.duolingo.com/">Duolingo</a> app, and it&#8217;s been great. The ratio of &#8220;learning new words&#8221; to &#8220;repeating things I already learned&#8221; is extremely well-tuned, and it seems like things actually stick to my brain. Furthermore, the Duolingo lessons only take a few minutes each, so I can squeeze in a bunch every day. This makes it feel like I&#8217;m always moving forward, which is great.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!B9ct!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895cfafc-e565-4b43-a195-536d3dc9d38c_1125x2436.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!B9ct!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895cfafc-e565-4b43-a195-536d3dc9d38c_1125x2436.png 424w, https://substackcdn.com/image/fetch/$s_!B9ct!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895cfafc-e565-4b43-a195-536d3dc9d38c_1125x2436.png 848w, https://substackcdn.com/image/fetch/$s_!B9ct!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895cfafc-e565-4b43-a195-536d3dc9d38c_1125x2436.png 1272w, https://substackcdn.com/image/fetch/$s_!B9ct!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895cfafc-e565-4b43-a195-536d3dc9d38c_1125x2436.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!B9ct!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895cfafc-e565-4b43-a195-536d3dc9d38c_1125x2436.png" width="320" height="692.9066666666666" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/895cfafc-e565-4b43-a195-536d3dc9d38c_1125x2436.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:2436,&quot;width&quot;:1125,&quot;resizeWidth&quot;:320,&quot;bytes&quot;:261303,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!B9ct!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895cfafc-e565-4b43-a195-536d3dc9d38c_1125x2436.png 424w, https://substackcdn.com/image/fetch/$s_!B9ct!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895cfafc-e565-4b43-a195-536d3dc9d38c_1125x2436.png 848w, https://substackcdn.com/image/fetch/$s_!B9ct!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895cfafc-e565-4b43-a195-536d3dc9d38c_1125x2436.png 1272w, https://substackcdn.com/image/fetch/$s_!B9ct!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F895cfafc-e565-4b43-a195-536d3dc9d38c_1125x2436.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is my first time learning a new language since I was a teenager, and it feels both intimidating and exciting at the same time. I&#8217;m really looking forward to connecting with Katalin on a deeper level, and reading some of the books she loved as a child.</p><p>Have you learned a language as an adult? Maybe even Hungarian? I&#8217;d love to hear your experience and any tips you might have in the comments below :^)</p><p>Viszontl&#225;t&#225;sra! (Goodbye)</p>]]></content:encoded></item><item><title><![CDATA[Running Ladybird with foreign GUI toolkits]]></title><description><![CDATA[One of my long-term goals for the Ladybird web browser is to make it feel native on every platform it runs on.]]></description><link>https://awesomekling.substack.com/p/running-ladybird-with-foreign-gui</link><guid isPermaLink="false">https://awesomekling.substack.com/p/running-ladybird-with-foreign-gui</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Tue, 25 Apr 2023 12:55:03 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!z70q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a98273b-48ab-4b26-a768-790d5e5d7b48_876x877.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One of my long-term goals for the <a href="https://awesomekling.substack.com/p/ladybird-a-new-cross-platform-browser-project">Ladybird</a> web browser is to make it feel native on every platform it runs on. We&#8217;re far from there today, and it&#8217;s going to take considerable effort to get there.</p><p>However, this week I&#8217;ve made one huge step forward in this area. Let me tell you about it!</p><h3>Born in the fires of SerenityOS</h3><p><a href="https://github.com/SerenityOS/serenity">SerenityOS</a>, the birthplace of Ladybird, is a mostly-closed environment. We control all the APIs from bottom to top, and this has given us a lot of flexibility in building a cohesive system.</p><p>Everything event-driven in the system (GUI applications, system services, the CLI shell, &#8230;) builds on top of <strong><a href="https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibCore/EventLoop.h">Core::EventLoop</a></strong>, <strong><a href="https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibCore/Timer.h">Core::Timer</a></strong> and <strong><a href="https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibCore/Notifier.h">Core::Notifier</a></strong>.</p><p>Let&#8217;s look at a simple example of <strong>Core::EventLoop</strong> so we know what we&#8217;re talking about:</p><pre><code><code>{
    Core::EventLoop event_loop;

    auto timer = Core::Timer::create_single_shot(3000, [&amp;] {
        event_loop.quit();
    });
    timer-&gt;start();

    event_loop.exec();
}</code></code></pre><p>In the example above, we create an event loop and start a single-shot timer that will fire 3 seconds from now and quit the loop. Then we enter the loop by calling <strong>exec()</strong>. After 3 seconds, <strong>exec()</strong> will return. Basic stuff that any event loop supports.</p><p>The trouble with event loops is that <em>every major GUI toolkit has its own event loop</em>, and their UI widgets typically only work when combined with their own event loop. Qt has <strong><a href="https://doc.qt.io/qt-6/qeventloop.html">QEventLoop</a></strong>, Gtk+ has the <strong><a href="https://docs.gtk.org/glib/main-loop.html">GMainLoop</a></strong>, macOS has the <strong><a href="https://developer.apple.com/documentation/corefoundation/cfrunloop-rht">CFRunLoop</a></strong>, etc&#8230;</p><p>In other words, to look native on other platforms, we have to run their event loops.</p><h3>Attempt 1: abstraction at the wrong layer</h3><p>Ladybird started life as a Qt wrapper around LibWeb, and as such, the path of least resistance was using a <strong><a href="https://doc.qt.io/qt-6/qguiapplication.html">QGuiApplication</a></strong> in its main function.</p><p>I added an interface to LibWeb called <strong>Web::Platform::EventLoopPlugin</strong> which allowed you to override the various operations that LibWeb would normally do on the <strong>Core::EventLoop</strong>.</p><p>The interface looked something like this:</p><pre><code>class EventLoopPlugin {
public:
    virtual void spin_until(JS::SafeFunction&lt;bool()&gt; condition) = 0;
    virtual void deferred_invoke(JS::SafeFunction&lt;void()&gt;) = 0;
    virtual NonnullRefPtr&lt;Timer&gt; create_timer() = 0;
    virtual void quit() = 0;
};</code></pre><p>And it worked out pretty well! LibWeb code was updated to stop calling <strong>Core::EventLoop</strong> and instead calling the installed <strong>EventLoopPlugin</strong>. Ladybird got its own plugin that proxied to Qt APIs and all was well&#8230;</p><p>&#8230; Of course, now we also had to patch LibIPC, which LibWeb uses under the hood to communicate between processes. <strong>IPC::Connection</strong> uses <strong>Core::EventLoop::deferred_invoke()</strong> to invoke callbacks some time in the future. These needed to be rerouted to the Qt event loop in Ladybird, so I added a new <strong>IPC::DeferredInvoker</strong> interface:</p><pre><code>class DeferredInvoker {
public:
    virtual void schedule(Function&lt;void()&gt;) = 0;
};</code></pre><p>Ladybird installs its own <strong>DeferredInvokerQt</strong> on its <strong>IPC::Connection</strong> objects, which bundles the callback in a <strong><a href="https://doc.qt.io/qt-6/qtimer.html#singleShot-prop">QTimer::singleShot()</a></strong> and we&#8217;re back on track&#8230;</p><p>&#8230; Well, we&#8217;re also not waking up when sockets become readable. LibWeb and our other libraries often use <strong>Core::Notifier</strong> which invokes a callback once a specific file descriptor becomes readable/writable. Since we&#8217;re in the Qt event loop, there&#8217;s no <strong>Core::EventLoop</strong> to notify our notifiers, and nothing happens.</p><p>No problem, we&#8217;ll just set up a helper <strong><a href="https://doc.qt.io/qt-6/qsocketnotifier.html">QSocketNotifier</a></strong> for each <strong>Core::Notifier</strong> in Ladybird. We ended up with this thing to equip each IPC client with the necessary assistance:</p><pre><code>template&lt;typename ClientType&gt;
void proxy_socket_through_notifier(ClientType&amp; client, QSocketNotifier&amp; notifier)
{
    notifier.setSocket(client.socket().fd().value());
    notifier.setEnabled(true);

    QObject::connect(&amp;notifier, &amp;QSocketNotifier::activated, [&amp;client]() mutable {
        client.socket().notifier()-&gt;on_activation();
    });

    client.set_deferred_invoker(make&lt;DeferredInvokerQt&gt;());
}</code></pre><p>Ladybird had to do this manually for anything at any level in the stack that wanted to use a <strong>Core::Notifier</strong>, but at least now things were working.</p><h3>Attempt 2: abstraction at the right layer</h3><p>Over time, as work continued on the browser, new things would sneak in that used LibCore without consideration for Ladybird and its Qt event loop. After playing whack-a-mole with these issues for months, I finally decided to do something about this.</p><p>The solution: Instead of patching everyone&#8217;s calls to go somewhere other than LibCore, let&#8217;s make LibCore pluggable and allow clients to install a different backend for the event loop.</p><p>I added a new interface called <strong><a href="https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibCore/EventLoopImplementation.h">Core::EventLoopImplementation</a></strong> and made <strong>Core::EventLoop</strong> a simple wrapper around calls to an underlying implementation object.</p><pre><code>class EventLoopImplementation {
public:
    enum class PumpMode {
        WaitForEvents,
        DontWaitForEvents,
    };
    virtual size_t pump(PumpMode) = 0;
    virtual int exec() = 0;
    virtual void quit(int) = 0;
    virtual void wake() = 0;
    virtual void deferred_invoke(Function&lt;void()&gt;) = 0;
    virtual int register_timer(Object&amp;, int milliseconds, bool should_reload, TimerShouldFireWhenNotVisible) = 0;
    virtual bool unregister_timer(int timer_id) = 0;
    virtual void register_notifier(Notifier&amp;) = 0;
    virtual void unregister_notifier(Notifier&amp;) = 0;
};</code></pre><p>(The API is actually a little more complex than that, due to esoteric features only used in SerenityOS, but the above gives you an idea of the interface.)</p><p>Our old event loop has moved into a new <strong><a href="https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibCore/EventLoopImplementationUnix.h">EventLoopImplementationUnix</a></strong> class which is what we use on SerenityOS by default. This is also portable to other Unix-like systems and runs fine on Linux, macOS, etc.</p><p>Ladybird gets its own <strong><a href="https://github.com/SerenityOS/serenity/blob/master/Ladybird/EventLoopImplementationQt.h">EventLoopImplementationQt</a></strong> class, which translates the various LibCore concepts to Qt equivalents. <strong>Core::Timer</strong> gets a <strong><a href="https://doc.qt.io/qt-6/qtimer.html">QTimer</a></strong>, <strong>Core::Notifier</strong> gets a <strong><a href="https://doc.qt.io/qt-6/qsocketnotifier.html">QSocketNotifier</a></strong>, etc.</p><p>Libraries no longer need to think about what event loop they might be running on. As far as they care, it&#8217;s <strong>Core::EventLoop</strong>.</p><h3>What&#8217;s so great about this?</h3><p>On the surface, this is a pretty pedestrian refactoring. However, what we&#8217;ve achieved is actually very important in the long term:</p><p>There&#8217;s now a blueprint for integrating Ladybird with other event loops. Wanna run on top of Gtk+? Implement <strong>EventLoopImplementationGlib</strong>! macOS? You&#8217;ll need an <strong>EventLoopImplementationCF</strong>.</p><p>Once you get those pieces into place, the rest of our browser stack should (in theory!) hum along nicely on top of it. In fact, one might even consider this a first step towards bringing other SerenityOS applications to other platforms as well. :^)</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!z70q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a98273b-48ab-4b26-a768-790d5e5d7b48_876x877.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!z70q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a98273b-48ab-4b26-a768-790d5e5d7b48_876x877.png 424w, https://substackcdn.com/image/fetch/$s_!z70q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a98273b-48ab-4b26-a768-790d5e5d7b48_876x877.png 848w, https://substackcdn.com/image/fetch/$s_!z70q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a98273b-48ab-4b26-a768-790d5e5d7b48_876x877.png 1272w, https://substackcdn.com/image/fetch/$s_!z70q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a98273b-48ab-4b26-a768-790d5e5d7b48_876x877.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!z70q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a98273b-48ab-4b26-a768-790d5e5d7b48_876x877.png" width="876" height="877" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a98273b-48ab-4b26-a768-790d5e5d7b48_876x877.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:877,&quot;width&quot;:876,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:123569,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!z70q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a98273b-48ab-4b26-a768-790d5e5d7b48_876x877.png 424w, https://substackcdn.com/image/fetch/$s_!z70q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a98273b-48ab-4b26-a768-790d5e5d7b48_876x877.png 848w, https://substackcdn.com/image/fetch/$s_!z70q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a98273b-48ab-4b26-a768-790d5e5d7b48_876x877.png 1272w, https://substackcdn.com/image/fetch/$s_!z70q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a98273b-48ab-4b26-a768-790d5e5d7b48_876x877.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Here&#8217;s what the documentation for QEventLoop looks like in Ladybird today!</figcaption></figure></div>]]></content:encoded></item><item><title><![CDATA[Resizing the Mac App Store: a tale of regret & redemption]]></title><description><![CDATA[In 2013, I worked on the Safari/WebKit team at Apple.]]></description><link>https://awesomekling.substack.com/p/resizing-the-mac-app-store-a-tale</link><guid isPermaLink="false">https://awesomekling.substack.com/p/resizing-the-mac-app-store-a-tale</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Wed, 19 Apr 2023 15:34:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ddkj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28afe1ce-84db-4129-88b5-5785fb530585_1231x948.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In 2013, I worked on the Safari/WebKit team at Apple. One day, I was assigned a bug from the Mac App Store team. They were developing a new version of the store app, built almost entirely on web content, and it was really sluggish when resizing.</p><p>I&#8217;d been on the team for a few years at that point, and while I was new, I felt confident that I could help the App Store folks improve performance somehow.</p><p>Here&#8217;s what the app looked like at the time:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ddkj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28afe1ce-84db-4129-88b5-5785fb530585_1231x948.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ddkj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28afe1ce-84db-4129-88b5-5785fb530585_1231x948.png 424w, https://substackcdn.com/image/fetch/$s_!ddkj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28afe1ce-84db-4129-88b5-5785fb530585_1231x948.png 848w, https://substackcdn.com/image/fetch/$s_!ddkj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28afe1ce-84db-4129-88b5-5785fb530585_1231x948.png 1272w, https://substackcdn.com/image/fetch/$s_!ddkj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28afe1ce-84db-4129-88b5-5785fb530585_1231x948.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ddkj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28afe1ce-84db-4129-88b5-5785fb530585_1231x948.png" width="1231" height="948" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/28afe1ce-84db-4129-88b5-5785fb530585_1231x948.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:948,&quot;width&quot;:1231,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:490553,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ddkj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28afe1ce-84db-4129-88b5-5785fb530585_1231x948.png 424w, https://substackcdn.com/image/fetch/$s_!ddkj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28afe1ce-84db-4129-88b5-5785fb530585_1231x948.png 848w, https://substackcdn.com/image/fetch/$s_!ddkj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28afe1ce-84db-4129-88b5-5785fb530585_1231x948.png 1272w, https://substackcdn.com/image/fetch/$s_!ddkj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28afe1ce-84db-4129-88b5-5785fb530585_1231x948.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Investigating the issue</h3><p>As with any performance issue, I fired up Instruments and captured a time profile while resizing the App Store window.</p><p>The profile showed us spending most of the time in three subsystems:</p><ol><li><p>Layout</p></li><li><p>Style recalculation</p></li><li><p>JavaScript execution</p></li></ol><p>I was pretty comfortable with style recalculation in WebKit (much thanks to great mentorship from <a href="https://twitter.com/anttikoivisto">Antti Koivisto</a>), so I decided to start looking there. Over the next few weeks, I found a number of inefficiencies and constructed optimizations for them.</p><h3>My optimizations</h3><p>I went through the WebKit history and found the various things I did to make it faster. In retrospect, I was pleased to discover that they were generally useful optimizations and not specific to the app store use-case.</p><p>Let&#8217;s go down memory lane and look briefly at the optimizations I added:</p><p><strong><a href="https://bugs.webkit.org/show_bug.cgi?id=115370">Don't check for @media rules affected by viewport changes in every layout.</a></strong></p><p>Before this change, WebKit would synchronously re-evaluate media queries for every step of a resize, instead of allowing them to coalesce with the next visual update.</p><p><strong><a href="https://bugs.webkit.org/show_bug.cgi?id=115199">When updating geometry, send JavaScript resize before layout/paint.</a></strong></p><p>The App Store content had a <strong>resize</strong> event listener in JavaScript that would sometimes make changes to the page layout. If I recall correctly, it was switching between displaying 3 and 4 apps per row.</p><p>Before this change, while resizing the window, WebKit would first relayout the page, then dispatch the <strong>resize</strong> event. If the event handler did something that changed the layout, we&#8217;d do another relayout, and then finally paint the results.</p><p>By dispatching the <strong>resize</strong> event first instead, we only do one relayout, even if the event handler does something that invalidates layout!</p><p><strong><a href="https://bugs.webkit.org/show_bug.cgi?id=115122">Setting an inline style property to "" shouldn't cause style recalc unless the property was present.</a></strong></p><p>Before this change, doing <strong>element.style.someProperty=""</strong> would always trigger a style recalculation, even if <strong>someProperty</strong> was not present in the element&#8217;s inline style.</p><p><strong><a href="https://bugs.webkit.org/show_bug.cgi?id=115116">CSS attribute selectors cause unnecessary style recalc when setting attribute to same value.</a></strong></p><p>Before this change, setting an element attribute to the same value it already had would cause a style recalculation if there were attribute selectors matching that attribute.</p><h3>An optimization that didn&#8217;t work out</h3><p>I also attempted this one, but we had to revert it because it broke real websites:</p><p><strong><a href="https://bugs.webkit.org/show_bug.cgi?id=114292">Throttle resize events during live window resize.</a></strong></p><p>The idea was to only send <strong>resize</strong> events at most every 0.2 seconds while the window was being actively resized. It was a nice idea, but as is often the case with browser optimizations, it broke real world content so we couldn&#8217;t do it.</p><h3>It got better, but still too laggy</h3><p>While my optimizations did improve performance, they only added up to something like a 20% speed-up in total. Good, but not great. And the App Store was still noticeably laggy.</p><p>I had managed to reduce the amount of style recalculation happening, but I hadn&#8217;t made much of a dent in layout, and that&#8217;s where most of the time was disappearing now.</p><p>The App Store content was using heavily nested CSS flexbox layouts (actually the old pre-specification <strong>-webkit-box</strong> version!), and I vividly remember profiling it and staring at a tree full of <strong>WebCore::RenderDeprecatedFlexibleBox</strong> functions.</p><p>While my comfort zone overlapped with style recalculation, anything layout related was strictly outside of it. This wasn&#8217;t unique to me, most of the people working on WebKit were uncomfortable with layout code. Ever since I joined the project, I&#8217;d heard people talk about layout (and those who worked on it) as something impossibly, almost mythologically, complicated.</p><p>I had totally bought into this mythology, and now that I was staring at a layout performance bug, I felt paralyzed. &#8220;There&#8217;s no way I can solve this, it&#8217;s layout!&#8221;</p><p>Still I picked at the profiles and poked around the code a bit for another week or so, but I wasn&#8217;t <em>really</em> making an effort to understand how it worked.</p><h3>Rescued by Dave Hyatt</h3><p>I forget exactly how it happened, but I think at some point I told my manager I was unable to make any more progress on the issue.</p><p>They asked one of the layout experts, <a href="https://en.wikipedia.org/wiki/Dave_Hyatt">Dave Hyatt</a>, to take a look. I tried explaining what I had learned to Dave, but in truth I didn&#8217;t have much of value to offer. He went off to investigate the issue on his own.</p><p>After a few days, Dave had devised a set of optimizations that dramatically improved performance:</p><p><strong><a href="https://bugs.webkit.org/show_bug.cgi?id=115543">Improve the performance of RenderDeprecatedFlexibleBox</a></strong></p><p>The main improvement came from deferring the painting of flex items until they had been placed in their final position by the flex layout algorithm. This avoided a huge chunk of work, and we could finally resize the App Store with much less lag.</p><p>At the time, I didn&#8217;t understand his changes. It may as well have been magic! And instead of trying to learn from this experience, I quickly moved on to issues where I felt more comfortable.</p><h3>From regret to redemption</h3><p>Afterwards, I felt bad about this for a long time. I was angry at myself for not being able to figure out a solution, and I decided to stay away from layout code from then on.</p><p>I spent the next few years all over WebKit (and deeper in the macOS/iOS software stacks) to improve browser performance, and learned thousands of things, but none of them about layout.</p><p>Flash forward to today, where I&#8217;ve implemented most of CSS flexbox (and many other layout modes) myself while working on the new <a href="https://awesomekling.substack.com/p/ladybird-a-new-cross-platform-browser-project">Ladybird</a> browser.</p><p>I&#8217;ve got a decent grasp on CSS layout, and I could definitely debug that App Store problem and improve their performance! Of course, it&#8217;s 10 years too late, but I feel good knowing I&#8217;ve repaired something that once made me feel so inadequate.</p><p>I don&#8217;t think I&#8217;ve ever mentioned this, but before I decided to start building a new browser, <em>having to deal with layout</em> was the main reason I kept putting it off. I was afraid that if I start building a browser, I&#8217;ll eventually get stuck on the mythologically impossible layout part, and fail.</p><p>Thankfully I decided to face my fears, and I&#8217;ve spent the last couple of years doing my best to help others <em>not</em> mythologize any aspect of software development by tackling &#8220;impossible&#8221; projects in public.</p><p>In retrospect, I should have ignored the mythologizing and made an effort to understand CSS layout sooner. It&#8217;s amazing fun once you get the hang of it!</p><p>If you&#8217;re interested, come hack on Ladybird with us in the <a href="https://github.com/SerenityOS/serenity">SerenityOS</a> project!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://awesomekling.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Implementing an underspecified CSS feature (flexbox gaps)]]></title><description><![CDATA[While the CSS specifications have come a long way in recent years, it is still the case that some features are vague and underspecified.]]></description><link>https://awesomekling.substack.com/p/implementing-an-underspecified-css-feature</link><guid isPermaLink="false">https://awesomekling.substack.com/p/implementing-an-underspecified-css-feature</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Mon, 17 Apr 2023 05:57:34 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!IEkd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F314fedac-50ef-4653-8fc7-879198d7fa29_822x657.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>While the CSS specifications have come a long way in recent years, it is still the case that some features are vague and underspecified.</p><p>I&#8217;ve recently implemented one such feature in <a href="https://awesomekling.substack.com/p/ladybird-a-new-cross-platform-browser-project">Ladybird</a>, and I thought it could be interesting to share how it went.</p><p>The feature in question is support for gaps (sometimes called &#8220;gutters&#8221;) between boxes in a CSS flex layout.</p><p>Here&#8217;s a quick example so you know what we&#8217;re talking about:</p><pre><code>&lt;div style="display: flex; <strong>column-gap: 1em;</strong>"&gt;                        
    &lt;div style="padding: 1em; background: #a8e6cf;"&gt;well&lt;/div&gt;       
    &lt;div style="padding: 1em; background: #dcedc1;"&gt;hello&lt;/div&gt;      
    &lt;div style="padding: 1em; background: #ffd3b6;"&gt;friends&lt;/div&gt;    
&lt;/div&gt;</code></pre><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XQzD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5128f29b-2e15-4801-88eb-65db80dff1cc_309x225.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XQzD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5128f29b-2e15-4801-88eb-65db80dff1cc_309x225.png 424w, https://substackcdn.com/image/fetch/$s_!XQzD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5128f29b-2e15-4801-88eb-65db80dff1cc_309x225.png 848w, https://substackcdn.com/image/fetch/$s_!XQzD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5128f29b-2e15-4801-88eb-65db80dff1cc_309x225.png 1272w, https://substackcdn.com/image/fetch/$s_!XQzD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5128f29b-2e15-4801-88eb-65db80dff1cc_309x225.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XQzD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5128f29b-2e15-4801-88eb-65db80dff1cc_309x225.png" width="309" height="225" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5128f29b-2e15-4801-88eb-65db80dff1cc_309x225.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:225,&quot;width&quot;:309,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:14329,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XQzD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5128f29b-2e15-4801-88eb-65db80dff1cc_309x225.png 424w, https://substackcdn.com/image/fetch/$s_!XQzD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5128f29b-2e15-4801-88eb-65db80dff1cc_309x225.png 848w, https://substackcdn.com/image/fetch/$s_!XQzD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5128f29b-2e15-4801-88eb-65db80dff1cc_309x225.png 1272w, https://substackcdn.com/image/fetch/$s_!XQzD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5128f29b-2e15-4801-88eb-65db80dff1cc_309x225.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p></p><h3>History of gaps in CSS</h3><p>If I&#8217;ve understood things correctly, gaps were first added to CSS with the <a href="https://www.w3.org/TR/css-grid-2/">CSS Grid Layout</a> module.</p><p>The original grid properties were called <strong>grid-column-gap</strong> and <strong>grid-row-gap</strong> but eventually someone realized that this feature could be useful in other layout modules as well, and two aliases were added: <strong>column-gap</strong> and <strong>row-gap</strong>.</p><p>Gaps are now specified in <a href="https://www.w3.org/TR/css-align-3/">CSS-ALIGN-3</a>, under &#8220;<a href="https://www.w3.org/TR/css-align-3/#gaps">Gaps Between Boxes</a>&#8221;.</p><pre><code>&lt;div style="display: flex; column-gap: 2em;"&gt;
    &lt;div style="padding: 1em; background: #a8e6cf;"&gt;well&lt;/div&gt;
    &lt;div style="padding: 1em; background: #dcedc1;"&gt;hello&lt;/div&gt;
    &lt;div style="padding: 1em; background: #ffd3b6;"&gt;friends&lt;/div&gt;
&lt;/div&gt;</code></pre><h3>Okay, let&#8217;s read the spec...</h3><p>Here&#8217;s what CSS-ALIGN-3 says about <a href="https://www.w3.org/TR/css-align-3/#gap-flex">gaps between items in a flex layout</a>:</p><blockquote><p>When applied to the main axis (e.g. column-gap in a row flex container), indicates minimum spacing between items (as if an additional fixed-size margin were inserted between adjacent flex items in a single line).</p><p>When applied to the cross axis (e.g. row-gap in a row flex container), indicates minimum spacing between adjacent flex lines.</p></blockquote><p>The flex layout spec itself, <a href="https://www.w3.org/TR/css-flexbox-1/">CSS-FLEXBOX-1</a>, does not mention gaps at all.</p><p>In other words, those two measly sentences from CSS-ALIGN-3 are everything we have to go on for implementing this feature!</p><h3>Implementing it in Ladybird</h3><p>Based on the text from CSS-ALIGN-3 above, here&#8217;s how I ended up implementing gaps in Ladybird&#8217;s LibWeb engine:</p><p>First, I defined two helper concepts: <strong>main gap</strong> and <strong>cross gap</strong> which are direction-dependent aliases for the <strong>column-gap</strong> and <strong>row-gap</strong> properties. These are important because the flex layout algorithm abstracts away the horizontal/vertical axes in favor of main/cross axes.</p><p>Then, I made the following adjustments to the flex layout algorithm:</p><ul><li><p>When <strong><a href="https://www.w3.org/TR/css-flexbox-1/#algo-line-break">collecting flex items into flex lines</a></strong> and <strong>flex-wrap</strong> is not <strong>no-wrap</strong>, we now take the <strong>main gap</strong> into account when deciding whether to begin a new flex line after an item.</p></li><li><p>When <strong><a href="https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths">resolving flexible lengths</a></strong>, we now include the <strong>main gap</strong> between items when calculating the <strong>sum of outer hypothetical sizes</strong> and <strong>sum of outer flex base sizes</strong>.</p></li><li><p>When <strong><a href="https://www.w3.org/TR/css-flexbox-1/#algo-line-stretch">handling &#8216;align-content: stretch&#8217;</a></strong>, we now include the <strong>cross gap</strong> between items when calculating the <strong>sum of flex line cross sizes</strong>. (Fun fact: I actually discovered while writing this post that this step was necessary!)</p></li><li><p>When <strong><a href="https://www.w3.org/TR/css-flexbox-1/#algo-main-align">distributing any remaining space</a></strong>, we now include the <strong>main gap</strong> between items when calculating the amount of used space in the main axis. We also use the <strong>main gap</strong> as an additional offset when aligning flex items along the main axis.</p></li><li><p>When <strong><a href="https://www.w3.org/TR/css-flexbox-1/#algo-line-align">aligning all flex lines</a></strong>, we now include the <strong>cross gap</strong> between flex lines when calculating the <strong>sum of flex line cross sizes</strong>. We also use the <strong>cross gap</strong> as an additional offset when aligning flex lines along the cross axis.</p></li><li><p>When calculating the <strong><a href="https://www.w3.org/TR/css-flexbox-1/#intrinsic-main-sizes">flex container&#8217;s intrinsic main size</a></strong>, we now include the <strong>main gap</strong> between items when calculating the largest sum of contributions within each flex line.</p></li><li><p>When calculating the <strong><a href="https://www.w3.org/TR/css-flexbox-1/#intrinsic-cross-sizes">flex container&#8217;s intrinsic cross size</a></strong>, we now include the <strong>cross gap</strong> between flex lines when calculating the <strong>sum of flex line cross sizes</strong>.</p></li></ul><p>While I&#8217;m not completely confident that these adjustments match exactly how other browsers behave, the various test cases I&#8217;ve constructed do render identically in Ladybird, WebKit, Blink, and Gecko.</p><h3>Before &amp; After</h3><p>Here&#8217;s what we looked like before:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IEkd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F314fedac-50ef-4653-8fc7-879198d7fa29_822x657.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IEkd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F314fedac-50ef-4653-8fc7-879198d7fa29_822x657.png 424w, https://substackcdn.com/image/fetch/$s_!IEkd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F314fedac-50ef-4653-8fc7-879198d7fa29_822x657.png 848w, https://substackcdn.com/image/fetch/$s_!IEkd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F314fedac-50ef-4653-8fc7-879198d7fa29_822x657.png 1272w, https://substackcdn.com/image/fetch/$s_!IEkd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F314fedac-50ef-4653-8fc7-879198d7fa29_822x657.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IEkd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F314fedac-50ef-4653-8fc7-879198d7fa29_822x657.png" width="822" height="657" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/314fedac-50ef-4653-8fc7-879198d7fa29_822x657.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:657,&quot;width&quot;:822,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:48380,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IEkd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F314fedac-50ef-4653-8fc7-879198d7fa29_822x657.png 424w, https://substackcdn.com/image/fetch/$s_!IEkd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F314fedac-50ef-4653-8fc7-879198d7fa29_822x657.png 848w, https://substackcdn.com/image/fetch/$s_!IEkd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F314fedac-50ef-4653-8fc7-879198d7fa29_822x657.png 1272w, https://substackcdn.com/image/fetch/$s_!IEkd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F314fedac-50ef-4653-8fc7-879198d7fa29_822x657.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>And here&#8217;s what we look like now:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Jp06!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857c82e9-bbcb-47ff-9ed0-56f4cd57fa4d_822x657.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Jp06!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857c82e9-bbcb-47ff-9ed0-56f4cd57fa4d_822x657.png 424w, https://substackcdn.com/image/fetch/$s_!Jp06!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857c82e9-bbcb-47ff-9ed0-56f4cd57fa4d_822x657.png 848w, https://substackcdn.com/image/fetch/$s_!Jp06!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857c82e9-bbcb-47ff-9ed0-56f4cd57fa4d_822x657.png 1272w, https://substackcdn.com/image/fetch/$s_!Jp06!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857c82e9-bbcb-47ff-9ed0-56f4cd57fa4d_822x657.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Jp06!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857c82e9-bbcb-47ff-9ed0-56f4cd57fa4d_822x657.png" width="822" height="657" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/857c82e9-bbcb-47ff-9ed0-56f4cd57fa4d_822x657.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:657,&quot;width&quot;:822,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:49000,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Jp06!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857c82e9-bbcb-47ff-9ed0-56f4cd57fa4d_822x657.png 424w, https://substackcdn.com/image/fetch/$s_!Jp06!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857c82e9-bbcb-47ff-9ed0-56f4cd57fa4d_822x657.png 848w, https://substackcdn.com/image/fetch/$s_!Jp06!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857c82e9-bbcb-47ff-9ed0-56f4cd57fa4d_822x657.png 1272w, https://substackcdn.com/image/fetch/$s_!Jp06!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857c82e9-bbcb-47ff-9ed0-56f4cd57fa4d_822x657.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h3>When will flexbox gaps be properly specified?</h3><p>There&#8217;s an <a href="https://github.com/w3c/csswg-drafts/issues/2336">open issue against CSS-FLEXBOX-1 about updating the layout algorithm to take gaps into account</a>. It appears that the work has been deferred to CSS-FLEXBOX-2, which (as far as I can tell) is not in development yet.</p><h3>Closing remarks</h3><p>While a large portion of modern browser development is translating specification text into code, there are still areas where specifications don&#8217;t yet cover everything that production browsers actually support.</p><p>Since Ladybird aims to render the contemporary web correctly, we have no choice but to support these features as well. Specifications will eventually catch up, and we&#8217;ll update our implementation when that happens.</p><p>Fun fact: I discovered multiple bugs in our flex layout implementation while editing this very post. They have now also been fixed!</p><p>Finally, if you like what I&#8217;m doing, please consider sponsoring me on <a href="https://github.com/sponsors/awesomekling/">GitHub</a> or <a href="https://patreon.com/awesomekling">Patreon</a>. I&#8217;m working on <a href="https://github.com/SerenityOS/serenity">SerenityOS</a> and Ladybird full time, thanks to the generous support of many sponsors. You can also follow me on <a href="https://twitter.com/awesomekling">Twitter</a>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://awesomekling.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How we're building a browser when it's supposed to be impossible]]></title><description><![CDATA[&#8220;How is the SerenityOS team making such good progress on building their Ladybird browser, when we've heard for years that it&#8217;s impossible&#8221;?]]></description><link>https://awesomekling.substack.com/p/how-were-building-a-browser-when</link><guid isPermaLink="false">https://awesomekling.substack.com/p/how-were-building-a-browser-when</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Tue, 11 Apr 2023 06:44:38 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!VhnS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522fddcf-e839-4409-bfc1-83b1478a7f93_888x827.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VhnS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522fddcf-e839-4409-bfc1-83b1478a7f93_888x827.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VhnS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522fddcf-e839-4409-bfc1-83b1478a7f93_888x827.png 424w, https://substackcdn.com/image/fetch/$s_!VhnS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522fddcf-e839-4409-bfc1-83b1478a7f93_888x827.png 848w, https://substackcdn.com/image/fetch/$s_!VhnS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522fddcf-e839-4409-bfc1-83b1478a7f93_888x827.png 1272w, https://substackcdn.com/image/fetch/$s_!VhnS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522fddcf-e839-4409-bfc1-83b1478a7f93_888x827.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VhnS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522fddcf-e839-4409-bfc1-83b1478a7f93_888x827.png" width="888" height="827" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/522fddcf-e839-4409-bfc1-83b1478a7f93_888x827.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:827,&quot;width&quot;:888,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:184189,&quot;alt&quot;:&quot;Screenshot of a blog post viewed in the Ladybird browser. The text \&quot;It is impossible to build a new web browser\&quot; is highlighted. :^)&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Screenshot of a blog post viewed in the Ladybird browser. The text &quot;It is impossible to build a new web browser&quot; is highlighted. :^)" title="Screenshot of a blog post viewed in the Ladybird browser. The text &quot;It is impossible to build a new web browser&quot; is highlighted. :^)" srcset="https://substackcdn.com/image/fetch/$s_!VhnS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522fddcf-e839-4409-bfc1-83b1478a7f93_888x827.png 424w, https://substackcdn.com/image/fetch/$s_!VhnS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522fddcf-e839-4409-bfc1-83b1478a7f93_888x827.png 848w, https://substackcdn.com/image/fetch/$s_!VhnS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522fddcf-e839-4409-bfc1-83b1478a7f93_888x827.png 1272w, https://substackcdn.com/image/fetch/$s_!VhnS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F522fddcf-e839-4409-bfc1-83b1478a7f93_888x827.png 1456w" sizes="100vw" loading="lazy" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#8220;How is the <a href="https://github.com/serenityos/serenity">SerenityOS</a> team making such good progress on building their <a href="https://awesomekling.substack.com/p/ladybird-a-new-cross-platform-browser-project">Ladybird</a> browser, when we've heard for years that it&#8217;s impossible&#8221;?</p><p>I've seen this question a few times on sites like Hacker News and Reddit, and I thought I&#8217;d offer my own personal take on it.</p><p>From my perspective, it comes down to five main things:</p><ol><li><p>Stronger web specifications</p></li><li><p>Focus on vertical slices</p></li><li><p>Deferring on performance work</p></li><li><p>Team culture</p></li><li><p>Having an experienced leader</p></li></ol><h3>Stronger web specifications</h3><p>We&#8217;ve come a long way from the wishy-washy days of HTML4 and CSS2.1.</p><p>The <a href="https://tc39.es/ecma262/">ECMAScript</a>, <a href="https://html.spec.whatwg.org/multipage/">HTML</a>, and <a href="https://www.w3.org/Style/CSS/specs.en.html">CSS</a> specifications today are (for the most part) stellar technical documents whose algorithms can be implemented with considerably less effort and guesswork than in the past.</p><p>The architecture of Ladybird tries really hard to match the architecture described in the various web specifications. This makes it significantly easier for new developers to get oriented, as they only have to learn one architecture instead of two.</p><p>There are still gaps though, and some features are less specified than others. Thankfully there&#8217;s an active community of editors at the <a href="https://www.w3.org/">W3C</a> working on improvements. A fair amount of the work we do on Ladybird involves adapting our codebase to updates made by these spec editors.</p><p>We try to do our part in improving the specs as well: the Ladybird developers often report spec bugs, and sometimes make PRs to improve specs directly. Not to mention that implementing a spec in a novel browser engine provides great validation that the spec is actually complete.</p><h3>Focus on vertical slices</h3><p>While the specs today are indeed better than ever before, it&#8217;s still the case that many features span multiple specs, often with subtle interactions between subsystems that must be understood by an implementer.</p><p>If you tried to build a browser one spec at a time, or even one feature at a time, you&#8217;d most likely run out of steam and lose interest altogether. </p><p>So instead of that, we tend to focus on building &#8220;vertical slices&#8221; of functionality. This means setting practical, cross-cutting goals, such as &#8220;let&#8217;s get <a href="https://twitter.com/awesomekling">twitter.com/awesomekling</a> to load&#8221;, &#8220;let&#8217;s get login working on <a href="https://discord.com/">discord.com</a>&#8221;, and other similar objectives.</p><p>Working on a vertical slice helps maintain momentum and it's highly motivating to see tangible improvement on real live websites as they progress.</p><p>This approach is in part possible because the web itself is designed around <a href="https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation">graceful degradation</a>, meaning that browsers can render web content while only supporting some of the features used by the site.</p><p>You might think this approach would produce a small collection of &#8220;compatible websites&#8221; and a vast desert of non-working things, but what really happens is that the rising tide lifts all boats, and as we focus on improving one website, thousands of other sites improve as well!</p><h3>Deferring on performance work</h3><p>We're currently not putting much effort into optimizing Ladybird for performance. Instead, our primary focus is on addressing issues related to correctness and compatibility. This approach helps us stay focused on making sure the browser works as intended before diving into optimizations.</p><p>It's not that we don't care about performance. We want our browser to be fast and responsive eventually. It's a matter of making a disciplined choice to not spend too much time optimizing a product that doesn't work correctly yet. For example, while we might identify areas where we could potentially optimize JavaScript execution or rendering speed, we consciously choose to prioritize fixing rendering bugs or compatibility issues with popular websites first.</p><p>We do make exceptions for things that are <em>unbearably</em> slow. We don't want working on the browser to be unpleasant, after all. In such cases, we'll invest time in addressing the most significant performance bottlenecks to maintain a tolerable development experience.</p><p>But we're not tracking benchmark scores and things like that at this stage. That day will come though, once we've achieved a solid foundation in terms of correctness and compatibility!</p><h3>Team culture</h3><p>In the time we&#8217;ve spent together working on this, a strong team culture has formed. The culture is highly optimistic and everyone has a &#8220;can do&#8221; attitude.</p><p>Although the team is distributed around the world, we all meet on Discord to chat and spur each other on. People frequently collaborate and pitch in on each other&#8217;s projects.</p><p>Everyone who works on Ladybird is encouraged to explore their own personal interests within the project. Most folks on the team had never looked at browser code before joining us, and they're quickly becoming world-class browser developers.</p><p>Also, since Ladybird is an offshoot from the SerenityOS project, it shares the same culture of accountability and self-reliance. We avoid 3rd party dependencies and build everything ourselves. In part because it&#8217;s fun, but also because it creates total accountability for what goes into our software.</p><h3>Having an experienced leader</h3><p>An experienced leader can make a significant difference in a project like this. I've had the opportunity to work on production browsers for many years (at Apple and Nokia), which has given me a strong understanding of how browsers are put together.</p><p>While other people may look at the huge stack of technologies and specifications that need to be combined to make a browser and feel overwhelmed, I&#8217;ve got a clear picture of how to put them together and make it work. We save a lot of time here by not worrying about whether it&#8217;s possible and just getting straight to the work instead!</p><p>In more practical terms, what I really end up working on is often between the seams &#8212; parts of the browser that are less rigidly specified (e.g layout &amp; rendering). Those parts can be much harder for less experienced browser developers to engage with.</p><h3>Conclusion</h3><p>Although I don&#8217;t believe our exact way of working is directly transferable to other software projects (unless they are also browsers), with a small team of smart, focused individuals and a knowledgeable leader to guide them, you can build any software you want.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://awesomekling.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Making myself uncomfortable again]]></title><description><![CDATA[In early 2019, some months after completing a rehab program for drug addiction, I was in a very open-minded headspace where I wanted to challenge myself and find ways to improve as a person.]]></description><link>https://awesomekling.substack.com/p/making-myself-uncomfortable-again</link><guid isPermaLink="false">https://awesomekling.substack.com/p/making-myself-uncomfortable-again</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Fri, 07 Apr 2023 00:00:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!t_mX!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05881c5b-1c63-4bfd-8c93-b93680f4b388_460x460.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In early 2019, some months after completing a rehab program for drug addiction, I was in a very open-minded headspace where I wanted to challenge myself and find ways to improve as a person.</p><p>Drugs had filled my life with secrecy and lies, but that life was over. Although I was unsure of my next steps, I was so tired of the secrecy that I couldn&#8217;t think of anything I&#8217;d rather change about myself.</p><p>So, roughly 6 months into the SerenityOS project, I started making YouTube videos to document my progress. At the time, I was still working on the project alone, and I thought video could be an engaging format.</p><p>The first videos were simple programming sessions, filled with clumsiness and mumbling. I made lots of mistakes and mumbled even more while fixing them. Over time, I improved, and also starting making monthly SerenityOS update videos &#8211; a tradition which is still going today.</p><p>Eventually, I began recording myself driving to work, and that&#8217;s when I truly opened up. In these car videos, I discussed programming, job experiences, motivation, operating systems, and many other topics. As I grew more comfortable, I eventually shared my history with drug addiction and how it inspired the name &#8220;SerenityOS&#8221; as well. People were incredibly supportive, and for the first time since childhood I felt accepted for who I really was.</p><h2>Where I am today</h2><p>Just two weeks ago, I posted my 1000th video to YouTube. That&#8217;s a <em>lot</em> of videos. With more than 39,600 subscribers, I&#8217;ve been able to share my work, thoughts and knowledge with more people than I ever imagined.</p><p>Moreover, I&#8217;ve attracted hundreds of people to collaborate on SerenityOS with me, many of whom are still active contributors today. This experience has brought me immense joy.</p><p>At this point, I feel like I&#8217;ve accomplished my goal of becoming comfortable on camera and learned to embrace authenticity, leaving the secrecy and lies behind.</p><p>Now, I&#8217;m ready to face a new challenge and feel uncomfortable once again!</p><h2>Writing: a new frontier</h2><p>Writing has always intimidated me. While I can manage short bursts (like Tweets), I&#8217;ve rarely practiced writing anything more substantial. This is something I&#8217;m <em>incredibly</em> uncomfortable with, and I feel like it&#8217;s time to confront it.</p><p>So, here&#8217;s what I&#8217;m going to do: I&#8217;ll be taking a break from making my usual &#8220;unprepared&#8221; videos (i.e everything other than SerenityOS update videos), and focus instead on writing. I will write posts like this one, but also technical posts, book reviews, etc.</p><p>As I write this, a mix of fear and excitement fills my heart. I feel intimidated and out of my element, and this tells me I&#8217;m on the right track.</p><p>With this, I aim to become significantly better at writing. I also hope to become better at thinking. I tend to think in English, so it seems logical that improving my command of English will improve my ability to think as well.</p><p>To follow along on my new journey, stay tuned for regular blog updates and post announcements on my <a href="https://twitter.com/awesomekling">Twitter</a>.</p><p>It&#8217;s going to be bumpy at first, but I&#8217;ll do my best to improve quickly.</p><p>Thank you for reading! I hope to see you here again.</p>]]></content:encoded></item><item><title><![CDATA[MutexProtected: A C++ Pattern for Easier Concurrency]]></title><description><![CDATA[In this post, we will discuss the challenges of programming with locks and how the C++ language offers some useful tools to make it easier.]]></description><link>https://awesomekling.substack.com/p/mutexprotected-a-c-pattern-for-easier-concurrency</link><guid isPermaLink="false">https://awesomekling.substack.com/p/mutexprotected-a-c-pattern-for-easier-concurrency</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Thu, 06 Apr 2023 00:00:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!t_mX!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05881c5b-1c63-4bfd-8c93-b93680f4b388_460x460.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this post, we will discuss the challenges of programming with locks and how the C++ language offers some useful tools to make it easier. We will start with an example in C and then use C++ to improve upon it in steps. The example APIs are based on real-life APIs from the <a href="https://github.com/SerenityOS/serenity">SerenityOS</a> kernel.</p><h3>Barebones example in C</h3><p>Let&#8217;s imagine a struct <code>Thing</code> with a field <code>field</code> that will be accessed by multiple threads. We&#8217;ll use a mutex <code>mutex</code> to ensure that only one thread can access it at a time.</p><pre><code>struct Thing {
    Mutex mutex;
    Field field;
};
</code></pre><p>In C, accessing <code>Thing</code> would typically look something like this:</p><pre><code>mutex_lock(&amp;thing-&gt;mutex);
use(&amp;thing-&gt;field);
mutex_unlock(&amp;thing-&gt;mutex);
</code></pre><p>An obvious problem in the C version is that the mutex must be manually unlocked. Forgetting to unlock a mutex tends to have unpleasant consequences.</p><h3>Improving it with a C++ RAII class</h3><p>The &#8220;forgot to unlock&#8221; problem is easily solved in C++ with a RAII class for automatic locking &amp; unlocking:</p><pre><code>{
    MutexLocker locker(thing-&gt;mutex);
    use(thing-&gt;field);
}
</code></pre><p>The <code>MutexLocker</code> locks the mutex when constructed and unlocks it when destroyed. No need for a manual call to <code>mutex_unlock()</code> anymore.</p><p>That&#8217;s already pretty good! However, the <code>MutexLocker</code> approach still has some major shortcomings:</p><ul><li><p>You can still forget to <em>lock</em> the mutex and access <code>field</code> anyway.</p></li><li><p>Developers who are unfamiliar with this code may not realize that <code>mutex</code> and <code>field</code> have this important relationship.</p></li></ul><p>Even so, the <code>MutexLocker</code> was our favored pattern in SerenityOS up until 2021, when we introduced a new pattern to the codebase.</p><h3>Adding lambdas to the mix: introducing <code>MutexProtected</code></h3><p><code>MutexProtected</code> is a powerful C++ construct that addresses the main issues with <code>MutexLocker</code> and makes it significantly easier to use mutexes correctly:</p><pre><code>struct Thing {
    MutexProtected&lt;Field&gt; field;
};

thing-&gt;field.with([&amp;](Field&amp; field) {
    use(field);
});
</code></pre><p>Essentially, <code>MutexProtected&lt;T&gt;</code> is a bundled mutex and <code>T</code>. However, you can&#8217;t access the <code>T</code> directly! The only way we&#8217;ll let you access the <code>T</code> is by calling <code>with()</code> and passing it a callback that takes a <code>T&amp;</code> parameter.</p><p>When called, <code>with()</code> locks the mutex, then invokes the callback, and finally unlocks the mutex again before returning.</p><p>As you can see, we&#8217;ve now also solved the issue of someone forgetting to <em>lock</em> the mutex before accessing the field. And not only that, but since the mutex and field have been combined into a single variable, you no longer have to be aware of the relationship between the two. It&#8217;s been encoded into the type system!</p><p>When multiple fields are protected by a single mutex, we can simply combine them into a struct:</p><pre><code>struct ManyFields {
    Field1 field1;
    Field2 field2;
    Field3 field3;
};

struct Thing {
    MutexProtected&lt;ManyFields&gt; fields;
};
</code></pre><p>Note that it&#8217;s still possible to make mistakes with <code>MutexProtected</code>, such as deadlocking the program by using multiple <code>MutexProtected</code> simultaneously in inconsistent order. Thankfully such bugs are generally trivial to diagnose compared to data races.</p><p>That concludes our look at <code>MutexProtected</code>. If you&#8217;re currently working on a C++ project using mainly the <code>MutexLocker</code> approach, consider adding something like our <code>MutexProtected</code> to further reduce the chances of using locks incorrectly.</p><p>You can find the SerenityOS implementation <a href="https://github.com/SerenityOS/serenity/blob/master/Kernel/Locking/MutexProtected.h">on GitHub</a>. (Note that it&#8217;s a little more sophisticated than the imaginary <code>MutexProtected</code> I&#8217;ve used for examples above.)</p><h3>Final notes</h3><p>Although <code>MutexProtected</code> was introduced to SerenityOS in 2021, we do still have a lot of code using the old <code>MutexLocker</code> pattern. There are still cases where <code>MutexLocker</code> works better, for example when a mutex is used to synchronize something other than data.</p><p>And yes, it <em>is</em> possible to make life difficult by persisting the <code>T&amp;</code> to an outside location while inside the callback. This is C++ after all, so the programmer does have great freedom. However, doing so is definitely not recommended, and we have yet to encounter anyone trying to do this in our codebase.</p>]]></content:encoded></item><item><title><![CDATA[Excellence is a habit, but so is failure]]></title><description><![CDATA[We often hear that making small incremental improvements every day can lead to great things.]]></description><link>https://awesomekling.substack.com/p/excellence-is-a-habit-but-so-is-failure</link><guid isPermaLink="false">https://awesomekling.substack.com/p/excellence-is-a-habit-but-so-is-failure</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Wed, 05 Apr 2023 00:00:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!t_mX!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05881c5b-1c63-4bfd-8c93-b93680f4b388_460x460.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We often hear that making small incremental improvements every day can lead to great things. This popular piece of advice rings true, and it&#8217;s a powerful reminder to keep pushing ourselves forward.</p><p>But there&#8217;s another side to this story that we don&#8217;t discuss as often: how incremental neglect and small missteps can accumulate and lead to negative outcomes. Recognizing and addressing these patterns of neglect early can make a significant difference in preventing larger problems down the road.</p><p>Reflecting on my own life, I&#8217;ve noticed that most of the things that really went off-track were indeed consequences of incremental neglect and numerous small yet poor choices:</p><ul><li><p>I didn&#8217;t become addicted to drugs overnight. It happened over hundreds of moments where I prioritized momentary pleasure over health and safety.</p></li><li><p>I didn&#8217;t become overweight overnight. It happened over hundreds of moments where I opted for immediate gratification over long-term health.</p></li><li><p>I didn&#8217;t ruin relationships overnight. It happened over hundreds of moments where I chose comfort over confronting difficult conversations, admitting my mistakes, or even just acknowledging that someone was better than me at something.</p></li></ul><p>From these experiences, I&#8217;ve realized that avoiding bad habits is just as important as cultivating good habits.</p><p>To address these kinds of issues, we must become aware of our patterns of incremental neglect and then take deliberate steps to counteract them and foster healthier habits.</p>]]></content:encoded></item><item><title><![CDATA[How SerenityOS declares ssize_t]]></title><description><![CDATA[This post explores one of my favorite hacks in SerenityOS. I don&#8217;t recommend doing this in your codebase, but it has worked for us so far. :^)]]></description><link>https://awesomekling.substack.com/p/how-serenityos-declares-ssize_t</link><guid isPermaLink="false">https://awesomekling.substack.com/p/how-serenityos-declares-ssize_t</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Tue, 04 Apr 2023 00:00:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!t_mX!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05881c5b-1c63-4bfd-8c93-b93680f4b388_460x460.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This post explores one of my favorite hacks in <a href="https://github.com/SerenityOS/serenity">SerenityOS</a>. I don&#8217;t recommend doing this in your codebase, but it has worked for us so far. :^)</p><div><hr></div><h2>Background</h2><p><code>size_t</code> and <code>ssize_t</code> are common types used in many POSIX APIs. <a href="https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html">According to POSIX</a>, they are used as follows:</p><ul><li><p><code>size_t</code>: Used for sizes of objects.</p></li><li><p><code>ssize_t</code>: Used for a count of bytes or an error indication.</p></li></ul><p>In practice, <code>ssize_t</code> is essentially a &#8220;signed <code>size_t</code>&#8221;.</p><p>Since we&#8217;re building the whole operating system, including the standard C library ourselves, we&#8217;re also responsible for declaring all the common system types, including <code>size_t</code> and <code>ssize_t</code>.</p><h2>How we declare them</h2><p>To <a href="https://github.com/SerenityOS/serenity/blob/master/Userland/Libraries/LibC/stddef.h#L20">declare</a> <code>size_t</code>, we leverage the C preprocessor&#8217;s predefined <code>__SIZE_TYPE__</code> macro:</p><pre><code>typedef __SIZE_TYPE__ size_t;
</code></pre><p>However, there is no <code>__SSIZE_TYPE__</code> macro for <code>ssize_t</code>, so I decided to get a little <a href="https://github.com/SerenityOS/serenity/blob/master/Kernel/API/POSIX/sys/types.h#L18">creative</a>:</p><pre><code>#define unsigned signed
typedef __SIZE_TYPE__ ssize_t;
#undef unsigned
</code></pre><p>Here&#8217;s what&#8217;s happening: The C preprocessor expands &#8220;<code>__SIZE_TYPE__</code>&#8221; to &#8220;<code>unsigned long</code>&#8221; or something similar. We trick it by temporarily defining a macro that replaces &#8220;<code>unsigned</code>&#8221; with &#8220;<code>signed</code>&#8221;, and so <code>ssize_t</code> is declared as a signed version of whatever the <code>size_t</code> type is!</p><h2>How others declare them</h2><p>Other C libraries typically use more careful techniques, such as wrapping the declarations in architecture-specific <code>#ifdef</code>s:</p><pre><code>#ifdef __i386__
typedef uint32_t size_t;
typedef int32_t ssize_t;
#endif

#ifdef __x86_64__
typedef uint64_t size_t;
typedef int64_t ssize_t;
#endif
</code></pre><p>That&#8217;s obviously a better approach if you&#8217;re building with compilers that don&#8217;t predefine <code>__SIZE_TYPE__</code>, or in an environment where <code>unsigned</code> has already been redefined.</p><p>In our case, we haven&#8217;t had any issues with our approach yet, so I&#8217;m inclined to hold on to the hack as it&#8217;s just so cute. :^)</p>]]></content:encoded></item><item><title><![CDATA[15 Minutes Every Day]]></title><description><![CDATA[Someone once suggested that I set aside a small portion of my income every month.]]></description><link>https://awesomekling.substack.com/p/15-minutes-every-day</link><guid isPermaLink="false">https://awesomekling.substack.com/p/15-minutes-every-day</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Mon, 03 Apr 2023 00:00:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!t_mX!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05881c5b-1c63-4bfd-8c93-b93680f4b388_460x460.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Someone once suggested that I set aside a small portion of my income every month. I&#8217;ve been doing this for years with an automatic bank transfer so I can&#8217;t mess it up.</p><p>I was reflecting on this habit recently, and had a thought: what if I treated cleaning the same way?</p><p>I&#8217;ve never been good at cleaning. Every place I ever lived was a bit messy, except when expecting a visitor.</p><p>The idea became: every day I&#8217;d &#8220;invest&#8221; 15 minutes into our home by picking a small cleaning task and doing it immediately after breakfast. I shared the idea with my wife, and we agreed to test it out together.</p><p>For the first few days, we found things to do easily, such as organizing cables, tools, pantry items and clothes. Then, we moved on to cleaning tasks, like scrubbing cabinets, washing floors, and cleaning the sofa.</p><p>These kind of things used to be so hard for me to get done, but now I&#8217;m doing them every day, and it&#8217;s even fun doing them together with my wife. We also do a little &#8220;demo&#8221; when we&#8217;re finished, to show each other what we accomplished.</p><p>We&#8217;re only 2 weeks into this, and our home looks noticeably nicer. I also find it easier to keep it nice, since any small mess now stands out way more than it did before.</p><p>I&#8217;m sure there&#8217;s a novelty aspect to why I&#8217;m enjoying this, but even so, it really does feel like a sustainable habit, especially since it&#8217;s only 15 minutes every day.</p>]]></content:encoded></item><item><title><![CDATA[How I make a living working on SerenityOS]]></title><description><![CDATA[This post describes in detail how I support myself while working on the SerenityOS project.]]></description><link>https://awesomekling.substack.com/p/how-i-make-a-living-working-on-serenityos</link><guid isPermaLink="false">https://awesomekling.substack.com/p/how-i-make-a-living-working-on-serenityos</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Sat, 29 Oct 2022 00:00:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!t_mX!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05881c5b-1c63-4bfd-8c93-b93680f4b388_460x460.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This post describes in detail how I support myself while working on the SerenityOS project.</p><div><hr></div><h3>Introduction</h3><p>Hello! My name is Andreas Kling and I&#8217;m the founder of the <a href="https://github.com/SerenityOS/serenity">SerenityOS</a> project. If you&#8217;re not familiar, SerenityOS is a from-scratch operating system that I started building in 2018. It combines a Unix-like core with the look &amp; feel of the late 1990s. Although it began as a one-man project, it has since blossomed into a lively open source community with <a href="https://github.com/SerenityOS/serenity/graphs/contributors">hundreds of amazing developers</a> working on it.</p><p>Since May of 2021, <a href="https://awesomekling.github.io/I-quit-my-job-to-focus-on-SerenityOS-full-time/">SerenityOS has been my full-time job</a>. This terrifying but exciting leap was made possible by recurring donations from generous people who like what I do and want to see it continue.</p><p>With so many people having my back, I&#8217;ve grown a bit more courageous with taking on large projects, which has led to SerenityOS expanding into both a <a href="https://awesomekling.github.io/Memory-safety-for-SerenityOS/">new memory-safe systems language (Jakt)</a> and a <a href="https://awesomekling.github.io/Ladybird-a-new-cross-platform-browser-project/">new cross-platform browser (Ladybird)</a> in the last year.</p><h3>&#8220;Okay, but how do you <em>actually</em> make a living?&#8221;</h3><p>I get this question regularly, so I&#8217;m going to do my best to answer it. Please understand that I&#8217;m publishing this for transparency, not to brag about making so much or complain about not making enough.</p><p>All currency amounts below are in USD.</p><h3>Main source of income: Individual sponsorships/donations</h3><p>I created a <a href="https://www.patreon.com/awesomekling">Patreon</a> back in April of 2019. I felt a bit silly at the time, with thoughts like <em>&#8220;who do I think I am&#8221;</em> and <em>&#8220;what am I even doing&#8221;</em> echoing in my head. I still did it though. I was too curious to see what would happen, even though I expected nothing. Amazingly, a couple of people actually signed up!</p><p>Later on, when <a href="https://github.com/sponsors/awesomekling">GitHub Sponsors</a> opened up, I got in as early as I could. Not only did they have zero fees, but they also offered to match the first $5,000 in donations. And once again, some people actually signed up!</p><p>On both platforms, people are invited to donate to me personally so that I can spend time working on SerenityOS. No goods/services are offered in return.</p><p>Here are the current numbers as of 2022-10-29. They tend to fluctuate as people join and leave as supporters.</p><p><strong>Platform</strong> <strong>Supporters</strong> <strong>$/month</strong> GitHub Sponsors 263 2,136 Patreon 268 1,411 PayPal &lt;5 &lt;200 Cryptocurrency &lt;5 &lt;50</p><p>Early on I would monitor these numbers closely, as I found it fascinating. Eventually it became a source of stress, so now I only check on them once a month when I do my accounting.</p><h3>Secondary source of income: YouTube</h3><p>I&#8217;ve been fairly active on <a href="https://youtube.com/andreaskling">YouTube</a> since early on in the SerenityOS project. I started uploading development videos about ~6 months into the project. They were awkward and clumsy, but people seemed to enjoy watching someone incrementally building a new operating system from scratch.</p><p>Once the channel grew large enough, I was able to enable monetization in the form of ads. I felt a bit weird about this, since I use an ad blocker myself, but I figured that the kind of person who watches my content is perfectly aware of ad blockers and can make their own decisions about them.</p><p>Income from ads varies wildly depending on how often I upload new videos, and how many views they get. YouTube&#8217;s terms &amp; conditions don&#8217;t allow me to disclose analytics in detail, but my estimated revenue this month is <strong>$315</strong>.</p><p>Outside of ads, I also have 58 people enrolled as &#8220;channel members&#8221;, which nets <strong>$65/month</strong> (part of the $315). YouTube allows you to create separate content for channel members, but I don&#8217;t make use of this feature as I prefer that everyone has access to everything I make. The one thing that channel members <em>do</em> get is the ability to use our fancy SerenityOS and yak-shaving-themed emotes when commenting/chatting the channel.</p><p>Finally, YouTube also allows people to send &#8220;super chats&#8221; during live streams. These are irregular, but this month I have received <strong>$50</strong> across 8 super chats (also part of the $315).</p><h3>Tertiary source of income: Merch</h3><p>People kept asking me for SerenityOS related merch like t-shirts and tea cups, so I ended up making a <a href="https://store.serenityos.org/">print-on-demand store</a> using Teespring. Afterwards, people have told me that they felt much more comfortable supporting the project if they got <em>something</em> in return, &#8220;like a laptop sticker or something&#8221;. This makes perfect sense, but had not occurred to me before.</p><p>Monthly income from merch varies greatly. Last month it was <strong>$0</strong> and so far this month it&#8217;s at <strong>$101</strong>. People are not going to buy the same t-shirt over and over again, so you have to put in some effort into making new designs/products available.</p><h3>Non-income: Sponsored/native advertising</h3><p>I&#8217;ve been approached to advertise a number of products, including (but not limited to) mobile games, VPN services, programming courses, zero-day exploit brokers, cloud platforms, etc.</p><p>So far I haven&#8217;t accepted any of these offers, as I made a rule for myself that I wouldn&#8217;t advertise something I&#8217;m not personally using.</p><p>The only thing that came somewhat close was when I started using the CLion IDE, the folks at JetBrains gave me three 1-year license codes to raffle out to my YouTube audience. Since I use CLion daily, both on and off YouTube, I would be happy to partner with them, but so far we&#8217;re just friends. :^)</p><h3>Non-income: Venture capital</h3><p>I&#8217;ve also been approached by a handful of folks from VC firms and while I have nothing against them, I&#8217;m not taking any meetings. I&#8217;m not interested in selling influence over the things I work on, and I&#8217;d much rather have many small donors who believe in me than one huge investor telling me what to do.</p><h3>Accounting and taxes</h3><p>All of the above is a fairly unusual way of making a living, so there isn&#8217;t a lot of guidance from Swedish tax authorities on how to report things, etc. I wanted to make sure that everything is done correctly, and that I pay all the taxes I&#8217;m supposed to, so I ended up hiring an accounting consultant.</p><p>They advised me to start a business, since it would make accounting much more straightforward, so in 2021 I formed <strong>Cerphus Software AB</strong>. The name comes from the imaginary software company I had as a child. :^)</p><blockquote><p>I was trying to come up with a nice name for my business, and while there were many good options, I decided to fulfill a childhood dream and turn my then-imaginary "software company" into a real one.<br><br>I've just signed the documents forming Cerphus Software AB &#128102;&#128187;&#129420; <a href="https://t.co/LK2lPfSw7Z">https://t.co/LK2lPfSw7Z</a></p><p>&#8212; Andreas Kling (@awesomekling) <a href="https://twitter.com/awesomekling/status/1409466985608585221?ref_src=twsrc%5Etfw">June 28, 2021</a></p></blockquote><p>I&#8217;m extremely glad that I hired an accountant. It cost a bit of money to get everything up and running, but we&#8217;ve settled into a comfortable routine, and they only need to spend a little bit of time per month on filing everything for me.</p><p>In fact, other than the occasional computer part, accounting services is the only real expense I have.</p><h3>Summary &amp; closing thoughts</h3><p>As you can see, the numbers above put me at roughly <strong>$4200</strong> this month. My wife and I live a modest life, and while taxes in Sweden are high, this is enough to break even where we are right now.</p><p>I know I could make a lot more money doing something else, but having the freedom to work on SerenityOS (and Jakt and Ladybird) in peace is worth infinitely more.</p><p>It&#8217;s incredibly humbling to have so many people support me financially so that I can continue my work. My massive heartfelt thanks to everyone who has supported me in the past and present! &#10084;&#65039;</p><p>In the future, I would love to be able to pay more people to work on the project. Especially now that we&#8217;re making a truly independent open source cross-platform web browser, I think there&#8217;s a lot of room to grow. I don&#8217;t know exactly how to do this yet, but when/if there&#8217;s enough support, I will find a way to restructure so I can hire people.</p><h3>My Links</h3><p><a href="https://youtube.com/andreaskling">YouTube</a> | <a href="https://patreon.com/awesomekling">Patreon</a> | <a href="https://github.com/sponsors/awesomekling">GitHub Sponsors</a> | <a href="https://paypal.me/awesomekling">PayPal</a> | <a href="https://store.serenityos.org/">Teespring</a> | <a href="https://etherscan.io/address/0xae3C5A0b9AD28F8C7B0d0c56C1109411dE4EC029">Ethereum</a></p>]]></content:encoded></item><item><title><![CDATA[Ladybird: A new cross-platform browser project]]></title><description><![CDATA[This post describes the Ladybird browser, based on the LibWeb and LibJS engines from SerenityOS.]]></description><link>https://awesomekling.substack.com/p/ladybird-a-new-cross-platform-browser-project</link><guid isPermaLink="false">https://awesomekling.substack.com/p/ladybird-a-new-cross-platform-browser-project</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Mon, 12 Sep 2022 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0ceb4437-7af2-4c68-b8ce-2e2c51fd8f61_822x659.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This post describes the Ladybird browser, based on the LibWeb and LibJS engines from SerenityOS.</p><div><hr></div><p>Since starting the <a href="https://serenityos.org">SerenityOS</a> project in 2018, my goal has been &#8220;to build a complete desktop operating system to eventually use as my daily driver&#8221;.</p><p>What started as <a href="https://awesomekling.github.io/I-quit-my-job-to-focus-on-SerenityOS-full-time/">a little therapy project</a> for myself has blossomed into a huge OSS community with hundreds of people working on it all over the world. We&#8217;ve gone from <em>nothing</em> to a capable system with its own browser stack in the last 4 years.</p><p>Throughout this incredible expansion, my own goals have remained the same. Today I&#8217;m updating them a little bit: <em>in addition to building a new OS for myself, I&#8217;m also going to build a cross-platform web browser.</em></p><h2>A browser is born</h2><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!LODF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e3552-7f36-469c-88ff-1e899f546cca_822x659.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!LODF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e3552-7f36-469c-88ff-1e899f546cca_822x659.png 424w, https://substackcdn.com/image/fetch/$s_!LODF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e3552-7f36-469c-88ff-1e899f546cca_822x659.png 848w, https://substackcdn.com/image/fetch/$s_!LODF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e3552-7f36-469c-88ff-1e899f546cca_822x659.png 1272w, https://substackcdn.com/image/fetch/$s_!LODF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e3552-7f36-469c-88ff-1e899f546cca_822x659.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!LODF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e3552-7f36-469c-88ff-1e899f546cca_822x659.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/362e3552-7f36-469c-88ff-1e899f546cca_822x659.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;This post in Ladybird&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="This post in Ladybird" title="This post in Ladybird" srcset="https://substackcdn.com/image/fetch/$s_!LODF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e3552-7f36-469c-88ff-1e899f546cca_822x659.png 424w, https://substackcdn.com/image/fetch/$s_!LODF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e3552-7f36-469c-88ff-1e899f546cca_822x659.png 848w, https://substackcdn.com/image/fetch/$s_!LODF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e3552-7f36-469c-88ff-1e899f546cca_822x659.png 1272w, https://substackcdn.com/image/fetch/$s_!LODF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F362e3552-7f36-469c-88ff-1e899f546cca_822x659.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a><p>The <a href="https://github.com/SerenityOS/ladybird">Ladybird</a> browser came to life on July 4th, when I recorded <a href="https://www.youtube.com/watch?v=X38MTKHt3_I">a video of myself making a simple Qt GUI for the LibWeb browser engine</a>. Thanks to some recent work by <a href="https://github.com/dexesttp">Dex</a> and others, we had LibWeb building on Linux in headless mode, so I decided to push ahead and build a simple GUI around it.</p><p>I originally imagined Ladybird as a debugging tool that made it easier for people to remain in Linux while working on LibWeb if they wanted to. It&#8217;s now two months later, and I find myself using Ladybird for most of my own browser development work.</p><p>At this point, we might as well tweak the scope from &#8220;browser engine for SerenityOS&#8221; to &#8220;cross-platform browser engine&#8221; and build something that many more people could potentially have use for some day. :^)</p><p>Note that <a href="https://github.com/SerenityOS/serenity/commit/a67e823838943b31fb7cea68bd592093e197cf16">LibWeb started back in 2019, then called LibHTML</a>:</p><pre><code>commit a67e823838943b31fb7cea68bd592093e197cf16
Author: Andreas Kling
Date:   Sat Jun 15 18:55:47 2019 +0200

    LibHTML: Start working on a simple HTML library.
</code></pre><p><a href="https://github.com/SerenityOS/serenity/commit/f5476be702009968468731df5e23cdeb68fdb6e0">LibJS began almost 9 months later, in 2020</a>:</p><pre><code>commit f5476be702009968468731df5e23cdeb68fdb6e0
Author: Andreas Kling
Date:   Sat Mar 7 19:42:11 2020 +0100

    LibJS: Start building a JavaScript engine for SerenityOS :^)
</code></pre><p>If you&#8217;re interested, you can see exactly how I started the LibJS engine, as the whole process was <a href="https://www.youtube.com/watch?v=byNwCHc_IIM">recorded for YouTube</a> :^)</p><h2>Basic architecture</h2><p>Both LibWeb and LibJS are novel engines. I have a personal history with the Qt and WebKit projects, so there&#8217;s some inspiration from them throughout, but all the code is new. Not to mention, hundreds of people have worked on the codebase since I started it, all adding their own personal influences, so it&#8217;s definitely its own thing.</p><p>The browser and libraries are all written in C++. (While our own memory-safe <a href="https://awesomekling.github.io/Memory-safety-for-SerenityOS/">Jakt</a> language is in heavy development, it&#8217;s not yet ready for use in Ladybird.)</p><p>Here&#8217;s a rough breakdown of the current stack:</p><ul><li><p><strong><a href="https://github.com/SerenityOS/ladybird">Ladybird</a></strong>: Tabbed browser GUI application</p></li><li><p><strong><a href="https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibWeb">LibWeb</a></strong>: Web engine, multiple standards: HTML, DOM, CSS, SVG, &#8230;</p></li><li><p><strong><a href="https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibJS">LibJS</a></strong>: The ECMAScript language, runtime library, garbage collector</p></li><li><p><strong><a href="https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibGfx">LibGfx</a></strong>: 2D graphics, text rendering, image formats (PNG, JPG, GIF, &#8230;)</p></li><li><p><strong><a href="https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibRegex">LibRegex</a></strong>: Regular expression engine</p></li><li><p><strong><a href="https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibXML">LibXML</a></strong>: XML parser</p></li><li><p><strong><a href="https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibWasm">LibWasm</a></strong>: WebAssembly parser and interpreter</p></li><li><p><strong><a href="https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibUnicode">LibUnicode</a></strong>: Unicode support library</p></li><li><p><strong><a href="https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibTextCodec">LibTextCodec</a></strong>: Text encoding conversion library</p></li><li><p><strong><a href="https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibMarkdown">LibMarkdown</a></strong>: Markdown parser</p></li><li><p><strong><a href="https://github.com/SerenityOS/serenity/tree/master/Userland/Libraries/LibCore">LibCore</a></strong>: Miscellaneous support functions (I/O, datetime, MIME data, &#8230;)</p></li><li><p><strong><a href="https://www.qt.io">Qt</a></strong>: Cross-platform GUI and networking</p></li></ul><p>LibWeb has a <code>Platform</code> layer where Ladybird injects Qt support code for event loops, timers, system font settings, etc. We currently use Qt for networking in Ladybird, as the multi-process RequestServer system is not available outside of SerenityOS yet. Likewise, Ladybird is currently single-process, while the SerenityOS browser is process-per-tab. All of this is temporary and will change over time.</p><h2>License &amp; &#8220;business model&#8221;</h2><p>Ladybird and its engine are freely available under the <a href="https://opensource.org/licenses/BSD-2-Clause">2-clause BSD license</a>. You cannot buy influence over the project, but you can improve the browser by participating in development!</p><p>I&#8217;m personally working on these projects <a href="https://awesomekling.github.io/I-quit-my-job-to-focus-on-SerenityOS-full-time/">full time since 2021</a> thanks to my generous supporters. If you like what I&#8217;m doing, you can help me do more of it by supporting me on <a href="https://github.com/sponsors/awesomekling">GitHub Sponsors</a>, <a href="https://patreon.com/awesomekling">Patreon</a> or <a href="https://paypal.me/awesomekling">PayPal</a>.</p><p>I would <em>love</em> to have enough money to pay others to work on Ladybird some day. At the moment, I make just enough to support my own family, but if things should grow past the point where I&#8217;m comfortable, I will look into restructuring so I can hire more help.</p><p>In addition to myself, you can already directly sponsor <a href="https://github.com/sponsors/linusg">Linus Groh</a> and <a href="https://github.com/sponsors/AtkinsSJ">Sam Atkins</a> who both do a lot of excellent browser work.</p><h2>A note on maturity</h2><p>Please note that we&#8217;re still early in development, and many web platform features are missing or broken. It&#8217;s going to take a long time before Ladybird is ready for day-to-day browsing.</p><p>We&#8217;re very much in the &#8220;make it work&#8221; part of the &#8220;make it work, make it good, make it faster&#8221; cycle. As such, we tend to focus a lot more on correctness and feature support rather than optimization. Performance work happens mostly at the architectural level, although targeted optimizations that relieve particular pain points do also happen.</p><p>Please note that this is <em>not</em> a product announcement or release, but more of a personal announcement that I&#8217;m adding <em>&#8220;a truly independent cross-platform browser&#8221;</em> to my list of personal goals. It&#8217;s also an invitation to anyone who might be interested in working on a completely new browser. :^)</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sIqi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d4ba42f-40c4-42c7-894d-643df64e35a7_822x659.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sIqi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d4ba42f-40c4-42c7-894d-643df64e35a7_822x659.png 424w, https://substackcdn.com/image/fetch/$s_!sIqi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d4ba42f-40c4-42c7-894d-643df64e35a7_822x659.png 848w, https://substackcdn.com/image/fetch/$s_!sIqi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d4ba42f-40c4-42c7-894d-643df64e35a7_822x659.png 1272w, https://substackcdn.com/image/fetch/$s_!sIqi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d4ba42f-40c4-42c7-894d-643df64e35a7_822x659.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sIqi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d4ba42f-40c4-42c7-894d-643df64e35a7_822x659.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5d4ba42f-40c4-42c7-894d-643df64e35a7_822x659.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Acid3 passes in Ladybird&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Acid3 passes in Ladybird" title="Acid3 passes in Ladybird" srcset="https://substackcdn.com/image/fetch/$s_!sIqi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d4ba42f-40c4-42c7-894d-643df64e35a7_822x659.png 424w, https://substackcdn.com/image/fetch/$s_!sIqi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d4ba42f-40c4-42c7-894d-643df64e35a7_822x659.png 848w, https://substackcdn.com/image/fetch/$s_!sIqi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d4ba42f-40c4-42c7-894d-643df64e35a7_822x659.png 1272w, https://substackcdn.com/image/fetch/$s_!sIqi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d4ba42f-40c4-42c7-894d-643df64e35a7_822x659.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p>As you can see above, we do pass the classic <a href="https://en.wikipedia.org/wiki/Acid3">Acid3 standards test</a>, which covers a bunch of basic CSS layout features, and various DOM/HTML APIs. However, the test does not cover many of the features used on the web today (like CSS flexbox, CSS grid, etc.)</p><p>Fidelity of modern websites in Ladybird is steadily improving, but you&#8217;ll often see lots of layout and compatibility issues. For example, here&#8217;s Reddit right now:</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!D75z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61230c4f-6f41-4f86-afa6-469ab138df52_822x659.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!D75z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61230c4f-6f41-4f86-afa6-469ab138df52_822x659.png 424w, https://substackcdn.com/image/fetch/$s_!D75z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61230c4f-6f41-4f86-afa6-469ab138df52_822x659.png 848w, https://substackcdn.com/image/fetch/$s_!D75z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61230c4f-6f41-4f86-afa6-469ab138df52_822x659.png 1272w, https://substackcdn.com/image/fetch/$s_!D75z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61230c4f-6f41-4f86-afa6-469ab138df52_822x659.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!D75z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61230c4f-6f41-4f86-afa6-469ab138df52_822x659.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/61230c4f-6f41-4f86-afa6-469ab138df52_822x659.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;/r/programming in Ladybird&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="/r/programming in Ladybird" title="/r/programming in Ladybird" srcset="https://substackcdn.com/image/fetch/$s_!D75z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61230c4f-6f41-4f86-afa6-469ab138df52_822x659.png 424w, https://substackcdn.com/image/fetch/$s_!D75z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61230c4f-6f41-4f86-afa6-469ab138df52_822x659.png 848w, https://substackcdn.com/image/fetch/$s_!D75z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61230c4f-6f41-4f86-afa6-469ab138df52_822x659.png 1272w, https://substackcdn.com/image/fetch/$s_!D75z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F61230c4f-6f41-4f86-afa6-469ab138df52_822x659.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><h2>Community acknowledgment</h2><p>Sometimes people write articles saying I&#8217;m &#8220;single-handedly&#8221; doing this or that. I&#8217;m not! Both Ladybird and SerenityOS are community efforts that hundreds of awesome people are pouring their heart and soul into. I write a lot of code, and I do my best to cheerlead for the community, but I absolutely wouldn&#8217;t be here without them!</p><h2>FAQ</h2><h4><strong>Q: Which platforms will Ladybird support?</strong></h4><p>So far, we&#8217;ve seen it running on Linux, macOS, Windows (WSL) and Android. The Linux version is definitely the most tested.</p><p>Since the libraries come from SerenityOS, they&#8217;re already self-contained, and we only need Qt to help us with GUI and networking. This makes the browser quite portable, and in <em>theory</em> we could run wherever Qt runs. In practice, we&#8217;ll see what happens.</p><h4><strong>Q: When will Ladybird be ready for use?</strong></h4><p>I don&#8217;t know. It depends on what you consider &#8220;ready&#8221;, but I&#8217;d expect a few more years of development before we have something solid. You can accelerate this process by participating in development and/or supporting our developers financially.</p><h4><strong>Q: How can I participate in development?</strong></h4><p>The most important work ahead of us is fixing bugs and adding missing features to LibWeb and LibJS. If you try opening your favorite website in Ladybird, you will find bugs! To participate in development, figure out the bug and fix it. Development discussion primarily happens in the <code>#browser</code> and <code>#js</code> channels on <a href="https://discord.gg/serenityos">our Discord server</a>, so come join us there.</p><h4><strong>Q: I found a website that does not work! Where do I report this?</strong></h4><p>At this point, there are <em>way</em> more websites that don&#8217;t work than websites that do. We&#8217;re not yet at the point where reporting individual site issues makes sense.</p><p>That said, if you&#8217;re going to actually work on fixing the problems, feel free to track them using GitHub issues if that helps you.</p><h4><strong>Q: Do you have a JavaScript JIT compiler?</strong></h4><p>No, we have a traditional AST interpreter that is being replaced by a bytecode VM. You can track the <a href="https://libjs.dev/test262/">LibJS test262 score for both backends here</a>. I&#8217;m not convinced that the complexity and security burdens of a JavaScript JIT are reasonable, and given recent developments like Microsoft Edge&#8217;s <a href="https://microsoftedge.github.io/edgevr/posts/Super-Duper-Secure-Mode/">Super Duper Secure Mode</a>, I&#8217;m interested in pushing for best-effort JIT-less performance while keeping the codebase simple.</p><h4><strong>Q: I opened Acid3 in my browser and I only got 97/100. What&#8217;s wrong?</strong></h4><p>You&#8217;re using an older version of the test that does not reflect settled web standards. The up-to-date version is here: <a href="http://wpt.live/acid/acid3/test.html">http://wpt.live/acid/acid3/test.html</a>.</p><h4><strong>Q: Why bother? You can&#8217;t make a new browser engine without billions of dollars and hundreds of staff.</strong></h4><p>Sure you can. Don&#8217;t listen to armchair defeatists who never worked on a browser.</p><h2>Contact</h2><p>If you&#8217;re interested in working on Ladybird, LibWeb, LibJS, or any part of the supporting stack, you can find us on the <a href="https://discord.gg/serenityos">SerenityOS Discord</a>. :^)</p>]]></content:encoded></item><item><title><![CDATA[Memory safety for SerenityOS]]></title><description><![CDATA[This post describes how we&#8217;re going to achieve memory safety in SerenityOS.]]></description><link>https://awesomekling.substack.com/p/memory-safety-for-serenityos</link><guid isPermaLink="false">https://awesomekling.substack.com/p/memory-safety-for-serenityos</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Thu, 19 May 2022 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fd680c7b-9631-4787-bc3e-a0ed58cdc142_486x86.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This post describes how we&#8217;re going to achieve memory safety in SerenityOS.</p><div><hr></div><p>After visiting my nephews for easter, I spent the drive back home thinking about the future they will grow up in. What will their computers look like? What kind of software will they use? Will any of my code still be running?</p><p>When I started the <a href="https://github.com/SerenityOS/serenity">SerenityOS</a> project in 2018, I used C++ for everything, simply because it was the language I was most comfortable with. It was the right choice at the time, as it allowed me to bootstrap the project (and a community) very quickly and efficiently.</p><p>In the time since then, SerenityOS has grown larger and more complex, and we recently passed 700 individual contributors! It&#8217;s <em>far</em> from a one-man hobby project at this point.</p><p>When thinking about the future, I would love for SerenityOS to be around in 30 years, when my nephews are my age (and I&#8217;m an old greybeard!)</p><p>While I believe our community and system architectures are strong enough to sustain development for years to come, I no longer believe that C++ is the right language for us.</p><p>As much as I enjoy using it, the lack of memory safety in C++ means that we&#8217;ll always have bugs that could have been avoided. I&#8217;m tired of this, and I don&#8217;t want to spend the next decades of my life debugging more avoidable bugs.</p><p>To improve the longevity of SerenityOS, we need to make the system memory-safe.</p><h2>So how do we achieve memory safety?</h2><p>I spent a few weeks exploring the current landscape of memory-safe systems languages. I learned a handful of new ones so I could see how they work, and understand what they do to achieve safety.</p><p>I tried rewriting parts of SerenityOS in different languages, and while there were some interesting options, they all came with idiosyncratic limitations and dependencies that made them unsuitable for adoption.</p><p>Since the beginning, SerenityOS has been about making everything ourselves, for fun, for love of programming, for control, for performance, for vertical integration, etc.</p><p>In fact, the main thing we haven&#8217;t made ourselves is a programming language.</p><h2>Yak-baiting a friend</h2><p>Throughout this process, I&#8217;d been talking to <a href="https://twitter.com/jntrnr">my friend JT</a> and sharing the struggle I had with the various languages. After talking their ear off about why some language wasn&#8217;t a good fit, I got this intriguing message:</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kZD1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74c2db43-aafe-47c1-ba76-06863f36233f_486x86.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kZD1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74c2db43-aafe-47c1-ba76-06863f36233f_486x86.png 424w, https://substackcdn.com/image/fetch/$s_!kZD1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74c2db43-aafe-47c1-ba76-06863f36233f_486x86.png 848w, https://substackcdn.com/image/fetch/$s_!kZD1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74c2db43-aafe-47c1-ba76-06863f36233f_486x86.png 1272w, https://substackcdn.com/image/fetch/$s_!kZD1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74c2db43-aafe-47c1-ba76-06863f36233f_486x86.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kZD1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74c2db43-aafe-47c1-ba76-06863f36233f_486x86.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/74c2db43-aafe-47c1-ba76-06863f36233f_486x86.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;JT saying \&quot;So... I'm totally yak baited at this point\&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="JT saying &quot;So... I'm totally yak baited at this point&quot;" title="JT saying &quot;So... I'm totally yak baited at this point&quot;" srcset="https://substackcdn.com/image/fetch/$s_!kZD1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74c2db43-aafe-47c1-ba76-06863f36233f_486x86.png 424w, https://substackcdn.com/image/fetch/$s_!kZD1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74c2db43-aafe-47c1-ba76-06863f36233f_486x86.png 848w, https://substackcdn.com/image/fetch/$s_!kZD1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74c2db43-aafe-47c1-ba76-06863f36233f_486x86.png 1272w, https://substackcdn.com/image/fetch/$s_!kZD1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F74c2db43-aafe-47c1-ba76-06863f36233f_486x86.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a><p><strong>NOTE: "yakbait" is <a href="https://github.com/SerenityOS/yaksplained#yakbait-nerd-snipe">SerenityOS slang</a> for baiting someone into shaving your yak</strong></p><p>JT went on to suggest that instead of settling for an existing language, we could design a new language by simply stealing the stuff we liked from other languages, and skipping the stuff we didn&#8217;t like or need.</p><p>To simplify incremental adoption, the new language would transpile to C++, which could then easily interact with our existing code.</p><p>I was hooked. <em>Transpiling to C++!?</em> I didn&#8217;t even realize that was an option!</p><p>We decided to name it <strong>Jakt</strong> (<a href="https://en.wiktionary.org/wiki/jakt#Swedish">Swedish for &#8220;hunt&#8221;</a>). What followed was 2 weeks of intense compiler bootstrapping with JT.</p><p>The language is now at a point where I feel comfortable telling you that we&#8217;re working on it, but it&#8217;s still a <em><strong>long</strong></em> way from &#8220;ready&#8221;.</p><h2>Jakt</h2><p>So, let&#8217;s take a look at <strong>Jakt</strong>! It&#8217;s&#8230;</p><ul><li><p>Object-oriented</p></li><li><p>Safe by default</p></li><li><p>Paranoid about integer overflow &amp; truncation</p></li><li><p>Immutable by default</p></li><li><p><em>Young and immature, not ready for anything serious</em></p></li></ul><p>The current Jakt compiler is written in Rust and spits out C++. Development happens in the <a href="https://github.com/SerenityOS/jakt">jakt</a> repository on GitHub.</p><h3>A little Jakt program</h3><pre><code>class Language {
    name: String
    age_in_days: i64
    
    function greet(this) {
        println("Hello from {}!", this.name)
        println("I am this many days old:")
        for i in 0..this.age_in_days {
            println(":^)")
        }
    }
}

function main() {
    let jakt = Language(name: "Jakt", age_in_days: 14)
    jakt.greet()
}
</code></pre><h3>Memory safety</h3><p>So how does Jakt achieve memory safety? Through a combination of these techniques:</p><ul><li><p>Automatic reference counting of all <code>class</code> instances.</p></li><li><p>Runtime bounds checking of arrays and slices.</p></li><li><p>No dereferencing raw pointers in safe (default) code. <code>unsafe</code> keyword for situations where it&#8217;s necessary.</p></li><li><p><code>weak</code> references that get emptied on pointee destruction.</p></li></ul><h2>Roadmap</h2><p>Here&#8217;s a <em>very</em> fluffy 10 year roadmap for this project:</p><ul><li><p>Bring up basic language features.</p></li><li><p>Improve static analysis in the compiler.</p></li><li><p>Write an auto-formatter for Jakt.</p></li><li><p>Rewrite the Jakt compiler in Jakt.</p></li><li><p>Integrate Jakt with the SerenityOS build system.</p></li><li><p>Incrementally rewrite SerenityOS in Jakt.</p></li><li><p>Stop transpiling to C++ and generate native code directly.</p></li></ul><h2>FAQ</h2><h4><strong>Q: Why not just use an existing language?</strong></h4><p>I have nothing bad to say about other languages. This is simply the option that makes the most sense for SerenityOS, which is fundamentally about having fun and implementing everything ourselves.</p><h4><strong>Q: Why does Jakt have flaws?</strong></h4><p>It&#8217;s two weeks old.</p><h4><strong>Q: When will Jakt be done? Will it hit 1.0?</strong></h4><p>It will evolve with SerenityOS, so as SerenityOS matures no doubt Jakt will as well.</p><h4><strong>Q: Why ARC (automatic reference counting) instead of a borrow checker?</strong></h4><p>ARC allows the language to feel lightweight without constantly asking the user to make decisions about memory management.</p><p>There&#8217;s a little bit of overhead from maintaining reference counts, but we&#8217;re betting that the comfort gained will outweigh the cost.</p><h4><strong>Q: What about iterator invalidation?</strong></h4><p>We&#8217;re discussing a number of approaches, but have not settled on one yet.</p><h4><strong>Q: What about thread safety?</strong></h4><p>Jakt currently does nothing to enforce thread safety. We have not started looking at this area yet.</p><h2>Contact</h2><p>If you&#8217;re interested in working on Jakt, you can find us on the <a href="https://discord.gg/serenityos">SerenityOS Discord</a>. :^)</p>]]></content:encoded></item><item><title><![CDATA[I quit my job to focus on SerenityOS full time]]></title><description><![CDATA[Hello friends!]]></description><link>https://awesomekling.substack.com/p/i-quit-my-job-to-focus-on-serenityos-full-time</link><guid isPermaLink="false">https://awesomekling.substack.com/p/i-quit-my-job-to-focus-on-serenityos-full-time</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Fri, 28 May 2021 00:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/eb0a77c0-6660-4e38-8190-49fae60be1f6_1024x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello friends! :^) Let me tell you a story&#8230;</p><p>It was October 2018 and I had just completed a 3-month rehab program at a state addiction clinic in Sweden. I was unemployed, staying with family, and had basically nothing going on.</p><p>With no drugs or other vices to pass the time, the days seemed impossibly long. I struggled to find activities to fill them. I enrolled in school for a while, but it wasn&#8217;t for me this time either. Eventually I turned to programming, since it&#8217;s always been my big interest in life.</p><p>Until that point, my career had been focused on web browsers (WebKit at Apple &amp; Nokia). However, I had always been interested in low-level things so I began <a href="http://www.serenityos.org/happy/1st/">tinkering</a> with some of that. I wrote a little ELF executable parser.. And an Ext2 filesystem browser.. And a little GUI framework with an event loop..</p><p>Out of this tinkering, an operating system began to take shape. I chose the name <strong><a href="https://www.serenityos.org/">SerenityOS</a></strong> because I wanted to always remember the <a href="https://en.wikipedia.org/wiki/Serenity_Prayer">Serenity Prayer</a>. I was quite worried about my future at the time, and I figured that this name would help me stay on the good path.</p><p>My general idea was to build my own dream system for daily use. It would be a combination of my two favorite computing paradigms: the 1990s GUI and the no-nonsense command-line of late-2000s Unix.</p><p>While at Apple, I really enjoyed how most of the software was made under one roof. Not only did this enable super tight integrations, but it made the system extremely hackable for its developers, and you could always find <em>the</em> experts somewhere nearby. I thought I could try bringing that same feeling to the open source world, so I decided that SerenityOS wasn&#8217;t going to be a patchwork of packages &#8211; no, we&#8217;re building everything ourselves! From kernel to web browser, and everything in between.</p><p>So I started hacking, day after day. Week after week. Month after month. The project became my rock as I slowly learned to navigate life again.</p><p>I decided to record some of my programming sessions and posted them to <a href="https://www.youtube.com/andreaskling">my YouTube channel</a>. I was <em>very</em> uncomfortable with this at first, but I kept at it since I liked the feeling of just being myself and letting people see me, instead of putting on a mask and pretending everything was fine.</p><p>Over time, more people discovered my little project (and my little channel), and many found something that really resonated with them. Since those humble beginnings, it has grown into a vibrant open source community with hundreds of <a href="https://github.com/SerenityOS/serenity/graphs/contributors">contributors</a> from all over the world. To say that this has been an amazing journey would be an understatement, yet in many ways we are still only getting started.</p><p>Until now, I&#8217;ve been juggling SerenityOS as a side project while also having a full time programming job.</p><p>That all changes today! I just wrapped up my last day at work, and I&#8217;m no longer employed. Instead, I will be focusing on SerenityOS full time starting <em>right now</em>! :^)</p><p>This is all made possible by the extremely generous support I&#8217;m receiving from folks via <a href="https://patreon.com/awesomekling">Patreon</a>, <a href="https://github.com/sponsors/awesomekling">GitHub Sponsors</a> and <a href="https://paypal.me/awesomekling">PayPal</a>! I feel super fortunate to have the trust &amp; support of so many people. Thank you all so much!!</p><p>At the time of writing, I&#8217;m receiving a bit over $2000 in donations per month. There&#8217;s also a modest amount from <a href="https://youtube.com/c/andreaskling">YouTube</a> (say $150/month), as well as sales of <a href="https://serenityos.creator-spring.com/">SerenityOS merchandise</a> (another $100).</p><p>This isn&#8217;t yet enough to fully sustain me and my family, but it&#8217;s close enough that I decided it&#8217;s time to take a chance and see what happens!</p><p>As you probably understand, I&#8217;m not trying to get rich by doing this. I&#8217;m just a human being trying to stay sane and healthy, and it just so happens that my therapy/self-care project resonates with thousands of people, many of whom want to support it and see where it goes.</p><p>It&#8217;s truly an honor to find myself in this position, and I promise that I will continue doing my best. Thank you so much for reading this post. I&#8217;m gonna have a nap and then we&#8217;ll start fresh in the morning! :^)</p><p>Andreas Kling</p><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7XRC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50ae6692-b42d-4e43-8ffa-9dcd894eb6b4_1024x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7XRC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50ae6692-b42d-4e43-8ffa-9dcd894eb6b4_1024x768.png 424w, https://substackcdn.com/image/fetch/$s_!7XRC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50ae6692-b42d-4e43-8ffa-9dcd894eb6b4_1024x768.png 848w, https://substackcdn.com/image/fetch/$s_!7XRC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50ae6692-b42d-4e43-8ffa-9dcd894eb6b4_1024x768.png 1272w, https://substackcdn.com/image/fetch/$s_!7XRC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50ae6692-b42d-4e43-8ffa-9dcd894eb6b4_1024x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7XRC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50ae6692-b42d-4e43-8ffa-9dcd894eb6b4_1024x768.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/50ae6692-b42d-4e43-8ffa-9dcd894eb6b4_1024x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Screenshot of editing this post in today's build of SerenityOS&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Screenshot of editing this post in today's build of SerenityOS" title="Screenshot of editing this post in today's build of SerenityOS" srcset="https://substackcdn.com/image/fetch/$s_!7XRC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50ae6692-b42d-4e43-8ffa-9dcd894eb6b4_1024x768.png 424w, https://substackcdn.com/image/fetch/$s_!7XRC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50ae6692-b42d-4e43-8ffa-9dcd894eb6b4_1024x768.png 848w, https://substackcdn.com/image/fetch/$s_!7XRC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50ae6692-b42d-4e43-8ffa-9dcd894eb6b4_1024x768.png 1272w, https://substackcdn.com/image/fetch/$s_!7XRC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F50ae6692-b42d-4e43-8ffa-9dcd894eb6b4_1024x768.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a>]]></content:encoded></item><item><title><![CDATA[Smarter C/C++ inlining with __attribute__((flatten))]]></title><description><![CDATA[This post describes a compile-time technique for getting the benefits of aggressive inlining in hot code while protecting cool code from its downsides.]]></description><link>https://awesomekling.substack.com/p/smarter-c-inlining-with-attribute-flatten</link><guid isPermaLink="false">https://awesomekling.substack.com/p/smarter-c-inlining-with-attribute-flatten</guid><dc:creator><![CDATA[Andreas Kling]]></dc:creator><pubDate>Mon, 27 Apr 2020 00:00:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!t_mX!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05881c5b-1c63-4bfd-8c93-b93680f4b388_460x460.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This post describes a compile-time technique for getting the benefits of aggressive inlining in hot code while protecting cool code from its downsides.</p><div><hr></div><p>Hello friends!</p><p>A common technique for improving performance of hot code in C/C++ is to inline the hottest functions called. While it often helps make things faster, there are some downsides to inlining. Let&#8217;s quickly review the pros &amp; cons:</p><p><strong>Pros of inlining:</strong></p><ul><li><p>Removes function call overhead (yay!)</p></li><li><p>May reveal additional optimization opportunities (sometimes yay!)</p></li></ul><p><strong>Cons of inlining:</strong></p><ul><li><p>Increases program size (boo!)</p></li><li><p>May reduce cache locality (sometimes boo!)</p></li><li><p>May increase build times (boo!)</p></li></ul><p>When compiling with optimizations, the compiler usually makes pretty reasonable choices about which functions to inline. It uses a combination of heuristics, with function size being the most important one AFAIK.</p><h2>Manual inlining with <code>__attribute__((always_inline))</code></h2><p>However, sometimes <em>you</em> know some code is <strong>hot</strong> and the compiler has no idea. This is usually when <code>__attribute__((always_inline))</code> comes in. If you add this attribute to a function, that function will now be inlined wherever it is called, even when the compiler would normally have dismissed it as too large. <em>(Note that there are exceptions to this, and some functions cannot be inlined.)</em></p><p>Here&#8217;s a contrived example of a very common scenario in larger codebases:</p><pre><code>__attribute__((always_inline)) inline void do_thing(int input)
{
    // this code is always inlined at the call site
}

void hot_code()
{
    // the program spends &gt;80% of its runtime in this function
    while (condition) {
        ...
        do_thing(y);
        ...
    }
}
</code></pre><p>The above is all well and good, but what happens when <code>do_thing()</code> is a popular function that gets called a lot?</p><pre><code>void cool_code()
{
    // the program spends &lt;5% of its runtime in this function
    ...
    do_thing(a);
    do_thing(b);
    do_thing(c);
}
</code></pre><p>Now the <code>cool_code()</code> function gets three copies of <code>do_thing()</code> inlined into it, invoking all of the cons from the list we made above (larger program size, worse cache locality, longer build time.)</p><h2>Targeted flattening instead of global inlining</h2><p>Now for the trick! Both GCC and Clang support <code>__attribute__((flatten))</code>. Putting it on a function causes all of its callees to be inlined into it. It&#8217;s dead simple.</p><pre><code>void do_thing(int input)
{
    // this code is not always inlined at the call site
}

__attribute__((flatten)) void hot_code()
{
    // the program spends &gt;80% of its runtime in this function
    while (condition) {
        call_something();   // inlined!
        do_thing(y);        // inlined!
        other_thing();      // also inlined!
    }
}

void cool_code()
{
    // the program spends &lt;5% of its runtime in this function
    ...
    do_thing(a);            // not inlined!
    do_thing(b);            // not inlined!
    do_thing(c);            // guess!
}
</code></pre><p><em>Note: Functions with <code>__attribute__((noinline))</code> will not be inlined. The same goes for functions where the compiler can&#8217;t see the body.</em></p><h2>In conclusion</h2><p><code>__attribute__((flatten))</code> lets you opt in to the pros of aggressive inlining on a per-function basis, while protecting the rest of your program from the cons!</p><p>Until next time! :^)</p>]]></content:encoded></item></channel></rss>