The CSS 2.1 specification defines a box model for displaying content inside a document. The WebKit and Blink engines adhere to this specification down to an implementation level, thus assuming elements are laid out as a single box when laying out a document. However, when adding fragmentation in the mix you may end up with multiple boxes for a single element. This means the engine must do some tricks to correctly render the box fragments. Most aspects of the fragmentation implementation are covered in my previous blog-post CSS Fragmentation in WebKit. In this article I’d like to focus on how painting is implemented for CSS Regions.
Let’s consider a document that defines a named flow (a
RenderFlowThread object) containing a large paragraph flowed through three regions with specified width and height. In the first step of the layout only the contents of the body element are laid out, ignoring the elements in the named flow. This step will compute the size and the position of the regions (the
RenderRegion objects) in the page. During the second step, the engine lays out the contents of the flow thread. As you can see, the regions have different widths which will reflect on the size of the line boxes. In Figure 1 you can see the flow thread sketched after layout. Each fragment of the paragraph is coloured differently to reflect the region where it will be positioned. As the spec says, the flow thread has the width of the widest region (in our case, region number three). The widths of the line boxes are set to the width of their containing block in each region by using information stored in objects of type
RenderBoxRegionInfo. They contain length values required to adjust the width of each block in every region where that block is present. The extra white space between the last line in a region and the first line in the next region is the pagination strut (represented by grey arrows in Figure 1), the offset applied to content to simulate unforced breaks. Because the height of the flow thread is the sum of the heights of the regions, if there is too much content to fit all the regions the content will overflow the flow thread (see the lines at the bottom of the flow thread).
The actual rendering of the content as fragments happens at paint time. When a region is called to paint itself it will adjust the position of the graphics context and offset the flow thread layer to paint inside the region content box at the correct position. The entire content of the flow thread has its width adjusted using the
RenderBoxRegionInfo objects so that everything will look as expected inside the region (e.g. the box decorations such as borders, shadows etc.). The parts of the flow thread that are not supposed to be painted (i.e. they belong to other regions) are clipped out as you can see in the greyed area of Figure 2. In the last region you can see the bottom overflow of the region is not clipped. That’s because the clipping is made using the visual overflow of the RenderFlowThread, which includes all the content that doesn’t have a self-painting layer. The content of the flow thread (including the overflow) is split by the regions content boxes and each region paints only its part of the flow. At the end of the painting process, the document looks like the one in Figure 3 below. Each region contains its portion of the flow and the overflow in the last region is visible.
The most important improvement we are currently working on is delegating the painting of the flow thread to the region layer, not the region renderer as it is currently implemented. This enables us to integrate the flow thread painting with the layer tree architecture and fix some of the major bugs remaining in the implementation (e.g. incorrect clipping of the relative positioned content overflowing a region).