An introduction to CSS Containment
Igalia has been recently working on the implementation of css-contain in Chromium by providing some fixes and optimizations based on this standard. This is a brief blog post trying to give an introduction to the spec, explain the status of things, the work done during past year, and some plans for the future.
What’s css-contain? #
The main goal of CSS Containment standard is to improve the rendering performance of web pages, allowing the isolation of a subtree from the rest of the document.
This specification only introduces one new CSS property called
contain with different possible values.
Browser engines can use that information to implement optimizations and avoid doing extra work when they know which subtrees are independent of the rest of the page.
Let’s explain what is this about and why this can be can bring performance improvements in complex websites. Imagine that you have a big HTML page which generates a complex DOM tree, but you know that some parts of that page are totally independent of the rest of the page and the content in those parts is modified at some point.
Browser engines usually try to avoid doing more work than needed and use some heuristics to avoid spending more time than required. However there are lots of corner cases and complex situations in which the browser needs to actually recompute the whole webpage. To improve these scenarios the author has to identify which parts (subtrees) of their website are independent and isolate them from the rest of the page thanks to the
contain property. Then when there are changes in some of those subrees the rendering engine will be able to avoid doing any work outside of the subtree boundaries.
Not everything is for free, when you use
contain there are some restrictions that will affect those elements, so the browser is totally certain it can apply optimizations without causing any breakage (e.g. you need to manually set the size of the elment if you want to use size containment).
The CSS Containment specification defines four values for the
contain property, one per each type of containment:
layout: The internal layout of the element is totally isolated from the rest of the page, it’s not affected by anything outside and its contents cannot have any effect on the ancestors.
paint: Descendants of the element cannot be displayed outside its bounds, nothing will overflow this element (or if it does it won’t be visible).
size: The size of the element can be computed without checking its children, the element dimensions are independent of its contents.
style: The effects of counters and quotes cannot escape this element, so they are isolated from the rest of the page.
Note that regarding style containment there is an ongoing discussion on the CSS Working Group about how useful it is (due to the narrowed scope of counters and quotes).
You can combine the different type of containments as you wish, but the spec also provides two extra values that are a kind of “shorthand” for the other four:
content: Which is equivalent to
contain: layout paint.
strict: This is the same than having
all four typescontent containment plus size containment, so it’s equivalent to
contain: layout paint size.
Update: The CSS Working Group has resolved to remove
strict, that’s why the previous lines have been updated. Thanks Robert Linder for reporting.
Let’s show an example of how CSS Containment can help to improve the performance of a webpage.
Imagine a page with lots of elements, in this case 10,000 elements like this:
And that it modifies the content of one of the inner DIVs trough
If you don’t use css-contain, even when the change is on a single element, Chromium spends a lot of time on layout because it traverses the whole DOM tree (which in this case is big as it has 10,000 elements).
Here is when
contain property comes to the rescue. In this example the DIV
item has fixed size, and the contents we’re changing in the inner DIV will never overflow it. So we can apply
contain: strict to the
item, that way the browser won’t need to visit the rest of the nodes when something changes inside an
item, it can stop checking things on that element and avoid going outside.
Notice that if the content overflows the
item it would get clipped, also if we don’t set a fixed size for the
item it’ll be rendered as an empty box so nothing would be visible (actually in this example the borders would be present but they would be the only visible thing).
Despite how simple is each of the items in this example, we’re getting a big improvement by using CSS Containment in layout time going down from ~4ms to ~0.04ms which is a huge difference. Imagine what would happen if the DOM tree has very complex structures and contents but only a small part of the page gets modified, if you can isolate that from the rest of the page you could get similar benefits.
State of the art #
This is not a new spec, Chrome 52 shipped the initial support by July 2016, but during last year there has been some extra development related to it and that’s what I want to highlight in this blog post.
First of all many specification issues have been fixed and some of them imply changes on the implementations, most of this work has been carried on by Florian Rivoal in collaboration with the CSS Working Group.
Not only that but on the tests side Gérard Talbot has completed the test suite in the web-platform-tests (WPT) repository, which is really important to fix bugs on the implementations and ensure interoperability.
In my case I’ve been working on the Chromium implementation fixing several bugs and interoperability issues and getting it up to date according to the last specification changes. I took advantage of the WPT test suite to do this work and also contributed back a bunch of tests there. I also imported Firefox tests into Chromium to improve interop (even did a small Firefox patch as part of this work).
Last, it’s worth to notice that Firefox has been actively working on the implementation of css-contain during last year (you can test it by enabling the runtime flag
layout.css.contain.enabled). Hopefully that would bring a second browser engine shipping the spec in the future.
CSS Containment is a nice and simple specification that can be useful to improve web rendering performance in many different use cases. It’s true that currently it’s only supported by Chromium (remember that Firefox is working on it too) and that more improvements and optimizations can be implemented based on it, still it seems to have a huge potential.
One more time all the work from Igalia related to css-contain has been sponsored by Bloomberg as part of our ongoing collaboration.
Bloomberg has some complex UIs that are taking advantage of css-contain to improve the rendering performance, in future blog posts we’ll talk about some of these cases and the optimizations that have been implemented on the rendering engine to improve them.