Site Search in Omnibox Explainer

This document provides a technical overview of the Omnibox Scoped Search (also referred to as “site search” or “keyword search”) functionality for Chrome engineers. The architecture and code paths described are shared across Windows, Mac, Linux, and ChromeOS. It is based on a detailed code analysis as of this writing (2025-09-24) covers two primary scenarios: scoped search via traditional keywords (e.g. youtube.com) and scoped search via built-in keywords (also known as “starter pack” keywords), which use the “@” syntax (e.g., @history).

Types of Keywords

Keywords in the Omnibox come from various sources and have different behaviors. Here's a breakdown of the most common types:

  • Starter Packs: Built-in keywords with the “@” prefix (e.g., @bookmarks, @history).
  • Prepopulated: Keywords for common search engines (e.g., bing.com, yahoo.com).
  • User Created (Traditional): A user-created keyword where the template URL contains a %s placeholder (e.g., youtube search).
  • Non-substituting User Created: A user-created keyword where the template URL does not contain a %s placeholder.
  • Default Search Engine (DSE): The user's currently selected default search engine, which is kind of orthogonal to the other types here.
  • Extension: Keywords registered by browser extensions (e.g., an ssh keyword registered by the Secure Shell extension).
  • Enterprise Policy: Keywords set by a device administrator through enterprise policies. These can be further subdivided into:
    • Aggregator: e.g.,@agentspace.
    • Non-aggregator: e.g., microsoft-documents.
  • Admin Policy: Keywords set by an admin policy.

High-Level Overview

The Omnibox uses a sophisticated system of providers, managed by the AutocompleteController, to generate suggestions. Site Search functionality is primarily handled by two distinct providers:

  1. FeaturedSearchProvider: Manages the “Omnibox Starter Pack,” which provides scoped search suggestions like @history, @bookmarks, and @tabs, as well as enterprise keywords (e.g. @agentspace or facebook-internal-documents).
  2. KeywordProvider: Manages searches directed at specific, non-default search engines using keywords. This is powered by the TemplateURLService, which stores URL templates for various sites.

While KeywordProvider and FeaturedSearchProvider manage surfacing the keyword entry points, most providers (e.g., tabs, bookmarks, history, search, ...) are responsible for populating the suggestions when in keyword mode. e.g.

  • ‘youtube.com query’ will be handled by search provider.
  • ‘youtube.com cat video’ to select a past youtube navigation will be handled by a history provider.

These providers rely on the TemplateURLService, which is populated with TemplateURL objects. These objects can come from several sources:

  • For enterprise and policy keywords, they're set by policy.
  • For pre-populated keywords, they're hardcoded into the binary, but in a json or some sort of data file, not necessarily in .cc directly.
  • For extensions, they come from the extensions.

The general flow is as follows:

  1. User types into the Omnibox.
  2. AutocompleteController starts and queries all registered providers with the user's input.
  3. FeaturedSearchProvider and KeywordProvider, among others, generate AutocompleteMatch objects based on the input.
  4. These matches are ranked and displayed to the user.
  5. When a user selects a match that happens to have a keyword chip, they will navigate to the match, ignoring the keyword chip. When a user selects the keyword chip on the match, they will enter keyword mode (to handle subsequent input) which may or may not ultimately navigate to the target site. (You can enter youtube keyword mode by typing ‘youtube.com query’ then select a drive.google.com document titled ‘youtube.com query’ that does not navigate to youtube.com.)

Key UI Component Roles

The LocationBarView is the top level component of omnibox view hierarchy. See Overview of the OmniboxView Hierarchy (internal document) for a picture.

  • LocationBarView owns the omnibox view, the popup view, and selected keyword view. It is mostly responsible for updating the keyword chip in the input row (e.g., “Search History”).

  • OmniboxView: This is the text field where the user types. Its implementation (e.g., OmniboxViewViews) is responsible for rendering the user's text, and handling input. It communicates user actions, like typing, to the OmniboxEditModel.

  • OmniboxPopupView: This is the dropdown list of suggestions that appears below the text field. Its implementation (e.g., OmniboxPopupViewViews) is responsible for rendering each suggestion line (using OmniboxResultView). It receives the list of AutocompleteMatch objects from the model and informs the model when the user interacts with the list (e.g., hovering or selecting a suggestion).

The OmniboxEditModel acts as a controller for these views. For example, when a user selects a suggestion in the OmniboxPopupView, the popup notifies the model. The model then updates its internal state and may, in turn, commands the OmniboxView to change its displayed text or show a keyword chip.

While the view and model are mutually aware of each other to facilitate this communication, their ownership is hierarchical to prevent circular dependencies. The OmniboxView is owned by its parent UI component (e.g., LocationBarView). The view, in turn, owns the OmniboxController, which owns the OmniboxEditModel. This creates a clear ownership chain: OmniboxViewOmniboxControllerOmniboxEditModel. Communication back up the chain is handled via non-owning raw_ptr references.

Scenario 1: Scoped Search (@history)

This scenario describes how a user can scope their search to their local browser history.

User Journey:

  1. User types @hist.
  2. An autocomplete suggestion for “Search History” appears.
  3. User selects this suggestion.
  4. The text @history disappears from the input field and is replaced by a “Search History” chip.
  5. User types a query, e.g., foo, and presses Enter.
  6. The browser navigates to chrome://history/?q=foo.

Technical Deep Dive

  1. Suggestion Generation (@hist)

    • Component: FeaturedSearchProvider, TemplateURLService
    • Mechanism: The @ prefixed suggestions are part of the “Omnibox Starter Pack” feature, which are managed as special TemplateURL objects.
      • The TemplateURLService is loaded with a set of default “starter pack” engines, including one for History with the keyword @history and a unique starter_pack_id. This data is loaded from components/search_engines/template_url_starter_pack_data.cc.
      • FeaturedSearchProvider::Start() is called on input. It detects that the input starts with @ and queries the TemplateURLService for matching keywords.
      • It finds the @history TemplateURL object.
      • FeaturedSearchProvider then creates an AutocompleteMatch with the type AutocompleteMatch::Type::STARTER_PACK.
  2. Mode Activation (Selecting the suggestion)

    • Component: OmniboxEditModel, AutocompleteController
    • Mechanism: When the user selects the STARTER_PACK match, OmniboxEditModel::OpenMatch() is called. This activates “keyword mode” with @history as the keyword.
      • The AutocompleteController is restarted with the new input. It recognizes that the omnibox is in keyword mode with a TemplateURL that has a starter_pack_id for history.
      • Crucially, the controller then adjusts its logic to only run a limited set of providers relevant to history search: HistoryURLProvider, HistoryEmbeddingsProvider, and FeaturedSearchProvider.
  3. UI State Management (The Chip and Text Replacement)

    • Component: OmniboxEditModel, OmniboxView
    • Mechanism: The separation between the model‘s state and the view’s displayed text is key.
      • The OmniboxEditModel sets its internal state to keyword mode, storing @history internally.
      • It then clears the user's visible text, replacing it with an empty string.
      • The model notifies the OmniboxView, which sees that the model is in keyword mode (is_keyword_selected() is true) and renders the “Search History” chip instead of the keyword text.
      • Specifically, the SelectedKeywordView is shown. It retrieves the TemplateURL for @history, gets its short name (“History”), and uses this to format a localized string (e.g., “Search History”).
  4. Final Navigation (foo + Enter)

    • Component: HistoryURLProvider, AutocompleteController
    • Mechanism: As the user types foo, the AutocompleteController runs the filtered set of providers (e.g., HistoryURLProvider) against the input foo.
      • HistoryURLProvider finds matching history entries for “foo”.
      • It creates AutocompleteMatch objects with destination_urls pointing to the relevant history pages (e.g., chrome://history/?q=foo).
      • When the user presses Enter, the browser navigates to the selected history result.

Scenario 2: Keyword Search (youtube.com)

This scenario describes how a user can search a specific website directly from the Omnibox using a keyword.

User Journey:

  1. User performs a search on youtube.com.
  2. Chrome automatically generates an inactive TemplateURL for YouTube search by detecting the site's OpenSearch description.
  3. The user navigates to chrome://settings/searchEngines.
  4. The YouTube entry appears in the “inactive” list of site search engines.
  5. The user explicitly activates the YouTube entry.
  6. Now, when the user types “you” in the Omnibox, a suggestion for youtube.com appears with a “Search YouTube” hint, and site search is fully enabled.

Technical Deep Dive

  1. Search Engine Discovery and Storage

    • Component: TemplateURLService
    • Mechanism: When a user submits a search form on a website that provides an OpenSearch description document, Chrome‘s renderer process can detect it and send an IPC message to the browser process. The TemplateURLService then creates and stores a TemplateURL object. This object contains the site’s name, the keyword (e.g., youtube.com), and the URL template for performing a search (e.g., https://www.youtube.com/results?search_query={searchTerms}).
  2. Automatic Keyword Generation

    • Component: SearchEngineTabHelper, TemplateURLService
    • Mechanism: When a user performs a search on a website, Chrome can automatically generate a keyword for that site.
      • SearchEngineTabHelper::GenerateKeywordIfNecessary() is called on navigation.
      • It checks if the navigation is a form submission and if a keyword can be generated from the URL.
      • If so, it calls TemplateURLService::CanAddAutogeneratedKeyword() to check if a new keyword can be added. This is to avoid adding duplicate or conflicting keywords.
      • If a new keyword can be added, a TemplateURLData object is created and added to the TemplateURLService.
      • This newly created TemplateURL is marked as safe_for_autoreplace, meaning it can be replaced by a more official keyword later (e.g., one from an OpenSearch description document).
      • The is_active status is set to kUnspecified, so the keyword is not immediately usable.
  3. Activation

    • Component: TemplateURLService
    • Mechanism: A newly created TemplateURL is not immediately active (is_active is kUnspecified). For it to be used for keyword search, it must be activated. There are two paths to activation:
      • Manual Activation: This is the immediate path. When a user goes to chrome://settings/searchEngines and activates an entry, TemplateURLService::SetIsActiveTemplateURL() is called. This function explicitly sets is_active to kTrue and also sets safe_for_autoreplace to false, ensuring the user's choice is preserved. This change takes effect immediately in the current session.
      • Automatic Generation: As described in the previous section, keywords can be generated automatically. However, they must be manually activated by the user in chrome://settings/searchEngines.
  4. Suggestion Generation (youtube.com foo)

    • Component: KeywordProvider
    • Mechanism: KeywordProvider::Start() is called with the user's input.
      • It calls AutocompleteInput::ExtractKeywordFromInput() to parse the input into a potential keyword (youtube.com) and the remaining query (foo).
      • It then calls TemplateURLService::AddMatchingKeywords() with the extracted keyword.
      • TemplateURLService finds the active TemplateURL object matching youtube.com.
      • KeywordProvider receives this TemplateURL and proceeds to call its internal CreateAutocompleteMatch() method. This creates an AutocompleteMatch of type AutocompleteMatch::Type::SEARCH_KEYWORD.
  5. Final Navigation (Enter)

    • Component: KeywordProvider
    • Mechanism: The final URL is constructed within KeywordProvider::FillInUrlAndContents().
      • This function is called by CreateAutocompleteMatch().
      • It uses the TemplateURLRef from the TemplateURL object. The ReplaceSearchTerms() method is called on this reference.
      • This method substitutes the {searchTerms} placeholder in the URL template with the user's query (foo).
      • The resulting URL (e.g., https://www.youtube.com/results?search_query=foo) is set as the destination_url of the AutocompleteMatch. When the user presses Enter, the browser navigates to this URL.

Keyword State in Core Data Structures

To fully understand the logic, it's crucial to see how keyword state is tracked within the core data structures: AutocompleteInput and AutocompleteMatch.

AutocompleteInput

The AutocompleteInput object represents the user‘s query and the omnibox’s state. It has two key fields, prefer_keyword_ and allow_exact_keyword_match_, which are used to signal that when the user is in keyword mode, providers should show more “keyword-y” suggestions. For example, a search for ‘query’ should suggest ‘youtube.com/q=query’ instead of ‘google.com/q=query’ when the user is in the YouTube keyword mode.

AutocompleteMatch

The AutocompleteMatch object represents a single suggestion and contains several nuanced fields for managing keywords:

  • associated_keyword: This field determines which keyword to activate via the keyword chip. For example, the @history keyword activates the chrome://history... template URL. This can be a source of confusion, because when a user types “youtube”, an autocomplete match might appear that navigates to ‘amazon.com/youtube’ but also has a keyword chip to activate the youtube.com keyword. In this case, associated_keyword will store ‘youtube.com’, even though the rest of the match fields are Amazon-related.

  • keyword: This field tracks which TemplateURL was used to create the match‘s destination URL. For example, if the user types ‘youtube.com query’, the keyword field will track ‘youtube’. This can also be confusing, because even when the user isn’t in keyword mode, most search suggestions are still generated using a keyword and will have their keyword field set (e.g., to google.com for the default search engine).

  • from_keyword: This field tracks if the match was generated while the user is in keyword mode. This is another potential point of confusion, as it can be true even if the match doesn‘t have a keyword field set. Conversely, from_keyword can be false even when keyword is set. In essence, from_keyword reflects the input state when the match was created, while keyword reflects the match’s data source.

Methods for Model-View Interaction

A key aspect of the Omnibox architecture is the interaction between the OmniboxEditModel (the model) and its two main views. The model holds the state, while the views are responsible for rendering the UI. The views are updated via a tightly-coupled observer-like pattern, where the model directly calls methods on the views when its state changes.

The following table summarizes the key methods that facilitate this interaction within the scope of the scenarios described in this document.

Initiator (Caller)Target (Callee)Method CalledPurpose
OmniboxViewOmniboxEditModelOnAfterPossibleChange()Notifies the model of user text input.
OmniboxEditModelOmniboxViewSetWindowTextAndCaretPos()Instructs the text view to change its content, e.g., when entering keyword mode.
OmniboxPopupViewOmniboxEditModelOpenMatch() / SetPopupSelection()Notifies the model that the user has selected or highlighted a suggestion.
OmniboxEditModelOmniboxPopupViewUpdatePopupAppearance()Instructs the popup to redraw itself based on the model's current state.
AutocompleteControllerOmniboxEditModelOnResultChanged()Notifies the model that the autocomplete results have changed.

Appendix: Architecture on Chrome for Android

The architecture described above is specific to desktop platforms which use the C++ Views UI framework. Chrome for Android has a distinct architecture where the UI is primarily implemented in Java and built upon a Coordinator pattern. This pattern distributes responsibilities among several specialized components, in contrast to the more monolithic OmniboxEditModel on desktop.

However, the core suggestion generation logic is shared. The C++ AutocompleteController is used by all platforms to generate suggestion results.

The Coordinator Pattern on Android

On Android, the Omnibox (known as the Location Bar) is managed by a top-level LocationBarCoordinator. This coordinator owns and orchestrates a set of sub-components, each with a specific responsibility. The responsibilities of the desktop OmniboxEditModel are split among these components:

  • LocationBarCoordinator: The overall owner and assembler. It creates the other coordinators and mediators and wires them together.

  • AutocompleteCoordinator: Manages the suggestion list UI, including the OmniboxSuggestionsDropdown view. It creates and owns the AutocompleteMediator.

  • AutocompleteMediator: The primary logic hub for autocomplete. It is responsible for communicating with the C++ AutocompleteController via JNI, processing the results, and updating the PropertyModel that drives the suggestion list UI.

  • UrlBarCoordinator: Manages the UrlBar view, which is the EditText field the user types into. It handles text state, cursor position, and selection, reporting user input to other components.

  • LocationBarMediator: A central logic hub that connects the other components. It handles events like focus changes and button clicks, and manages the overall state of the location bar that doesn't directly involve text editing or autocomplete suggestions.

Interaction Flow for Suggestions

  1. A user types into the UrlBar view (managed by UrlBarCoordinator).
  2. During initialization, LocationBarCoordinator sets up a listener so that text changes in UrlBarCoordinator directly call AutocompleteCoordinator.onTextChanged().
  3. AutocompleteCoordinator.onTextChanged() forwards the call to AutocompleteMediator.onTextChanged().
  4. The AutocompleteMediator takes the user's input and initiates a request to the shared C++ AutocompleteController through the JNI bridge.
  5. When results are ready, the C++ AutocompleteController sends them back to the AutocompleteMediator via its onSuggestionsReceived JNI callback.
  6. The AutocompleteMediator receives the AutocompleteResult, processes it, builds a list of view models, and updates the main PropertyModel for the suggestion list.
  7. The PropertyModel change is observed by the OmniboxSuggestionsDropdownAdapter, which then renders the final views in the suggestion dropdown list.