We left a question in the previous article, how does the renderer load the document? How are HTML, CSS, and JS parsed? Let’s find out.

preface

Rendering processes are involved in many aspects of Web Performance, and there must be quite a few things involved. This is just a general overview. If you’re interested in taking a look at it, check out the Performance Section of Web Fundamentals

The rendering process processes web page content

As mentioned in the previous chapter, everything in a TAB is controlled by the rendering process. The main thread handles most of your code in the rendering process. If you use a Web worker or a service worker, the Wokrder thread handles part of your code. Compositor and raster threads also run within the renderer process to render pages efficiently and smoothly.

In short, the core task of the rendering process is to transform HTML, CSS, and JS into a web page that users can interact with.

The renderer process has a main thread, multiple worker threads, a Compositor thread and a raster thread

Page parsing process

The construction of the DOM

When the renderer process receives the navigation submission information from the browser process and begins to receive HTML data, the main thread begins to parse the HTML text string into the DOM (Document Object Model).

DOM is the internal representation of a web page in a browser, as well as the data structure and API through which we interact with the web page through JS.

The HTML Standard defines how to parse HTML documents into the DOM. You may also have noticed that the browser never fails to parse HTML. If we forget to put a close tag on the tag, we’re not gonna get an error here. Or this string of Hi! I’m Chrome!
can also be parsed normally, which looks like normal writing Hi! I’m Chrome!
makes no difference. If you want to know why, that’s how it’s designed, that’s how HTML is designed to help us deal with these errors. See “An Introduction to Error Handling and Strange Cases in the Parser “for more information.

Subresource loading

External resources such as images,CSS, and JS are often used in HTML documents. These resources are either retrieved from the network or from the cache. The main thread can find these tags and request these resources as it parses the DOM. But for efficiency, the browser launches a preload scanner at the same time. The preload scanner looks for tokens generated by the HTML parserIf it does, it sends the request to the web thread of the browser process.

The main thread parses the HTML and builds the DOM tree

JavaScript blocks parsing

If a

Tell the browser how you want to load the resource

There are many ways to tell the browser how to load resources. For example, you can add async or defer properties to the

Style calculation

DOM alone is not enough; we also need styles to present our pages well. The main thread parses the CSS and determines the computed style (computed Style) for each DOM node. Computed styles are based on information from CSS selectors that indicate what style to apply to each element. You can see this information in the computed section of the developer tool.

The main thread parses the Css and styles the Dom nodes computatively

Even if we don’t write any CSS code, each DOM node has a computed style. For example, the

tag is larger than the H2 tag, and a margin is defined for each element. This is because the browser has a default style sheet. Chrome’s default stylesheet can be found by referring to You Can see the source code here

Layout (Layout)

Now the renderer knows the structure of the document and the style of the Dom nodes, but it’s not enough to render the page. For example, let’s play a picture guessing game. You draw a picture to your friend on your mobile phone. You say here’s a big red circle and here’s a small square. I’m sure your friend has no idea. Because your friend doesn’t know where the big circles and squares are in the picture.

Paint a picture to your friend

Layout is the process of calculating the geometry of elements. The main thread traverses the DOM tree and evaluates styles to create the layout tree, which contains information like X and Y coordinates and bounding box sizes. Layout trees are generally smaller than DOM trees and only contain information related to the visible content of the page. For example, the display: None element will not be included in the layout tree (note :visibility: hidden will be included in the layout tree, but not visible to us). Then there are pseudo-elements, such as p::before{content:”Hi!” }, although the pseudo-elements are not included in the DOM, they are included in the layout tree.

The main thread traverses the DOM tree with computed style and produces the layout tree

The layout process is not that simple and easy. For even the simplest page layout, such as arranging paragraphs from top to bottom, this process must take into account the size of the font and where each line ends, as these affect the size and shape of paragraphs and, in turn, the location of the next paragraph.

Layout changes due to line breaks

You can do a lot of things with CSS, such as float elements, hide overflow parts, or change the direction of input fields. So d can imagine how complex the layout phase can be, with a dedicated team in the Chrome team responsible for the layout process. If you’re interested in their work, check out this video few Talks from BlinkOn Conference

Paint process

Now we have the DOM, style, and layout, but that’s not enough to render the page. Just like drawing, knowing the size, shape and position of the elements in your drawing, you have to consider which elements to start with.

A man stands in front of a canvas and wonders whether he should draw a circle or a square first

For example, if you assign different Z-index attributes to certain elements and then render the elements in the order they are in the HTML, the rendered page will have problems.

Page elements appear in the order in the HTML, rendering the page incorrectly because z-index is not taken into account

At the draw step, the main thread traverses the layout tree to create the draw record. Drawing notes are notes of the drawing process, as in “background should be drawn before text and then rectangle”. If you’ve ever used canvas, this process should be familiar.

The main thread traverses the layout tree to generate draw records

Updating the rendering pipeline is expensive

It is important to understand that each step in the rendering pipeline is based on the data generated in the previous step. For example, if the layout tree changes, the drawing order needs to be regenerated for the affected part.

DOM+Style, Layout, and Paint trees are generated sequentially

If you use the animation, the line must be made within the clearance between frame and frame, how long is the gap, at present most of the device screen refresh rate of 60 times per second, if the browser to render the rate at which each frame animation screen refresh rate consistent with equipment, which is 1 second / 60 = 16.66 milliseconds, So what we see is a smooth screen, if the time is longer than this, the page will drop frames. If you are interested, you can refer to it here

The render did not keep pace with the screen refresh and dropped frames

Even if rendering keeps up with the refresh rate of the screen, it is still possible to encounter frame lag, because this process is running on the main thread along with JS, and js execution can completely block the main thread.

Js blocks the main thread

We can split the JS operations into requestAnimationFrame. Or pure computing tasks we can put into Web workers. See Optimize JavaScript Execution and JavaScript in Web Workers for details

Execute small block JS using requestAnimationFrame

Compositing

How to draw the page?

Now that the browser knows the structure of the document, the style of each element, the geometry of the page, and the order in which it draws the page, how does it draw the page? The process of converting this information into pixels is called rasterizing.

Chrome initially handled rasterization by rasterizing parts of the viewport, moving parts that were already rasterized as the user scrolled through the page, and then rasterizing parts that entered the viewport to fill in missing parts of the page.

Simple raster process

But browsers now run a more complex process called compositing to improve performance and efficiency.

What is composition

Compositing involves breaking the page into different layers, rasterizing it individually, and compositing the entire page in a single compositor thread. If the page scrolls, since each layer is rasterized, the only thing left to do is to compose new frames to display the page. Animation can be done in the same way, by moving “layers” to combine layers of new frames.

Compositing process animation

We can use the Layers Panel in the developer tools to see how our pages are layered.

Page layered

To find out which elements should be in which layers the main thread traverses the layout tree to create the Layer tree, this section is called “Update Layer Tree” in the Developer Tools Performance panel. If some parts of the page should be placed in a separate layer but are not (such as the sidebar menu), we can tell the browser to layer them through the csswill-change attribute.

The main thread traverses the layout tree to produce the layer tree

However, you can’t give as many layers as you want, and when there are too many layers the compositing operation is slower than rasterizing a small portion of the page in each frame, so evaluating the rendering performance of the page is essential. For more information, see the article Stick to Compositor-only Properties and Manage Layer Count.

Rasterized and synthesized outside the main thread

Once the layer tree is created and the drawing order is determined, the main thread submits this information to the Compositor thread. The composite thread rasterizes each layer. But some layers can be as big as the entire page, so the composite thread breaks them up into smaller pieces and sends each piece to the raster thread. The raster thread rasterizes each chunk and stores it in GPU memory.

The raster thread creates each bitmap and sends it to the GPU

Composite threads can assign priority to different raster threads so that pages in or near the viewport can be rasterized more quickly. In order to handle operations like scaling, the same layer is equipped with different blocks for different sharpness.

After the blocks are rasterized, the compositing thread collects information about the blocks, called draw quads, and creates a compositing frame.

  • Draw quads: Draw quads contain information such as the location of blocks in memory and the position of the composable blocks on the page
  • Compositor Frame: A collection of picture quadrilaterals that represent a frame of a page

The synthesized frame is then submitted to the browser process via IPC. Additional compositing frames may be added from the UI thread, or from other renderers for extension purposes, due to changes in the browser UI. These composite frames are sent to the GPU and displayed on the screen. If there is a scroll event, the compositing thread will create a new compositing frame and send it to the GPU.

The composition thread creates the composition frame. Frames are sent to the browser and finally to the GPU

The advantage of composition is that this process is independent of the main thread and does not block the main thread, nor will it be blocked by style calculations and JS execution on the main thread.

This is why composing only animations is considered to have good performance. If the layout or drawing needs to be evaluated again, the main thread must be involved.

The resources

inside-browser-part3