Source code

Revision control

Copy as Markdown

Other Tools

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.
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 <https://searchfox.org/mozilla-central/source/browser/components/urlbar/UrlbarTokenizer.sys.mjs>`_
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
--------------
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.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
------------
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,