preface
This article is my translation of Mario Kosaka’s inside Look at Modern Web Browser series. Translation here does not mean literal translation, but to express the meaning of the author based on personal understanding, and try to add some relevant content to help people better understand.
What happens when you navigate
This article is the second in a four-part series exploring the inner workings of Chrome, where we looked at how different processes, or threads, are responsible for different parts of the browser. In this article, we’ll take a closer look at how each process and thread communicates and collaborates to present web content to us.
Let’s look at the simplest scenario for a user browsing the web: You type a URL into the browser’s navigation bar and press Enter. The browser then retrieves relevant data from the Internet and displays the web page. In this article, we will focus on the site data request in this simple scenario and the preparation that the browser does before rendering the page – that is, navigation.
It all starts with the browser process
As we mentioned in our last article on CPU, GPU, Memory, and multi-process architecture, everything that happens outside the TAB in the browser is controlled by the browser process. Browser processes have many worker threads responsible for different tasks. These include UI threads that draw components such as buttons at the top of the browser and input boxes in the navigation bar, network threads that manage network requests, and storage threads that control file reads and writes. When you enter a URL in the navigation bar, it’s the UI thread that handles your input.
The UI, network, and storage threads all belong to the browser process
A simple navigation
Step 1: Process the input
When the user starts typing in the navigation bar, the first thing the UI thread does is ask: “Is the string you entered a search query or a URL?” . For Chrome, the input in the navigation bar is either a domain name that can be requested directly or a keyword that the user wants to search for in a search engine like Google. So when a user enters information in the navigation bar, the UI thread performs a series of parses to determine whether to send the user’s input to the search engine or request your input directly from the site resource.
The UI thread is asking if the entered string is a search term or a URL
Step 2: Start navigation
When the user presses enter, the UI thread asks the Network thread to initiate a network request to retrieve the site’s content. The TAB displays a rotating circle indicating that the resource is being loaded, and the network thread performs a series of operations such as DNS addressing and establishing a TLS connection for the request.
The UI thread tells the network thread to jump to mysite.com
If the network thread receives an HTTP 301 redirect response from the server, it will tell the UI thread to redirect and then it will issue a new network request again.
Step 3: Read the response
When a network thread receives a stream of HTTP responses, it checks the first few bytes of the stream to determine the MIME Type of the response body, if necessary. The media Type of the response body can usually be determined by the CONTent-type of the HTTP header, but the content-type can sometimes be missing or incorrect, in which case the browser must perform MIME Type sniffing to determine the response Type. MIME Type sniffing is not an easy thing to do, and you can see from Chrome’s source code comments how different browsers can determine which media Type a body belongs to based on the content-Type.
The header of the response has content-Type information, and the body of the response has real data
If the body of the response is an HTML file, the browser passes the retrieved response data to the Renderer process for further work. If the response data is a zip file or other type of file, the response data is handed over to the Download Manager for processing.
The network thread is asking if the data in the response came from an HTML file from a secure source
Network threads also do SafeBrowsing of content before handing it over to the rendering process. If the requested domain name or the content of the response matches a known virus site, the web thread will show the user a warning page. In addition, network threads do a CORB (Cross Origin Read Blocking) check to ensure that sensitive cross-site data is not being sent to the renderer process.
Step 4: Find a Renderer process
After the network thread has done all the checking and is able to determine which site the browser should navigate to for the request, it tells the UI thread that all the data is ready. The UI thread receives confirmation from the network thread to find a renderer process for the site to render the interface.
The network thread tells the UI thread to find a renderer process to render the interface
Since network requests can take hundreds of milliseconds to complete, the browser optimizes some of the previous steps to reduce the navigation time. For example, in step 2, when the UI thread sends the URL link to the web thread, it already knows which site it is being navigated to, so while the web thread is working, the UI thread will actively start a render thread for the web request. If all goes well (no redirects or anything like that), the page’s renderer will be ready as soon as the network thread is ready for the data, saving time for new renderers. But if, for example, the site is redirected to a different site, the render process will not be used, it will be discarded and a new render process will be started.
Step 5: Commit navigation
At this point, the data and renderer are ready, and the browser process tells the renderer via IPC to commit navigation. In addition, the browser process passes the response data it just received to the corresponding renderer process to continue receiving incoming HTML data. Once the browser process receives a response from the render thread that the navigation has been committed, the navigation process ends and the document loading phase begins.
At this point, the navigation bar will be updated, and the Security Indicator and Site Settings UI will display site information related to the new page. The session history of the current TAB is also updated so that you can navigate to the page you just navigated by clicking the browser’s forward and back buttons. In order to restore the current TAB and session contents when you close a TAB or window, the current session history is saved on disk.
The browser process requests the renderer process to render the page through IPC
Additional step: Initial Load complete
When the navigation is submitted, the rendering process starts loading the resources and rendering the page. I’ll cover the details of how a rendering process renders a page in a later series of articles. Once the renderer has “finished” rendering, it notifies the browser process via IPC (note that this happens when the onload event for all frames on the page has been fired and the handler has completed), and the UI thread stops the navigation loop.
I use the word “done” here because the JavaScript on the client side can continue to load resources and change the contents of the view.
The renderer tells the browser process via IPC that the page is loaded
Navigate to different sites
A simple navigation scenario has been described! But what happens when the user enters a different URL in the navigation bar? If so, the browser process repeats the previous steps to complete the navigation of the new site. But before the browser process can do that, it needs to let the current rendering page do some finishing work by asking if the current rendering process needs to handle the beforeUnload event.
Beforeunload shows the user a “Are you sure you want to leave the current page?” message when the user renavigates or closes the current TAB. The second confirmation frame. The reason why the browser process needs to check with the current renderer when it renavigates is that everything that happens on the current page (including the JavaScript execution of the page) is controlled by the renderer, so it doesn’t know what’s going on inside.
Note: Do not add a beforeUnload event listener to a page. The listener you define will be executed when the page is being navigated, thus increasing the renavigation delay. Beforeunload event listeners can only be added when necessary, such as when the user enters data on the page and the data disappears as the page disappears.
The browser process tells the renderer via IPC that it is about to leave the current page and navigate to a new page
What if the renavigation is initiated within the page? For example, the user clicks on a link on the page or the client’s JavaScript code executes code such as window.location = “newsite.com”. In this case, the renderer checks for a listener that it has registered with the beforeUnload event, and executes it if it has. After that, what happens is the same as before, except that this time the navigation request is made by the renderer to the browser process.
In the case of re-navigation to a different site, another renderer will be started to complete the re-navigation and the current renderer will continue to handle some of the end work of the current page, such as listening function execution for the Unload event. Overview of Page Lifecycle States This article will introduce all of the page lifecycle states. The Page Lifecycle API will teach you how to listen for changes in the page state.
The browser process tells the new renderer to render the new page and tells the current renderer to wrap things up
Service Worker scenario
A recent change to this navigation process is the introduction of the service worker concept. Because Service workers can be used to write network proxies for websites, developers can have more control over network requests, such as deciding which data is cached locally and which data needs to be retrieved from the network. If the developer sets the current page content in the service worker to be retrieved from the cache, the rendering of the current page does not need to re-send the network request, which greatly speeds up the overall navigation process.
The important thing to note here is that a service worker is really just some JavaScript code running in the render process. So when the navigation starts, how does the browser process determine if the site to navigate has a service worker and start a rendering process to execute it?
When a service worker is registered, its scope will be recorded (you can learn more about The scope of a service worker in The Service Worker Lifecycle article). At the beginning of navigation, the network thread will search for the corresponding service worker in the scope of registered service worker according to the requested domain name. If a service worker hits the URL, the UI thread starts a renderer process for the service worker to execute its code. The Service worker may either use previously cached data or initiate a new network request.
The network thread will look for the corresponding service worker after receiving the navigation task
The UI thread starts a renderer process to run the found service worker code, which is executed by worker threads in the renderer process
Navigation Preload – Navigation Preload
In the example above, you can get a sense that the back and forth communication between the browser and renderer process, including the time it takes for the service worker to start, actually adds to the page navigation delay if the starting service worker decides to send a network request anyway. Navigation preloading is a technique that speeds up the efficiency of the entire navigation process by loading corresponding resources in parallel when the service worker starts. The preloaded resource request header has special flags that let the server decide whether to send the new content to the client or just the updated data to the client.
The UI thread sends network requests in parallel while starting a renderer process to run the Service worker code
conclusion
In this article, we discussed exactly what happens in navigation and some of the techniques browsers use to optimize navigation efficiency. In the next article, we’ll take a closer look at how browsers parse our HTML/CSS/JavaScript to render web content.
Keep an eye on my technical updates
I am the green onion of the attack, pay attention to me and I progress together into a full stack engineer on my own!
A peek into the modern Browser Architecture (PART 2)
Follow my personal official account to get my latest technical updates!