This specification defines a mechanism for Web applications to register themselves as being able to handle certain specific types of services ("grant wishes") with a user agent. Once a service handler is registered, any Web application can then request its functionality from the user agent in order to compose the service into itself.

This specification is a proposed extension specification to HTML and has no official standing whatsoever.

Introduction

This specification defines a mechanism for Web applications to register themselves as being able to handle certain specific types of services ("grant wishes") with a user agent. Once a service handler is registered, any Web application can then request its functionality from the user agent in order to compose the service into itself.

For instance, a Web application could register itself as an image editor. When another application needs image editing functionality (e.g. a meme generator) then it can instantiate the image editor as part of itself (typically in an iframe). The editor is selected based on user preference rather than by hardcoding it. This provides a simple yet powerful component model for Web applications.

This functionality is similar to that provided in Android Intents or in FirefoxOS Activities. However, in order to ship more quickly and to explore the simpler options first this specification is limited to a subset of the functionality afforded in existing systems.

Usage Scenarios

This section details a variety of usage scenarios for Wishes in order to help define how exactly they should work.

Note that all code examples are provisional and use completely made up syntax. The scenarios rely on the following loose terminology (to be tightened up when the view is clearer).

application
A regular Web application running in the user's browser, and typically calling upon wishes in the following scenarios.
wish service
A Web page written in such a way that it knows how to grant a wish and that exposes functionality to the application in that way.

Fire & Forget

This is the simplest case. Here the application formulates a wish but does not care about any return value, or even about the success, of the wish.

A typical example is the wish to share. The user is visiting an application page, finds it most cromulous, and wishes to share it with the world at large.

User clicks the “share” button in order to share this most cromulous piece of humour.

Creating a share button is a simple matter of attaching a handler to it and formulating a wish.

          <button id='share'>😻 Share this!</button>
          <!-- … -->
          <script>
            document.querySelector("#share").onclick = function () {
                new Wish("share", "text/plain", location.href).make();
            };
          </script>
        

The user is then presented with a list of wish services that she has installed in her user agent (or, possibly, more generally on her operating system). Of these she selects the “Unicorner” social sharing site where she can share with her fellow unicorns.

User selects “Unicorner” in the list of services that can grant the wish to share.

The wish service picker requires no work from the developer and is entirely handled by the user agent. The list matches wish services that the user agent knows can fulfil this wish (this can comprise those installed by the user, but can also come from other sources such as system application, a specialised search engine, etc.). Installing a wish into a user agent is covered in a later section.

The “Unicorner” web site, which is a social sharing web site that the user happens to like (here showing how Wishes can help open up the Web to new contenders without having to battle the inertia of millions of authors having to add new buttons to their pages), opens up atop the application being shared (with some sort of signal that the wish service is related to the application — all of this is naturally implementation-dependent). The user can interact with it and hit “Share”.

Unicorner's next generation sharing UX.

There are two essential parts to the wish service's handling of the wish. The first part is being notified of the wish, including of the wish's payload and acting accordingly in order to grant it. The code for this is straightforward.

          <textarea id='content'></textarea>
          <button id='unishare'>Share!</button>
          <!-- … -->
          <script>
            window.onload = function () {
                if (currentWish)
                    document.querySelector("#content").value = currentWish.data;
            };
            document.querySelector("#unishare").onclick = function () {
                // …code to actually post the message to the site…
                if (currentWish) currentWish.grant();
            };
          </script>
        

Note that the code above still needs to call grant() on the wish once the action is completed, even though it is returning no data to the application. This tells the user agent that the wish is granted and it can use that notification for instance to close the tab containing the wish service.

Return Value

While “fire & forget” wishes definitely have their uses, it is common for applications to expect data to be returned from the interaction with a wish service.

In this scenario we consider the following workflow: a user wishes to create a hilarious meme, and goes to a meme publishing web site for that purpose. The meme publishing web site first asks the user to pick an image to serve as the starting point for the meme. That operation relies on <input type=file> in order to show how wishes can integrate with that part of the platform. Then it will offer to edit the image, using a wish to edit.

In both cases, data is returned from the wish service, and in the second case data is also received by it.

The application's UI is simple:

MemeCat — The World's Laziest Meme Maker

Clicking the first “Pick a picture” step hits an <input type=file> control, which in fact just formulates the wish to pick a file. To give a better idea for how this works, here is how you would get a list of files from a file input type.

          <input type="file" accept="image/*" multiple id="uploads">
          <script>
            document.querySelector("#uploads").onchange = function (ev) {
                handleFiles(Array.prototype.slice.call(ev.target.files));
            };
          </script>
        

And here is the equivalent code using a wish:

          <button id="uploads">Upload</button>
          <script>
            document.querySelector("#uploads").onclick = function () {
                new Wish("pick", "image/*", { multiple: true }).then(handleFiles);
            };
          </script>
        

These come out at the same amount of code. With the wish we need to handle clicking ourselves which the file input type provides free, but if you ever wish to style your file input control then that's a plus because you will need this sort of indirection anyway. A positive side effect of the wish side is that you don't need no stinking FileList.

It is important that the file input type and the wish to pick produce the same user experience since to the user, who neither knows nor cares for the page source, they are the same operation.

For the code examples we use the multiple attribute in order to demonstrate its mapping, but the MemeCat application naturally only wants a single image.

Irrespective of how MemeCat chose to implement its file picking, the user would be presented with the familiar wish service picker:

The system file picker, as depicted if you let a standards geek create icons.

A wish service that knows how to grant a pick (of any kind) will return either a File or an array thereof depending on the value of the multiple field. Such a service could be implemented using code of the kind that follows:

          <div id="pics">
            <-- a list of images that can be clicked to select them -->
          </div>
          <script>
            document.querySelector("#pics").onclick = function (ev) {
                if (ev.target.localName.toLowerCase() !== "img") return;
                if (!currentWish) return;
                var xhr = new XMLHttpRequest();
                xhr.open("GET", ev.target.src);
                xhr.responseType = "blob";
                xhr.onload = function () {
                    var file = new File(xhr.response
                                      , ev.target.src.replace(/^.*\//, "")
                                      , { type: xhr.getResponseHeader("Content-Type") }
                    );
                    currentWish.grant(currentWish.data.multiple ? [file] : file);
                };
                xhr.send();
            };
          </script>
        

Once the MemeCat has obtained a picture file, editing it is no harder.

          <button id="edit">Edit</button>
          <script>
            document.querySelector("#edit").onclick = function () {
                new Wish("edit", file.type, { content: file }).then(handleMeme);
            };
          </script>
        

It is simply a wish to edit the given file MIME type, operating on the provided content. Once done the edited content is sent back using the same mechanism as for the file picker. It is worth noting here that it is entirely possible for this wish to be granted by a native application (so long as the user agent knows how to interact with the platform's conventions that support such communication).

Sharing can then be supported just as it is in the first section.

Explicit Wish

It is conceivable that there are cases in which an application may wish to operate with just one explicitly well-defined wish service, making use of the wish conventions for communication (likely because they are already supported by a given service) but without giving the user the opportunity to choose the service (presumably because it may result in a predictably poor decision).

To take the MemeCat example again, it is likely that the image source should be open-ended, but the service might wish to enforce a given editor service that consistently enforces Web meme conventions relying on the Impact font, specific stroke and fill, etc. where a general-purpose image editor might let the user overlay her cat picture with, say, Comic Sans text — clearly a bad thing.

Making an explicit wish to edit can be done as follows:

          <button id="edit">Edit</button>
          <script>
            document.querySelector("#edit").onclick = function () {
                new Wish("edit", file.type, { content: file })
                      .explicit("http://cool-editor.meme")
                      .then(handleMeme);
            };
          </script>
        

It is unclear at this point whether explicit memes are required in an initial instalment of this technology.

Installing a Wish

So far we have looked at scenarios involving formulating wishes from the application's point of view, as well as how a given wish service might reply. However in order for a service to grant a wish, the user agent needs to know about it.

There are many ways in which a user agent can become aware of wishes that it can offer as options to the user when needed. Here is a non-exhaustive list:

Explicit installation
The user may explicitly wish to “install” a given application as part of a list of wishes. This may happen if the Web application itself is being installed (something which can happen in some contexts), or as a form of bookmarking specific to wishes. When visiting a site offering a wish service, the user agent may provide an affordance for the user to specifically perform this action. Alternatively, it could be made possible for Web applications to give users a way of installing them (instead of relying on UA UI, which Web developers are often hesitant to.)
Remembered navigation
As the user navigates across the Web, her browser will come across pages that are declaring wishes. While having wish picker dialogs immediately show all such wishes would be a bad idea in that it would encourage spamming of that dialog (and even without malicious intent would likely cause it to be overcrowded), it can nevertheless be a good idea for browsers to remember that information so as to be able to offer those sites as options, notably in case no other known service can grant a given wish.
Wish Search
The wish picker could expose a search affordance that would make it possible to find wishes across the Web using a dedicated search engine.
Built-in or default wishes
User agents would likely ship with a number of wish services that they would grant themselves (e.g. the wish to pick a file for instance). Likewise, they could have a list of pre-configured services for various wishes.
Platform wishes
The browser can query the platform it is running on to find out if there are local applications that could grant a given wish.

A wish is in effect nothing more than a Web application that can be interfaced with programmatically following a specific interface. A wish service simply needs to express a little bit of metadata so that it can be recognised by user agents, crawlers, etc., but that metadata is very similar to that which can be used to describe a Web application.

Because of that, wish services can declare themselves using additional information from web apps manifest [[!appmanifest]]. Manifests can be linked in the usual manner:

          <link rel="manifest" href="memecat.json">
        

This entails that any process exposed by the browser to install the wish would work through installing the application. If there are cases in which one desires to solely make the wish “installable” then a different syntax (but using the exact same JSON) can be used:

          <link rel="wish" href="memecat.json">
        

The JSON itself is an extension to the basic web apps manifest format.

          {
            "name": "Unicorner"
          , "icons": [{
                        "src":    "icons/lowres.webp"
                      , "sizes":  "64x64"
                      , "type":   "image/webp"
                      }
                    , {
                        "src":    "icons/hd.png"
                      , "sizes":  "128x128"
                      }]
          , "start_url": "/index.html"
          , "wishes": [{
                        "name":       "Edit"
                      , "start_url":  "/wish/edit.html"
                      , "action":     "edit"
                      , "types":      ["image/*"]
                      , "icons": [{
                                    "src":    "icons/edit.webp"
                                  , "sizes":  "64x64"
                                  }]
                      }
                    , {
                        "name":       "Share"
                      , "start_url":  "/wish/share.html"
                      , "action":     "share"
                      , "types":      ["text/plain"]
                      }
                    , {
                        "name":       "Pick Media"
                      , "start_url":  "/wish/pick.html"
                      , "action":     "pick"
                      , "types":      ["image/*", "video/*", "audio/*"]
                      }]
          }
        

Authors traditionally do not like to have to rely on UA affordances (because it means they can't emphasise it if needed, also when explaining to users one needs to document for each browser, stay up to date, etc.). If installing pages (in a sense specific to the Web) becomes more mainstream, it would likely be welcome to support installation from actions carried out inside of the viewport. Such a mechanism is not, however, catered to at this point.

The Wish interface

Is it really useful to distinguish between fire&forget and return value wish modalities through different methods? Maybe instead of once()/then() it would be clearer to have it be send()/ask(). That depends on whether it makes sense to enable Wishes to behave like Promises in some cases.

Future Scenarios

A number of different scenarios were considered for this functionality, but only a subset thereof were kept in scope for this version. Those that were excluded, but that may return in future, are captured below.

Full Duplex Communication

Once a wish service is instantiated, in theory there is no reason why it could not remain permanently open and remain open to continuous, full-duplex communication with the application. The use cases for this are however more tenuous than for the simpler interactions, and more importantly the implementation is more difficult to convey in the user interface. Because of this, at this point it is expected that this scenario will not be covered by this version of the specification. A quick discussion of the scenario can nevertheless prove useful.

One common application of full duplex wishes is patching together application components that today are usually shipped as a single bundle. Imagine for instance that you two web applications. One is a code management system which allows you to track progress, commits, etc. on a project you are working on. Another is an IRC client which you use to communicate with your team. Both already have your login information, settings, etc. that should not need to be shared with anyone, even through tokens (with insufficient granularity).

You can get wish services for these two talking together with the following application.

          <button id="chat">Select chat to publish to</button>
          <button id="vcs">Select project to subscribe to</button>
          <script>
            var chat;
            document.querySelector("#chat").onclick = function () {
                chat = new Wish("publish", "text/simple-chat").port();
            };
            document.querySelector("#vcs").onclick = function () {
                var vcs = new Wish("subscribe", "application/vcs-notify+json").port();
                vcs.onmessage = function (ev) {
                    if (!chat) return;
                    if (ev.data.type !== "commit") return;
                    chat.postMessage("Commit: [" + ev.data.author + "] " + ev.data.msg + "\n", "*");
                };
            };
          </script>
        

The above is, clearly, a simplification, done in order to give the right idea. But as such it could well enough operate.

It should be noted that because wishes match on both action (the verb) and on payload type they likely encourage a relative proliferation of MIME type minting, as seen above. That is not necessarily a bad thing. Whether a specific format should be encourage (typically, MIME types) or whether we should just match any string (with prefix matching when terminated with a *) is an open question.

Embedded Wish

While user agents are free to implement a user interface for wish services in any way they see fit, some constraints (notably of relying on trusted chrome) are such that we can expect typical implementations to render any wish service in a clearly separated context.

This approach is clearly sufficient for many interactions, as can be readily seen in mobile applications that make use of wish-like functionality.

It is, however, possible to conceive of cases involving more advanced applicative composition for which it would be desirable to be able to embed multiple wish services inside a given application, in order to produce a more useful whole.

An example of such a scenario could be for a mail user agent. It is a truth universally acknowledged that MUAs are terrible to point of obeying principles of Churchillian democracy: whichever one you choose is the worst form of MUA, except all the others that you have tried. Part of the problem likely stems from the fact that a single MUA needs to offer multiple different views on email (list of mailboxes, list of emails, emails, etc.) all of which can be done in many different ways, and users do not fit into a simple set of profiles that enjoy specific configurations: they will have preference combinations all over the place. The combinatorics of the situation are such that the likelihood for any given MUA vendor to produce a combination that matches your particular needs are negligible enough that we can consider the task impossible.

This problem can be solved by making each component separate, and making it easy for users to compose their own client from multiple sources, possibly replacing components when they find better ones. We could therefore envision a world in which one's MUA is a relatively dumb application that does nothing more than expose the data and lay out replaceable embedded wishes for all the parts that make up a MUA's UI.

Such a universe of wonder is, however, far enough out of reach that wish embedding is not at this point a driving consideration here.

Acknowledgements

The following people have contributed to the elaboration of this standard.

Of particular note, the original Web Intents people: Greg Billock, James Hawkins, and Paul Kinlan. Many thanks for your patience, your dedication when surrounded by mockery, and being fun to work with.

Mathias Bynens, Karl Dubost, Dominique Hazaël-Massieux, Mounir Lamouri, Jonas Sicking.