This article is translated, and the content should be as consistent as possible with the original text. If there is any mistake in translation, criticism and correction are welcome.

Originally written by Mariko Kosaka

Inside Look at Modern Web Browser (Part 3)

The original link: developers.google.com/web/updates…

The inner workings of the renderer process

This is the third in a four-part blog series on how browsers work. Earlier we introduced multi-process architecture and navigation flows. In this article, we’ll look at what happens inside the renderer process.

The renderer process involves many aspects of Web performance. Due to a lot of things happening inside the renderer process, this article is just a general overview. If you want to dig deeper, there are more resources in the Performance section of Web basics.

The renderer process processes Web content

The renderer process is responsible for everything that happens inside the TAB. In the renderer process, the main thread handles most of the code you send to the user. If you use a Web Worker or Service Worker, sometimes part of the JavaScript is handled by a Worker thread. The synthesizer and raster threads also run within the renderer process to render the page efficiently and smoothly.

The core job of the renderer process is to transform HTML, CSS, and JavaScript into web pages that users can interact with.

Figure 1: A renderer process with a main thread, worker thread, synthesizer thread, and raster thread inside

parsing

The construction of the DOM

When the renderer process receives the submission message for the navigation and begins to receive THE HTML data, the main thread begins to parse the text string (HTML) and convert it into the Document Object model (DOM).

The DOM is the browser’s internal representation of a page, as well as a data structure and API that Web developers can interact with through JavaScript.

Parsing HTML documents into the DOM is defined by the HTML standard. As you may have noticed, providing HTML to a browser never raises an error. For example, lack of closure

I’m Chrome
!
I’m Chrome
!
Error handling and an introduction to strange situations in the parser

Subresource loading

Websites often use external resources such as images, CSS, and JavaScript. These files need to be loaded from the network or cache. The main thread can find them during parsing to build the DOM and request them individually, but to speed things up, the “preload scanner” runs concurrently. If it exists in the HTML documentorThe preload scanner looks at the token generated by the HTML parser and sends the request to the network thread in the browser process.

Figure 2: The main thread parses the HTML and builds the DOM tree

JavaScript prevents parsing

When the HTML parser comes across there is a nice chart). This is why HTML parsers must wait for JavaScript to run before they can continue parsing HTML documents. If you’re curious about what’s going on in JavaScript implementation, the V8 team has talks and blogs about it.

Prompt browser how do you want to load resources

Web developers can send prompts to browsers in a number of ways to load resources nicely. If your JavaScript doesn’t use document.write(), you can add async or defer properties to the

Style calculation

Having the DOM is not enough to know what the page looks like, because we can style the page elements in CSS. The main thread parses the CSS and determines the computational style of each DOM node. This is information about which style is applied to each element based on the CSS selector. You can see this information in the computing section of DevTools.

Figure 3: Main thread parses CSS to add computing styles

layout

Now the renderer process knows the structure of the document and the style of each node, but this is not enough to render the page. Imagine that you are trying to describe a painting to your friend over the phone. “There is a big red circle and a small blue square” is not enough to let your friend know exactly what the painting looks like.

Figure 4: A person standing in front of a picture with telephone lines connected to another person

Layout is a process of finding the geometry of elements. The main thread iterates through the DOM and evaluates the style, and creates a layout tree that contains information like x and Y coordinates and bounding box sizes. A layout tree may have a similar structure to a DOM tree, but it contains only information related to what is visible on the page. If display: None is applied, the element is not part of the layout tree (however, an element that uses visibility: hidden is still part of the layout tree). Similarly, if you apply something like p:before{content: “Hi!” }, which is included in the layout tree even if it is not in the DOM.

Figure 5: The main thread uses computational style to traverse the DOM tree and generate the layout tree

Determining the layout of a page can be a challenging task. Even for simple page layouts, such as fast streaming from top to bottom, you have to consider how big the font is and where it runs, because these affect the size and shape of paragraphs; This affects where the next paragraph needs to go.

CSS can float elements to one side, shield overflow items, and change the direction of writing. It is conceivable that this layout phase task is arduous. In Chrome, entire teams of engineers are responsible for layout, and if you want to check out the details of their work, some of the presentations at the BlinkOn Conference were recorded and very interesting.

Player.bilibili.com/player.html…

Figure 6: Paragraph box layout moved due to newline changes

draw

Having DOM, style, and layout is still not enough to render a page. Suppose you’re trying to copy a painting and you know the size, shape and position of the elements, but you still need to judge the order in which you draw them.

Figure 7: A person holding a brush in front of a canvas, not knowing whether to draw a circle or a square first

For example, you might set the Z-index for some elements, in which case drawing in the order of the elements written in HTML would result in an error.

Figure 8: Page elements appear in the order of HTML tags, resulting in rendering errors because z-index is not considered

In this draw step, the main thread traverses the layout tree to create the draw record. Drawing record is a record of the painting process of “background first, then text, then rectangle”. If you use JavaScript to draw on elements, you may want to be familiar with this process.

Figure 9: The main thread traverses the layout tree and generates draw records

The cost of updating the render pipeline is high

One of the most important aspects of the rendering pipeline is that at each step, new data is created using the results of the previous operation. For example, if something in the layout tree changes, the drawing order needs to be regenerated for the affected portions of the document.

Player.bilibili.com/player.html…

Figure 10: DOM+Style, Layout, and Paint tree generation order

If you are animating elements, the browser must run these actions between each frame. Most of our monitors refresh the screen 60 times per second (60 FPS); As each frame moves an object on the screen, the animation appears smooth to the human eye.

Figure 11: Animation frames on the timeline

Even if your render operation keeps up with the screen refresh, these calculations are running on the main thread, which means your application may be blocked when it runs JavaScript.

Figure 12: Animation frames on the timeline, but one frame is blocked by JavaScript

You can break up JavaScript operations into small chunks and schedule them to run on each frame using requestAnimationFrame(). For more information on this topic, see Optimizing JavaScript execution. You can also run JavaScript in Web Workers to avoid blocking the main thread.

synthetic

How would you draw a 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? Converting this information into pixels on the screen is called rasterization.

Perhaps a naive approach to this problem is to raster parts within the viewport. If the user scrolls the page, the raster frame is moved and the missing sections are filled in with more rasters. That’s how Chrome handled rasterization when it was first released. Modern browsers, however, run a more complex process called compositing.

Player.bilibili.com/player.html…

Figure 14: Animation of a simple rasterization process

What is composition?

Composition is a technique for dividing parts of a page into multiple layers, rasterizing them individually, and combining them into a single page in a separate thread called a synthesizer thread. If scrolling happens, because the layer has been rasterized, all it has to do is compose a new frame. Animation can be done in the same way by moving layers and compositing new frames.

You can use the Layers panel in DevTools to see how your site is divided into layers.

Player.bilibili.com/player.html…

Figure 15: Compositing process animation

layered

To figure out which elements need to be in which layers, the main thread traverses the layout tree to create a layer tree (this part is called “update layer tree” in the DevTools performance panel). If parts of the page that should be separate layers (such as slide-in side menus) are not available, you can alert the browser by using the will-change property in your CSS.

Figure 16: The main thread traverses the layout tree to generate the layer tree

It may be tempting to provide layers for each element, but composing over too many layers can result in slower operations than rasterizing a small portion of the page in each frame, so measuring your application’s rendering performance is critical. For more information on the topic, see Sticking to Synthesizer Properties only and managing Layer calculations.

Rasterization and compounding of main thread

Once the layer tree is created and the drawing order determined, the main thread submits this information to the synthesizer thread. The composite thread then rasterizes each layer. A layer may be as large as the entire length of the page, so the synthesizer thread divides them into blocks and sends each block to the raster thread. Raster threads raster each tile and store them in GPU memory.

Figure 17: Raster thread that creates slice bitmap and sends it to GPU

The synthesizer thread can prioritize different raster threads so that things in (or near) the viewport can be rasterized first. A layer also has tiling of different resolutions to handle actions such as zoom operations.

After the slice is rasterized, the synthesizer thread will collect the slice information of drawing quadrilateral to create the synthesizer frame.

Draw a quadrilateral Contains information such as the location of the tiles in memory and the location of the tiles to draw on the page with page composition in mind.
Synthesizer frame A collection of drawing quadrangles that represent a page frame

The synthesizer framework is then submitted to the browser process via IPC. At this point, another synthesizer framework can be added from the UI thread used for browser UI changes or from other renderer processes used for extensions. These synthesizer frames are sent to the GPU to display them on the screen. If a scroll event occurs, the synthesizer thread creates another synthesizer frame to send to the GPU.

Figure 18: The synthesizer thread creates the composite frame, and the frame is sent to the browser process and then to the GPU

The advantage of composition is that it is done without involving the main thread, and the synthesizer thread does not have to wait for style calculations or JavaScript execution. This is why compositing animation only is considered the best choice for smooth performance. If you need to recalculate the layout or draw, the main thread must be involved.

conclusion

In this article, we explored the rendering pipeline from parsing to compositing, and hope you now have the right to read more about optimizing site performance.

In the next and final article in this series, we’ll take a closer look at synthesizer threads to see what happens when user input such as mouse movements and clicks comes in.

Did you enjoy this article? If you have any questions or suggestions for future posts, I’d love to hear from you in the comments section below or @Kosamari on Twitter.