This specification defines an API that allows websites to declare themselves as web share targets, which can receive shared content from either the Web Share API, or system events (e.g., shares from native apps).
This is a similar mechanism to navigator.registerProtocolHandler
,
in that it works by registering the website with the user agent, to
later be invoked from another site or native application via the
user agent (possibly at the discretion of the user). The difference is
that registerProtocolHandler
registers the handler via a programmatic API, whereas a Web Share
Target is declared in the Web App
Manifest, to be registered at a time of the user agent or user's
choosing.
This is an early draft of the Web Share Target spec.
In order to implement this API, it is REQUIRED that the user agent implements Web App Manifest. This spec also re-uses some definitions from the Web Share API spec. Implementation of that spec is NOT REQUIRED to implement this one (but it is RECOMMENDED).
To register a site as a share target, a share_target entry is added to the Web App Manifest, as shown:
{ "name": "Includinator", "share_target": { "action": "share.html", "params": { "title": "name", "text": "description", "url": "link" } } }
The params keys correspond to the key names in ShareData from [[!WebShare]], while the values are arbitrary names that will be used as query parameters when the target is launched.
When a share takes place, if the user selects this share target, the
user agent opens a new browsing context at the action
URL,
with query parameter values containing the shared data, just like an
HTML form submission.
For the purpose of this example, we assume the manifest is located at
https://example.org/includinator/manifest.webmanifest
.
<html> <head> <link rel="manifest" href="manifest.webmanifest"> </head> <body> <script> window.addEventListener('load', () => { var parsedUrl = new URL(window.location); console.log('Title shared: ' + parsedUrl.searchParams.get('name')); console.log('Text shared: ' + parsedUrl.searchParams.get('description')); console.log('URL shared: ' + parsedUrl.searchParams.get('link')); }); </script> </body> </html>
If an incoming share contains the title "My News" and the URL
http://example.com/news
, the user agent will open a new
window or tab and navigate to:
https://example.org/includinator/share.html?name=My+News&link=http%3A%2F%2Fexample.com%2Fnews
U+0020 (SPACE) characters are encoded as "+
", due to the
use of application/x-www-form-urlencoded
encoding, not "%20
" as might be expected. Processors must
take care to decode U+002B (+) characters as U+0020 (SPACE), which some
URL decoding libraries, including ECMAScript's decodeURIComponent
function, may not do automatically.
The query parameters are populated with information from the
ShareData
being
shared. If the ShareData contains no information for a given member,
the query parameter is omitted.
A share target might only be interested in a subset of the
ShareData members. This
example also shows a share target that receives data as a
POST
request, which should be the case if the request
causes an immediate side effect.
{ "name": "Bookmark", "share_target": { "action": "/bookmark", "method": "POST", "enctype": "multipart/form-data", "params": { "url": "link" } } }
The shared information might be read by a service worker, rather than being sent over the network to the server.
self.addEventListener('fetch', event => { if (event.request.method !== 'POST') { event.respondWith(fetch(event.request)); return; } event.respondWith((async () => { const formData = await event.request.formData(); const link = formData.get('link') || ''; saveBookmark(link); return new Response('Bookmark saved: ' + link); })()); });
If files are accepted by the share target, the method is set to 'POST' and a files entry is added to params, as shown:
{ "name": "Aggregator", "share_target": { "action": "/cgi-bin/aggregate", "method": "POST", "params": { "title": "name", "text": "description", "url": "link", "files": [ { "name": "records", "accept": "text/csv" }, { "name": "graphs", "accept": "image/svg+xml" } ] } } }
The target will be invoked with a multipart/form-data POST request, with field names as specified in params. Each shared file is assigned to the first files entry that accepts its MIME type.
How the handler deals with the shared data is at the handler's discretion, and will generally depend on the type of app. Here are some suggestions:
title
as the subject of an email, with text
and url
concatenated together as the body, and the files
as
attachments.
title
, using text
as the body of the message
and adding url
as a link. If text
is missing,
it might use url
in the body as well. If url
is missing, it might scan text
looking for a URL and add
that as a link.
title
and using text
and url
concatenated together. It might truncate the text or replace
url
with a short link to fit into the message size.
The following IDL extends the WebAppManifest dictionary.
dictionary ShareTargetFiles { required USVString name; USVString accept; } dictionary ShareTargetParams { USVString title; USVString text; USVString url; (ShareTargetFiles or sequence<ShareTargetFiles>) files; }; dictionary ShareTarget { required USVString action; DOMString method = "GET"; DOMString enctype = "application/x-www-form-urlencoded"; required ShareTargetParams params; }; partial dictionary WebAppManifest { ShareTarget share_target; };
The following steps are added to the extension point in the steps for processing a manifest:
share_target
member
given manifest["share_target"],
manifest["scope
"], and
manifest URL.
share_target
member
The share_target member of the manifest is a ShareTarget dictionary that declares this application to be a web share target, and describes how the application receives share data.
A web share target is a web site in a secure context with a valid manifest containing a share_target member. A web share target is a type of share target.
The steps for post-processing the share_target
member is given by the following algorithm. The algorithm takes
a ShareTarget share target, a URL scope URL, and a URL manifest URL. This algorithm
returns a ShareTarget or undefined
.
undefined
, then return
undefined
.
"GET"
nor "POST"
,
issue a
developer warning that the method is not supported, and return
undefined
.
"POST"
and share
target["enctype"] is neither
an ASCII
case-insensitive match for the strings
"application/x-www-form-urlencoded"
nor
"multipart/form-data"
, issue a developer
warning that the enctype is not supported, and return
undefined
.
undefined
.
undefined
.
ShareTarget
and its members
The ShareTarget dictionary contains the following members.
The action member specifies the URL for the web share target.
The method member specifies the HTTP request method for the web share target.
A use case for GET
requests is when the share target
drafts a message for subsequent user approval. If the share target
performs a side-effect without any user interaction,
POST
requests should be used.
The enctype member specifies how the share data is encoded
in the body of a POST
request. It is ignored when
method is "GET"
.
The params member contains a ShareTargetParams dictionary.
ShareTargetParams
and its members
The ShareTargetParams dictionary contains the following members.
The title member specifies the name of the query parameter used for the title of the document being shared.
The text member specifies the name of the query parameter used for the arbitrary text that forms the body of the message being shared.
The url member specifies the name of the query parameter used for the URL string referring to a resource being shared.
The files member contains zero or more
ShareTargetFiles dictionaries. It is ignored when
method is "GET"
.
ShareTargetFiles
and its members
The ShareTargetFiles dictionary contains the following members.
The name member specifies the name of the form field used to share the files.
The accept member specifies a comma-separated list of accepted MIME type(s). The missing value default is "*/*".
How and when web share targets are "registered" is at the discretion of the user agent and/or the end user. In fact, "registration" is a user-agent-specific concept that is not formally defined here; user agents are NOT REQUIRED to "register" web share targets at all; they are only REQUIRED to provide some mechanism to convey shared data to a web share target of the end user's choosing. User agents MAY consider a web share target "registered" even if it is not installed.
The user agent MAY automatically register all web share targets as the user visits the site, but it is RECOMMENDED that more discretion is applied, to avoid overwhelming the user with the choice of a large number of targets.
Examples of registration strategies that user agents can employ are:
navigator.registerProtocolHandler
).
When presenting the end user with a list of web share targets, the user agent MAY use an online service which has pre-indexed manifests, and therefore show the user targets that they have never visited or explicitly registered.
If the user is sharing files, and a file being shared is not accepted by any of a web share target's files entries, the user will not be presented with that web share target as an option.
A web share target is invoked when the end user is sharing some data intended for a generic application, and indicates that specific web share target as the receiver of the data.
It is not specified where the data comes from, or how the end user
indicates the web share target as the receiver. However, one possible
source is a call to navigator.share
in the
same user agent.
Examples of other possible sources of a web share target invocation are:
title
" and the current page URL as
"url
").
ShareData
When a web share target is invoked, the data MAY be in
an unspecified format. The user agent MUST first convert the data
into a ShareData
dictionary, if
it is not already, by mapping to the fields of ShareData
from equivalent concepts in the host system. If the source was a call
to navigator.share
, the
user agent SHOULD use the ShareData
argument
unmodified (but this is not always possible, as it might have to
round-trip through some other format in a lossy manner). The user
agent MAY employ heuristics to map the data onto the
ShareData
fields as well as possible.
For example, the host share system may not have a dedicated URL
field, but a convention that both plain text and URLs are sometimes
transmitted in a "text" field. This is the case on Android. The user
agent can check whether all or part of the "text" field is a
valid URL string, and if so,
move that part of the "text" field to the ShareData
's url
field.
When web share target having WebAppManifest
manifest is invoked with ShareData
data,
run the following steps:
"title"
, "text"
, "url"
»,
undefined
or an empty
string, then continue.
undefined
, then continue.
"GET"
:
application/x-www-form-urlencoded
serializer with entry list and no encoding
override.
"POST"
and
enctype is "application/x-www-form-urlencoded"
:
application/x-www-form-urlencoded
serializer with entry list and no encoding
override.
"Content-Type"
/"application/x-www-form-urlencoded"
to header list.
"POST"
and
enctype is "multipart/form-data"
:
multipart/form-data
encoding algorithm with
entry list and UTF-8
encoding.
"multipart/form-data;"
, a U+0020 SPACE character,
the string "boundary="
, and the multipart/form-data
boundary string generated by the multipart/form-data
encoding algorithm.
"Content-Type"
/MIME type to header
list.
This algorithm assumes that manifest has had the
post-processing the share_target
member algorithm
run on it and still has a share_target afterwards.
Thanks to the Web Intents team, who laid the groundwork for the web app interoperability use cases. In particular, Paul Kinlan, who did a lot of early advocacy for Web Share and Web Share Target.
Thanks to Connie Pyromallis, who wrote an early draft of this spec, and helped design and prototype the API.
Thanks to Alex Russell and David Baron, for their feedback on early drafts of this spec.