Complete high-frequency question bank warehouse address: github.com/hzfe/awesom…

The full frequency question bank reading address: febook.hzfe.org/

Issues related to

  • How does the browser render the page

  • What can be done to improve browser rendering performance

Answer key points

DOM CSSOM threads mutually exclusive render tree Compositing GPU acceleration

When the browser process gets the first byte of HTML, it tells the renderer process to start parsing the HTML, turning it into a DOM tree, and entering the rendering process. In general, all browsers go through five steps:

  1. PARSE: Parses HTML and builds a DOM tree.

  2. STYLE: Calculates the final valid STYLE for each node.

  3. LAYOUT: Calculates LAYOUT information such as position and size for each node.

  4. PAINT: PAINT different boxes, split into layers to avoid unnecessary repainting.

  5. COMPOSITE & RENDER: Combine the above different layers into a bitmap, send it to the GPU, RENDER it on the screen.

In order to improve the rendering performance of the browser, the usual means is to ensure that the rendering process is not blocked, avoid unnecessary rendering calculation and rearrangement redrawing, and use GPU hardware acceleration and other technologies to improve the rendering performance.

Knowledge depth

1. Browser rendering process

The main steps of Chromium’s rendering process are shown below:

Image credit: Life of a Pixel

1.1 Parse Phase: Parsing HTML

Build a DOM tree

The main thread of the renderer process parses the HTML and builds a DOM tree of structured data structures through the following steps:

  1. Conversion: The browser reads the raw bytes of an HTML file from the network or disk and converts the bytes to characters based on the specified file encoding (such as UTF-8).

  2. Tokenizing: The browser converts strings into different tags (e.g. < HTML >, ) according to the HTML specification.

  3. Lexing (Syntax analysis) : The tags generated in the previous step are converted into objects that contain various information about HTML syntax, such as attributes, attribute values, text, and so on.

  4. DOM Construction: Because HTML tags define relationships between different tags, the objects generated in the previous step are linked in a tree data structure to identify parent and sibling relationships.

The process for building the DOM is shown below:

On the Constructing the Object Model

Secondary resource loading

A web page often uses multiple external resources, such as images, JavaScript, CSS, fonts, and so on. The main thread requests each of these resources as it encounters them while parsing the DOM. To speed up the rendering process, a thread called the Preload scanner runs concurrently. If something like IMG or link is present in the HTML, the preloaded scanner looks at the tags generated by the HTML Parser and sends a request to the web thread of the browser process to get those resources.

JavaScript can block parsing

When the HTML parser finds aScript tag, it pauses parsing of the HTML and starts loading, parsing, and executing JavaScript instead. JS can change the structure of the DOM. If you don’t want JS to block parsing of the HTML, you can add the defer attribute to the script tag or place the script before the body closing tag. The browser will execute the JS code at the end to avoid blocking the DOM build.

1.2 Style stage: Style calculation

The CSS engine processes styles in three stages:

  1. Collect, partition, and index style rules in all stylesheets. The CSS engine collects and indexes all style rules from style tags, CSS files, and browser proxy styles for efficient subsequent queries.

  2. Accessing each element and finding all the rules that apply to that element, the CSS engine traverses the DOM nodes, makes selector matches, and performs style Settings for matching nodes.

  3. Combine the cascade rules with other information to generate the final calculation styles for the nodes, whose values can be obtained via window.getComputedStyle().

In large web sites, there are a lot of CSS rules, and if you keep a copy of style values for each node, it will consume too much memory. Instead, CSS engines typically create shared style structures, and computed style objects typically have Pointers to the same shared structure.

A DOM tree with a computational style attached, commonly called CSSOM (CSS Object Model) :

On the Constructing the Object Model

CSSOM and DOM are built in parallel, and building CSSOM does not block the building of the DOM. But CSSOM blocks the execution of JS because JS may manipulate style information. While CSSOM does not block the build of the DOM, you must wait for the CSSOM build to complete before moving on to the next phase. This is also commonly known as CSSOM blocking rendering.

1.3 Layout stage

Create the LayoutObject (RenderObject) tree

Once you have the DOM tree and the computed styles of the elements in the TREE, the browser combines this information into a layout tree that collects all the visible DOM nodes and all the style information for each node.

Layout trees and DOM trees do not necessarily correspond one to one. To build a Layout tree, browsers do the following:

  1. Each visible node is traversed starting at the root of the DOM tree.
  • Some invisible nodes (such as script, head, meta, etc.) do not show up in the render output and are ignored.

  • Some nodes that are hidden by setting display to None are also ignored in the rendering tree.

  • Create a LayoutObject for the pseudo-element.

  • Creates a LayoutObject corresponding to the anonymous contain block for the inline element.

  1. For each visible node, find suitable CSSOM rules for it and apply them.

  2. Outputs visible nodes that contain their contents and style of computation.

Image credit: Render- Tree Construction

Layout calculation

The previous step calculated the visible nodes and their styles, followed by the need to calculate their exact position and size within the device viewport, a process commonly known as automatic rearrangement.

The browser layout calculation includes the following:

  1. Calculate the size and location of various generation boxes for each element based on the CSS box model and visual formatting model.

  2. Calculates the size and position of block-level elements, inline elements, floating elements, and various positioning elements.

  3. Calculate the size and position of text, scroll area.

  4. LayoutObject comes in two types:

  • The traditional LayoutObject node writes the result of the layout operation back into the layout tree.

  • The output of the LayoutNG (enabled in Chrome 76) node is immutable and will be stored in NGLayoutResult, which is a tree-like structure with much less backtracking than the previous LayoutObject, improving performance.

1.4 Paint stage

The Paint phase transforms the LayoutObject tree into an efficient rendering format for use by the synthesizer, including a CC ::Layers list containing a list of display items, associated with cc::PropertyTrees.

Build the PaintLayer (RenderLayer) tree

The LayoutObject tree that has been built cannot be displayed yet because it does not contain the order of drawing (z-index). At the same time, in order to account for some complex cases, such as 3D transformation, page scrolling, the browser will layer the previous node. This process is called establishing a cascading context.

The browser will set up a cascading context according to the CSS cascading context specification. Common situations are as follows:

  1. The Document node of the DOM tree corresponds to the RenderView node.

  2. The child of the Document node in the DOM tree, which is the RenderBlock node corresponding to the HTML node.

  3. Node that explicitly specifies the CSS position (position is absolute or fixed).

  4. A node with a transparent effect.

  5. Nodes with CSS 3D properties.

  6. Nodes that use Canvas element or Video element.

When the browser iterates through the LayoutObject tree, it creates a PaintLayer tree, and LayoutObject and PaintLayer don’t necessarily correspond one to one. Each LayoutObject is associated either with its own PaintLayer or with the first ancestor of the PaintLayer that owns the PaintLayer.

Build CC ::Layer and display items

The browser continues to create the CC ::Layer list from the PaintLayer tree. Cc ::Layer is a listlike structure. Each Layer contains a list of DisplayItems, and each DisplayItem contains the actual Paint OP directive. By layering pages, one layer can be transformed and rasterized independently of the others.

  1. Compositing Update
  • GraphicsLayers depending on the PaintLayer

  • This policy is called CompositeBeforePaint and will be replaced by CompositeAfterPaint in the future.

  1. PrePaint
  • The PaintInvalidator does an invalidation check to find the display items to draw.

  • Build a Paint Property tree that enables animations, page scrolling, clip, and other changes to run only on the composite thread to improve performance.

    Image courtesy Compositor Property Trees

  1. Paint
  • Walk through the LayoutObject tree and create the display items list.

  • Create paint Chunks for the display Items list that shares the same property tree state.

  • Commit the results to the Compositor.

  • The CompositeAfterPaint determines the layering at this point.

  • Paint Chunks are passed to the Compositor through the CC ::Layer list.

  • Convert the property tree to CC ::PropertyTrees.

In the above process, there are two different times to create CompositeBeforePaint, which is done in the render main thread. One is CompositeAfterPaint, and the subsequent layer creation is done in the Chromium Compositor (CC) thread.

1.5 the synthesis of Compositing

The synthesis phase takes place in the Chromium Compositor thread (CC).

commit

When the Paint phase is complete, the main thread enters the COMMIT phase, and updates the Layer list and property tree in the CC ::Layer to the cc thread’s LayerImpl, and the COMMIT phase is complete. The main thread is blocked during commit.

tiling & raster

Raster is the process of converting drawing operations in a display item into a bitmap.

The main operation process of rasterization is as follows:

  1. Tiling: Divide the layer into tiles. Because some layers may be large (such as the scrolling root node of the entire document), rasterization of the entire layer is expensive, and some parts of the layer are not visible, resulting in unnecessary waste.

  2. Tiles are the basic unit of rasterization. Rasterization is handled through a raster thread pool. Tiles closer to the viewport have higher priority and will be processed first.

  3. A layer actually generates tiles of multiple resolutions.

  4. Raster also processes the image resources referenced by the page. Paint Ops in display Items references these compressed data, and Raster calls the appropriate decoder to extract the data.

  5. Raster will make OpenGL calls through Skia to raster the data.

  6. The renderer process runs in a sandbox and cannot make system calls directly. Paint OPS is passed to the GPU process via IPC (MOJO), and the GPU process performs real OpenGL (converted to DirectX on Windows for performance) calls.

  7. Rasterized bitmap results are stored in GPU memory, usually as OpenGL material objects.

  8. Double buffering mechanism: The current rasterization is performed on the pending Tree (LayerImpl). Once the rasterization is completed, the pending tree becomes active. Subsequent draw operations are performed on the Active Tree.

draw

When all the tiles have been rasterized, draw quads are generated. Each Draw Quads contains a command that draws tiles at a specific location on the screen, taking into account all transformations applied to the Layer Tree. Each quadrilateral references the rasterized output of an in-memory tile. The quadrilateral is wrapped in a Compositor Frame object, which is then submitted to the browser process.

Display Compositor (Viz, Visual)

Viz resides in the GPU process and receives composited frames from the browser, from multiple renderers, as well as the Compositor of the browser’s own UI.

The composited frame is associated with the position on the screen to be drawn, called the surface. Surfaces can be nested with other surfaces. The surface of the browser UI is nested with the surface of the renderer, and the surface of the renderer is nested with the surface of other cross-domain Iframes (homologous Iframes sharing the same renderer). Viz synchronizes incoming frames and handles surface aggregation of nested surfaces.

Final display flow:

  1. Viz will issue an OpenGL call to send the Quads in the composite frame to the BACKbuffer of the GPU thread.

  2. In the new mode, Viz will use Skia instead of the original OpenGL call.

  3. On most platforms, the viz output is also a double-buffering structure, Draw first reaches the backbuffer, converted into frontbuffer through Considerations and finally displayed on the screen.

Thread handling of browser events

The advantage of composition is that it is done without involving the render main thread. The synthesizer does not need to wait for style calculations or JavaScript execution. Composit-only animations are considered the best choice for fluency. At the same time, synthesizer is also responsible for dealing with the page scrolling, scrolling, synthesizer will update the position of the page, and update the content of the page.

When a page scrolls without any binding events, the synthesizer can create composite frames independently of the main render thread to ensure that the page flows. When a Region of the page is bound to a JS event handler, the CC thread marks that Region as a non-fast Scrollable Region. If the event comes from outside the region, the CC thread continues to synthesize new frames without waiting for the main thread.

In development, we usually use event delegates to simplify the logic, but this makes the entire Region of bound events a non-fast Scrollable Region. To mitigate the scrolling effects of this situation, you can pass passive: true to the event listener.

document.body.addEventListener(
  "touchstart",
  (event) => {
    if (event.target === area) {
      event.preventDefault();
    }
  },
  { passive: true }
);

Copy the code

2. Optimization of browser rendering performance

In the previous section, we looked at a typical browser rendering process. After the process is complete, various tree data structures, such as DOM, CSSOM, LayoutObject, PaintLayer, etc., are retained so that the rendering process can be retriggered when user actions, network requests, JS executions, etc.

2.1 Reduce rearrangement and redraw in rendering

When the browser rerenders, it may start at any of the intermediate steps until the rendering is complete. Therefore, shortening the rendering path as much as possible can achieve better rendering performance. When a browser redraws a frame, it typically goes through three main stages: layout, drawing, and composition. Of the three phases, calculating layout and drawing takes time, while composing takes less time.

Take animation as an example, if the JS timer is used to control animation, it may need more operations to modify the layout and drawing, generally there are the following two methods for optimization:

  1. Use appropriate page layering techniques: for example, using a multi-layer canvas, layering the animation background, moving body, and secondary objects so that each frame needs to change only one or part of the composition layer, not the entire page.

  2. Using CSS Transforms and Animations: This allows the browser to animate all layers simply by using a synthesizer, without having to recalculate the layout and redraw the graphics. Triggering only the properties of Composite in the CSS Triggers is the optimal choice.

2.2 Optimize resources that affect rendering

Both CSS and JS can affect the rendering of a page while the browser is parsing HTML. Optimization methods include the following points:

  1. Key CSS resources are loaded in the header.

  2. JS is usually placed at the bottom of the page.

  3. Add async and defer properties to JS.

  4. Try to keep CSS and JS out of the body.

  5. Specify width and height for img to avoid rearranging the image after it has been loaded.

  6. Avoid slow elements such as tables and iframe. The reason is that the table will wait until its DOM tree is fully generated before being inserted into the page at once. Iframe resources download process will block the parent page static resources download and CSS, DOM tree parsing.

Image from The Script Element

The resources

  1. How browsers work: Behind the scenes of the new Web browser
  2. Render pages: How browsers work
  3. Constructing the Object Model
  4. Inside a super fast CSS engine
  5. Render-tree Construction, Layout, and Paint
  6. Inside Look at Modern Web Browser (Part 3)
  7. Inside Look at Modern Web Browser (Part 4)
  8. DOM
  9. CSS
  10. Layout
  11. Paint
  12. how cc works
  13. Life of a Pixel