urlbar API
Audience
The initial audience for the API described here is specifically teams internal to Mozilla and Mozilla partners. See the Motivation section below for why we're creating it. While we may consider wider use cases in the future, our design initially will be guided by what Mozilla and its partners need.
Background
Firefox's urlbar implementation is spread across several layers in the codebase:
Problems
That's a reasonable architecture for Firefox to use, but it can also make it hard for people, especially new people, to modify the look and feel of the urlbar and its popup or provide new data sources for it to integrate. Problems include the following:
Motivation
Recently there's been interest at Mozilla in experimenting with novel UIs and data sources in the urlbar. For example, the Universal Search team has been running a Test Pilot experiment where the urlbar popup offers suggestions drawn from sources on the web based on what the user has typed in the urlbar. If the user types in the name of a movie for example, Universal Search might modify the popup so that it displays show times or movie reviews. The user doesn't even need to hit the Return key.
Even more recently, Mozilla's new partnership with Cliqz has created another scenario where we and the team at Cliqz would like the ability to radically alter the look of the urlbar popup and the information it displays.
Both of these cases involve teams that aren't themselves working on the urlbar. In fact the team that is working on the urlbar is quite small. We would like to be able to experiment with the urlbar without funneling everything through the urlbar team.
Solution
We are creating a new urlbar API expressly for the purpose of addressing these problems and use cases. Its goals are:
There's a tradeoff for the consumer between flexibility and simplicity. The more flexible the API is, the more work potentially the consumer needs to do. The simpler the API is, the less work potentially the consumer needs to do, but obviously that would exclude more use cases.
We considered a simpler alternative where the consumer simply provides a JSON blob that describes the results with which it wants to populate the urlbar, but after speaking with some people who are target users of the API, we think that's not flexible enough. We also considered permitting consumers to modify only the topmost result in the popup, but that again would make UXes like Cliqz's impossible.
Architecture
The API consumer's code runs entirely in an iframe that the runtime injects into the urlbar popup. The entire contents of the popup are replaced with the iframe, which is sandboxed for security. Currently it is not remote, but that could change. The API is designed in such a way that if that does change, it should be able to change transparently to existing API consumers.
The API runtime comprises two pieces: (1) a small shim that lives in Firefox itself, in mozilla-central (“the shim”), and (2) the actual API implementation that is packaged in the add-on (“the runtime”).
Part (1) is a shim between Firefox and the add-on. It creates and tears down the iframe in the popup. It also provides hooks to part (2), the runtime, and insulates the runtime from breaking changes across Firefox versions. The shim is small as possible.
Part (2), the runtime, is where the API is actually implemented. Its code is kept in a non-mozilla-central repo. Consumers pull the repo and include it in their add-ons. The runtime adds a `urlbar` property to `window` inside the iframe that the consumer uses to interact with it. The runtime also fires events on the iframe. (See below.)
The distinction between these two parts means that we can iterate on the API rapidly, and consumers can use new iterations immediately, outside the Firefox release cycle. We expect these two properties to be especially useful as consumers start to use the API and find problems with it. Once the API becomes stable, we can consider shipping it as part of Firefox.
[To be determined: In 2017, traditional add-ons will be deprecated in favor of WebExtensions. Need to look into how WebExtensions work and how the API implementation can be made compatible with them.]
Contention for the iframe between Multiple Add-ons
We’re not currently planning on supporting multiple consumer add-ons at once. It will probably be the case that the first add-on wins if the user were to install multiple consumer add-ons.
The reason is that this API is intended to be used internally at Mozilla and by Mozilla partners, at least initially. In that context, we can control contention and work around any problems ad hoc that might arise.
Certainly designing for multiple add-ons and for the add-on community at large is a potential future goal.
Current State
Part (1), the shim, is being implemented in https://bugzilla.mozilla.org/show_bug.cgi?id=1257550. We want to land that by the end of 2016.
Part (2), the API runtime, is partially implemented in this repo, along with a toy consumer add-on that uses it: https://hg.mozilla.org/users/dwillcoxon_mozilla.com/urlbar-api-toy-addon/
Intended Use & Long Term
The API and runtime should work well enough that experimental, non-production UXes can be shipped with it as part of Shield studies, Test Pilot, etc.
It's unlikely that we'll want to ship real Firefox features with it. It's possible that we might use the API to prototype real features, but we'll probably want to reimplement them in XUL, etc. before shipping them.
It's also possible that we take lessons learned from consumers of this API and apply them to a "version 2.0" API that from the beginning is designed and intended for wide use or specific cases.
API: Entry Point
To initialize the iframe in the popup, a consumer add-on imports UrlbarAPI.jsm and calls UrlbarAPI.init() (in its bootstrap.js file, for example):
API: window.urlbar
The runtime injects a `window.urlbar` object into the JS running in the iframe. It has the following API.
API: Events
The runtime notifies the consumer of various events by firing DOM events on the window. The text box retains focus while the popup is open; that’s the usual behavior in Firefox and the runtime doesn’t change that. Therefore, the consumer needs to be notified when keypresses and input happen in the text box, for example.
To be notified when the iframe is moused over and of other events inside the iframe, the consumer can add event listeners on `window` or elsewhere, as web pages normally do.
The events fired by the runtime are:
code,
key,
altKey,
ctrlKey,
metaKey,
shiftKey,
}
url,
faviconURI,
title,
type,
searchStr,
}
To Be Determined
What about the one-off search buttons/settings? Are they expected to be part of the iframe or part of the popup and not changeable? [urlbar.showSearchButtons() should cover this.]
If they are contained within the iframe, we’ll also need to consider about allowing a context menu to be created via the API. Otherwise you’ll be constrained to having the context menu within the iframe boundaries (i.e. different results to what we have on nightly today).
What about search suggestions? Would those still stream in to fill up the popup if the total number of results is less than maxResults? If so, what would the event look like, since it’s not a complete result? [“Result” means any entry that’s normally shown in the urlbar popup, including search suggestions. There’s a `type` property on the event detail object to tell you the type (search, history, etc.)]
How do we handle a11y? I never did figure out if an iframe inside a XUL popup is accessible to screen readers. [Yikes, dunno. Would need to look into it.]
What happens to the history popup (the popup shown if you click the down caret in the urlbar)? [Good question! Right now you would get a reset and result events, but without any input event. Maybe worth handling separately.]
Is it possible to build our current awesomebar results list using this API. Is there any benefit or drawback to doing so? Not saying that we should, yet, but if we can’t it suggests the API is missing something. [Good question. The toy add-on I have basically does that. There are some uncanny valley problems to solve though. This is a good way to test the API, I agree.]