<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.1.1">Jekyll</generator><link href="https://blogs.igalia.com/obrufau/feed/by_tag/planet.xml" rel="self" type="application/atom+xml" /><link href="https://blogs.igalia.com/obrufau/" rel="alternate" type="text/html" /><updated>2021-07-21T14:55:34+02:00</updated><id>https://blogs.igalia.com/obrufau/feed/by_tag/planet.xml</id><title type="html">Oriol Brufau’s blog</title><subtitle>Just another Igalia Blogs site</subtitle><entry><title type="html">Improved raster transforms with non-uniform scales</title><link href="https://blogs.igalia.com/obrufau/2021/07/20/improved-raster-transforms-with-non-uniform-scales.html" rel="alternate" type="text/html" title="Improved raster transforms with non-uniform scales" /><published>2021-07-20T23:30:00+02:00</published><updated>2021-07-20T23:30:00+02:00</updated><id>https://blogs.igalia.com/obrufau/2021/07/20/improved-raster-transforms-with-non-uniform-scales</id><content type="html" xml:base="https://blogs.igalia.com/obrufau/2021/07/20/improved-raster-transforms-with-non-uniform-scales.html">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.w3.org/TR/css-transforms-1/&quot;&gt;CSS Transforms Level 1&lt;/a&gt;
introduced 2D transforms, that can be specified using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transform&lt;/code&gt; property.
For example, they can be used to rotate or scale an element:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;span class=&quot;transform-example&quot; style=&quot;transform: none&quot;&gt;&lt;/span&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transform: none&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;span class=&quot;transform-example&quot; style=&quot;transform: rotate(45deg)&quot;&gt;&lt;/span&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transform: rotate(45deg)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;span class=&quot;transform-example&quot; style=&quot;transform: scale(1, 0.5)&quot;&gt;&lt;/span&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transform: scale(1, 0.5)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://www.w3.org/TR/css-transforms-2/&quot;&gt;CSS Transforms Level 2&lt;/a&gt; extends that feature to allow transforms in 3D space, for example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;span class=&quot;transform-example&quot; style=&quot;transform: rotate3d(1, 1, 1, 45deg)&quot;&gt;&lt;/span&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transform: rotate3d(1, 1, 1, 45deg)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;span class=&quot;transform-example&quot; style=&quot;transform: scale3d(1, 0.5, 2)&quot;&gt;&lt;/span&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transform: scale3d(1, 0.5, 2)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Typically, using 3D transforms forces the element into its own rendering layer.
This is sometimes desired by authors, since it can improve performance if for example the element is moving around.&lt;/p&gt;

&lt;p&gt;Therefore, identity transformations in the Z axis,
like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scale3d(X, Y, 1)&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scale(X, Y)&lt;/code&gt;,
are sometimes used to opt-in into this behavior.
This trick works on Chromium, but note it’s &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/3305&quot;&gt;not compliant with the spec&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;problems&quot;&gt;Problems&lt;/h2&gt;

&lt;p&gt;Forcing an element to be rasterized in its own layer can have some disadvantages.&lt;/p&gt;

&lt;p&gt;For example, Chromium used to rasterize it using a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;float&lt;/code&gt; scale.
When the transform had different X and Y scale components,
Chromium just picked the bigger one,
clamped by 5 times the smaller one (to avoid memory problems).
And then it used this raster scale for both axes,
producing suboptimal results.&lt;/p&gt;

&lt;p&gt;Also, Chromium only uses LCD text antialiasing when the internal raster scale
matches the actual X and Y scales in the transform.
Therefore, non-uniform scales prevented the nicer LCD antialiasing.&lt;/p&gt;

&lt;p&gt;And unrelated to uniform scales,
if the transformed element doesn’t have an opaque background,
LCD antialiasing is not used either,
since Chromium needs to know the color behind the text.&lt;/p&gt;

&lt;p&gt;The last problem remains unsolved, but I fixed the other two in Chromium 92,
which has been released today.&lt;/p&gt;

&lt;p&gt;Thanks to Bloomberg for sponsoring Igalia to do it!&lt;/p&gt;

&lt;h2 id=&quot;solution&quot;&gt;Solution&lt;/h2&gt;

&lt;p&gt;The main patch that addressed both problems was &lt;a href=&quot;https://crrev.com/872117&quot;&gt;https://crrev.com/872117&lt;/a&gt;.
But LCD text antialiasing was still not used because I made a mistake 😳,
which I fixed in &lt;a href=&quot;https://crrev.com/872974&quot;&gt;https://crrev.com/872974&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Basically, it was a matter of changing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AxisTransform2d&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PictureLayerImpl&lt;/code&gt;
to store a 2D scale rather than a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;float&lt;/code&gt;.
I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gfx::Vector2dF&lt;/code&gt;, which is like a pair of floats with some nice methods
to clamp by a minim or maximum, scale both floats by the same factor, etc.&lt;/p&gt;

&lt;p&gt;I kept most tiling logic as it was,
just taking the maximum component of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gfx::Vector2dF&lt;/code&gt; as the “scale key”.
However, different 2D scales can have the same key, for example by dynamically changing
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scale3d(1, 5, 1)&lt;/code&gt; into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scale3d(5, 1, 1)&lt;/code&gt;, both with a scale key of 5.
Therefore, when finding if there already was a tiling with the desired scale key,
I made sure to the check the 2D scales, and recreate the tiling if they were different.&lt;/p&gt;

&lt;p&gt;This is an example of how it looked like in Chromium:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2021/07/chromium-bad.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is how it looked when internally using 2D scales:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2021/07/chromium-good.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And finally, with LCD text antialiasing:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2021/07/chromium-good-lcd.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For reference, this is how your browser renders it (live example):&lt;/p&gt;

&lt;p class=&quot;test&quot;&gt;Lorem ipsum&lt;/p&gt;

&lt;p&gt;Comparing the 1st and 2nd images,
using 2D scales clearly improved the text,
which was hard to read due to missing some thin parts of the glyphs,
and also note the border diagonals in the corners look less jagged.&lt;/p&gt;

&lt;p&gt;At first glance it may be hard to notice
the difference between the 2nd and 3rd images,
so you can compare the text antialiasing in these magnified images:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2021/07/chromium-lcd-comparison.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At the top, the edges of the glyphs simply use grayscale antialiasing,
while at the bottom, the LCD antialiasing uses some colored pixels.&lt;/p&gt;

&lt;h2 id=&quot;limitations&quot;&gt;Limitations&lt;/h2&gt;

&lt;p&gt;While my patch improved the common basic cases,
Chromium will still fall back to a 1D scale in these cases:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Directly composited images&lt;/li&gt;
  &lt;li&gt;Animations&lt;/li&gt;
  &lt;li&gt;Heads up layers&lt;/li&gt;
  &lt;li&gt;Scrollbar layers&lt;/li&gt;
  &lt;li&gt;Mirror layers&lt;/li&gt;
  &lt;li&gt;When the layer has perspective&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of them may be addressed in the future, this is tracked in
&lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=1196414&quot;&gt;bug 1196414&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, this live example uses a CSS animation so it still looks wrong in Chromium 92:&lt;/p&gt;

&lt;p class=&quot;animation test&quot;&gt;Lorem ipsum&lt;/p&gt;

&lt;p&gt;I actually started &lt;a href=&quot;https://crrev.com/c/2860126&quot;&gt;a patch&lt;/a&gt;
to address animations, and it seemed to work well in simple cases,
but it could be wrong when ancestors had additional transforms.
Handling that properly would have required more complexity,
and it wasn’t clear that it was worth it.&lt;/p&gt;

&lt;p&gt;Therefore, I don’t plan to continue working on these edge cases,
but if you are affected by them,
you can star &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=1196414&quot;&gt;bug 1196414&lt;/a&gt;
and provide a good testcase.
This may help increase the priority of the bug!&lt;/p&gt;</content><author><name>Oriol Brufau</name></author><category term="planet" /><category term="chromium" /><summary type="html">Elements with 3D transforms with different X and Y scales look better in Chromium 92.</summary></entry><entry><title type="html">CSS ::marker pseudo-element in Chromium</title><link href="https://blogs.igalia.com/obrufau/2020/12/21/css-marker-pseudo-element-in-chromium.html" rel="alternate" type="text/html" title="CSS ::marker pseudo-element in Chromium" /><published>2020-12-21T22:00:00+01:00</published><updated>2020-12-21T22:00:00+01:00</updated><id>https://blogs.igalia.com/obrufau/2020/12/21/css-marker-pseudo-element-in-chromium</id><content type="html" xml:base="https://blogs.igalia.com/obrufau/2020/12/21/css-marker-pseudo-element-in-chromium.html">&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#introduction&quot; id=&quot;markdown-toc-introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#implementing-list-style-type-string&quot; id=&quot;markdown-toc-implementing-list-style-type-string&quot;&gt;Implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-style-type: &amp;lt;string&amp;gt;&lt;/code&gt;&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#parsing-and-computation&quot; id=&quot;markdown-toc-parsing-and-computation&quot;&gt;Parsing and computation&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#layout&quot; id=&quot;markdown-toc-layout&quot;&gt;Layout&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#marker-parsing-and-computation&quot; id=&quot;markdown-toc-marker-parsing-and-computation&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; parsing and computation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#using-marker-styles&quot; id=&quot;markdown-toc-using-marker-styles&quot;&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; styles&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#support-content-in-layoutng&quot; id=&quot;markdown-toc-support-content-in-layoutng&quot;&gt;Support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; in LayoutNG&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#default-styles&quot; id=&quot;markdown-toc-default-styles&quot;&gt;Default styles&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#some-fun-999-performance-regression&quot; id=&quot;markdown-toc-some-fun-999-performance-regression&quot;&gt;Some fun: 99.9% performance regression&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#developer-tools&quot; id=&quot;markdown-toc-developer-tools&quot;&gt;Developer tools&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#layoutng-markers-as-real-pseudo-elements&quot; id=&quot;markdown-toc-layoutng-markers-as-real-pseudo-elements&quot;&gt;LayoutNG markers as real pseudo-elements&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#legacy-markers-as-real-pseudo-elements&quot; id=&quot;markdown-toc-legacy-markers-as-real-pseudo-elements&quot;&gt;Legacy markers as real pseudo-elements&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#animations--transitions&quot; id=&quot;markdown-toc-animations--transitions&quot;&gt;Animations &amp;amp; transitions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#counterlist-item-inside-ol&quot; id=&quot;markdown-toc-counterlist-item-inside-ol&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;counter(list-item)&lt;/code&gt; inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;ol&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#support-content-in-legacy&quot; id=&quot;markdown-toc-support-content-in-legacy&quot;&gt;Support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; in legacy&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#marker-enabled-by-default&quot; id=&quot;markdown-toc-marker-enabled-by-default&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; enabled by default&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#allowing-more-properties&quot; id=&quot;markdown-toc-allowing-more-properties&quot;&gt;Allowing more properties&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#overview&quot; id=&quot;markdown-toc-overview&quot;&gt;Overview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Did you know that CSS makes it possible to style &lt;a href=&quot;https://drafts.csswg.org/css-lists/#markers&quot;&gt;list markers&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;In the past, if you wanted to customize the bullets or numbers in a list,
you would probably have to hide the native markers with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-style: none&lt;/code&gt;,
and then add fake markers with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::before&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, now you can just use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; pseudo-element
in order to style the native markers directly!&lt;/p&gt;

&lt;p&gt;If you are not familiar with it, I suggest reading these articles first:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/::marker&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/::marker&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://web.dev/css-marker-pseudo-element/&quot;&gt;https://web.dev/css-marker-pseudo-element/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, I will explain the deep technical details of how I implemented
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; in Chromium.&lt;/p&gt;

&lt;p&gt;Thanks to Bloomberg for sponsoring Igalia to do it!&lt;/p&gt;

&lt;h2 id=&quot;implementing-list-style-type-string&quot;&gt;Implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-style-type: &amp;lt;string&amp;gt;&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;Before starting working on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; itself, I decided to add support for string values in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-style-type&lt;/code&gt;.
It seemed a quite useful feature for authors, and Firefox already shipped it in 2015.
Also, it’s like a more limited version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt;, so it was a good introduction.&lt;/p&gt;

&lt;p&gt;It was relatively straight-forward to implement.
I did it in a single patch, &lt;a href=&quot;https://crrev.com/704563&quot;&gt;https://crrev.com/704563&lt;/a&gt;,
which landed in Chromium 79.
Then I also ported it into Webkit, it’s avilable since Safari Technology Preview 115.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ol&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;list-style-type: '★ '&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Lorem&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Ipsum&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2020/12/list-style-type-string.png&quot; alt=&quot;screenshot&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;parsing-and-computation&quot;&gt;Parsing and computation&lt;/h3&gt;

&lt;p&gt;The interesting thing to mention is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-style-type&lt;/code&gt; had been implemented with a keyword template,
so its value would be internally stored using an enum, and it would benefit from the parser fast path for keywords.
I didn’t want to lose that, so I followed the same approach as for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;display&lt;/code&gt;,
which also used to be a single-keyword property,
until Houdini extended its syntax with &lt;a href=&quot;https://www.w3.org/TR/css-layout-api-1/#valdef-display-layout&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;layout(&amp;lt;ident&amp;gt;)&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Basically, I treated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-style-type&lt;/code&gt; as a partial keyword property.
This means that it keeps the parser fast path for keyword values,
but in case of failure it falls back to the normal parser, where I accepted a string value.&lt;/p&gt;

&lt;p&gt;When a string is provided,
the internal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-style-type&lt;/code&gt; value is set to
a special &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EListStyleType::kString&lt;/code&gt; enum value,
and the string is stored in an extra &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListStyleStringValue&lt;/code&gt; field.&lt;/p&gt;

&lt;h3 id=&quot;layout&quot;&gt;Layout&lt;/h3&gt;

&lt;p&gt;From a layout point of view, I had to modify both LayoutNG and legacy code.
&lt;a href=&quot;http://www.chromium.org/blink/layoutng&quot;&gt;LayoutNG&lt;/a&gt; is a new layout engine for Chromium
that has been designed for the needs of modern scalable web applications.
It was released in Chrome 77 for block and inline layout,
but some CSS features like multi-column haven’t been implemented in LayoutNG yet,
so they force Chromium to use the old legacy engine.&lt;/p&gt;

&lt;p&gt;It was mostly a matter of tweaking
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGListItem&lt;/code&gt; (for LayoutNG) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutListMarker&lt;/code&gt; (for legacy)
in order to retrieve the string from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListStyleStringValue&lt;/code&gt; when
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListStyleType&lt;/code&gt; was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EListStyleType::kString&lt;/code&gt;,
and making sure to update the marker when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListStyleStringValue&lt;/code&gt; changed.&lt;/p&gt;

&lt;p&gt;Also, string values are somewhat special because they don’t have a suffix,
unlike numeric values that are suffixed with a dot and space (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1. &lt;/code&gt;),
or symbolic values that get a trailing space (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;◾ &lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;It’s noteworthy that until this point, markers didn’t have to care about mixed bidi.
But now you can have things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-style-type: &quot;aال&quot;&lt;/code&gt;, that is: U+0061 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt;, U+0627 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ا&lt;/code&gt;, U+0644 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ل&lt;/code&gt;.
Note that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ا&lt;/code&gt; is written before &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ل&lt;/code&gt;, but since they are arabic characters, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ا&lt;/code&gt; appears at the right.&lt;/p&gt;

&lt;p&gt;This is relevant because the marker is supposed to be isolated from the text in the list item,
so in LayoutNG I had to set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unicode-bidi: isolate&lt;/code&gt; to inside markers.
It wasn’t necessary for outside markers since they are implemented as inline-blocks,
which are already isolated.&lt;/p&gt;

&lt;p&gt;In legacy layout, markers don’t actually have their text as a child, it’s just a paint-time effect.
As such, no bidi reordering happens, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aال&lt;/code&gt; doesn’t render correctly:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;li&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;list-style: 'aال - ' inside&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;text&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;LayoutNG: &lt;img src=&quot;/obrufau/files/2020/12/list-style-type-mixed-bidi-ng.png&quot; alt=&quot;screenshot&quot; /&gt; vs.
legacy: &lt;img src=&quot;/obrufau/files/2020/12/list-style-type-mixed-bidi-legacy.png&quot; alt=&quot;screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;At that point I decided to leave it this way,
but this kind of problems in legacy layout would keep haunting me while implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt;.
Keep reading to know the bloody details!&lt;/p&gt;

&lt;h2 id=&quot;marker-parsing-and-computation&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; parsing and computation&lt;/h2&gt;

&lt;p&gt;Here I started working on the actual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; pseudo-element.
As a first step, in &lt;a href=&quot;https://crrev.com/709390&quot;&gt;https://crrev.com/709390&lt;/a&gt;
I recognized &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; as a valid selector (behind a flag), added a usage counter,
and defined a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PseudoId::kPseudoIdMarker&lt;/code&gt; to identify it in the node tree.&lt;/p&gt;

&lt;p&gt;It’s important to note that list markers were still anonymous boxes,
there was no actual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; pseudo-element,
so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kPseudoIdMarker&lt;/code&gt; wasn’t actually used yet.&lt;/p&gt;

&lt;p&gt;Something that needs to be taken into account when using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt;
is that the layout model for outside positioning is not fully defined.
Therefore, in order to prevent authors from relying on implementation-defined behaviors
that may change in the future, the CSSWG decided to restrict which properties
can actually be used on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I implemented this restriction in &lt;a href=&quot;https://crrev.com/710995&quot;&gt;https://crrev.com/710995&lt;/a&gt;,
using a ValidPropertyFilter just like it was done for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::first-letter&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::cue&lt;/code&gt;.
But note this was later refactored,
and now whether a property applies to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; or not
is specified in the property definition in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;css_properties.json5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At this point, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; only allowed:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;All font properties&lt;/li&gt;
  &lt;li&gt;Custom properties&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;color&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;direction&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-combine-upright&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unicode-bidi&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;using-marker-styles&quot;&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; styles&lt;/h2&gt;

&lt;p&gt;At this point, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; was a valid selector,
but list markers weren’t using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; styles.
So in &lt;a href=&quot;https://crrev.com/711883&quot;&gt;https://crrev.com/711883&lt;/a&gt;
I just took these styles and assigned them to the markers.&lt;/p&gt;

&lt;p&gt;This simple patch was the real deal,
making Chromium’s implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; match WebKit’s one,
which shipped in 2017.
When enabling the runtime flag, you could style markers:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;::marker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;green&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bold&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;ol&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Lorem&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Ipsum&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2020/12/marker-style.png&quot; alt=&quot;screenshot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This landed in Chromium 80. So, how come I didn’t ship &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; until 86?&lt;/p&gt;

&lt;p&gt;The answer is that, while the basic functionality was working fine,
I wanted to provide a full and solid implementation.
And it was not yet the case, since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; was not working,
and markers were still anonymous boxes that just happened to get assigned
the styles for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; pseudo-elements,
but there were no actual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; pseudo-elements.&lt;/p&gt;

&lt;h2 id=&quot;support-content-in-layoutng&quot;&gt;Support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; in LayoutNG&lt;/h2&gt;

&lt;p&gt;Adding support for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; property was relatively easy in LayoutNG,
since I could reuse the existing logic for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::before&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::after&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Roughly it was a matter of ignoring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-style-type&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-style-image&lt;/code&gt;
in non-&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;normal&lt;/code&gt; cases, and using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutObject&lt;/code&gt; of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContentData&lt;/code&gt;
as the children. This was not possible in legacy, since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutListMarker&lt;/code&gt;
can’t have children.&lt;/p&gt;

&lt;p&gt;It may be worth it to summarize the different &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutObject&lt;/code&gt; classes for list markers:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutListMarker&lt;/code&gt;, based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutBox&lt;/code&gt;, for legacy markers.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGListMarker&lt;/code&gt;, based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGBlockFlowMixin&amp;lt;LayoutBlockFlow&amp;gt;&lt;/code&gt;,
 for LayoutNG markers with an outside position.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGInsideListMarker&lt;/code&gt;, based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutInline&lt;/code&gt;,
 for LayoutNG markers with an inside position.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s important to note that non-&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;normal&lt;/code&gt; markers were actual pseudo-elements,
their &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGListMarker&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGInsideListMarker&lt;/code&gt; were no longer anonymous,
they had an originating &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PseudoElement&lt;/code&gt; in the node tree.&lt;/p&gt;

&lt;p&gt;This means that I had to add logic for attaching, dettaching and rebuilding
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kPseudoIdMarker&lt;/code&gt; pseudo-elements, add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutObjectFactory::CreateListMarker()&lt;/code&gt;,
and make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutTreeBuilderTraversal&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Node::PseudoAware*&lt;/code&gt; methods be
aware of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Most of it was done in &lt;a href=&quot;https://crrev.com/718609&quot;&gt;https://crrev.com/718609&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another problem that I had to address was that, until this point,
both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content: normal&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content: none&lt;/code&gt; were considered to be synonymous,
and were internally stored as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nullptr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, unlike in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::before&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::after&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;normal&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;none&lt;/code&gt; have different behaviors in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt;:
the former decides the contents from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-style&lt;/code&gt; properties,
the latter prevents the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; from generating boxes.&lt;/p&gt;

&lt;p&gt;Therefore, in &lt;a href=&quot;https://crrev.com/732549&quot;&gt;https://crrev.com/732549&lt;/a&gt;
I implemented &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content: none&lt;/code&gt; as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NoneContentData&lt;/code&gt;,
and replaced the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HasContent()&lt;/code&gt; helper function with the more specific
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContentBehavesAsNormal()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContentPreventsBoxGeneration()&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;default-styles&quot;&gt;Default styles&lt;/h2&gt;

&lt;p&gt;According to the spec, markers needed to get assigned these styles in UA origin:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;unicode-bidi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;isolate&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;font-variant-numeric&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;tabular-nums&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ComputedStyle&lt;/code&gt; for a marker could be created in
different ways:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;If there was some &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; selector, by running the cascade normally.&lt;/li&gt;
  &lt;li&gt;Otherwise, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutListMarker&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGListItem&lt;/code&gt; would create the
style from scratch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First, in &lt;a href=&quot;https://crrev.com/720875&quot;&gt;https://crrev.com/720875&lt;/a&gt;
I made all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StyleResolver::PseudoStyleForElementInternal&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutListMarker::ListItemStyleDidChange&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGListItem::UpdateMarker&lt;/code&gt;
set these UA rules.&lt;/p&gt;

&lt;p&gt;Then in &lt;a href=&quot;https://crrev.com/725913&quot;&gt;https://crrev.com/725913&lt;/a&gt;
I made it so that markers would always run the cascade,
unifying the logic in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PseudoStyleForElementInternal&lt;/code&gt;.
But this way of injecting the UA styles was a bit hacky and problematic.&lt;/p&gt;

&lt;p&gt;So finally, in &lt;a href=&quot;https://crrev.com/779284&quot;&gt;https://crrev.com/779284&lt;/a&gt;
I implemented it in the proper way, using a real UA stylesheet.
However, I took care of preventing that from triggering &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SetHasPseudoElementStyle&lt;/code&gt;,
which would have defeated some optimizations.&lt;/p&gt;

&lt;p&gt;Interestingly, these UA styles use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; selector, but they also affect
nested &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::before::marker&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::after::marker&lt;/code&gt; pseudo-elements.
That’s because I took advantage of a bug in the style resolver,
so that I wouldn’t have to implement the nested &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; selectors.
The bug is disabled for non-UA styles.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGListItem::UpdateMarker&lt;/code&gt; also had some style tweaks that I moved into
the style adjuster instead of to the UA sheet,
because the exact styles depend on the marker:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Outside markers get &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;display: inline-block&lt;/code&gt;, because they must be
block containers.&lt;/li&gt;
  &lt;li&gt;Outside markers get &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;white-space: pre&lt;/code&gt;, to prevent their trailing space
from being trimmed.&lt;/li&gt;
  &lt;li&gt;Inside markers get some margins, depending on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-style-type&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I did that in &lt;a href=&quot;https://crrev.com/725091&quot;&gt;https://crrev.com/725091&lt;/a&gt;
and &lt;a href=&quot;https://crrev.com/728176&quot;&gt;https://crrev.com/728176&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;some-fun-999-performance-regression&quot;&gt;Some fun: 99.9% performance regression&lt;/h2&gt;

&lt;p&gt;An implication of my work on the default marker styles was that
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;StyleType()&lt;/code&gt; became &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kPseudoIdMarker&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kPseudoIdNone&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This made &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutObject::PropagateStyleToAnonymousChildren()&lt;/code&gt; do more work,
causing the flexbox_with_list_item perf test to worsen by a 99.9%!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2020/12/marker-perf-regression.png&quot; alt=&quot;Performance graph&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I fixed it in &lt;a href=&quot;https://crrev.com/722421&quot;&gt;https://crrev.com/722421&lt;/a&gt;
by returning early for markers with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content: normal&lt;/code&gt;,
which didn’t need that work anyways.&lt;/p&gt;

&lt;p&gt;Once I completed the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; implementation, I tried reverting the fix,
and then the test only worsened by a 2-3%.
So I guess the big regression was caused by the interaction of multiple factors,
and the other factors were later fixed or avoided.&lt;/p&gt;

&lt;h2 id=&quot;developer-tools&quot;&gt;Developer tools&lt;/h2&gt;

&lt;p&gt;It was important for me to expose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; in the devtools
just like a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::before&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::after&lt;/code&gt;.
Not just because I thought it would be beneficial for authors,
but also because it helped me a lot when implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So first I made the Styles panel expose the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; styles
when inspecting the originating list item (&lt;a href=&quot;https://crrev.com/724094&quot;&gt;https://crrev.com/724094&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2020/12/marker-devtools-styles.png&quot; alt=&quot;Devtools ::marker styles&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And then I made &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; pseudo-elements inspectable
in the Elements panel (&lt;a href=&quot;https://crrev.com/724122&quot;&gt;https://crrev.com/724122&lt;/a&gt; and &lt;a href=&quot;https://crrev.com/c/1934220&quot;&gt;https://crrev.com/c/1934220&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2020/12/marker-devtools-tree.png&quot; alt=&quot;Devtools ::marker tree&quot; /&gt;&lt;/p&gt;

&lt;p&gt;However, note this only worked for actual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; pseudo-elements.&lt;/p&gt;

&lt;h2 id=&quot;layoutng-markers-as-real-pseudo-elements&quot;&gt;LayoutNG markers as real pseudo-elements&lt;/h2&gt;

&lt;p&gt;As previously stated, only non-normal markers were internally implemented
as actual pseudo-elements, markers with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content: normal&lt;/code&gt; were just
annymous boxes.&lt;/p&gt;

&lt;p&gt;So normal markers wouldn’t appear in devtools, and would yield
incorrect values in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getComputedStyle&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;getComputedStyle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;listItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;::marker&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &quot;auto&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;According to &lt;a href=&quot;https://drafts.csswg.org/cssom/#resolved-value&quot;&gt;CSSOM&lt;/a&gt;
that’s supposed to be the used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;width&lt;/code&gt; in pixels,
but since there was no actual &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; pseudo-element,
it would just return the computed value: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auto&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So in &lt;a href=&quot;https://crrev.com/731964&quot;&gt;https://crrev.com/731964&lt;/a&gt;
I implemented LayoutNG normal markers as real pseudo-elements.
It’s a big patch, though mostly that’s because I had to update
several test expectations.&lt;/p&gt;

&lt;p&gt;Another advantage was that non-normal markers benefited from the much
vaster test coverage for normal ones.
For example, some accessibility code was expecting markers to be
anonymous, I noticed this thanks to existing tests with normal markers.
Without this change I might have missed that non-normal ones weren’t
handled properly.&lt;/p&gt;

&lt;p&gt;And a nice side-effect that I wasn’t expecting was that
the flexbox_with_list_item perf test improved by a 30-40%. Nice!&lt;/p&gt;

&lt;p&gt;It’s worth noting that until this point,
pseudo-elements could only be originated by an element.
However, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::before&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::after&lt;/code&gt; pseudo-elements can have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;display: list-item&lt;/code&gt;
and thus have a nested marker.&lt;/p&gt;

&lt;p&gt;Due to the lack of support for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::before::marker&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::after::marker&lt;/code&gt; selectors,
I could previously assume that nested markers would have the initial &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content: normal&lt;/code&gt;,
and thus be anonymous.
But this was no longer the case, so in &lt;a href=&quot;https://crrev.com/730531&quot;&gt;https://crrev.com/730531&lt;/a&gt;
I added support for nested pseudo-elements.
However, the style resolver is still not able to handle them properly,
so nested selectors don’t work.&lt;/p&gt;

&lt;p&gt;A consequence of implementing LayoutNG markers as pseudo-elements
was that they became independent,
they were no longer created and destroyed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGListItem&lt;/code&gt;.
But the common logic for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGListMarker&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGInsideListMarker&lt;/code&gt;
was still in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGListItem&lt;/code&gt;, so this made it difficult to keep the
marker information in sync.
Therefore, in &lt;a href=&quot;https://crrev.com/735167&quot;&gt;https://crrev.com/735167&lt;/a&gt;
I moved the common logic into a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListMarker&lt;/code&gt; class,
and each LayoutNG marker class would own a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListMarker&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;I also renamed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGListMarker&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGOutsideListMarker&lt;/code&gt;,
since the old name was misleading.&lt;/p&gt;

&lt;h2 id=&quot;legacy-markers-as-real-pseudo-elements&quot;&gt;Legacy markers as real pseudo-elements&lt;/h2&gt;

&lt;p&gt;Since I had already added the changes needed to implement
all LayoutNG markers as pseudo-elements,
I thought that doing the same for legacy markers would be easier.&lt;/p&gt;

&lt;p&gt;But I was wrong! The thing is that legacy layout already had some bugs
affecting markers, but they would only be triggered when dynamically
updating the styles of the list item.
But there aren’t many tests that do that, so they went unnoticed…
until I tried my patch, which surfaced these issues in the initial layout,
making some test fail.&lt;/p&gt;

&lt;p&gt;So first I had to fix &lt;a href=&quot;https://crbug.com/1048672&quot;&gt;bug 1048672&lt;/a&gt;,
&lt;a href=&quot;https://crbug.com/1049633&quot;&gt;1049633&lt;/a&gt;, and &lt;a href=&quot;https://crbug.com/1051114&quot;&gt;1051114&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then there was also &lt;a href=&quot;https://crbug.com/1051685&quot;&gt;bug 1051685&lt;/a&gt;,
involving selections or drag-and-drop with pseudo-elements like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::before&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::after&lt;/code&gt;.
So turning markers into pseudo-elements made them have the same problem,
causing a test failure.&lt;/p&gt;

&lt;p&gt;I could finally land my patch in &lt;a href=&quot;https://crrev.com/745012&quot;&gt;https://crrev.com/745012&lt;/a&gt;,
which also improved performance like in LayoutNG.&lt;/p&gt;

&lt;h2 id=&quot;animations--transitions&quot;&gt;Animations &amp;amp; transitions&lt;/h2&gt;

&lt;p&gt;While I was still working on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt;,
the CSSWG decided to expand the list of allowed properties
in order to include animations and transitions.
I did so in &lt;a href=&quot;https://crrev.com/753752&quot;&gt;https://crrev.com/753752&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The tricky part was that only allowed properties could be animated.
For example,&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@keyframes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;anim&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#c0c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#0cc&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;to&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#0cc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#c0c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;::marker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;anim&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1s&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;infinite&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;alternate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&lt;/span&gt;Non-animated text&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2020/12/marker-animation.gif&quot; alt=&quot;Animated ::marker&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Only the color of the marker is animated, not the background.&lt;/p&gt;

&lt;h2 id=&quot;counterlist-item-inside-ol&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;counter(list-item)&lt;/code&gt; inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;ol&amp;gt;&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::before&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::after&lt;/code&gt; pseudo-elements already had the bug that,
when referencing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list-item&lt;/code&gt; counter inside an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;ol&amp;gt;&lt;/code&gt;,
they would produce the wrong number, usually 1 unit greater.&lt;/p&gt;

&lt;p&gt;Of course, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; inherited the same problem.
And this was breaking one of the important use-cases,
which is being able to customize the marker text with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example,&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;::marker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&quot;[&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list-item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&quot;] &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;ol&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;foo&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;bar&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;baz&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;would start counting from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2020/12/marker-counter-bug.png&quot; alt=&quot;::marker counter bug&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Luckily, WebKit had already fixed this problem, so I could copy their solution.
Unluckily, they mixed it with a big irrelevant refactoring,
so I had to spend some time understanding which part was the actual fix.
I ported it into Chromium in &lt;a href=&quot;https://crrev.com/783493&quot;&gt;https://crrev.com/783493&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;support-content-in-legacy&quot;&gt;Support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; in legacy&lt;/h2&gt;

&lt;p&gt;The only missing thing to do was adding support for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt; in legacy layout.
The problem was that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutListMarker&lt;/code&gt; can’t have children,
so it’s not possible to just insert the layout object produced by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ContentData&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, my idea was replacing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutListMarker&lt;/code&gt; with two new classes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutOutsideListMarker&lt;/code&gt;, for markers with outside positioning.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutInsideListMarker&lt;/code&gt;, for markers with inside positioning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and they could share the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListMarker&lt;/code&gt; logic with LayoutNG markers.&lt;/p&gt;

&lt;p&gt;However, when I started working on this, something big happened:
the COVID-19 pandemic.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2020/12/coronavirus.webp&quot; alt=&quot;SARS-CoV-2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And Google decided the skip Chromium 82 due to the situation,
which is relevant because, in order to be able to merge patches easily,
they wanted to avoid big refactorings.&lt;/p&gt;

&lt;p&gt;And a big refactoring is precisely what I needed!
So I had to wait until Chromium 83 reached stable.&lt;/p&gt;

&lt;p&gt;Also, Google engineers were not convinced by my proposal,
because it would imply that legacy markers
would use more memory and would be slower,
since they would have children even with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content: normal&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So I changed my strategy as such:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Keep &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutListMarker&lt;/code&gt; for normal markers.&lt;/li&gt;
  &lt;li&gt;Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutOutsideListMarker&lt;/code&gt; for non-normal outside markers.&lt;/li&gt;
  &lt;li&gt;Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutInsideListMarker&lt;/code&gt; for non-normal inside markers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was done in this chain of CLs:
&lt;a href=&quot;https://crrev.com/c/2109697&quot;&gt;2109697&lt;/a&gt;,
&lt;a href=&quot;https://crrev.com/c/2109771&quot;&gt;2109771&lt;/a&gt;,
&lt;a href=&quot;https://crrev.com/c/2110630&quot;&gt;2110630&lt;/a&gt;,
&lt;a href=&quot;https://crrev.com/c/2246514&quot;&gt;2246514&lt;/a&gt;,
&lt;a href=&quot;https://crrev.com/c/2252244&quot;&gt;2252244&lt;/a&gt;,
&lt;a href=&quot;https://crrev.com/c/2252041&quot;&gt;2252041&lt;/a&gt;,
&lt;a href=&quot;https://crrev.com/c/2252189&quot;&gt;2252189&lt;/a&gt;,
&lt;a href=&quot;https://crrev.com/c/2246573&quot;&gt;2246573&lt;/a&gt;,
&lt;a href=&quot;https://crrev.com/c/2258732&quot;&gt;2258732&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;marker-enabled-by-default&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; enabled by default&lt;/h2&gt;

&lt;p&gt;Finally the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; implementation was complete!&lt;/p&gt;

&lt;p&gt;To summarize, list markers ended up implemented among 5 different layout classes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutListMarker&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;Used for normal markers in legacy layout.&lt;/li&gt;
      &lt;li&gt;Based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutBox&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;Can’t have children, doesn’t use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListMarker&lt;/code&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutOutsideListMarker&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;Used for outside markers in legacy layout.&lt;/li&gt;
      &lt;li&gt;Based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutBlockFlow&lt;/code&gt;, i.e. it’s a block container.&lt;/li&gt;
      &lt;li&gt;Has children, uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListMarker&lt;/code&gt; to keep them updated.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutInsideListMarker&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;Used for inside markers in legacy layout.&lt;/li&gt;
      &lt;li&gt;Based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutInline&lt;/code&gt;, i.e. it’s an inline box.&lt;/li&gt;
      &lt;li&gt;Has children, uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListMarker&lt;/code&gt; to keep them updated.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGOutsideListMarker&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;Used for outside markers in LayoutNG.&lt;/li&gt;
      &lt;li&gt;Based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGBlockFlowMixin&amp;lt;LayoutBlockFlow&amp;gt;&lt;/code&gt;, i.e. it’s a block container.&lt;/li&gt;
      &lt;li&gt;Has children, uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListMarker&lt;/code&gt; to keep them updated.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutNGInsideListMarker&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;Used for inside markers in LayoutNG.&lt;/li&gt;
      &lt;li&gt;Based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LayoutInline&lt;/code&gt;, i.e. it’s an inline box.&lt;/li&gt;
      &lt;li&gt;Has children, uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListMarker&lt;/code&gt; to keep them updated.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So at this point I just landed &lt;a href=&quot;https://crrev.com/786976&quot;&gt;https://crrev.com/786976&lt;/a&gt;
to enable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; by default. This happened in Chromium 86.0.4198.0.&lt;/p&gt;

&lt;h2 id=&quot;allowing-more-properties&quot;&gt;Allowing more properties&lt;/h2&gt;

&lt;p&gt;After shipping &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt;, I continued doing small tweaks
in order to align the behavior with more recent CSSWG resolutions.&lt;/p&gt;

&lt;p&gt;The first one was that, if you set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-transform&lt;/code&gt; on a list item or ancestor,
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; shouldn’t inherit it by default. For example,&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ol&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;list-style-type: lower-alpha; text-transform: uppercase&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;foo&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;should have a lowercase &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt;, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2020/12/marker-text-transform.png&quot; alt=&quot;::marker text-transform&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Therefore, in &lt;a href=&quot;https://crrev.com/791815&quot;&gt;https://crrev.com/791815&lt;/a&gt;
I added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-tranform: none&lt;/code&gt; to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; UA rules,
but also allowed authors to specify another value if they want so.&lt;/p&gt;

&lt;p&gt;Then, the CSSWG also resolved that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; should allow
inherited properties that apply to text which don’t depend on box geometry.
And other properties, unless whitelisted, shouldn’t affect markers,
even when inherited from an ancestor.&lt;/p&gt;

&lt;p&gt;Therefore, I added support for some text and text decoration properties,
and also for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;line-height&lt;/code&gt;.
On the other hand, I blocked inheritance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-indent&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-align&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That was done in CLs
&lt;a href=&quot;https://crrev.com/c/791815&quot;&gt;791815&lt;/a&gt;,
&lt;a href=&quot;https://crrev.com/c/2382750&quot;&gt;2382750&lt;/a&gt;,
&lt;a href=&quot;https://crrev.com/c/2388384&quot;&gt;2388384&lt;/a&gt;,
&lt;a href=&quot;https://crrev.com/c/2391242&quot;&gt;2391242&lt;/a&gt;,
&lt;a href=&quot;https://crrev.com/c/2396125&quot;&gt;2396125&lt;/a&gt;,
&lt;a href=&quot;https://crrev.com/c/2438413&quot;&gt;2438413&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The outcome was that, in Chromium, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; accepts these properties:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Animation properties:
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animation-delay&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animation-direction&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animation-duration&lt;/code&gt;,
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animation-fill-mode&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animation-iteration-count&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animation-name&lt;/code&gt;,
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animation-play-state&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animation-timeline&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;animation-timing-function&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Transition properties:
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transition-delay&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transition-duration&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transition-property&lt;/code&gt;,
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transition-timing-function&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Font properties:
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-family&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-kerning&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-optical-sizing&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-size&lt;/code&gt;,
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-size-adjust&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-stretch&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-style&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-variant-ligatures&lt;/code&gt;,
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-variant-caps&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-variant-east-asian&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-variant-numeric&lt;/code&gt;,
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-weight&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-feature-settings&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font-variation-settings&lt;/code&gt;,&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Text properties:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hyphens&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;letter-spacing&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;line-break&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;overflow-wrap&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tab-size&lt;/code&gt;, 
 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-transform&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;white-space&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;word-break&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;word-spacing&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Text decoration properties:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-decoration-skip-ink&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-shadow&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-webkit-text-emphasis-color&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-webkit-text-emphasis-position&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-webkit-text-emphasis-style&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Writing mode properties:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;direction&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-combine-upright&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unicode-bidi&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Others:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;color&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;line-height&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, note that they may end up not having the desired effect in some cases:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;The style adjuster forces &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;white-space: pre&lt;/code&gt; in outside markers,
so you can only customize &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;white-space&lt;/code&gt; in inside ones.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text-combine-upright&lt;/code&gt; doesn’t work in pseudo-elements
(&lt;a href=&quot;https://crbug.com/1060007&quot;&gt;bug 1060007&lt;/a&gt;).
So setting it will only affect the computed style,
and will also force legacy layout,
but it won’t turn the marker text upright.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In legacy layout, the marker has no actual contents.
So text properties, text decoration properties,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unicode-bidi&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;line-height&lt;/code&gt; don’t work.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And this is the default UA stylesheet for markers:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;::marker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;unicode-bidi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isolate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;font-variant-numeric&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tabular-nums&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;text-transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;text-indent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;text-align-last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The final change, in &lt;a href=&quot;https://crrev.com/837424&quot;&gt;https://crrev.com/837424&lt;/a&gt;,
was the removal of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CSSMarkerPseudoElement&lt;/code&gt; runtime flag.
Since 89.0.4358.0, it’s no longer possible to disable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;/h1&gt;

&lt;p&gt;Implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; needed more than 100 patches in total,
several refactorings, some existing bug fixes, and various CSSWG resolutions.&lt;/p&gt;

&lt;p&gt;I also added lots of new WPT tests, additionally to the existing ones
created by Apple and Mozilla.
For every patch that had an observable improved behavior,
I tried to cover it with a test.
Most of them are in &lt;a href=&quot;https://wpt.fyi/results/css/css-pseudo?q=marker&quot;&gt;https://wpt.fyi/results/css/css-pseudo?q=marker&lt;/a&gt;,
though some are in css-lists, and others are Chromium-internal
since they were testing non-standard behavior.&lt;/p&gt;

&lt;p&gt;Note my work didn’t include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::before::marker&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::after::marker&lt;/code&gt; selectors,
which haven’t been implemented in WebKit nor Firefox either.
What remains to be done is making the selector parser handle nested pseudo-elements properly.&lt;/p&gt;

&lt;p&gt;Also, I kept the disclosure triangle of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::-webkit-details-marker&lt;/code&gt;,
but since Chromium 89 it’s a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::marker&lt;/code&gt; as expected,
thanks to Kent Tamura.&lt;/p&gt;</content><author><name>Oriol Brufau</name></author><category term="planet" /><category term="chromium" /><summary type="html">The CSS ::marker pseudo-element has shipped in Chromium 86. Implementation details.</summary></entry><entry><title type="html">Open Prioritization for implementing selector list argument of :not() in Chrome</title><link href="https://blogs.igalia.com/obrufau/2020/07/13/open-prioritization-for-not-selector.html" rel="alternate" type="text/html" title="Open Prioritization for implementing selector list argument of :not() in Chrome" /><published>2020-07-13T12:45:00+02:00</published><updated>2020-07-13T12:45:00+02:00</updated><id>https://blogs.igalia.com/obrufau/2020/07/13/open-prioritization-for-not-selector</id><content type="html" xml:base="https://blogs.igalia.com/obrufau/2020/07/13/open-prioritization-for-not-selector.html">&lt;h2 id=&quot;css-not-selector&quot;&gt;CSS &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not()&lt;/code&gt; selector&lt;/h2&gt;

&lt;p&gt;As you probably know, CSS selectors are patterns that can be used to apply a group of style declarations to multiple elements.
For example, if you want to select all elements with the class &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;, you can use the selector &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.foo&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[class~=foo]&lt;/code&gt;.
But sometimes, instead of styling the elements that have a specific characteristic, we want to exclude these and style the other ones.&lt;/p&gt;

&lt;p&gt;For example, some developers prefer the &lt;a href=&quot;https://www.w3.org/TR/css-sizing/#valdef-box-sizing-border-box&quot;&gt;border box&lt;/a&gt; sizing model
rather than the &lt;a href=&quot;https://www.w3.org/TR/css-sizing/#valdef-box-sizing-content-box&quot;&gt;content box&lt;/a&gt; one, so they use&lt;/p&gt;
&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;box-sizing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border-box&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;But then, imagine there is an image like&lt;/p&gt;
&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;img.png&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;50&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;padding: 5px&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The size attributes will include the padding, so the resulting content area will be 90x40.
Not only will the image be downscaled, possibly producing artifacts, but it will also be stretched: the aspect ratio becomes 2.25 instead of the natural 2.&lt;/p&gt;

&lt;p&gt;The above shows that excluding images can be a good idea.
In CSS2, the solution was simply using a more specific selector to undo the general one:&lt;/p&gt;
&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;box-sizing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border-box&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;box-sizing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content-box&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;However, in more complex cases this can get tedious, and the reset value may not be that obvious.
Therefore, the Selectors Level 3 specification introduced the &lt;a href=&quot;https://www.w3.org/TR/selectors-3/#negation&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not()&lt;/code&gt;&lt;/a&gt; pseudo-class.
It accepts a selector argument, and matches all elements that do &lt;em&gt;not&lt;/em&gt; match the argument.
The example above can then be&lt;/p&gt;
&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;img&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;box-sizing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;border-box&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;There were some limitations though. The selector argument had to be a &lt;a href=&quot;https://www.w3.org/TR/selectors/#simple&quot;&gt;simple selector&lt;/a&gt;, that is, one of:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A &lt;a href=&quot;https://www.w3.org/TR/selectors/#type-selectors&quot;&gt;type selector&lt;/a&gt;, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(div)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;The &lt;a href=&quot;https://www.w3.org/TR/selectors/#the-universal-selector&quot;&gt;universal selector&lt;/a&gt;, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(*)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;An &lt;a href=&quot;https://www.w3.org/TR/selectors/#attribute-selectors&quot;&gt;attribute selector&lt;/a&gt;, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not([reversed])&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;A &lt;a href=&quot;https://www.w3.org/TR/selectors/#class-html&quot;&gt;class selector&lt;/a&gt;, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(.foo)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;An &lt;a href=&quot;https://www.w3.org/TR/selectors/#id-selectors&quot;&gt;ID selector&lt;/a&gt;, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(#bar)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;A &lt;a href=&quot;https://www.w3.org/TR/selectors/#pseudo-classes&quot;&gt;pseudo-class&lt;/a&gt;, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(:focus)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, nesting negations like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(:not(...))&lt;/code&gt; was also invalid.&lt;/p&gt;

&lt;h2 id=&quot;not-with-selector-list-argument&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not()&lt;/code&gt; with selector list argument&lt;/h2&gt;

&lt;p&gt;Selectors level 4 has made &lt;a href=&quot;https://www.w3.org/TR/selectors-4/#negation&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not()&lt;/code&gt;&lt;/a&gt; more flexible, now it accepts any selector list as the argument.&lt;/p&gt;

&lt;p&gt;In particular, it allows a &lt;a href=&quot;https://www.w3.org/TR/selectors/#compound&quot;&gt;compound selector&lt;/a&gt; argument, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(ul[reversed])&lt;/code&gt;,
which matches all elements except the ones that both are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ul&lt;/code&gt; and have the attribute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reversed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another example of what you can do in level 4 is using a &lt;a href=&quot;https://www.w3.org/TR/selectors/#complex&quot;&gt;complex selector&lt;/a&gt;, that is,
a sequence of compound selectors separated by &lt;a href=&quot;https://www.w3.org/TR/selectors/#selector-combinator&quot;&gt;combinators&lt;/a&gt;.
For instance, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(div &amp;gt; p)&lt;/code&gt; matches all elements except the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt; which have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt; parent.&lt;/p&gt;

&lt;p&gt;And finally, an example with a &lt;a href=&quot;https://www.w3.org/TR/selectors/#selector-list&quot;&gt;selector list&lt;/a&gt; could be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(div, p)&lt;/code&gt;, matching all elements except the ones that are either a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt; or a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Is that completely new behavior? Well, using &lt;a href=&quot;https://en.wikipedia.org/wiki/De_Morgan's_laws&quot;&gt;De Morgan’s laws&lt;/a&gt;, the selectors above can be transformed into others which were valid in level 3:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Level 4 new syntax&lt;/th&gt;
      &lt;th&gt;Level 3 alternative&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(ul[reversed])&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(ul), :not([reversed])&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(div &amp;gt; p)&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(p), :not(div) &amp;gt; p, p:root&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(div, p)&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(div):not(p)&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;However, the &lt;a href=&quot;https://www.w3.org/TR/selectors/#specificity&quot;&gt;specificities&lt;/a&gt; are not completely equivalent.
The specificity of a selector is a tern of 3 natural numbers &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(A,B,C)&lt;/code&gt;, where in simple cases
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A&lt;/code&gt; is the number of ID selectors in the whole selector,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B&lt;/code&gt; is the number of attribute or class selectors and pseudo-classes,
and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C&lt;/code&gt; is the number of type selectors and pseudo-elements.
The specificity is one of the criteria used to solve conflicts when different CSS rules set the same property to the same element:
the winning declaration will be the one with the most specific selector, when comparing specificities in &lt;a href=&quot;https://en.wikipedia.org/wiki/Lexicographical_order&quot;&gt;lexicographical order&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The specificity of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not()&lt;/code&gt; pseudo-class is the greatest among the specificities of the complex selectors in the argument,
while the specificity of a selector list is the greatest among the complex selectors that match.&lt;/p&gt;

&lt;p&gt;For example, for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(ul[reversed])&lt;/code&gt;, the specificity is the same as for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ul[reversed]&lt;/code&gt;, i.e. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(0,1,1)&lt;/code&gt;.
However, the specificity of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(ul), :not([reversed])&lt;/code&gt; can either be:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;If only &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(ul)&lt;/code&gt; matches: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(0,0,1)&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;If only &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not([reversed])&lt;/code&gt; matches: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(0,1,0)&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;If both match, the maximum: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(0,1,0)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, the level 3 alternatives can become cumbersome when used as part of a bigger selector.
For instance, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(.a1.a2) :not(.b1.b2) :not(.c1.c2)&lt;/code&gt; would become&lt;/p&gt;
&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.a1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.b1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.c1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.a1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.b1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.c2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.a1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.b2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.c1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.a1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.b2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.c2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.a2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.b1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.c1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.a2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.b1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.c2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.a2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.b2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.c1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.a2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.b2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.c2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Note the combinatorial explosion! It could be avoided using &lt;a href=&quot;https://www.w3.org/TR/selectors-4/#matches-pseudo&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:is()&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://www.w3.org/TR/selectors-4/#where-pseudo&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:where()&lt;/code&gt;&lt;/a&gt;,
but they are also new level 4 additions.&lt;/p&gt;

&lt;p&gt;Moreover, not all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not()&lt;/code&gt; selectors have a finite alternative in level 3.
Consider &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not(div p)&lt;/code&gt;, that is, all elements which either are not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt; or don’t have any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt; ancestor.
The problem is that we can’t directly enforce a constraint over all ancestors, instead we need something like&lt;/p&gt;
&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;:root:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;:root:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nd&quot;&gt;:root:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;:not&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;/* ... */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;and if a priori the DOM tree can be arbitrarily deep, we don’t know when to end the selector.&lt;/p&gt;

&lt;p&gt;It’s for all these reasons that allowing selector lists in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not()&lt;/code&gt; is such a nice addition!&lt;/p&gt;

&lt;h2 id=&quot;browser-support-and-igalias-open-prioritization&quot;&gt;Browser support and Igalia’s Open Prioritization&lt;/h2&gt;

&lt;p&gt;If the new capabilities of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not()&lt;/code&gt; with a selector list sound cool to you, you might want to start using it right now!
However, while major browsers have supported the level 3 version for a long time, only WebKit supports the level 4 one, Chrome and Firefox don’t.&lt;/p&gt;

&lt;p&gt;But here is where &lt;a href=&quot;https://www.igalia.com/about/&quot;&gt;Igalia&lt;/a&gt; comes into play!
We are happy to announce &lt;a href=&quot;https://www.igalia.com/open-prioritization/&quot;&gt;Open Prioritization&lt;/a&gt;, an experiment for crowdfunding web platform features.&lt;/p&gt;

&lt;p&gt;Historically, which features are implemented sooner and which ones are delayed has been decided by browser vendors, and big companies that can fund the work.
But wouldn’t it be great if web developers could also have a saying in this prioritization?&lt;/p&gt;

&lt;p&gt;At Igalia we have selected a few tasks that we think the community might be interested in.
During a first stage, anybody can pledge their desired amount of money for their preferred choices.
The first feature that reaches the goal will be selected for a 2nd stage, in which the actual funding will happen.
And once funded, Igalia will do the implementation work.
Note: don’t worry if you pledge for an option which is not selected, your money is only deducted when funding.
See the &lt;a href=&quot;https://www.igalia.com/open-prioritization/#faq&quot;&gt;FAQ&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;One of the features that we offer is precisely implementing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:not()&lt;/code&gt; with selector lists in Chrome.
If you like the idea, I invite you to pledge &lt;a href=&quot;https://www.igalia.com/open-prioritization/#selectorchrome&quot;&gt;here&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/obrufau/files/2020/07/open-prioritization.png&quot; alt=&quot;Open Prioritization by Igalia. An experiment in crowd-funding prioritization.&quot; /&gt;&lt;/p&gt;</content><author><name>Oriol Brufau</name></author><category term="planet" /><category term="chromium" /><summary type="html">Selectors level 4 allows the :not() pseudo-class to contain selector list argument rather than just a simple selector. Igalia is accepting pledges to implement that in Chrome, as part of the Open Prioritization experiment.</summary></entry></feed>