Robin Berjon

It's What's For Dinner

Application-Level Menus for HTML

A little while back you might recall that Paul Rouget posted a little demo about Experimenting with HTML5 and native controls. The title is broad but much of the content is about a cool demo of what can be done if browsers implement the HTML menu element. Allowing a site to integrate with the native UI is a powerful approach to making the web easier to use and increase its world domination but it naturally comes with its share of potential security issues.

Right before that demo, we had a little conversation with Paul about various ideas as to how this could be done. No definitive findings of any kind came up, but the different options are, I think, quite interesting. This is particularly relevant since an API to manage application menus is one of the items that are part of the plan for the new Device APIs Working Group. (This was previously bundled under User Interaction API but that's a really big bag and not very meaningful. It was even discussed but we've had a lot of things on our plate.)

There are several ways in which site menus could be integrated into browsers: pure API, pure declarative, or a mix of both. There are also constraints in that it needs to be implementable in a device independent manner (you don't want to enforce the same menus with the same rendering everywhere); in fact it should be implementable in a completely different way by different browsers on the same device. When you enter native land you get to play by native rules. And of course, you want to be careful with bad things that could happen such as tricking the user into using a menu they think is from their system when it comes from the site.

The pure API approach is frankly quite inelegant — building user interfaces with just API calls is a pain in the arse and can be hard to scale. Additionally, if you really want an API for this, it's a lot easier to (in current browsers) to wrap an API around a declarative approach (i.e. manipulating the DOM) than it is to build a declarative approach to an API (since it requires listening to document mutations). Besides, there's a HTML menu that's right there for the taking and it would be a shame not to reuse it.

The menu element captures the general semantics of menus in a fairly straightforward fashion (see the spec). You can easily build context menus and toolbars with it. But it currently has no provision for application-level menus. How could these be added?

The most straightforward manner is to stick to the pure declarative approach. You add application to the list of supported types, define it to represent application-level menus, and let implementations worry about the rest (how to present them but also what to do when multiple such menus are defined in a single app). In fact, those of you seasoned enough to have enjoyed using the Mozilla browser (from back when there was no Firefox) might remember that it would generate its own application-level menus for <link rel=next> and other such niceties. It's not such a new idea.

An alternative, would be to have an API to promote an existing menu element to an application menu, à la navigator.addAppMenu(document.getElementById("your-menu"));.

My first instinct would be to go for the declarative option. It's sweetly simple to author, it has rather clear built-in semantics, and it seems pretty low impact. But things may not be so simple. First, you need to be able to feature-detect. A very typical usage scenario would be make this an application menu if you can, otherwise keep it visible in the page as a regular web application menu:

if (navigator.addAppMenu) {
    someMenu.style.display = "none";
    navigator.addAppMenu(someMenu);
}

I could certainly be missing something, but I don't see an obvious way of performing feature detection with the menu element as currently specified. I guess that implementations that accept the menu being added at the application level could automatically hide it but that seems like a kludge (and reusing hidden for this seems like a misuse of it). A better way could be to dispatch an event when a menu is accepted as an application menu — that's worth mulling over.

Second, the security model also needs to be figured out. As noted above, adding an application-level menu is a potential security threat, and a browser could be justified in refusing to do so even if it supports the feature. This does not necessarily mean that using application menus would require a security prompt (the web's favourite mood-killer) — it could for instance always accept them in special "application" or "pinned" tabs and always reject them in regular use. This points to your familiar asynchronous security entry point:

navigator.addAppMenu(someMenu, menuOK, menuRejected);
function menuOK () {
    someMenu.hidden = true;
}
function menuRejected () {
    // attach some fancy animation code to your menu
    // because it's staying in the page
}

Additionally, this latter approach would also work with the Feature Permissions API (initiated by the Web Notification people, now in DAP's lap) as well as with its weird cousin FRAC (currently not on anyone's lap, but feedback is very welcome).

At the end of the day I'm not sure which approach is best but I like the feature. As usual, thoughts and comments are welcome, either below, on Twitter, and of course on the DAP mailing list (which is open to all).

This article is part of a series on the Device APIs Working Group (DAP).