This informative Working Group Note defines a service discovery and light-weight RPC mechanism for web apps called Web Intents.

This document defines DOM interfaces and markup used by client and service pages to create, receive, and reply to Web Intents messages, and the procedures the User Agent carries out to facilitate that process.

This document was produced by the Web Intents Task Force, a joint activity of the Device APIs Working Group and the Web Applications Working Group. Members of these working groups have agreed not to progress the Web Intents specification further as a Recommendation track document, electing to publish it as an informative Working Group Note. The Working Groups have not performed interop testing on the material in this document. Implementers are cautioned that this material is subject to change and that an alternative design may be pursued in the future.

Changes since the previous version include clarifications, corrections, and updates to the document (diff).

The email address provided for feedback is that of the Task Force, where all feedback is welcome.

Introduction

Web Intents enable rich integration between web applications. Increasingly, services available on the web have a need to pass rich data back and forth as they do their jobs. Web Intents facilitate this interchange while maintaining the kind of loose coupling and open architecture that has proven so advantageous for the web. They exist purely client-side, mediated through the User Agent, allowing the user a great degree of control over the security and privacy of the exchanged data.

An Intent is a user-initiated action delegated to be performed by a service. It consists of an "action" string which tells the service what kind of activity the user expects to be performed (e.g. "share" or "edit"), a "type" string which specifies the data payload the service should expect, and the data payload itself.

The lifecycle of an Intent is that first, a client requests an Intent be handled. This Intent data is then passed to the User Agent, which allows the user to select which of potentially many possible services to use. Then the service is passed the Intent data and is provided a UI by the User Agent in which to perform the action specified in the Intent. Finally, the service may also return data as output back to the client.

Web Intents provides a declarative syntax that allows services to list the Intents they handle. Using this method, services mark up what actions they can handle and which data types they expect.

Example

Suppose there is a photo hosting application. This application allows a user to select images to be shared, edit those images, and then share them with friends. The application is built around making photos available to users, but has no built-in editor or sharing interface. But beside each photo, it can place an Edit button, with this kind of accompanying code:

      document.getElementById('edit-photo').addEventListener("click", function() {
        var intent = new Intent({"action":"http://webintents.org/edit",
                                 "type":"image/jpeg",
                                 "data":getImageDataBlob(...)});
        navigator.startActivity(intent, imageEdited);
      }, false);

      function imageEdited(data) {
        document.getElementById('image').src = data;
      }
      

This code delegates image editing to third-party applications which can consume images prepared as blobs, and produce results in the same format. For instance, one such editor might be a meme-maker—an application allowing the user to place humorous messages on pictures they take.

Now that a picture has been edited in the selected service, and meme text added, the user undoubtedly wants to share the result with friends. Again, the photo hosting application may not have built-in sharing capabilities, but by adding a Share button near images, and with this kind of accompanying code, it can accomplish this integration:

      document.getElementById('share-photo').addEventListener("click", function() {
        var intent = new Intent({"action":"http://webintents.org/share",
                                 "type":"text/uri-list",
                                 "data":getPublicURIForImage(...)});
        navigator.startActivity(intent);
      }, false);
      

This code delegates sharing functionality to an existing services chosen by the user which are capable of sharing urls. (getPublicURIForImage() is a marker for an application-specific piece of functionality getting the URL to be shared) So a social networking site selected by the user might produce a status update with a thumbnail of the image. A blogging site might provide a UI allowing the user to post the picture.

Note that with this integration, other more high-minded services can be selected by the user as well. Instead of using the service to add funny captions, the user might use a sophisticated photo editing application to adjust exposure, remove red-eye, or do any number of other transformations on the image. The user can have many such tools registered, and choose any of that set to use for any given image editing task. The photo hosting application isn't controlling which application the user chooses; it is loosely coupled with such applications by providing the data necessary for them to carry out their task, and controls allowing the user to launch these activities on the data.

In this way, an Intent is like the dual of a hyperlink. With a hyperlink, the source page specifies the exact URL to be navigated to. With an Intent, the source page specifies the nature of the task to be done, and the user can select any of a number of applications to be used to complete the task.

On the service side, the page needs to register itself as a Web Intents service, and handle the incoming intent data, possibly producing a response. That is done like this:

      <html>
      <head>
      <title>Image Meme Editor</title>
      </head>
      <body>
      <intent action="http://webintents.org/edit" type="text/uri-list image/*"></intent>
      <script>
        window.addEventListener("load", function() {
          if (window.intent) {
            setImageContentURI(window.intent.data);
          }
        }, false);

        document.getElementById('save-button').addEventListener("click", function() {
          window.intent.postResult(getImageDataURI(...));
        }, false);
      </script>
      

The assumed pieces here (on both client and service pages) are functions which deal with the application's image display, for instance putting the image into a canvas and taking it back out again as a data URI. Or producing the public URI of the image on the site.

Normative parts

The normative parts of this specification are the API Description and the User Agent Behavior section. All other parts are informative.

Terminology

An Intent is an action with accompanying data, which is to be performed by a Service of the user's choosing.

Actors

A web page which creates an Intent and invokes the User Agent with it is a Client. The User Agent may also allow non-web-page sources invoke Intents. For instance, the User Agent may have UI which invokes Intents delivery, or may register for external OS hooks which trigger Intents.

A web page which can handle an Intent is a Service, possibly returning a piece of data to the calling Client page. (Note that the User Agent may have ways to deliver Intents to Services which are not web pages as well. These may be extension APIs, plug-in helpers, external OS handlers, etc.)

Life cycle of Intents and Services

Registration is how a web page page informs the User Agent that it (or another same-origin Service page) is capable of handling Intents.

Invocation refers to the API by which a Client page dispatches an Intent for handling.

Selection is the mechanism in which the User Agent decides which Service will handle a particular Intent.

Delivery is the means by which the User Agent passes an Intent to a Service page for handling.

Response is the means in which the Service can respond to an Intent by passing data back through the User Agent to the Client page.

The steps in a particular Intent invocation are asynchronous. The Service receives the Intent delivery and prepares its Response in a separate execution context. That Response is then returned to the calling Client in an asynchronous callback.

API Description

Intent parameters dictionary

This object can be used in the object-literal constructor of the Intent object. When using the object literal constructor, the action and type fields are required; all others are optional.

DOMString action
An opaque string indicating the action type of the intent. The string MUST NOT be empty.
DOMString type
A string indicating the type of the data payload. The data payload MUST be described by this parameter, which MUST NOT be empty.
any? data
The data payload used MUST be an object upon which the structured clone algorithm can be performed, including Transferables.
sequence<Transferable>? transfer
The list of Transferables, for use in the structured clone algorithm.
URL? service
When present, this field marks the intent as an explicit intent. The value MUST be an absolute URL.
sequence<URL>? suggestions
When present, this field provides a list of suggested Service URLs, each of which is an absolute URL that the Client is aware of and which can handle the intent.

Intent object

The Intent object models a particular task which can be requested to be handled by Services. A Client page may invoke multiple Intents simultaneously. Specific Intent objects are immutable once created.

readonly attribute DOMString action
This is an opaque string. Chosen strings SHOULD be namespaced by a URL namespace convention. The string MUST NOT be empty, or the constructor MUST throw an exception.
readonly attribute DOMString type
The data payload MUST be described by the type parameter. Recommended type strings are MIME strings or self-documenting urls. The string MUST NOT be empty, or the constructor MUST throw an exception.
readonly attribute any data
The object used MUST be an object upon which the structured clone algorithm can be performed, including Transferables, or the constructor MUST throw an exception.
readonly attribute MessagePort[] ports
Only present when the Intent object is delivered to the Service page. Any ports used in the transferList of the constructor during invocation will be delivered to the service page in the ports attribute. See [[!POSTMSG]]
void postResult(any data, optional sequence<Transferable> transferable)
Only present when the Intent object is delivered to the Service page. The payload passed to this method will be returned to the onSuccess callback registered by the client page in the startActivity call (if any). The payload must be an object upon which the structured clone algorithm can be performed. The Transferables array, if present, will specify transferable elements in the data payload argument, as per [[!HTML5]] [[!POSTMSG]]
void postFailure(any data)
Only present when the Intent object is delivered to the Service page. The payload passed to this method will be returned to the onFailure callback registered by the client page in the startActivity call (if any). The playload must be an object upon which the structured clone algorithm can be performed, including Transferables. [[!HTML5]]

Notes on the intent object: The transferList parameter is not available post-creation. It becomes an internal implementation detail directing the User Agent to interpret some fields of the data argument as Transferable. The data and transferList arguments MUST be implemented following the W3C Web Messaging spec [[!POSTMSG]].

The User Agent MUST perform the structured clone algorithm on creation of the Intent object.

Invocation API

The client invokes an intent by constructing an Intent object as above and calling the navigator.startActivity function on it. The callbacks passed to this method are invoked when the intent has been handled by the service. The User Agent will mediate the Service selection by enumerating the list of registered Services that match the requested Intent action and type. The user is then able to select which Service should handle the Intent.

void startActivity (Intent intent, optional IntentSuccessCallback onSuccess, optional IntentFailureCallback onFailure)
Called to invoke an intent Service. The Intent object is described above. The onSuccess handler, if any, will be called by the user agent if the service is dispatched, processes the intent, and calls postResult on the Intent object it receives. The handler will be invoked with one parameter: the data received from the service. The onFailure handler, if any, will be called by the user agent if the service cannot be dispatched, if the user aborts the selection process, or if a service was chosen, received the intent, processes the intent, and calls postFailure on the Intent object it receives. The handler will be invoked with one parameter: the data received from the service. User Agents SHOULD restrict this method to only be successfully callable within the context of an explicit user gesture. An exception SHOULD be thrown if the Intent is invalid (i.e. null), or if the method is invoked without being the result of an explicit user gesture.

The invocation API is implemented by the window.navigator object.

The callbacks passed to startActivity MUST provide these signatures:

optional any data
The data passed will be the payload data from the structured cloneable object passed to the postResult method of the delivered Intent.
optional MessagePort[] ports
The ports will be any ports in the payload, as resulting from the structured clone algorithm with Transferables, as in the Web Messaging spec. [[!POSTMSG]]
optional any data
The data passed will be the payload data passed to the postFailure method of the delivered Intent.

Delivery and Response API

When the User Agent loads a Service page to handle an Intent invocation, it will place a window.intent object in the scope of the page.

readonly attribute Intent intent
The intent object as delivered to the service page (includes postResult and postFailure methods).

This object will only be made available to Service pages when they are loaded in the context of an intent invocation. Even if the same URL is loaded in other situations, the User Agent MUST NOT make Window implement IntentProvider. Similarly, if a Service page is registered, but the User Agent loads it and it does not contain the declarative markup marking it as expecting to handle the Intent the User Agent is delivering, the User Agent MUST NOT make Window implement IntentProvider.

The window.intent object MUST be made available across same-origin redirects of the service page. It MUST NOT be made available if redirects cross an origin boundary.

So the following redirect sequence will work: http://example.com/service to http://example.com/login back to http://example.com/service. In this case, the window.intent data would be made available to all three page loads.

This will also work: http://example.com/service to http://example.com/newservice. In this sequence, the window.intent data is available to both pages.

In this sequence, the window.intent is only available to example.com pages: http://example.com/service to http://login.example.com and back to http://example.com/service. The intent data is not provided to http://login.example.com

The window.intent object MUST be made available to a page on the same-origin as the originally-delivered page if the user initiates a navigation on the browsing context in which that Service page is displayed, AND the new page has declarative syntax marking it as a handler for the Intent type the User Agent is delivering.

In other words, in the browsing context in which the Intent is originally delivered, the intent data MUST be made available to pages in a redirect or navigation sequence when they are in the same origin as that to which it was originally delivered (and have the enabling markup), but MUST NOT be made available to any other pages. This allows Service pages to delegate handling, to redirect to login screens, and so forth, but does not make the Intent data available to any such pages encountered which are not in the same origin.

Multiple invocation by code in the service page MUST throw a Javascript exception. The User Agent MUST NOT allow a particular Intent to be replied to multiple times.

Registration Markup

Web pages declaratively mark themselves (or other same-origin Service pages) as providing handling functionality for particular intent actions and types using the <intent> tag. A User Agent MUST NOT deliver an Intent to a web app service page which does not include markup describing what intents it can handle which matches the intent being delivered (See the section on Matching action and type for delivery).

attribute DOMString action
An opaque string indicating the behavior class the service supports. It is recommended that users use a fully qualified URI. If the action attribute is missing, the intent Service is assumed to handle display of the provided type(s) in the type attributes.
attribute DOMString type
A string specifying the type of payload data the service can accept. Must be a space separated list of type specifiers. If these parse as MIME types, or the MIME wildcard types "*/*" or "*", they will be interpreted as such (see the section on matching action and type for delivery). If not, they will be interpreted as string literal type specifiers. Empty type attributes are not valid registrations.
attribute DOMString href
Should be a fully qualified URI. If the href attribute is missing, the service URI will be set to the URI in which the tag is found.
attribute DOMString title
A human-readable title which the User Agent SHOULD use to indicate the service to the user. If the title attribute is missing, the User Agent SHOULD use the title of the registered Service page as the service title.
attribute DOMString disposition
The disposition attribute allows a service to choose which context to be opened in. The User Agent MUST NOT allow the Client page any ability to change the disposition. The window disposition means that the service is opened in a new tab or window context. The inline disposition means that the User Agent SHOULD open the service in a context directly related to the client page context in an overlappable way. The User Agent MUST NOT allow this UI surface to be under the control of the client page.

This section should not be construed as limiting the ability of the User Agent to manage registration of non-web services. That is, the User Agent may handle intent invocations locally, using extensions, or with third-party system services. This section solely governs how web-app service pages register and unregister with the User Agent with the <intent> tag.

Same-origin registration

The User Agent MUST NOT obey any registration markup which is not same-origin. That is, a page may only register href attributes for other service handlers on its same origin. A page may register itself as a Service handler, by leaving the href attribute empty.

For cross-page registration, when the href attribute points to a different resource, the User Agent SHOULD interpret intent registrations additively. That is, an intent descriptor from the tag would be added to the registry if it is not present (if the user approves the addition).

Unregistering

The User Agent SHOULD treat any registration markup which has both action and type attributes missing as if the markup specified no intent capability on the href page. That is, a page may unregister itself implicitly by removing all intent tags, or explicitly by keeping the tag present, but without action or type attributes. Such explicit unregistration SHOULD be honored for any tag with a same-origin href attribute.

The intent tags on the service handler page itself SHOULD be interpreted by the User Agent as authoritative. That is, if the User Agent loads a service page, and sees that its intent descriptors do not match the set currently registered for that page, it would replace the currently-registered set with the set discovered on the more current load of the page.

HTTP Status Codes on Service Pages

If a registered service page is retrieved and has a non-20x error code [[!HTTP11]], the User Agent SHOULD take the following actions:

30x: redirect to the indicated page. If the page is on-domain, and has intent markup, the intent will be made available to the redirect page. If the redirect is permanent (301), the User Agent MAY update its service registration records to reflect that.

4xx: indicate to the user that the page cannot be loaded, along with any remediation capabilities (i.e. authentication for 403 or payments for 402). In the case of error 410, the User Agent SHOULD unregister the handler from its internal registry.

50x: indicate to the user that the page cannot be loaded.

If the desired registered service page cannot be loaded, the User Agent SHOULD display UI allowing the user to select another service.

User Agent Behavior

Service Registration

When the User Agent loads a page with registration markup, it SHOULD allow the user to configure that page as a web intents service. The details of this process is left up to the User Agent. The model is that the page advises of the ability to handle intents, and the User Agent may remember that. The User Agent MUST NOT deliver an intent to a Service discovered in this way before the user has made a specific action allowing it.

The User Agent MUST NOT allow web pages the ability to discover passively which services the user has configured to handle particular intents, or any intents, whether by enumeration or exact query. There may be mechanisms for the user to actively grant this information to web pages, but it MUST NOT be made available passively.

The User Agent MAY provide additional mechanisms for web intents service registration. For example, by external applications, through a separate API, as a result of a permissions bundle in a downloaded web application, or pre-bundled. (TODO: add example of a local network service)

The User Agent MAY act as a Client or a Service. For example, the User Agent may implement particular affordances which directly launch Intents that may be handled by registered Services, or present UI allowing its own functionality to be used alongside other registered Services to handle Intents. For instance, User Agents MAY also dispatch intents directly based on data-specific controls derived from microdata in pages, or based on other User Agent-level features.

Invocation and Dispatch

For intents invoked by client web applications, the User Agent MUST require that such invocations be in the context of a user gesture. User Agents MAY also dispatch intents invoked through other mechanisms. For example, hardware events (i.e. plugging in a USB storage device) or software events (i.e. downloading a file).

When a client page invokes an intent, the User Agent dispatches it to a chosen service. The details of this process are left up to the User Agent. The User Agent may dispatch intents to web application service pages, helper applications, proxy them through connections to other hardware, etc. In general, though, the User Agent MUST provide a way for the user to configure which intents are delivered to which services. This process SHOULD be configurable on a per-invocation basis. Defaulting rules, as long as they are configurable by the user, are expected to result in the User Agent not presenting full UI controls on every invocation.

When the User Agent delivers an intent payload to a web application, it MUST make the window.intent object available as the document is loaded and parsed, so that scripts on the page may process the intent data as they load. User agents MUST NOT make available a window.intent object in the scope of pages which do not have registration metadata declaring themselves as intent handlers. This means that any use of window.intent in pages which do not explicitly declare themselves as web intents handlers MUST NOT be overwritten by the User Agent. It also means that Service pages cannot access window.intent in scripts which occur before the page parses the intents registration markup. If such scripts are simply declaring functions to be called later, that will work, but scripts which run before the registration markup is parsed won't find window.intent made available, even though it may be made available later in the page load, and so are likely broken.

When a new context is opened for the service page, the User Agent MUST connect the postResult and postFailure methods of the window.intent object so that they return their serializable payloads to the registered handlers the User Agent received in the invoking navigator.startActivity call. If the user closes the service page before it has responded, the User Agent MUST invoke the onFailure callback in the client page invocation, if any. If the user cancels a service selection UI control the User Agent displays in the course of dispatching an intent, the User Agent MUST invoke the onFailure callback in the client page invocation, if any.

The User Agent SHOULD allow any serializable and/or Transferable object to be passed between client to service and back from service to client. This includes Blobs [[!BLOB]], MessagePorts, etc. The User Agent MAY inspect the payload of intents and present specialized UI corresponding to well-known intent types. As an example, the User Agent may present specialized messaging to the user indicating that an intent is of the "share" type, or that the Client is asking them to pick an image.

If the user has no services registered for a particular type of intent, the User Agent MAY display options from other sources of data about services it knows can handle that intent type so that the user can complete the activity.

The User Agent MUST NOT categorically prohibit dispatch of unknown intent types. This is not meant to prohibit the User Agent from performing filtering functions on intents, such as suppressing unwanted intent invocations, intents as used as an attack vector, and other mis-use.

Explicit Intents

When handling an Intent marked as explicit (that is, constructed with the object literal constructor with a non-empty service field), the expected User Agent behavior is that if this "service" attribute is present, it SHOULD NOT display a service selection mechanism to the user. Instead, the service url SHOULD be loaded directly to handle the intent. (This is not a hard restriction. The User Agent MAY provide a way for the user to intercept even an explicit invocation.)

During delivery, all restrictions are still in place. That is, the User Agent MUST follow the above requirements on placing the window.intent object in the scope of the page, and respect any declarative metadata in that service page. If the user agent needs information about the service (i.e. disposition, title, etc) for use in preparing its UI, it MAY load the service URL and examine the page for any declarative metadata.

The User Agent MAY ask the user if they wish to install this service, just like for any other visit of the page.

Matching action and type for delivery

When an Intent is delivered, the User Agent must verify that the Service to which the intent is to be delivered (either from an explicit invocation or from the result of a user selection) has a registration record supporting the action and type of the Intent being delivered by following these steps:

  1. Let intent action be the action field of the invoked intent.
  2. Let intent type be the type field of the invoked intent.
  3. For every distinguishable registration (that is, having unique action/type fields) present for the Service page, follow these steps:
    1. When comparing field values, "different from" means that the strings under comparison represent different sequences of code points.
    2. When considering whether a field value is a MIME type, it is considered so if it parses as a valid MIME type [[!RFC2046]], or is equal to the MIME wildcard strings "*" or "*/*".
    3. Let service action be the value of the action field of the service registration.
    4. Let service type be the value of the type field of the service registration.
    5. If the service type or service action is empty, the registration record is invalid. Continue to the next registration record.
    6. If the intent action is different from the service action, continue to the next registration record.
    7. If the intent type is a MIME type, and the service type is not, continue to the next registration record.
    8. If the service type is a MIME type, and the intent type is not, continue to the next registration record.
    9. If both service type and intent type are MIME types, then check whether the MIME specifiers overlap. This is true if the top-level and sublevel types match exactly, or if one or both are represented by the MIME wild card ("*"). If any MIME parameters are present in the service type or the intent type, they must be present in both and match exactly. A MIME parameter present in only one of service type and intent type does not disqualify the match. If the MIME types do not overlap, continue to the next registration record.
    10. If neither service type nor intent type are MIME types, then if they are different, continue to the next registration record.
  4. If no satisfying match is found in the registrations of the Service page, the Intent MUST NOT be delivered. If any satisfying match is found, the Intent MUST be delivered to the Service page.

Handling Service suggestions from Intent Invocation

If the user has no persistent information about a qualifying service for a particular intent registered with the User Agent, the User Agent SHOULD present the user with the option to select from the default services proposed by the client at invocation (from the suggestions parameter).

If the user agent needs information about the all or any of the suggested services (i.e. disposition, title, icon, etc) for use in preparing its UI, it MAY load the suggested default service URL(s) and examine the page(s) for the <intent> tag, reading off such information, or load the favicon for the site(s). The User Agent MAY ask the user if they wish to install all or any of the suggested services, just as for any other visit of those pages.

The User Agent MUST follow the matching algorithm of the "Matching action and type for delivery" section before delivering the Intent to a suggested service, just as for any Intent delivery.

Use Cases and Requirements

Sharing

Web Intents should be useful to enable users to share content as they encounter it on web pages. Whether by implicit controls made available by the User Agent or explicit controls placed in web pages, Web Intents should handle the use case of a user wishing to share a page as a whole, or particular content on the page. The user should be able to select the sharing application of choice to accomplish this task.

Integration with local web apps

Local web apps should be able to invoke and handle intents.

Persistent connections

Web Intents invocations are modeled on RPC, but there are times when a persistent connection is desired. There are a few different methods that Web Intents should support for this. One is returning URIs which can be loaded by clients in an iframe and then messaged using other web platform features. Another is returning a defaulting token which the client can then use to target other intents directly at a known target. A third is returning a MessagePort to the client which can be used for subsequent communication.

In these cases, Web Intents is acting as a way for the user to attach a particular client page to a persistently-available service. It is up to the particular types of intents to describe exactly how this should work. i.e. whether the connection is permanent or temporary, whether tokens are user-consumable or opaque.

Integration with external applications

It should be possible for intents to be routed to external helper applications. For instance, a locally available photo editing tool could be configured to interact with the browser to handle an image edit intent. Or the browser could discover home networking equipment on the local network and make it available for particular types of intents.

Translating existing web platform features to intents

It should be possible to translate a few existing features to use Web Intents, thus putting web applications on the same footing as local resources. For instance, it should be possible for the User Agent to translate file selection controls to intents such that the user can choose to upload a file from a cloud storage locker as well as from local disk. In these cases, the User Agent may supply built-in intent handlers corresponding to existing functionality.

Another use case is allowing web applications to function as plug-ins. For example, a link returning a resource of a type which the user agent doesn't know how to display can be translated into an intent which allows the user to configure a web application capable of reading that resource type and viewing it. This would let web apps function as plug-ins.

Authentication

It is expected that many services for Web Intents will be ones the user has an account with. The service should be able to use standard login mechanisms in the context the User Agent places them within to perform authentication. That is, the service page handling an intent should be loaded with the same cookie jar and access to localStorage, etc. with which it runs in a normal browsing context. Intent data should be persisted by the User Agent across login redirects.

Privacy Considerations

The user needs to have confidence that the Service will use the data associated with the action for the purpose intended and not share or retain the data inappropriately [[WEBAPP-PRIVACY-BESTPRACTICES]]. For this reason it is important that the user have control over Intents, in particular the selection mechanism which determines which Service will handle a particular Intent. This offers the possibility of user decision and control related to the choice of Service, allowing the user to take into account expectations regarding the Service, including Service policies related to retention and secondary use. This relates to the privacy principles of control and consent [[DAP-PRIVACY-REQS]]. For this reason a user should be made aware of explicit intents and be able to view and change them; implementations should be encouraged to offer this functionality. The minimum data necessary for a Service should be included as Intent parameters, corresponding to the privacy principle of minimization [[DAP-PRIVACY-REQS]].

Acknowledgements

Many thanks to Robin Berjon for making our lives so much easier with his cool tool.

See also webintents.org for more examples and a sample Javascript implementation of Web Intents.