forked from w3c/web-share
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
604 lines (604 loc) · 24.9 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>
Web Share API
</title>
<script src="https://www.w3.org/Tools/respec/respec-w3c" class=
"remove"></script>
<script class='remove'>
var respecConfig = {
specStatus: "ED",
previousMaturity: "FPWD",
previousPublishDate: "2019-12-17",
github: {
repoURL: "w3c/web-share",
branch: "main"
},
testSuiteURI: "https://w3c-test.org/web-share/",
group: "webapps",
editors: [{
name: "Matt Giuca",
company: "Google Inc.",
companyURL: "https://google.com",
w3cid: 91260
},
{
name: "Eric Willigers",
company: "Google Inc.",
companyURL: "https://google.com",
w3cid: 67534
}],
caniuse: {
feature: "web-share",
browsers: ["chrome", "firefox", "safari", "edge", "and_chr", "and_ff", "ios_saf"],
},
xref: "web-platform",
// The Wylecial breaks the link checker with bad HTTP code response, so disabling this for now.
// localBiblio: {
// "Wylecial": {
// "date": "2020-08-25",
// "title": "Stealing local files using Safari Web Share API",
// "Author": "Pawel Wylecial",
// "href": "https://blog.redteam.pl/2020/08/stealing-local-files-using-safari-web.html"
// }
// }
};
</script>
</head>
<body data-cite="FILEAPI">
<section id="abstract">
<p>
This specification defines an API for sharing text, links and other
content to an arbitrary destination of the user's choice.
</p>
<p>
The available share targets are not specified here; they are provided
by the user agent. They could, for example, be apps, websites or
contacts.
</p>
</section>
<section id="sotd">
<p>
This is a work in progress. Wide review and feedback welcome.
</p>
</section>
<section class="informative">
<h2>
Usage Examples
</h2>
<p>
This example shows a basic share operation. In response to a button
click, this JavaScript code shares the current page's URL.
</p>
<pre class="example javascript" title="Basic usage">
shareButton.addEventListener("click", async () => {
try {
await navigator.share({ title: "Example Page", url: "" });
console.log("Data was shared successfully");
} catch (err) {
console.error("Share failed:", err.message);
}
});
</pre>
<p>
Note that a {{ShareData/url}} of `''` refers to the current page URL,
just as it would in a link. Any other absolute or relative URL can also
be used.
</p>
<p>
In response to this call to {{Navigator/share()}}, the user agent would
display a picker or chooser dialog, allowing the user to select a
target to share this title and the page URL to.
</p>
</section>
<section>
<h2>
API definition
</h2>
<section data-dfn-for="Navigator">
<h3>
Extensions to the `Navigator` interface
</h3>
<pre class="idl">
partial interface Navigator {
[SecureContext] Promise<undefined> share(optional ShareData data = {});
};
</pre>
<p>
User agents that do not support sharing SHOULD NOT expose
{{Navigator/share()}} on the {{Navigator}} interface.
</p>
<div class="note">
The above statement is designed to permit feature detection. If
{{Navigator/share()}} is present, there is a reasonable expectation
that it will work and present the user with at least one <a>share
target</a>. Clients can use the presence or absence of this method to
determine whether to show UI that triggers its use.
</div>
<section>
<h4>
Internal Slots
</h4>
<p>
This API adds the following internal slot to the {{Navigator}}
interface.
</p>
<dl>
<dt>
{{Promise}}? <dfn>[[\sharePromise]]</dfn>
</dt>
<dd>
The {{[[sharePromise]]}} is a promise that represents a user's
current intent to share some data with a <a>share target</a>. It
is initialized to `null`.
</dd>
</dl>
</section>
<section>
<h4>
<dfn>share()</dfn> method
</h4>
<p>
When the {{Navigator/share()}} method is called with argument
|data:ShareData|, run the following steps:
</p>
<ol class="algorithm">
<li>If the current settings object's responsible document is not
<a>allowed to use</a> the "[=web-share-feature|web-share=]"
permission, return [=a promise rejected with=] with a
{{"NotAllowedError"}} {{DOMException}}.
</li>
<li>If {{[[sharePromise]]}} is not `null`, return <a>a promise
rejected with</a> {{InvalidStateError}}.
</li>
<li>Let |window| be [=relevant global object=] of [=this=].
</li>
<li>If |window| does not have [=transient activation=], return [=a
promise rejected with=] with a {{"NotAllowedError"}}
{{DOMException}}.
</li>
<li>[=Consume user activation=] of |window|.
</li>
<li>If none of |data|'s members {{ShareData/title}},
{{ShareData/text}}, or {{ShareData/url}} or {{ShareData/file}} are
present, return <a>a promise rejected with</a> a {{TypeError}}.
</li>
<li>If |data|'s {{ShareData/files}} member is present:
<ol>
<li>If |data|'s {{ShareData/files}} member is empty, or if the
implementation does not support file sharing, return <a>a
promise rejected with</a> a {{TypeError}}, and abort these
steps.
</li>
</ol>
</li>
<li>If |data|'s {{ShareData/url}} member is present:
<ol>
<li>Let |base:URL| be the [=this=] value's <a>relevant settings
object</a>'s [=environment settings object/api base URL=].
</li>
<li>Let |url:URL| be the result of running the <a>URL
parser</a> on |data|'s {{ShareData/url}} with |base|.
</li>
<li>If |url| is failure, return <a>a promise rejected with</a>
{{TypeError}}.
</li>
<li>If |url|'s [=URL/scheme=] is not "http" or "https", return
<a>a promise rejected with</a> {{TypeError}}.
</li>
<li>Set |data| to a copy of |data|, with its {{ShareData/url}}
member set to the result of running the <a>URL serializer</a>
on |url|.
</li>
</ol>
</li>
<li>If a file type is being blocked due to security considerations,
return <a>a promise rejected with</a> with a {{"NotAllowedError"}}
{{DOMException}}.
</li>
<li>Set {{[[sharePromise]]}} to be <a>a new promise</a>.
</li>
<li>Return {{[[sharePromise]]}} and <a>in parallel</a>:
<ol>
<li>If there are no <a>share targets</a> available:
<ol>
<li>
<a>Reject</a> {{[[sharePromise]]}} with an
{{"AbortError"}} {{DOMException}}.
</li>
<li>Set {{[[sharePromise]]}} to `null`.
</li>
<li>Abort these steps.
</li>
</ol>
</li>
<li>Present the user with a choice of one or more <a>share
targets</a>, selected at the user agent's discretion. The user
MUST be given the option to cancel rather than choosing any of
the share targets. Wait for the user's choice.
</li>
<li>If the user chose to cancel the share operation:
<ol>
<li>
<a>Reject</a> {{[[sharePromise]]}} with an
{{"AbortError"}} {{DOMException}},
</li>
<li>Set {{[[sharePromise]]}} to `null`.
</li>
<li>Abort these steps.
</li>
</ol>
</li>
<li>Activate the chosen <a>share target</a>, <a>convert |data|
to a format suitable for ingestion into the target</a>, and
transmit the converted data to the target.
</li>
<li>If an error occurs starting the target or transmitting the
data:
<ol>
<li>
<a>Reject</a> {{[[sharePromise]]}} with an
{{"DataError"}} {{DOMException}}.
</li>
<li>Set {{[[sharePromise]]}} to `null`.
</li>
<li>Abort these steps.
</li>
</ol>
</li>
<li>Once the data has been successfully transmitted to the
target, <a>resolve</a> {{[[sharePromise]]}} with `undefined`.
</li>
<li>Set {{[[sharePromise]]}} to `null`.
</li>
</ol>
</li>
</ol>
<p>
The user agent MUST NOT allow the website to learn which share
targets are available, or the identity of the chosen target.
</p>
<div class="note">
{{Navigator/share()}} always shows some form of UI, to give the
user a choice of application and get their approval to invoke and
send data to a potentially native application (which carries a
security risk). For this reason, user agents are prohibited from
showing any kind of "always use this target in the future" option,
or bypassing the UI if there is only a single share target.
</div>
</section>
</section>
<section data-dfn-for="ShareData">
<h3>
`ShareData` dictionary
</h3>
<pre class="idl">
dictionary ShareData {
sequence<File> files;
USVString title;
USVString text;
USVString url;
};
</pre>
<p>
The <dfn>ShareData</dfn> dictionary consists of several optional
members:
</p>
<dl data-sort="">
<dt>
<dfn>files</dfn> member
</dt>
<dd>
Files to be shared.
</dd>
<dt>
<dfn>title</dfn> member
</dt>
<dd>
The title of the document being shared. May be ignored by the
target.
</dd>
<dt>
<dfn>text</dfn> member
</dt>
<dd>
Arbitrary text that forms the body of the message being shared.
</dd>
<dt>
<dfn>url</dfn> member
</dt>
<dd>
A URL string referring to a resource being shared.
</dd>
</dl>
<div class="note">
These members are {{USVString}} (as opposed to {{DOMString}}) because
they are not allowed to contain surrogate code points. Among other
things, this means that the user agent can serialize them into any
Unicode encoding, such as <a data-cite="rfc3629#section-3">UTF-8</a>,
without change or loss of data or the generation of replacement
characters.
</div>
<div class="note">
The {{ShareData/url}} member can contain a <a>relative-URL
string</a>. In this case, it will be automatically resolved relative
to the current page location, just like a {{HTMLBaseElement/href}} on
an [^a^] element, before being given to the share target.
</div>
</section>
</section>
<section>
<h2>
Share targets
</h2>
<p>
A <dfn data-export="">share target</dfn> is the abstract concept of a
destination that the user agent will transmit the share data to. What
constitutes a share target is at the discretion of the user agent.
</p>
<p>
A share target might not be directly able to accept a {{ShareData}}
(due to not having been written with this API in mind). However, it
MUST have the ability to receive data that matches some or all of the
concepts exposed in {{ShareData}}. To <dfn>convert data to a format
suitable for ingestion into the target</dfn>, the user agent SHOULD map
the members of {{ShareData}} onto equivalent concepts in the target. It
MAY discard or combine members if necessary. The meaning of each member
of the payload is at the discretion of the share target.
</p>
<div class="note">
Mapping the {{ShareData}} to the share target's (or operating system's)
native format can be tricky as some platforms will not have an
equivalent set of members. For example, if the target has a "text"
member but not a "URL" member (as is the case on Android), one solution
is to concatenate both the {{ShareData/text}} and {{ShareData/url}}
members of {{ShareData}} and pass the result in the "text" member of
the target.
</div>
<p>
Each share target MAY be made conditionally available depending on the
{{ShareData}} payload delivered to the {{Navigator/share()}} method.
</p>
<div class="note">
Once a share target has been given the payload, the share is considered
successful. If the target considers the data unacceptable or an error
occurs, it can either recover gracefully, or show an error message to
the end-user; it cannot rely on the sender to handle errors. In other
words, the {{Navigator/share()}} method is "fire and forget"; it does
not wait for the target to approve or reject the payload.
</div>
<section class="informative">
<h3>
Examples of share targets
</h3>
<p>
The list of share targets can be populated from a variety of sources,
depending on the user agent and host operating system. For example:
</p>
<ul>
<li>Built-in service (e.g., "copy to clipboard").
</li>
<li>Native applications written for the host operating system.
</li>
<li>Contacts (e.g., the user agent directly shares to a person from
the user's address book, using a specific app).
</li>
<li>Websites (e.g., the user agent fills in a template URL with the
members of the {{ShareData}}, then navigates to that URL in a new
browsing context).
</li>
</ul>
<div class="note">
There is an attempt to standardize the registration of websites to
receive share data for that final use case; see <a href=
"https://github.com/WICG/web-share-target">Web Share Target</a>.
</div>
<p>
In some cases, the host operating system will provide a sharing or
intent system similar to Web Share. In these cases, the user agent
can simply forward the share data to the operating system and not
talk directly to native applications.
</p>
</section>
</section>
<section id="permissions-policy" data-cite="permissions-policy">
<h2>
Permission Policy integration
</h2>
<p>
This specification defines a policy-controlled permission identified by
the string "<code><dfn>web-share</dfn></code>". Its <a>default
allowlist</a> is '`self`'.
</p>
<div class="note">
<p>
A <a>document</a>’s permission policy determines whether a
{{Navigator/share()}} call immediately rejects with a
{{"NotAllowedError"}} {{DOMException}}.
</p>
</div>
</section>
<section class="informative">
<h2>
Accessibility considerations
</h2>
<p>
When this specification is used to present information in the user
interface, implementors will want to follow the OS level accessibility
guidelines for the platform.
</p>
</section>
<section class="informative" data-cite="secure-contexts">
<h2>
Security and privacy considerations
</h2>
<p>
Web Share enables data to be sent from websites to native applications.
While this ability is not unique to Web Share, it does come with a
number of potential security issues that can vary in severity
(depending on the underlying platform).
</p>
<ul>
<li>There is a requirement to not allow the website to learn which apps
are installed, or which app was chosen from {{Navigator/share()}},
because this information could be used for fingerprinting, as well as
leaking details about the user's device.
</li>
<li>Implementors will want to carefully consider what information is
revealed in the error message when {{Navigator/share()}} is rejected.
Even distinguishing between the case where no targets are available and
user cancellation could reveal information about which apps are
installed on the user's device.
</li>
<li>There is a requirement that {{Navigator/share()}} presents the user
with a dialog asking them to select a target application (even if there
is only one possible target). This surface serves as a security
confirmation, ensuring that websites cannot silently send data to
native applications.
</li>
<li>Due to the capabilities of the API surface, {{Navigator/share()}}
is only [=exposed=] in [=secure contexts=] (such as `https://`
schemes).
</li>
<li>Use of {{Navigator/share()}} from a <a href=
"https://en.wikipedia.org/wiki/Privacy_mode">private browsing mode</a>
might leak private data to a third-party application that does not
respect the user's privacy setting. User agents could present
additional warnings or disable the feature entirely when in a private
browsing mode, but this is not mandated as the chooser UI could be
considered sufficient warning.
</li>
<li>The data passed to {{Navigator/share()}} might be used to exploit
buffer overflow or other remote code execution vulnerabilities in
native applications that receive shares. There is no general way to
guard against this, but implementors will want to be aware that it is a
possibility.
</li>
<li>
<p>
Share targets that dereference a shared URL and forward that
information on might inadvertently forward information that might
be otherwise confidential. This can lead to unexpected information
leakage if shares reference content that is only accessible by that
application, the host on which it runs, or its network location.
</p>
<p>
Malicious sites might exploit share targets that leak information
by providing URLs that ultimately resolve to local resources,
including, but not limited to, "file:" URLs or local services that
might otherwise be inaccessible. Even though this API limits shared
URLS to "http:" and "https:", use of redirects to other URLs or
tweaks to DNS records for hosts in those URLs might be used to
cause applications to acquire content.
</p>
<p>
To avoid being used in these attacks, share targets can consume the
URL, retrieve the content, and process that information without
sharing it. For instance, a photo editing application might
retrieve an image that is "shared" with it. A share target can also
share the URL without fetching any of the referenced content.
</p>
<p>
Share targets that fetch content for the purposes of offering a
preview or for sharing content risk information leakage. Content
that is previewed and authorized by a user might be safe to
forward, however it is not always possible for a person to identify
when information should be confidential, so forwarding any content
presents a risk. In particular, the {{ShareData/title}} might be
used by an attacker to trick a user into misinterpreting the nature
of the content. <!--
, as demonstrated in the [[Wylecial]] <a data-cite=
"Wylecial#">proof of concept attack</a>
-->
</p>
</li>
</ul>
</section>
<section class="appendix informative">
<h2>
Extensibility of this API
</h2>
<p>
The Web Share API is designed to be extended in the future by way of
new members added to the {{ShareData}} dictionary, to allow both
sharing of new types of data (<i>e.g.</i>, <a href=
"https://github.com/w3c/web-share/issues/12">images</a>) and strings
with new semantics (<i>e.g.</i> author).
</p>
<div class="warning">
This doesn't mean user agents can add whatever members they like. It
means that new members can be added to the standard in the future.
</div>
<p>
The three members {{ShareData/title}}, {{ShareData/text}}, and
{{ShareData/url}}, are part of the base feature set, and
implementations that provide {{Navigator/share()}} need to accept all
three. Any new members that are added in the future will be
<em>individually feature-detectable</em>, to allow for
backwards-compatibility with older implementations that don't recognize
those members. These new members might also be added as optional "MAY"
requirements.
</p>
<div class="note">
There is <a href="https://github.com/heycam/webidl/issues/107">an open
discussion</a> about how to provide feature-detection for dictionary
members. Web Share will use the mechanism produced by that discussion.
</div>
<p>
The {{Navigator/share()}} method returns a rejected promise with a
{{TypeError}} if none of the specified members are present. The
intention is that when a new member is added, it will also be added to
this list of recognized members. This is for future-proofing
implementations: if a web site written against a future version of this
spec uses <em>only</em> new members (<i>e.g.</i>,
`navigator.share({image: x})`), it will be valid in future user agents,
but a {{TypeError}} on user agents implementing an older version of the
spec. Developers will be asked to feature-detect any new members they
rely on, to avoid having errors surface in their program.
</p>
<p>
Editors of this spec will want to carefully consider the genericity of
any new members being added, avoiding members that are closely
associated with a particular service, user agent or operating system,
in favour of members that can potentially be applied to a wide range of
platforms and targets.
</p>
</section>
<section id="conformance"></section>
<section id="idl-index"></section>
<section class="informative">
<h2>
Changlog
</h2>
<script class="remove">
const ignoredHashes = [
"2ed0b468aacf7a4e3e351f25f114e9cb2fbef009"
];
function removeCommits(entry) {
const { message, hash } = entry;
for (const ignoredHash of ignoredHashes) {
if (ignoredHash.startsWith(hash)) {
return false;
};
}
return !/^editorial|^chore|^\[chore|^fix|^refactor|^tests?|^docs|^typo|^nit/i.test(message);
}
</script> <rs-changelog from="1589731080e09b9c7d485e54340ba66bc3ae1be0"
filter="removeCommits"></rs-changelog>
</section>
<section class="appendix">
<h2>
Acknowledgments
</h2>
<p>
Thanks to the <a href="https://www.w3.org/TR/web-intents">Web
Intents</a> team, who laid the groundwork for the web app
interoperability use cases. In particular, <a href=
"https://paul.kinlan.me/">Paul Kinlan</a>, who did a lot of early
advocacy for Web Share.
</p>
</section>
</body>
</html>