CINXE.COM
Inside look at modern web browser (part 3) | Blog | Chrome for Developers
<!doctype html> <html lang="en" dir="ltr"> <head> <meta name="google-signin-client-id" content="157101835696-ooapojlodmuabs2do2vuhhnf90bccmoi.apps.googleusercontent.com"> <meta name="google-signin-scope" content="profile email https://www.googleapis.com/auth/developerprofiles https://www.googleapis.com/auth/developerprofiles.award"> <meta property="og:site_name" content="Chrome for Developers"> <meta property="og:type" content="website"><meta name="theme-color" content="#1a73e8"><meta charset="utf-8"> <meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="manifest" href="/_pwa/chrome/manifest.json" crossorigin="use-credentials"> <link rel="preconnect" href="//www.gstatic.com" crossorigin> <link rel="preconnect" href="//fonts.gstatic.com" crossorigin> <link rel="preconnect" href="//fonts.googleapis.com" crossorigin> <link rel="preconnect" href="//apis.google.com" crossorigin> <link rel="preconnect" href="//www.google-analytics.com" crossorigin><link rel="stylesheet" href="//fonts.googleapis.com/css?family=Google+Sans:400,500|Roboto:400,400italic,500,500italic,700,700italic|Roboto+Mono:400,500,700&display=swap"> <link rel="stylesheet" href="//fonts.googleapis.com/css2?family=Material+Icons&family=Material+Symbols+Outlined&display=block"><link rel="stylesheet" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/chrome/css/app.css"> <link rel="stylesheet" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/chrome/css/dark-theme.css" disabled> <link rel="shortcut icon" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/chrome/images/favicon.png"> <link rel="apple-touch-icon" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/chrome/images/touchicon-180.png"><link rel="canonical" href="https://developer.chrome.com/blog/inside-browser-part3"><link rel="search" type="application/opensearchdescription+xml" title="Chrome for Developers" href="https://developer.chrome.com/s/opensearch.xml"> <link rel="alternate" hreflang="en" href="https://developer.chrome.com/blog/inside-browser-part3" /><link rel="alternate" hreflang="x-default" href="https://developer.chrome.com/blog/inside-browser-part3" /><link rel="alternate" hreflang="ar" href="https://developer.chrome.com/blog/inside-browser-part3?hl=ar" /><link rel="alternate" hreflang="bn" href="https://developer.chrome.com/blog/inside-browser-part3?hl=bn" /><link rel="alternate" hreflang="zh-Hans" href="https://developer.chrome.com/blog/inside-browser-part3?hl=zh-cn" /><link rel="alternate" hreflang="zh-Hant" href="https://developer.chrome.com/blog/inside-browser-part3?hl=zh-tw" /><link rel="alternate" hreflang="nl" href="https://developer.chrome.com/blog/inside-browser-part3?hl=nl" /><link rel="alternate" hreflang="fa" href="https://developer.chrome.com/blog/inside-browser-part3?hl=fa" /><link rel="alternate" hreflang="fr" href="https://developer.chrome.com/blog/inside-browser-part3?hl=fr" /><link rel="alternate" hreflang="de" href="https://developer.chrome.com/blog/inside-browser-part3?hl=de" /><link rel="alternate" hreflang="he" href="https://developer.chrome.com/blog/inside-browser-part3?hl=he" /><link rel="alternate" hreflang="hi" href="https://developer.chrome.com/blog/inside-browser-part3?hl=hi" /><link rel="alternate" hreflang="id" href="https://developer.chrome.com/blog/inside-browser-part3?hl=id" /><link rel="alternate" hreflang="it" href="https://developer.chrome.com/blog/inside-browser-part3?hl=it" /><link rel="alternate" hreflang="ja" href="https://developer.chrome.com/blog/inside-browser-part3?hl=ja" /><link rel="alternate" hreflang="ko" href="https://developer.chrome.com/blog/inside-browser-part3?hl=ko" /><link rel="alternate" hreflang="pl" href="https://developer.chrome.com/blog/inside-browser-part3?hl=pl" /><link rel="alternate" hreflang="pt-BR" href="https://developer.chrome.com/blog/inside-browser-part3?hl=pt-br" /><link rel="alternate" hreflang="ru" href="https://developer.chrome.com/blog/inside-browser-part3?hl=ru" /><link rel="alternate" hreflang="es-419" href="https://developer.chrome.com/blog/inside-browser-part3?hl=es-419" /><link rel="alternate" hreflang="th" href="https://developer.chrome.com/blog/inside-browser-part3?hl=th" /><link rel="alternate" hreflang="tr" href="https://developer.chrome.com/blog/inside-browser-part3?hl=tr" /><link rel="alternate" hreflang="vi" href="https://developer.chrome.com/blog/inside-browser-part3?hl=vi" /><link rel="alternate" hreflang="en-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3" /><link rel="alternate" hreflang="x-default" href="https://developer.chrome.google.cn/blog/inside-browser-part3" /><link rel="alternate" hreflang="ar-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=ar" /><link rel="alternate" hreflang="bn-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=bn" /><link rel="alternate" hreflang="zh-Hans-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=zh-cn" /><link rel="alternate" hreflang="zh-Hant-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=zh-tw" /><link rel="alternate" hreflang="nl-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=nl" /><link rel="alternate" hreflang="fa-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=fa" /><link rel="alternate" hreflang="fr-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=fr" /><link rel="alternate" hreflang="de-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=de" /><link rel="alternate" hreflang="he-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=he" /><link rel="alternate" hreflang="hi-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=hi" /><link rel="alternate" hreflang="id-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=id" /><link rel="alternate" hreflang="it-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=it" /><link rel="alternate" hreflang="ja-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=ja" /><link rel="alternate" hreflang="ko-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=ko" /><link rel="alternate" hreflang="pl-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=pl" /><link rel="alternate" hreflang="pt-BR-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=pt-br" /><link rel="alternate" hreflang="ru-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=ru" /><link rel="alternate" hreflang="es-419-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=es-419" /><link rel="alternate" hreflang="th-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=th" /><link rel="alternate" hreflang="tr-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=tr" /><link rel="alternate" hreflang="vi-cn" href="https://developer.chrome.google.cn/blog/inside-browser-part3?hl=vi" /><title>Inside look at modern web browser (part 3) | Blog | Chrome for Developers</title> <meta property="og:title" content="Inside look at modern web browser (part 3) | Blog | Chrome for Developers"><meta name="description" content="Inner workings of a browser rendering engine"> <meta property="og:description" content="Inner workings of a browser rendering engine"><meta property="og:url" content="https://developer.chrome.com/blog/inside-browser-part3"><meta property="og:locale" content="en"><script type="application/ld+json"> { "@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": [{ "@type": "ListItem", "position": 1, "name": "Blog", "item": "https://developer.chrome.com/blog" },{ "@type": "ListItem", "position": 2, "name": "Inside look at modern web browser (part 3)", "item": "https://developer.chrome.com/blog/inside-browser-part3" }] } </script> <link rel="stylesheet" href="/extras.css"></head> <body class="" template="page" theme="chrome-theme" type="blog" appearance layout="docs" display-toc pending> <devsite-progress type="indeterminate" id="app-progress"></devsite-progress> <section class="devsite-wrapper"> <devsite-cookie-notification-bar></devsite-cookie-notification-bar><devsite-header role="banner"> <div class="devsite-header--inner nocontent"> <div class="devsite-top-logo-row-wrapper-wrapper"> <div class="devsite-top-logo-row-wrapper"> <div class="devsite-top-logo-row"> <button type="button" id="devsite-hamburger-menu" class="devsite-header-icon-button button-flat material-icons gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Navigation menu button" visually-hidden aria-label="Open menu"> </button> <div class="devsite-product-name-wrapper"> <a href="/" class="devsite-site-logo-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Site logo" track-type="globalNav" track-name="chromeForDevelopers" track-metadata-position="nav" track-metadata-eventDetail="nav"> <picture> <source srcset="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/chrome/images/lockup-dark-theme.svg" media="(prefers-color-scheme: dark)" class="devsite-dark-theme" alt="Chrome for Developers"> <img src="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/chrome/images/lockup.svg" class="devsite-site-logo" alt="Chrome for Developers"> </picture> </a> <span class="devsite-product-name"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> </li> </ul> </span> </div> <div class="devsite-top-logo-row-middle"> <div class="devsite-header-upper-tabs"> <devsite-tabs class="upper-tabs"> <nav class="devsite-tabs-wrapper" aria-label="Upper tabs"> <tab > <a href="https://developer.chrome.com/case-studies" track-metadata-eventdetail="https://developer.chrome.com/case-studies" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - get inspired" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Get inspired" track-name="get inspired" > Get inspired </a> </tab> <tab > <a href="https://developer.chrome.com/blog" track-metadata-eventdetail="https://developer.chrome.com/blog" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - blog" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Blog" track-name="blog" > Blog </a> </tab> <tab class="devsite-dropdown devsite-dropdown-full "> <a href="https://developer.chrome.com/docs" track-metadata-eventdetail="https://developer.chrome.com/docs" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - docs" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Docs" track-name="docs" > Docs </a> <a href="#" role="button" aria-haspopup="true" aria-expanded="false" aria-label="Dropdown menu for Docs" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs" track-metadata-position="nav - docs" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Docs" track-name="docs" class="devsite-tabs-dropdown-toggle devsite-icon devsite-icon-arrow-drop-down"></a> <div class="devsite-tabs-dropdown" aria-label="submenu" hidden> <div class="devsite-tabs-dropdown-content"> <div class="devsite-tabs-dropdown-column "> <ul class="devsite-tabs-dropdown-section build-icon dcc-subnav"> <li class="devsite-nav-title" role="heading" tooltip>Build with Chrome</li> <li class="devsite-nav-description">Learn how Chrome works, participate in origin trials, and build with Chrome everywhere. </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/web-platform" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/web-platform" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="build with chrome" tooltip > <div class="devsite-nav-item-title"> Web Platform </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/capabilities" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/capabilities" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="build with chrome" tooltip > <div class="devsite-nav-item-title"> Capabilities </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/chromedriver" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/chromedriver" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="build with chrome" tooltip > <div class="devsite-nav-item-title"> ChromeDriver </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/extensions" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/extensions" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="build with chrome" tooltip > <div class="devsite-nav-item-title"> Extensions </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/webstore" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/webstore" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="build with chrome" tooltip > <div class="devsite-nav-item-title"> Chrome Web Store </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/chromium" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/chromium" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="build with chrome" tooltip > <div class="devsite-nav-item-title"> Chromium </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/aurora" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/aurora" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="build with chrome" tooltip > <div class="devsite-nav-item-title"> Aurora </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/android" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/android" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="build with chrome" tooltip > <div class="devsite-nav-item-title"> Web on Android </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/origintrials/" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/origintrials/" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="build with chrome" tooltip > <div class="devsite-nav-item-title"> Origin trials </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/release-notes" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/release-notes" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="build with chrome" tooltip > <div class="devsite-nav-item-title"> Release notes </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column "> <ul class="devsite-tabs-dropdown-section productivity-icon dcc-subnav"> <li class="devsite-nav-title" role="heading" tooltip>Productivity</li> <li class="devsite-nav-description">Create the best experience for your users with the web's best tools.</li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/devtools" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/devtools" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="productivity" tooltip > <div class="devsite-nav-item-title"> DevTools </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/lighthouse" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/lighthouse" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="productivity" tooltip > <div class="devsite-nav-item-title"> Lighthouse </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/crux" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/crux" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="productivity" tooltip > <div class="devsite-nav-item-title"> Chrome UX Report </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/accessibility" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/accessibility" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="productivity" tooltip > <div class="devsite-nav-item-title"> Accessibility </div> </a> </li> </ul> <ul class="devsite-tabs-dropdown-section dcc-subnav second-column-list"> <li class="devsite-nav-description">Get things done quicker and neater, with our ready-made libraries. </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/workbox" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/workbox" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="productivity" tooltip > <div class="devsite-nav-item-title"> Workbox </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/puppeteer" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/puppeteer" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="productivity" tooltip > <div class="devsite-nav-item-title"> Puppeteer </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column "> <ul class="devsite-tabs-dropdown-section experience-icon dcc-subnav"> <li class="devsite-nav-title" role="heading" tooltip>Experience</li> <li class="devsite-nav-description">Design a beautiful and performant web with Chrome. </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/ai" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/ai" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="experience" tooltip > <div class="devsite-nav-item-title"> AI </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/performance" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/performance" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="experience" tooltip > <div class="devsite-nav-item-title"> Performance </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/css-ui" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/css-ui" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="experience" tooltip > <div class="devsite-nav-item-title"> CSS and UI </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/identity" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/identity" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="experience" tooltip > <div class="devsite-nav-item-title"> Identity </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/payments" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/payments" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="experience" tooltip > <div class="devsite-nav-item-title"> Payments </div> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs/privacy-security" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs/privacy-security" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="experience" tooltip > <div class="devsite-nav-item-title"> Privacy and security </div> </a> </li> </ul> </div> <div class="devsite-tabs-dropdown-column "> <ul class="devsite-tabs-dropdown-section resources-icon dcc-subnav"> <li class="devsite-nav-title" role="heading" tooltip>Resources</li> <li class="devsite-nav-description">More from the Chrome team. </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/docs" track-type="nav" track-metadata-eventdetail="https://developer.chrome.com/docs" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="resources" tooltip > <div class="devsite-nav-item-title"> All documentation </div> </a> </li> <li class="devsite-nav-item"> <a href="https://web.dev/baseline" track-type="nav" track-metadata-eventdetail="https://web.dev/baseline" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="resources" tooltip > <div class="devsite-nav-item-title"> Baseline </div> </a> </li> <li class="devsite-nav-item"> <a href="https://web.dev" track-type="nav" track-metadata-eventdetail="https://web.dev" track-metadata-position="nav - docs" track-metadata-module="tertiary nav" track-metadata-module_headline="resources" tooltip > <div class="devsite-nav-item-title"> web.dev </div> </a> </li> </ul> </div> </div> </div> </tab> <tab > <a href="https://developer.chrome.com/new" track-metadata-eventdetail="https://developer.chrome.com/new" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - new in chrome" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: New in Chrome" track-name="new in chrome" > New in Chrome </a> </tab> </nav> </devsite-tabs> </div> <devsite-search enable-signin enable-search enable-suggestions enable-query-completion project-name="Blog" tenant-name="Chrome for Developers" > <form class="devsite-search-form" action="https://developer.chrome.com/s/results" method="GET"> <div class="devsite-search-container"> <button type="button" search-open class="devsite-search-button devsite-header-icon-button button-flat material-icons" aria-label="Open search"></button> <div class="devsite-searchbox"> <input aria-activedescendant="" aria-autocomplete="list" aria-label="Search" aria-expanded="false" aria-haspopup="listbox" autocomplete="off" class="devsite-search-field devsite-search-query" name="q" placeholder="Search" role="combobox" type="text" value="" > <div class="devsite-search-image material-icons" aria-hidden="true"> </div> <div class="devsite-search-shortcut-icon-container" aria-hidden="true"> <kbd class="devsite-search-shortcut-icon">/</kbd> </div> </div> </div> </form> <button type="button" search-close class="devsite-search-button devsite-header-icon-button button-flat material-icons" aria-label="Close search"></button> </devsite-search> </div> <devsite-appearance-selector></devsite-appearance-selector> <devsite-language-selector> <ul role="presentation"> <li role="presentation"> <a role="menuitem" lang="en" >English</a> </li> <li role="presentation"> <a role="menuitem" lang="de" >Deutsch</a> </li> <li role="presentation"> <a role="menuitem" lang="es_419" >Español – América Latina</a> </li> <li role="presentation"> <a role="menuitem" lang="fr" >Français</a> </li> <li role="presentation"> <a role="menuitem" lang="id" >Indonesia</a> </li> <li role="presentation"> <a role="menuitem" lang="it" >Italiano</a> </li> <li role="presentation"> <a role="menuitem" lang="nl" >Nederlands</a> </li> <li role="presentation"> <a role="menuitem" lang="pl" >Polski</a> </li> <li role="presentation"> <a role="menuitem" lang="pt_br" >Português – Brasil</a> </li> <li role="presentation"> <a role="menuitem" lang="vi" >Tiếng Việt</a> </li> <li role="presentation"> <a role="menuitem" lang="tr" >Türkçe</a> </li> <li role="presentation"> <a role="menuitem" lang="ru" >Русский</a> </li> <li role="presentation"> <a role="menuitem" lang="he" >עברית</a> </li> <li role="presentation"> <a role="menuitem" lang="ar" >العربيّة</a> </li> <li role="presentation"> <a role="menuitem" lang="fa" >فارسی</a> </li> <li role="presentation"> <a role="menuitem" lang="hi" >हिंदी</a> </li> <li role="presentation"> <a role="menuitem" lang="bn" >বাংলা</a> </li> <li role="presentation"> <a role="menuitem" lang="th" >ภาษาไทย</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_cn" >中文 – 简体</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_tw" >中文 – 繁體</a> </li> <li role="presentation"> <a role="menuitem" lang="ja" >日本語</a> </li> <li role="presentation"> <a role="menuitem" lang="ko" >한국어</a> </li> </ul> </devsite-language-selector> <devsite-user enable-profiles id="devsite-user"> <span class="button devsite-top-button" aria-hidden="true" visually-hidden>Sign in</span> </devsite-user> </div> </div> </div> <div class="devsite-collapsible-section devsite-header-no-lower-tabs "> <div class="devsite-header-background"> <div class="devsite-product-id-row" > <div class="devsite-product-description-row"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> <a href="https://developer.chrome.com/blog" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Lower Header" data-value="1" track-type="globalNav" track-name="breadcrumb" track-metadata-position="1" track-metadata-eventdetail="Blog" > Blog </a> </li> </ul> </div> </div> </div> </div> </div> </devsite-header> <devsite-book-nav scrollbars hidden> <div class="devsite-book-nav-filter" hidden> <span class="filter-list-icon material-icons" aria-hidden="true"></span> <input type="text" placeholder="Filter" aria-label="Type to filter" role="searchbox"> <span class="filter-clear-button hidden" data-title="Clear filter" aria-label="Clear filter" role="button" tabindex="0"></span> </div> <nav class="devsite-book-nav devsite-nav nocontent" aria-label="Side menu"> <div class="devsite-mobile-header"> <button type="button" id="devsite-close-nav" class="devsite-header-icon-button button-flat material-icons gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Close navigation" aria-label="Close navigation"> </button> <div class="devsite-product-name-wrapper"> <a href="/" class="devsite-site-logo-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Site logo" track-type="globalNav" track-name="chromeForDevelopers" track-metadata-position="nav" track-metadata-eventDetail="nav"> <picture> <source srcset="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/chrome/images/lockup-dark-theme.svg" media="(prefers-color-scheme: dark)" class="devsite-dark-theme" alt="Chrome for Developers"> <img src="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/chrome/images/lockup.svg" class="devsite-site-logo" alt="Chrome for Developers"> </picture> </a> <span class="devsite-product-name"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> </li> </ul> </span> </div> </div> <div class="devsite-book-nav-wrapper"> <div class="devsite-mobile-nav-top"> <ul class="devsite-nav-list"> <li class="devsite-nav-item"> <a href="/case-studies" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Get inspired" track-name="get inspired" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Get inspired" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Get inspired </span> </a> </li> <li class="devsite-nav-item"> <a href="/blog" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Blog" track-name="blog" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Blog" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Blog </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Docs" track-name="docs" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Docs" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Docs </span> </a> <ul class="devsite-nav-responsive-tabs devsite-nav-has-menu "> <li class="devsite-nav-item"> <span class="devsite-nav-title" tooltip data-category="Site-Wide Custom Events" data-label="Tab: Docs" track-name="docs" > <span class="devsite-nav-text" tooltip menu="Docs"> More </span> <span class="devsite-nav-icon material-icons" data-icon="forward" menu="Docs"> </span> </span> </li> </ul> </li> <li class="devsite-nav-item"> <a href="/new" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: New in Chrome" track-name="new in chrome" data-category="Site-Wide Custom Events" data-label="Responsive Tab: New in Chrome" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > New in Chrome </span> </a> </li> </ul> </div> <div class="devsite-mobile-nav-bottom"> <ul class="devsite-nav-list" menu="Docs" aria-label="Side menu" hidden> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Build with Chrome </span> </span> </li> <li class="devsite-nav-item"> <a href="/docs/web-platform" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Web Platform" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Web Platform </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/capabilities" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Capabilities" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Capabilities </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/chromedriver" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: ChromeDriver" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > ChromeDriver </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/extensions" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Extensions" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Extensions </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/webstore" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Chrome Web Store" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Chrome Web Store </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/chromium" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Chromium" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Chromium </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/aurora" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Aurora" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Aurora </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/android" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Web on Android" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Web on Android </span> </a> </li> <li class="devsite-nav-item"> <a href="https://developer.chrome.com/origintrials/" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Origin trials" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Origin trials </span> </a> </li> <li class="devsite-nav-item"> <a href="/release-notes" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Release notes" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Release notes </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Productivity </span> </span> </li> <li class="devsite-nav-item"> <a href="/docs/devtools" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: DevTools" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > DevTools </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/lighthouse" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Lighthouse" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Lighthouse </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/crux" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Chrome UX Report" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Chrome UX Report </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/accessibility" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Accessibility" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Accessibility </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/workbox" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Workbox" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Workbox </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/puppeteer" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Puppeteer" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Puppeteer </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Experience </span> </span> </li> <li class="devsite-nav-item"> <a href="/docs/ai" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: AI" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > AI </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/performance" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Performance" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Performance </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/css-ui" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: CSS and UI" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > CSS and UI </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/identity" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Identity" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Identity </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/payments" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Payments" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Payments </span> </a> </li> <li class="devsite-nav-item"> <a href="/docs/privacy-security" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Privacy and security" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Privacy and security </span> </a> </li> <li class="devsite-nav-item devsite-nav-heading"> <span class="devsite-nav-title" tooltip > <span class="devsite-nav-text" tooltip > Resources </span> </span> </li> <li class="devsite-nav-item"> <a href="/docs" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: All documentation" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > All documentation </span> </a> </li> <li class="devsite-nav-item"> <a href="https://web.dev/baseline" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: Baseline" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Baseline </span> </a> </li> <li class="devsite-nav-item"> <a href="https://web.dev" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Responsive Tab: web.dev" track-type="navMenu" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > web.dev </span> </a> </li> </ul> </div> </div> </nav> </devsite-book-nav> <section id="gc-wrapper"> <main role="main" class="devsite-main-content" has-sidebar > <div class="devsite-sidebar"> <div class="devsite-sidebar-content"> <devsite-toc class="devsite-nav" role="navigation" aria-label="On this page" depth="2" scrollbars ></devsite-toc> <devsite-recommendations-sidebar class="nocontent devsite-nav"> </devsite-recommendations-sidebar> </div> </div> <devsite-content> <article class="devsite-article"> <div class="devsite-article-meta nocontent" role="navigation"> <ul class="devsite-breadcrumb-list" aria-label="Breadcrumb"> <li class="devsite-breadcrumb-item "> <a href="https://developer.chrome.com/" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="1" track-type="globalNav" track-name="breadcrumb" track-metadata-position="1" track-metadata-eventdetail="Chrome for Developers" > Chrome for Developers </a> </li> <li class="devsite-breadcrumb-item "> <div class="devsite-breadcrumb-guillemet material-icons" aria-hidden="true"></div> <a href="https://developer.chrome.com/blog" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="2" track-type="globalNav" track-name="breadcrumb" track-metadata-position="2" track-metadata-eventdetail="Blog" > Blog </a> </li> </ul> <devsite-thumb-rating position="header"> </devsite-thumb-rating> </div> <h1 class="devsite-page-title" tabindex="-1"> Inside look at modern web browser (part 3) </h1> <devsite-feature-tooltip ack-key="AckCollectionsBookmarkTooltipDismiss" analytics-category="Site-Wide Custom Events" analytics-action-show="Callout Profile displayed" analytics-action-close="Callout Profile dismissed" analytics-label="Create Collection Callout" class="devsite-page-bookmark-tooltip nocontent" dismiss-button="true" id="devsite-collections-dropdown" dismiss-button-text="Dismiss" close-button-text="Got it"> <devsite-bookmark></devsite-bookmark> <span slot="popout-heading"> Stay organized with collections </span> <span slot="popout-contents"> Save and categorize content based on your preferences. </span> </devsite-feature-tooltip> <div class="devsite-page-title-meta"><devsite-view-release-notes></devsite-view-release-notes></div> <devsite-toc class="devsite-nav" depth="2" devsite-toc-embedded > </devsite-toc> <div class="devsite-article-body clearfix "> <p><style> .wd-authors { --avatar-size: 65px; display: flex; gap: 2em; } .wd-author { display: flex; flex-wrap: wrap; gap: 1em; line-height: calc(var(--avatar-size) / 2); } .wd-author img { border-radius: 50%; height: var(--avatar-size, 65px); width: var(--avatar-size, 65px); } .dcc-authors { --avatar-size: 65px; display: flex; gap: 2em; } .dcc-author { display: flex; flex-wrap: wrap; gap: 1em; line-height: calc(var(--avatar-size) / 2); } .dcc-author img { border-radius: 50%; height: var(--avatar-size, 65px); width: var(--avatar-size, 65px); } .dcc-author__links a { margin-inline-end: 6px; } .dcc-author__links a:last-of-type { margin-inline-end: 0; } </style> <div class="dcc-authors" translate="no"> <div class="dcc-author"> <div> <span> Mariko Kosaka </span> <div class="dcc-author__links"> <a href="https://twitter.com/kosamari" aria-label="Mariko Kosaka on X" rel="me"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 300 271"> <title>X</title> <path fill="currentColor" d="m236 0h46l-101 115 118 156h-92.6l-72.5-94.8-83 94.8h-46l107-123-113-148h94.9l65.5 86.6zm-16.1 244h25.5l-165-218h-27.4z"></path> </svg></a> </div> </div> </div> </div></p> <h2 id="inner_workings_of_a_renderer_process" data-text="Inner workings of a Renderer Process" tabindex="-1">Inner workings of a Renderer Process</h2> <p>This is part 3 of 4 part blog series looking at how browsers work. Previously, we covered <a href="https://developers.google.com/web/updates/2018/09/inside-browser-part1">multi-process architecture</a> and <a href="https://developers.google.com/web/updates/2018/09/inside-browser-part2">navigation flow</a>. In this post, we are going to look at what happens inside of the renderer process.</p> <p>Renderer process touches many aspects of web performance. Since there is a lot happening inside of the renderer process, this post is only a general overview. If you'd like to dig deeper, <a href="https://developers.google.com/web/fundamentals/performance/why-performance-matters/">the Performance section of Web Fundamentals</a> has many more resources.</p> <h2 id="renderer_processes_handle_web_contents" data-text="Renderer processes handle web contents" tabindex="-1">Renderer processes handle web contents</h2> <p>The renderer process is responsible for everything that happens inside of a tab. In a renderer process, the main thread handles most of the code you send to the user. Sometimes parts of your JavaScript is handled by worker threads if you use a web worker or a service worker. Compositor and raster threads are also run inside of a renderer processes to render a page efficiently and smoothly.</p> <p>The renderer process's core job is to turn HTML, CSS, and JavaScript into a web page that the user can interact with.</p> <figure> <img src="/static/blog/inside-browser-part3/image/renderer-process-df424472d0633.png" alt="Renderer process" width="800" height="421" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/renderer-process-df424472d0633_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/renderer-process-df424472d0633_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/renderer-process-df424472d0633_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/renderer-process-df424472d0633_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/renderer-process-df424472d0633_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/renderer-process-df424472d0633_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/renderer-process-df424472d0633_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/renderer-process-df424472d0633_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/renderer-process-df424472d0633_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/renderer-process-df424472d0633_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/renderer-process-df424472d0633_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 1: Renderer process with a main thread, worker threads, a compositor thread, and a raster thread inside </figcaption> </figure> <h2 id="parsing" data-text="Parsing" tabindex="-1">Parsing</h2> <h3 id="construction_of_a_dom" data-text="Construction of a DOM" tabindex="-1">Construction of a DOM</h3> <p>When the renderer process receives a commit message for a navigation and starts to receive HTML data, the main thread begins to parse the text string (HTML) and turn it into a <strong>D</strong>ocument <strong>O</strong>bject <strong>M</strong>odel (<strong>DOM</strong>).</p> <p>The DOM is a browser's internal representation of the page as well as the data structure and API that web developer can interact with via JavaScript.</p> <p>Parsing an HTML document into a DOM is defined by the <a href="https://html.spec.whatwg.org/">HTML Standard</a>. You may have noticed that feeding HTML to a browser never throws an error. For example, missing closing <code translate="no" dir="ltr"></p></code> tag is a valid HTML. Erroneous markup like <code translate="no" dir="ltr">Hi! <b>I'm <i>Chrome</b>!</i></code> (b tag is closed before i tag) is treated as if you wrote <code translate="no" dir="ltr">Hi! <b>I'm <i>Chrome</i></b><i>!</i></code>. This is because the HTML specification is designed to handle those errors gracefully. If you are curious how these things are done, you can read on "<a href="https://html.spec.whatwg.org/multipage/parsing.html#an-introduction-to-error-handling-and-strange-cases-in-the-parser">An introduction to error handling and strange cases in the parser</a>" section of the HTML spec.</p> <h3 id="subresource_loading" data-text="Subresource loading" tabindex="-1">Subresource loading</h3> <p>A website usually uses external resources like images, CSS, and JavaScript. Those files need to be loaded from network or cache. The main thread <em>could</em> request them one by one as they find them while parsing to build a DOM, but in order to speed up, "preload scanner" is run concurrently. If there are things like <code translate="no" dir="ltr"><img></code> or <code translate="no" dir="ltr"><link></code> in the HTML document, preload scanner peeks at tokens generated by HTML parser and sends requests to the network thread in the browser process.</p> <figure> <img src="/static/blog/inside-browser-part3/image/dom-ffeed8e96a6e8.png" alt="DOM" width="800" height="421" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/dom-ffeed8e96a6e8_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/dom-ffeed8e96a6e8_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/dom-ffeed8e96a6e8_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/dom-ffeed8e96a6e8_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/dom-ffeed8e96a6e8_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/dom-ffeed8e96a6e8_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/dom-ffeed8e96a6e8_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/dom-ffeed8e96a6e8_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/dom-ffeed8e96a6e8_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/dom-ffeed8e96a6e8_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/dom-ffeed8e96a6e8_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 2: The main thread parsing HTML and building a DOM tree </figcaption> </figure> <h3 id="javascript_can_block_the_parsing" data-text="JavaScript can block the parsing" tabindex="-1">JavaScript can block the parsing</h3> <p>When the HTML parser finds a <code translate="no" dir="ltr"><script></code> tag, it pauses the parsing of the HTML document and has to load, parse, and execute the JavaScript code. Why? because JavaScript can change the shape of the document using things like <code translate="no" dir="ltr">document.write()</code> which changes the entire DOM structure (<a href="https://html.spec.whatwg.org/multipage/parsing.html#overview-of-the-parsing-model">overview of the parsing model</a> in the HTML spec has a nice diagram). This is why the HTML parser has to wait for JavaScript to run before it can resume parsing of the HTML document. If you are curious about what happens in JavaScript execution, <a href="https://mathiasbynens.be/notes/shapes-ics">the V8 team has talks and blog posts on this</a>.</p> <h2 id="hint_to_browser_how_you_want_to_load_resources" data-text="Hint to browser how you want to load resources" tabindex="-1">Hint to browser how you want to load resources</h2> <p>There are many ways web developers can send hints to the browser in order to load resources nicely. If your JavaScript does not use <code translate="no" dir="ltr">document.write()</code>, you can add <a href="https://developer.mozilla.org/docs/Web/HTML/Element/script#attr-async"><code translate="no" dir="ltr">async</code></a> or <a href="https://developer.mozilla.org/docs/Web/HTML/Element/script#attr-defer"><code translate="no" dir="ltr">defer</code></a> attribute to the <code translate="no" dir="ltr"><script></code> tag. The browser then loads and runs the JavaScript code asynchronously and does not block the parsing. You may also use <a href="https://developers.google.com/web/fundamentals/primers/modules">JavaScript module</a> if that's suitable. <code translate="no" dir="ltr"><link rel="preload"></code> is a way to inform browser that the resource is definitely needed for current navigation and you would like to download as soon as possible. You can read more on this at <a href="https://developers.google.com/web/fundamentals/performance/resource-prioritization">Resource Prioritization – Getting the Browser to Help You</a>.</p> <h2 id="style_calculation" data-text="Style calculation" tabindex="-1">Style calculation</h2> <p>Having a DOM is not enough to know what the page would look like because we can style page elements in CSS. The main thread parses CSS and determines the computed style for each DOM node. This is information about what kind of style is applied to each element based on CSS selectors. You can see this information in the <code translate="no" dir="ltr">computed</code> section of DevTools.</p> <figure> <img src="/static/blog/inside-browser-part3/image/computed-style-5e3c65a01f3f1.png" alt="Computed style" width="800" height="421" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/computed-style-5e3c65a01f3f1_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/computed-style-5e3c65a01f3f1_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/computed-style-5e3c65a01f3f1_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/computed-style-5e3c65a01f3f1_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/computed-style-5e3c65a01f3f1_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/computed-style-5e3c65a01f3f1_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/computed-style-5e3c65a01f3f1_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/computed-style-5e3c65a01f3f1_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/computed-style-5e3c65a01f3f1_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/computed-style-5e3c65a01f3f1_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/computed-style-5e3c65a01f3f1_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 3: The main thread parsing CSS to add computed style </figcaption> </figure> <p>Even if you do not provide any CSS, each DOM node has a computed style. <code translate="no" dir="ltr"><h1></code> tag is displayed bigger than <code translate="no" dir="ltr"><h2></code> tag and margins are defined for each element. This is because the browser has a default style sheet. If you want to know what Chrome's default CSS is like, <a href="https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/html/resources/html.css">you can see the source code here</a>.</p> <h2 id="layout" data-text="Layout" tabindex="-1">Layout</h2> <p>Now the renderer process knows the structure of a document and styles for each nodes, but that is not enough to render a page. Imagine you are trying to describe a painting to your friend over a phone. "There is a big red circle and a small blue square" is not enough information for your friend to know what exactly the painting would look like.</p> <figure> <img src="/static/blog/inside-browser-part3/image/game-human-fax-machine-17f10d8b400ac.png" alt="game of human fax machine" width="800" height="426" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/game-human-fax-machine-17f10d8b400ac_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/game-human-fax-machine-17f10d8b400ac_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/game-human-fax-machine-17f10d8b400ac_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/game-human-fax-machine-17f10d8b400ac_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/game-human-fax-machine-17f10d8b400ac_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/game-human-fax-machine-17f10d8b400ac_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/game-human-fax-machine-17f10d8b400ac_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/game-human-fax-machine-17f10d8b400ac_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/game-human-fax-machine-17f10d8b400ac_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/game-human-fax-machine-17f10d8b400ac_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/game-human-fax-machine-17f10d8b400ac_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 4: A person standing in front of a painting, phone line connected to the other person </figcaption> </figure> <p>The layout is a process to find the geometry of elements. The main thread walks through the DOM and computed styles and creates the layout tree which has information like x y coordinates and bounding box sizes. Layout tree may be similar structure to the DOM tree, but it only contains information related to what's visible on the page. If <code translate="no" dir="ltr">display: none</code> is applied, that element is not part of the layout tree (however, an element with <code translate="no" dir="ltr">visibility: hidden</code> is in the layout tree). Similarly, if a pseudo-element with content like <code translate="no" dir="ltr">p::before{content:"Hi!"}</code> is applied, it is included in the layout tree even though that is not in the DOM.</p> <figure> <img src="/static/blog/inside-browser-part3/image/layout-9d8ed8c743f45.png" alt="layout" width="800" height="421" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/layout-9d8ed8c743f45_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layout-9d8ed8c743f45_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layout-9d8ed8c743f45_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layout-9d8ed8c743f45_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layout-9d8ed8c743f45_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layout-9d8ed8c743f45_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layout-9d8ed8c743f45_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layout-9d8ed8c743f45_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layout-9d8ed8c743f45_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layout-9d8ed8c743f45_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layout-9d8ed8c743f45_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 5: The main thread going over DOM tree with computed styles and producing layout tree </figcaption> </figure> <figure> <video autoplay controls loop muted playsinline > <source src="/static/blog/inside-browser-part3/video/T4FyVKpzu4WKF1kBNvXepbi08t52/rXSCtc21M00XrRqcw56C.mp4" type="video/mp4" /> </video> <figcaption class="dcc-caption"> Figure 6: Box layout for a paragraph moving due to line break change </figcaption> </figure> <p>Determining the Layout of a page is a challenging task. Even the simplest page layout like a block flow from top to bottom has to consider how big the font is and where to line break them because those affect the size and shape of a paragraph; which then affects where the following paragraph needs to be.</p> <p>CSS can make element float to one side, mask overflow item, and change writing directions. You can imagine, this layout stage has a mighty task. In Chrome, a whole team of engineers works on the layout. If you want to see details of their work, <a href="https://www.youtube.com/watch?v=Y5Xa4H2wtVA">few talks from BlinkOn Conference</a> are recorded and quite interesting to watch.</p> <div class="clearfix"></div> <h2 id="paint" data-text="Paint" tabindex="-1">Paint</h2> <figure> <img src="/static/blog/inside-browser-part3/image/drawing-game-7902ad8a91d2f.png" alt="drawing game" width="800" height="300" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/drawing-game-7902ad8a91d2f_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/drawing-game-7902ad8a91d2f_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/drawing-game-7902ad8a91d2f_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/drawing-game-7902ad8a91d2f_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/drawing-game-7902ad8a91d2f_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/drawing-game-7902ad8a91d2f_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/drawing-game-7902ad8a91d2f_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/drawing-game-7902ad8a91d2f_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/drawing-game-7902ad8a91d2f_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/drawing-game-7902ad8a91d2f_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/drawing-game-7902ad8a91d2f_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 7: A person in front of a canvas holding paintbrush, wondering if they should draw a circle first or square first </figcaption> </figure> <p>Having a DOM, style, and layout is still not enough to render a page. Let's say you are trying to reproduce a painting. You know the size, shape, and location of elements, but you still have to judge in what order you paint them.</p> <p>For example, <code translate="no" dir="ltr">z-index</code> might be set for certain elements, in that case painting in order of elements written in the HTML will result in incorrect rendering.</p> <figure> <img src="/static/blog/inside-browser-part3/image/z-index-fail-2529cf989dc65.png" alt="z-index fail" width="800" height="428" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/z-index-fail-2529cf989dc65_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/z-index-fail-2529cf989dc65_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/z-index-fail-2529cf989dc65_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/z-index-fail-2529cf989dc65_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/z-index-fail-2529cf989dc65_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/z-index-fail-2529cf989dc65_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/z-index-fail-2529cf989dc65_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/z-index-fail-2529cf989dc65_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/z-index-fail-2529cf989dc65_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/z-index-fail-2529cf989dc65_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/z-index-fail-2529cf989dc65_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 8: Page elements appearing in order of an HTML markup, resulting in wrong rendered image because z-index was not taken into account </figcaption> </figure> <p>At this paint step, the main thread walks the layout tree to create paint records. Paint record is a note of painting process like "background first, then text, then rectangle". If you have drawn on <code translate="no" dir="ltr"><canvas></code> element using JavaScript, this process might be familiar to you.</p> <figure> <img src="/static/blog/inside-browser-part3/image/paint-records-151165f18a91e.png" alt="paint records" width="800" height="421" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/paint-records-151165f18a91e_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/paint-records-151165f18a91e_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/paint-records-151165f18a91e_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/paint-records-151165f18a91e_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/paint-records-151165f18a91e_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/paint-records-151165f18a91e_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/paint-records-151165f18a91e_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/paint-records-151165f18a91e_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/paint-records-151165f18a91e_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/paint-records-151165f18a91e_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/paint-records-151165f18a91e_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 9: The main thread walking through layout tree and producing paint records </figcaption> </figure> <h3 id="updating_rendering_pipeline_is_costly" data-text="Updating rendering pipeline is costly" tabindex="-1">Updating rendering pipeline is costly</h3> <figure> <video autoplay class="float-right" controls loop muted playsinline > <source src="/static/blog/inside-browser-part3/video/T4FyVKpzu4WKF1kBNvXepbi08t52/d7zOpwpNIXIoVnoZCtI9.mp4" type="video/mp4" /> </video> <figcaption class="dcc-caption"> Figure 10: DOM+Style, Layout, and Paint trees in order it is generated </figcaption> </figure> <p>The most important thing to grasp in rendering pipeline is that at each step the result of the previous operation is used to create new data. For example, if something changes in the layout tree, then the Paint order needs to be regenerated for affected parts of the document.</p> <p>If you are animating elements, the browser has to run these operations in between every frame. Most of our displays refresh the screen 60 times a second (60 fps); animation will appear smooth to human eyes when you are moving things across the screen at every frame. However, if the animation misses the frames in between, then the page will appear "janky".</p> <figure> <img src="/static/blog/inside-browser-part3/image/jage-jank-missing-frames-187496b8fbc8e.png" alt="jage jank by missing frames" width="800" height="185" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-missing-frames-187496b8fbc8e_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-missing-frames-187496b8fbc8e_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-missing-frames-187496b8fbc8e_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-missing-frames-187496b8fbc8e_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-missing-frames-187496b8fbc8e_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-missing-frames-187496b8fbc8e_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-missing-frames-187496b8fbc8e_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-missing-frames-187496b8fbc8e_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-missing-frames-187496b8fbc8e_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-missing-frames-187496b8fbc8e_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-missing-frames-187496b8fbc8e_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 11: Animation frames on a timeline </figcaption> </figure> <p>Even if your rendering operations are keeping up with screen refresh, these calculations are running on the main thread, which means it could be blocked when your application is running JavaScript.</p> <figure> <img src="/static/blog/inside-browser-part3/image/jage-jank-javascript-b19c0193934a3.png" alt="jage jank by JavaScript" width="800" height="185" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-javascript-b19c0193934a3_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-javascript-b19c0193934a3_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-javascript-b19c0193934a3_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-javascript-b19c0193934a3_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-javascript-b19c0193934a3_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-javascript-b19c0193934a3_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-javascript-b19c0193934a3_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-javascript-b19c0193934a3_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-javascript-b19c0193934a3_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-javascript-b19c0193934a3_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/jage-jank-javascript-b19c0193934a3_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 12: Animation frames on a timeline, but one frame is blocked by JavaScript </figcaption> </figure> <p>You can divide JavaScript operation into small chunks and schedule to run at every frame using <code translate="no" dir="ltr">requestAnimationFrame()</code>. For more on this topic, please see <a href="https://developers.google.com/web/fundamentals/performance/rendering/optimize-javascript-execution">Optimize JavaScript Execution</a> . You might also run your <a href="https://www.youtube.com/watch?v=X57mh8tKkgE">JavaScript in Web Workers</a> to avoid blocking the main thread.</p> <figure> <img src="/static/blog/inside-browser-part3/image/request-animation-frame-3b04e26660019.png" alt="request animation frame" width="800" height="185" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/request-animation-frame-3b04e26660019_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/request-animation-frame-3b04e26660019_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/request-animation-frame-3b04e26660019_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/request-animation-frame-3b04e26660019_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/request-animation-frame-3b04e26660019_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/request-animation-frame-3b04e26660019_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/request-animation-frame-3b04e26660019_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/request-animation-frame-3b04e26660019_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/request-animation-frame-3b04e26660019_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/request-animation-frame-3b04e26660019_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/request-animation-frame-3b04e26660019_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 13: Smaller chunks of JavaScript running on a timeline with animation frame </figcaption> </figure> <h2 id="compositing" data-text="Compositing" tabindex="-1">Compositing</h2> <h3 id="how_would_you_draw_a_page" data-text="How would you draw a page?" tabindex="-1">How would you draw a page?</h3> <figure> <video autoplay controls loop muted playsinline > <source src="/static/blog/inside-browser-part3/video/T4FyVKpzu4WKF1kBNvXepbi08t52/AiIny83Lk4rTzsM8bxSn.mp4" type="video/mp4" /> </video> <figcaption class="dcc-caption"> Figure 14: Animation of naive rastering process </figcaption> </figure> <p>Now that the browser knows the structure of the document, the style of each element, the geometry of the page, and the paint order, how does it draw a page? Turning this information into pixels on the screen is called rasterizing.</p> <p>Perhaps a naive way to handle this would be to raster parts inside of the viewport. If a user scrolls the page, then move the rastered frame, and fill in the missing parts by rastering more. This is how Chrome handled rasterizing when it was first released. However, the modern browser runs a more sophisticated process called compositing.</p> <h3 id="what_is_compositing" data-text="What is compositing" tabindex="-1">What is compositing</h3> <figure> <video autoplay controls loop muted playsinline > <source src="/static/blog/inside-browser-part3/video/T4FyVKpzu4WKF1kBNvXepbi08t52/Aggd8YLFPckZrBjEj74H.mp4" type="video/mp4" /> </video> <figcaption class="dcc-caption"> Figure 15: Animation of compositing process </figcaption> </figure> <p>Compositing is a technique to separate parts of a page into layers, rasterize them separately, and composite as a page in a separate thread called compositor thread. If scroll happens, since layers are already rasterized, all it has to do is to composite a new frame. Animation can be achieved in the same way by moving layers and composite a new frame.</p> <p>You can see how your website is divided into layers in DevTools using <a href="https://blog.logrocket.com/eliminate-content-repaints-with-the-new-layers-panel-in-chrome-e2c306d4d752?gi=cd6271834cea">Layers panel</a>.</p> <h3 id="dividing_into_layers" data-text="Dividing into layers" tabindex="-1">Dividing into layers</h3> <p>In order to find out which elements need to be in which layers, the main thread walks through the layout tree to create the layer tree (this part is called "Update Layer Tree" in the DevTools performance panel). If certain parts of a page that should be separate layer (like slide-in side menu) is not getting one, then you can hint to the browser by using <code translate="no" dir="ltr">will-change</code> attribute in CSS.</p> <figure> <img src="/static/blog/inside-browser-part3/image/layer-tree-cc92336966c73.png" alt="layer tree" width="800" height="421" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/layer-tree-cc92336966c73_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layer-tree-cc92336966c73_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layer-tree-cc92336966c73_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layer-tree-cc92336966c73_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layer-tree-cc92336966c73_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layer-tree-cc92336966c73_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layer-tree-cc92336966c73_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layer-tree-cc92336966c73_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layer-tree-cc92336966c73_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layer-tree-cc92336966c73_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/layer-tree-cc92336966c73_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 16: The main thread walking through layout tree producing layer tree </figcaption> </figure> <p>You might be tempted to give layers to every element, but compositing across an excess number of layers could result in slower operation than rasterizing small parts of a page every frame, so it is crucial that you measure rendering performance of your application. For more about on topic, see <a href="https://developers.google.com/web/fundamentals/performance/rendering/stick-to-compositor-only-properties-and-manage-layer-count">Stick to Compositor-Only Properties and Manage Layer Count</a>.</p> <h3 id="raster_and_composite_off_of_the_main_thread" data-text="Raster and composite off of the main thread" tabindex="-1">Raster and composite off of the main thread</h3> <p>Once the layer tree is created and paint orders are determined, the main thread commits that information to the compositor thread. The compositor thread then rasterizes each layer. A layer could be large like the entire length of a page, so the compositor thread divides them into tiles and sends each tile off to raster threads. Raster threads rasterize each tile and store them in GPU memory.</p> <figure> <img src="/static/blog/inside-browser-part3/image/raster-9dfd7af5a9554.png" alt="raster" width="800" height="421" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/raster-9dfd7af5a9554_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/raster-9dfd7af5a9554_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/raster-9dfd7af5a9554_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/raster-9dfd7af5a9554_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/raster-9dfd7af5a9554_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/raster-9dfd7af5a9554_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/raster-9dfd7af5a9554_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/raster-9dfd7af5a9554_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/raster-9dfd7af5a9554_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/raster-9dfd7af5a9554_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/raster-9dfd7af5a9554_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 17: Raster threads creating the bitmap of tiles and sending to GPU </figcaption> </figure> <p>The compositor thread can prioritize different raster threads so that things within the viewport (or nearby) can be rastered first. A layer also has multiple tilings for different resolutions to handle things like zoom-in action.</p> <p>Once tiles are rastered, compositor thread gathers tile information called <strong>draw quads</strong> to create a <strong>compositor frame</strong>.</p> <table class="responsive"> <tr> <td>Draw quads</td> <td> Contains information such as the tile's location in memory and where in the page to draw the tile taking in consideration of the page compositing. </td> </tr> <tr> <td>Compositor frame</td> <td>A collection of draw quads that represents a frame of a page.</td> </tr> </table> <p>A compositor frame is then submitted to the browser process via IPC. At this point, another compositor frame could be added from UI thread for the browser UI change or from other renderer processes for extensions. These compositor frames are sent to the GPU to display it on a screen. If a scroll event comes in, compositor thread creates another compositor frame to be sent to the GPU.</p> <figure> <img src="/static/blog/inside-browser-part3/image/composit-266744978ac93.png" alt="composit" width="800" height="421" srcset="https://developer.chrome.com/static/blog/inside-browser-part3/image/composit-266744978ac93_36.png 36w,https://developer.chrome.com/static/blog/inside-browser-part3/image/composit-266744978ac93_48.png 48w,https://developer.chrome.com/static/blog/inside-browser-part3/image/composit-266744978ac93_72.png 72w,https://developer.chrome.com/static/blog/inside-browser-part3/image/composit-266744978ac93_96.png 96w,https://developer.chrome.com/static/blog/inside-browser-part3/image/composit-266744978ac93_480.png 480w,https://developer.chrome.com/static/blog/inside-browser-part3/image/composit-266744978ac93_720.png 720w,https://developer.chrome.com/static/blog/inside-browser-part3/image/composit-266744978ac93_856.png 856w,https://developer.chrome.com/static/blog/inside-browser-part3/image/composit-266744978ac93_960.png 960w,https://developer.chrome.com/static/blog/inside-browser-part3/image/composit-266744978ac93_1440.png 1440w,https://developer.chrome.com/static/blog/inside-browser-part3/image/composit-266744978ac93_1920.png 1920w,https://developer.chrome.com/static/blog/inside-browser-part3/image/composit-266744978ac93_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="dcc-caption"> Figure 18: Compositor thread creating compositing frame. Frame is sent to the browser process then to GPU </figcaption> </figure> <p>The benefit of compositing is that it is done without involving the main thread. Compositor thread does not need to wait on style calculation or JavaScript execution. This is why <a href="https://www.html5rocks.com/en/tutorials/speed/high-performance-animations/">compositing only animations</a> are considered the best for smooth performance. If layout or paint needs to be calculated again then the main thread has to be involved.</p> <h2 id="wrap_up" data-text="Wrap Up" tabindex="-1">Wrap Up</h2> <p>In this post, we looked at rendering pipeline from parsing to compositing. Hopefully, you are now empowered to read more about performance optimization of a website.</p> <p>In the next and last post of this series, we'll look at the compositor thread in more details and see what happens when user input like <code translate="no" dir="ltr">mouse move</code> and <code translate="no" dir="ltr">click</code> comes in.</p> <p>Did you enjoy the post? If you have any questions or suggestions for the future post, I'd love to hear from you in the comment section below or <a href="https://twitter.com/kosamari">@kosamari</a> on Twitter.</p> <p><a class="button button-primary gc-analytics-event attempt-right" href="https://developers.google.com/web/updates/2018/09/inside-browser-part4" data-category="InsideBrowser" data-label="Part3 / Next"> Next: Input is coming to the compositor </a></p> </div> <devsite-thumb-rating position="footer"> </devsite-thumb-rating> <div class="devsite-floating-action-buttons"> </div> </article> <devsite-content-footer class="nocontent"> <p>Except as otherwise noted, the content of this page is licensed under the <a href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 License</a>, and code samples are licensed under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 License</a>. For details, see the <a href="https://developers.google.com/site-policies">Google Developers Site Policies</a>. Java is a registered trademark of Oracle and/or its affiliates.</p> <p>Last updated 2018-09-20 UTC.</p> </devsite-content-footer> <devsite-notification > </devsite-notification> <div class="devsite-content-data"> <template class="devsite-content-data-template"> [[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2018-09-20 UTC."],[],[]] </template> </div> </devsite-content> </main> <devsite-footer-promos class="devsite-footer"> </devsite-footer-promos> <devsite-footer-linkboxes class="devsite-footer"> <nav class="devsite-footer-linkboxes nocontent" aria-label="Footer links"> <ul class="devsite-footer-linkboxes-list"> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Contribute</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="https://issuetracker.google.com/issues/new?component=1400036&template=1897236" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > File a bug </a> </li> <li class="devsite-footer-linkbox-item"> <a href="https://issuetracker.google.com/issues?q=status:open%20componentid:1400036&s=created_time:desc" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > See open issues </a> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Related content</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="https://blog.chromium.org/" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > Chromium updates </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/case-studies" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > Case studies </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/deprecated" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > Archive </a> </li> <li class="devsite-footer-linkbox-item"> <a href="https://web.dev/shows" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > Podcasts & shows </a> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Follow</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="https://twitter.com/ChromiumDev" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > @ChromiumDev on X </a> </li> <li class="devsite-footer-linkbox-item"> <a href="https://www.youtube.com/user/ChromeDevelopers" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > YouTube </a> </li> <li class="devsite-footer-linkbox-item"> <a href="https://www.linkedin.com/showcase/chrome-for-developers" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > Chrome for Developers on LinkedIn </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/static/blog/feed.xml" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > RSS </a> </li> </ul> </li> </ul> </nav> </devsite-footer-linkboxes> <devsite-footer-utility class="devsite-footer"> <div class="devsite-footer-utility nocontent"> <nav class="devsite-footer-utility-links" aria-label="Utility links"> <ul class="devsite-footer-utility-list"> <li class="devsite-footer-utility-item "> <a class="devsite-footer-utility-link gc-analytics-event" href="//policies.google.com/terms" data-category="Site-Wide Custom Events" data-label="Footer Terms link" > Terms </a> </li> <li class="devsite-footer-utility-item "> <a class="devsite-footer-utility-link gc-analytics-event" href="//policies.google.com/privacy" data-category="Site-Wide Custom Events" data-label="Footer Privacy link" > Privacy </a> </li> <li class="devsite-footer-utility-item glue-cookie-notification-bar-control"> <a class="devsite-footer-utility-link gc-analytics-event" href="#" data-category="Site-Wide Custom Events" data-label="Footer Manage cookies link" aria-hidden="true" > Manage cookies </a> </li> </ul> <devsite-language-selector> <ul role="presentation"> <li role="presentation"> <a role="menuitem" lang="en" >English</a> </li> <li role="presentation"> <a role="menuitem" lang="de" >Deutsch</a> </li> <li role="presentation"> <a role="menuitem" lang="es_419" >Español – América Latina</a> </li> <li role="presentation"> <a role="menuitem" lang="fr" >Français</a> </li> <li role="presentation"> <a role="menuitem" lang="id" >Indonesia</a> </li> <li role="presentation"> <a role="menuitem" lang="it" >Italiano</a> </li> <li role="presentation"> <a role="menuitem" lang="nl" >Nederlands</a> </li> <li role="presentation"> <a role="menuitem" lang="pl" >Polski</a> </li> <li role="presentation"> <a role="menuitem" lang="pt_br" >Português – Brasil</a> </li> <li role="presentation"> <a role="menuitem" lang="vi" >Tiếng Việt</a> </li> <li role="presentation"> <a role="menuitem" lang="tr" >Türkçe</a> </li> <li role="presentation"> <a role="menuitem" lang="ru" >Русский</a> </li> <li role="presentation"> <a role="menuitem" lang="he" >עברית</a> </li> <li role="presentation"> <a role="menuitem" lang="ar" >العربيّة</a> </li> <li role="presentation"> <a role="menuitem" lang="fa" >فارسی</a> </li> <li role="presentation"> <a role="menuitem" lang="hi" >हिंदी</a> </li> <li role="presentation"> <a role="menuitem" lang="bn" >বাংলা</a> </li> <li role="presentation"> <a role="menuitem" lang="th" >ภาษาไทย</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_cn" >中文 – 简体</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_tw" >中文 – 繁體</a> </li> <li role="presentation"> <a role="menuitem" lang="ja" >日本語</a> </li> <li role="presentation"> <a role="menuitem" lang="ko" >한국어</a> </li> </ul> </devsite-language-selector> </nav> </div> </devsite-footer-utility> <devsite-panel></devsite-panel> </section></section> <devsite-sitemask></devsite-sitemask> <devsite-snackbar></devsite-snackbar> <devsite-tooltip ></devsite-tooltip> <devsite-heading-link></devsite-heading-link> <devsite-analytics> <script type="application/json" analytics>[]</script> <script type="application/json" tag-management>{"at": "True", "ga4": [], "ga4p": [], "gtm": [{"id": "GTM-5QF3RT2", "purpose": 0}], "parameters": {"internalUser": "False", "language": {"machineTranslated": "False", "requested": "en", "served": "en"}, "pageType": "blog", "projectName": "Blog", "signedIn": "False", "tenant": "chrome", "recommendations": {"sourcePage": "", "sourceType": 0, "sourceRank": 0, "sourceIdenticalDescriptions": 0, "sourceTitleWords": 0, "sourceDescriptionWords": 0, "experiment": ""}, "experiment": {"ids": ""}}}</script> </devsite-analytics> <devsite-badger></devsite-badger> <script nonce="8wPsgJDNPb7HK40qLjV662fdNw18RX"> (function(d,e,v,s,i,t,E){d['GoogleDevelopersObject']=i; t=e.createElement(v);t.async=1;t.src=s;E=e.getElementsByTagName(v)[0]; E.parentNode.insertBefore(t,E);})(window, document, 'script', 'https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/chrome/js/app_loader.js', '[53,"en",null,"/js/devsite_app_module.js","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/chrome","https://chrome-dot-devsite-v2-prod-3p.appspot.com",1,null,["/_pwa/chrome/manifest.json","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/images/video-placeholder.svg","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/chrome/images/favicon.png","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/chrome/images/lockup.svg","https://fonts.googleapis.com/css?family=Google+Sans:400,500|Roboto:400,400italic,500,500italic,700,700italic|Roboto+Mono:400,500,700&display=swap"],1,null,[1,6,8,12,14,17,21,25,50,52,63,70,75,76,80,87,91,92,93,97,98,100,101,102,103,104,105,107,108,109,110,112,113,116,117,118,120,122,124,125,126,127,129,130,131,132,133,134,135,136,138,140,141,147,148,149,151,152,156,157,158,159,161,163,164,168,169,170,179,180,182,183,186,191,193,196],"AIzaSyCNm9YxQumEXwGJgTDjxoxXK6m1F-9720Q","AIzaSyCc76DZePGtoyUjqKrLdsMGk_ry7sljLbY","developer.chrome.com","AIzaSyB9bqgQ2t11WJsOX8qNsCQ6U-w91mmqF-I","AIzaSyAdYnStPdzjcJJtQ0mvIaeaMKj7_t6J_Fg",null,null,null,["Cloud__enable_llm_concierge_chat","Search__enable_ai_eligibility_checks","MiscFeatureFlags__enable_explain_this_code","Search__enable_dynamic_content_confidential_banner","Profiles__enable_developer_profiles_callout","Cloud__enable_cloudx_experiment_ids","MiscFeatureFlags__emergency_css","Cloud__enable_cloudx_ping","Search__enable_page_map","Profiles__enable_public_developer_profiles","Cloud__enable_legacy_calculator_redirect","TpcFeatures__enable_required_headers","Concierge__enable_pushui","Cloud__enable_free_trial_server_call","Cloud__enable_cloud_facet_chat","TpcFeatures__enable_mirror_tenant_redirects","OnSwitch__enable","Cloud__enable_cloud_shell_fte_user_flow","MiscFeatureFlags__enable_project_variables","Search__enable_suggestions_from_borg","BookNav__enable_tenant_cache_key","DevPro__enable_developer_subscriptions","MiscFeatureFlags__developers_footer_dark_image","Profiles__enable_completecodelab_endpoint","Experiments__reqs_query_experiments","Profiles__enable_dashboard_curated_recommendations","MiscFeatureFlags__developers_footer_image","MiscFeatureFlags__enable_variable_operator","CloudShell__cloud_shell_button","Analytics__enable_clearcut_logging","Cloud__enable_cloud_shell","MiscFeatureFlags__enable_firebase_utm","Profiles__enable_page_saving","Profiles__enable_awarding_url","CloudShell__cloud_code_overflow_menu","Cloud__enable_cloud_dlp_service","Profiles__enable_complete_playlist_endpoint","Profiles__enable_profile_collections","EngEduTelemetry__enable_engedu_telemetry","Profiles__require_profile_eligibility_for_signin","Profiles__enable_recognition_badges","DevPro__enable_cloud_innovators_plus","Profiles__enable_release_notes_notifications","MiscFeatureFlags__enable_view_transitions"],null,null,"AIzaSyA58TaKli1DculwmAmbpzLVGuWc8eCQgQc","https://developerscontentserving-pa.googleapis.com","AIzaSyDWBU60w0P9hEkr29kkksYs8Z7gvZ8u_wc","https://developerscontentsearch-pa.googleapis.com",2,4,null,"https://developerprofiles-pa.googleapis.com",[53,"chrome","Chrome for Developers","developer.chrome.com",null,"chrome-dot-devsite-v2-prod-3p.appspot.com",null,null,[null,null,null,null,null,null,null,null,null,null,null,[1],null,null,null,null,null,null,[1],null,null,null,null,[1,null,1],[1,1,null,1,1]],null,[69,null,null,null,null,null,"/images/lockup.svg","/images/touchicon-180.png",null,null,null,1,1,null,null,null,null,null,null,null,null,2,null,null,null,"/images/lockup-dark-theme.svg",[]],[],null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[[],[1,1]],[[null,null,null,null,null,["GTM-5QF3RT2"],null,null,null,null,null,[["GTM-5QF3RT2",1]],1]],null,4]]') </script> <devsite-a11y-announce></devsite-a11y-announce> </body> </html>