Architecture Overview ===================== The address bar is implemented as a *model-view-controller* (MVC) system. One of the scopes of this architecture is to allow easy replacement of its components, for easier experimentation. Each search is represented by a unique object, the *UrlbarQueryContext*. This object, created by the *View*, describes the search and is passed through all of the components, along the way it gets augmented with additional information. The *UrlbarQueryContext* is passed to the *Controller*, and finally to the *Model*. The model appends results to a property of *UrlbarQueryContext* in chunks, it sorts them through a *Muxer* and then notifies the *Controller*. See the specific components below, for additional details about each one's tasks and responsibilities. The UrlbarQueryContext ---------------------- The *UrlbarQueryContext* object describes a single instance of a search. It is augmented as it progresses through the system, with various information: .. code:: JavaScript UrlbarQueryContext { allowAutofill; // {boolean} If true, providers are allowed to return // autofill results. Even if true, it's up to providers // whether to include autofill results, but when false, no // provider should include them. isPrivate; // {boolean} Whether the search started in a private context. maxResults; // {integer} The maximum number of results requested. It is // possible to request more results than the shown ones, and // do additional filtering at the View level. searchString; // {string} The user typed string. userContextId; // {integer} The user context ID (containers feature). // Optional properties. muxer; // {string} Name of a registered muxer. Muxers can be registered // through the UrlbarProvidersManager. providers; // {array} List of registered provider names. Providers can be // registered through the UrlbarProvidersManager. sources: {array} list of accepted UrlbarUtils.RESULT_SOURCE for the context. // This allows to switch between different search modes. If not // provided, a default will be generated by the Model, depending on // the search string. engineName: // {string} if sources is restricting to just SEARCH, this // property can be used to pick a specific search engine, by // setting it to the name under which the engine is registered // with the search service. currentPage: // {string} url of the page that was loaded when the search // began. prohibitRemoteResults: // {boolean} This provides a short-circuit override for // context.allowRemoteResults(). If it's false, then allowRemoteResults() // will do its usual checks to determine whether remote results are // allowed. If it's true, then allowRemoteResults() will immediately // return false. Defaults to false. // Properties added by the Model. results; // {array} list of UrlbarResult objects. tokens; // {array} tokens extracted from the searchString, each token is an // object in the form {type, value, lowerCaseValue}. } The Model --------- The *Model* is the component responsible for retrieving search results based on the user's input, and sorting them accordingly to their importance. At the core is the `UrlbarProvidersManager `_, a component tracking all the available search providers, and managing searches across them. The *UrlbarProvidersManager* is a singleton, it registers internal providers on startup and can register/unregister providers on the fly. It can manage multiple concurrent queries, and tracks them internally as separate *Query* objects. The *Controller* starts and stops queries through the *UrlbarProvidersManager*. It's possible to wait for the promise returned by *startQuery* to know when no more results will be returned, it is not mandatory though. Queries can be canceled. .. note:: Canceling a query will issue an interrupt() on the database connection, terminating any running and future SQL query, unless a query is running inside a *runInCriticalSection* task. The *searchString* gets tokenized by the `UrlbarTokenizer `_ component into tokens, some of these tokens have a special meaning and can be used by the user to restrict the search to specific result type (See the *UrlbarTokenizer::TYPE* enum). .. caution:: The tokenizer uses heuristics to determine each token's type, as such the consumer may want to check the value before applying filters. .. code:: JavaScript UrlbarProvidersManager { registerProvider(providerObj); unregisterProvider(providerObj); registerMuxer(muxerObj); unregisterMuxer(muxerObjOrName); async startQuery(queryContext); cancelQuery(queryContext); // Can be used by providers to run uninterruptible queries. runInCriticalSection(taskFn); } UrlbarProvider ~~~~~~~~~~~~~~ A provider is specialized into searching and returning results from different information sources. Internal providers are usually implemented in separate *sys.mjs* modules with a *UrlbarProvider* name prefix. External providers can be registered as *Objects* through the *UrlbarProvidersManager*. Each provider is independent and must satisfy a base API, while internal implementation details may vary deeply among different providers. .. important:: Providers are singleton, and must track concurrent searches internally, for example mapping them by UrlbarQueryContext. .. note:: Internal providers can access the Places database through the *PlacesUtils.promiseLargeCacheDBConnection* utility. .. code:: JavaScript class UrlbarProvider { /** * Unique name for the provider, used by the context to filter on providers. * Not using a unique name will cause the newest registration to win. * @abstract */ get name() { return "UrlbarProviderBase"; } /** * The type of the provider, must be one of UrlbarUtils.PROVIDER_TYPE. * @abstract */ get type() { throw new Error("Trying to access the base class, must be overridden"); } /** * Whether this provider should be invoked for the given context. * If this method returns false, the providers manager won't start a query * with this provider, to save on resources. * @param {UrlbarQueryContext} queryContext The query context object * @returns {boolean} Whether this provider should be invoked for the search. * @abstract */ isActive(queryContext) { throw new Error("Trying to access the base class, must be overridden"); } /** * Gets the provider's priority. Priorities are numeric values starting at * zero and increasing in value. Smaller values are lower priorities, and * larger values are higher priorities. For a given query, `startQuery` is * called on only the active and highest-priority providers. * @param {UrlbarQueryContext} queryContext The query context object * @returns {number} The provider's priority for the given query. * @abstract */ getPriority(queryContext) { // By default, all providers share the lowest priority. return 0; } /** * Starts querying. * @param {UrlbarQueryContext} queryContext The query context object * @param {function} addCallback Callback invoked by the provider to add a new * result. A UrlbarResult should be passed to it. * @note Extended classes should return a Promise resolved when the provider * is done searching AND returning results. * @abstract */ startQuery(queryContext, addCallback) { throw new Error("Trying to access the base class, must be overridden"); } /** * Cancels a running query, * @param {UrlbarQueryContext} queryContext The query context object to cancel * query for. * @abstract */ cancelQuery(queryContext) { throw new Error("Trying to access the base class, must be overridden"); } } UrlbarMuxer ~~~~~~~~~~~ The *Muxer* is responsible for sorting results based on their importance and additional rules that depend on the UrlbarQueryContext. The muxer to use is indicated by the UrlbarQueryContext.muxer property. .. caution:: The Muxer is a replaceable component, as such what is described here is a reference for the default View, but may not be valid for other implementations. .. code:: JavaScript class UrlbarMuxer { /** * Unique name for the muxer, used by the context to sort results. * Not using a unique name will cause the newest registration to win. * @abstract */ get name() { return "UrlbarMuxerBase"; } /** * Sorts UrlbarQueryContext results in-place. * @param {UrlbarQueryContext} queryContext the context to sort results for. * @abstract */ sort(queryContext) { throw new Error("Trying to access the base class, must be overridden"); } } The Controller -------------- `UrlbarController `_ is the component responsible for reacting to user's input, by communicating proper course of action to the Model (e.g. starting/stopping a query) and the View (e.g. showing/hiding a panel). It is also responsible for reporting Telemetry. .. note:: Each *View* has a different *Controller* instance. .. code:: JavaScript UrlbarController { async startQuery(queryContext); cancelQuery(queryContext); // Invoked by the ProvidersManager when results are available. receiveResults(queryContext); // Used by the View to listen for results. addQueryListener(listener); removeQueryListener(listener); } The View -------- The View is the component responsible for presenting search results to the user and handling their input. .. caution The View is a replaceable component, as such what is described here is a reference for the default View, but may not be valid for other implementations. `UrlbarInput.sys.mjs `_ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Implements an input box *View*, owns an *UrlbarView*. .. code:: JavaScript UrlbarInput { constructor(options = { textbox, panel }); // Uses UrlbarValueFormatter to highlight the base host, search aliases // and to keep the host visible on overflow. formatValue(val); openResults(); // Converts an internal URI (e.g. a URI with a username or password) into // one which we can expose to the user. makeURIReadable(uri); // Handles an event which would cause a url or text to be opened. handleCommand(); // Called by the view when a result is selected. resultsSelected(); // The underlying textbox textbox; // The results panel. panel; // The containing window. window; // The containing document. document; // An UrlbarController instance. controller; // An UrlbarView instance. view; // Whether the current value was typed by the user. valueIsTyped; // Whether the context is in Private Browsing mode. isPrivate; // Whether the input box is focused. focused; // The go button element. goButton; // The current value, can also be set. value; } `UrlbarView.sys.mjs `_ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Represents the base *View* implementation, communicates with the *Controller*. .. code:: JavaScript UrlbarView { // Manage View visibility. open(); close(); // Invoked when the query starts. onQueryStarted(queryContext); // Invoked when new results are available. onQueryResults(queryContext); // Invoked when the query has been canceled. onQueryCancelled(queryContext); // Invoked when the query is done. This is invoked in any case, even if the // query was canceled earlier. onQueryFinished(queryContext); // Invoked when the view opens. onViewOpen(); // Invoked when the view closes. onViewClose(); } UrlbarResult ------------ An `UrlbarResult `_ instance represents a single search result with a result type, that identifies specific kind of results. Each kind has its own properties, that the *View* may support, and a few common properties, supported by all of the results. .. note:: Result types are also enumerated by *UrlbarUtils.RESULT_TYPE*. .. code-block:: JavaScript UrlbarResult { constructor(resultType, payload); type: {integer} One of UrlbarUtils.RESULT_TYPE. source: {integer} One of UrlbarUtils.RESULT_SOURCE. title: {string} A title that may be used as a label for this result. icon: {string} Url of an icon for this result. payload: {object} Object containing properties for the specific RESULT_TYPE. autofill: {object} An object describing the text that should be autofilled in the input when the result is selected, if any. autofill.value: {string} The autofill value. autofill.selectionStart: {integer} The first index in the autofill selection. autofill.selectionEnd: {integer} The last index in the autofill selection. suggestedIndex: {integer} Suggest a preferred position for this result within the result set. Undefined if none. isSuggestedIndexRelativeToGroup: {boolean} Whether the suggestedIndex property is relative to the result's group instead of the entire result set. } The following RESULT_TYPEs are supported: .. code:: JavaScript // An open tab. // Payload: { icon, url, userContextId } TAB_SWITCH: 1, // A search suggestion or engine. // Payload: { icon, suggestion, keyword, query, providesSearchMode, inPrivateWindow, isPrivateEngine } SEARCH: 2, // A common url/title tuple, may be a bookmark with tags. // Payload: { icon, url, title, tags } URL: 3, // A bookmark keyword. // Payload: { icon, url, keyword, postData } KEYWORD: 4, // A WebExtension Omnibox result. // Payload: { icon, keyword, title, content } OMNIBOX: 5, // A tab from another synced device. // Payload: { icon, url, device, title } REMOTE_TAB: 6, // An actionable message to help the user with their query. // Payload: { buttons, helpL10n, helpUrl, icon, titleL10n, type } TIP: 7, // A type of result which layout is defined at runtime. // Payload: { dynamicType } DYNAMIC: 8,