Search Lifecycle

When a character is typed into the address bar, or the address bar is focused, we initiate a search. What follows is a simplified version of the lifetime of a search, describing the pipeline that returns results for a typed string. Some parts of the query lifetime are intentionally omitted from this document for clarity.

The search described in this document is internal to the address bar. It is not the search sent to the default search engine when you press Enter. Parts of this process often occur multiple times per keystroke, as described below.

It is recommended that you first read the Address Bar Nontechnical Overview to become familiar with the terminology in this document. This document is current as of August 2026.

  1. The user types a query (e.g. “coffee near me”) into the UrlbarInput <input> DOM element. That DOM element tells UrlbarInput that text is being input.

  2. UrlbarInput starts a search. It creates a UrlbarQueryContext and passes it to the UrlbarChildController, which forwards it to the UrlbarParentController. The controller is split in two: the UrlbarChildController lives alongside the input, while the UrlbarParentController owns the ProvidersManager and runs the query. This split is what lets the input run in a content process (such as about:newtab) while the providers stay in the parent process. The query context is an object that will exist for the lifetime of the query and it’s how we keep track of what results to show. It contains information like what kind of results are allowed, the search string (“coffee near me”, in this case), and other information about the state of the Urlbar. A new UrlbarQueryContext is created every time the text in the input changes.

  3. UrlbarParentController tells ProvidersManager that the providers should fetch results.

  4. ProvidersManager tells each provider to decide if it wants to provide results for this query by calling their isActive methods. The provider can decide whether or not it will be active for this query. Some providers are rarely active: for example, UrlbarProviderTopSites isn’t active if the user has typed a search string.

  5. ProvidersManager then tells the active providers to fetch results by calling their startQuery method.

  6. Each provider fetches its results in its own way and asynchronously. As one example, if the default search engine is Google, UrlbarProviderSearchSuggestions would send the string “coffee near me” to Google. Google would return a list of suggestions and UrlbarProviderSearchSuggestions would create a UrlbarResult for each one.

    The providers send their results back to ProvidersManager. They do this one result at a time by calling the addCallback callback passed into startQuery. ProvidersManager takes all the results from all the providers and puts them into the list of unsorted results.

    Due to the asynchronous and parallel nature of providers, this and the following steps may occur multiple times per search. Some providers may take longer than others to return their results. We don’t want to wait for slow providers before showing results. To handle slow providers, ProvidersManager gathers results from providers in “chunks”. A timer fires at an interval. Every time the timer fires, we take whatever results we have from the active providers (the “chunk” of results) and perform the following steps.

  7. ProvidersManager asks UrlbarMuxer to sort the unsorted results.

  8. UrlbarMuxer chooses the results that will be shown to the user. It groups and sorts the results to determine the order in which the results will be shown. This process usually involves discarding irrelevant and duplicate results. We also cap results at a limit, defined in the browser.urlbar.maxRichResults preference.

  9. Once the results are sorted, ProvidersManager tells UrlbarParentController that results are ready to be shown.

  10. UrlbarParentController sends out a notification that results are ready to be shown. The notification is dispatched by the UrlbarChildController, which UrlbarView was listening to. Once the view gets the notification, it calls #updateResults to create DOM nodes for each UrlbarResult and inserts them into the view’s DOM element.

    As described above, we may reach this step multiple times per search. That means we may be updating the view multiple times per keystroke. A view that visibly changes many times after a single keystroke is perceived as “flickering” by the user. As a result, we try to limit the number of times the view needs to update.

Diagram

The blue rounded boxes are the UI modules, which run wherever the input lives; the amber rectangles always run in the parent process. The solid lines show a query traveling to the providers. The dotted lines show its results coming back to the view.

        ---
config:
  flowchart:
    wrappingWidth: 400
---
%% wrappingWidth works around https://github.com/mermaid-js/mermaid/issues/5785,
%% which makes Firefox drop labels containing long words.
flowchart TD
    dom([DOM])
    input(UrlbarInput)
    child(UrlbarChildController)
    view(UrlbarView)
    parent[UrlbarParentController]
    manager[UrlbarProvidersManager]
    providers[UrlbarProviders]
    muxer[UrlbarMuxer]

    dom -- "1: text input" --> input
    input -- "2: UrlbarQueryContext" --> child
    child -- "2: UrlbarQueryContext" --> parent
    parent -- "3: fetch results" --> manager
    %% dagre places siblings in edge order, so the muxer edges come first to
    %% keep the numbered path descending on the right and returning on the left.
    manager -. "7: sort" .-> muxer
    muxer -. "8: sorted results" .-> manager
    manager -- "4: isActive<br/>5: startQuery" --> providers
    providers -. "6: results" .-> manager
    manager -. "9: results ready" .-> parent
    parent -. "10: notification" .-> child
    child -. "10: notification" .-> view
    view -. "result rows" .-> dom

    classDef uiModule fill:#dbeafe,stroke:#1e40af,color:#1a1a1a;
    classDef parentModule fill:#fef3c7,stroke:#92400e,color:#1a1a1a;
    classDef domNode fill:#e5e7eb,stroke:#4b5563,color:#1a1a1a;
    class input,child,view uiModule;
    class parent,manager,providers,muxer parentModule;
    class dom domNode;
    

One trip through the search pipeline, from a keystroke to result rows.