Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add canShare() method #177

Merged
merged 19 commits into from
Aug 20, 2021
Merged
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
264 changes: 239 additions & 25 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
};
</script>
</head>
<body data-cite="FILEAPI">
<body data-cite="FILEAPI secure-contexts mimesniff">
<section id="abstract">
<p>
This specification defines an API for sharing text, links and other
Expand Down Expand Up @@ -93,17 +93,24 @@ <h2>
target to share this title and the page URL to.
</p>
</section>
<section>
<section data-dfn-for="Navigator">
<h2>
API definition
</h2>
<section data-dfn-for="Navigator">
<section>
<h3>
Extensions to the `Navigator` interface
</h3>
<pre class="idl">
partial interface Navigator {
[SecureContext] Promise&lt;undefined&gt; share(optional ShareData data = {});
[SecureContext]
Promise&lt;undefined&gt; share(optional ShareData data = {});

[SecureContext]
boolean canShare(optional ShareData data = {});

[SecureContext]
boolean canShare(DOMString name, optional CanShareQuery options = {});
};
</pre>
<p>
Expand Down Expand Up @@ -145,10 +152,10 @@ <h4>
|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>If the [=current settings object=]'s [=environment settings
object/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 {{Navigator/[[sharePromise]]}} is not `null`, return <a>a
promise rejected with</a> {{InvalidStateError}}.
Expand All @@ -165,28 +172,19 @@ <h4>
{{ShareData/text}}, or {{ShareData/url}} or {{ShareData/files}} 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>Let |base:URL| be the [=this=] value's <a>relevant settings
object</a>'s [=environment settings object/api base URL=].
</li>
<li>If running the steps to [=validate share data=] with |data| and
|base| return false, then return a promise rejected with a
{{TypeError}}.
</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>Assert: |url| is {{URL}}.
</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>
Expand Down Expand Up @@ -270,6 +268,110 @@ <h4>
or bypassing the UI if there is only a single share target.
</div>
</section>
<section>
<h3>
`canShare(data)` method
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be marked as historical if we think this is a broken approach?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only if we are not going to ship an alternative - but I don't think anyone plans to.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only if we are not going to ship an alternative

Are going to?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, yes... there is no plans for any engine to ship anything different. Let's just ship this and be done for now. If we need a new method, we can add it when there is demand for it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think Gecko would want to ship anything proven to be broken as a sole solution 🤔

cc @annevk, context: #108 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's ok, but if we can't shift the other implementers then we need to make a process call here (i.e., Mozilla can "formally object" and we figure it out from there).

We have two engines that already ship .canShare() already (and have now for over a year).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it depends on real world usage of canShare.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, that too... however, we (you and I) are kinda limited in that we can't actually get that data.

We are at an impasse here :( without input from other implementers willing to change (or even respond to issues), I don't know what we can do 😭

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, that too... however, we (you and I) are kinda limited in that we can't actually get that data.

https://chromestatus.com/metrics/feature/timeline/popularity/2737

</h3>
<aside class="note" title="When to use canShare(data)">
<p>
Calling {{Navigator/canShare()}} method with a {{ShareData}}
dictionary [=validate share data|validates=] the shared data. It
also works without requiring a user gesture, so, unlike
{{Navigator/share()}}, it doesn't [=consume user activation=].
</p>
<pre class="example">
// Check if files are supported
const file = new File([], "some.png", { type: "image/png" });
if (navigates.canShare({files: [file]})) {
// Sharing a png file would probably be ok...
}

// Check if a URL is ok to share...
if (navigates.canShare({ url: someURL })) {
// The URL is valid and can probably be shared...
}
</pre>
</aside>
<p>
When the <dfn data-lt="canShare()">canShare(data)</dfn> method is
called with argument {{ShareData}} |data:ShareData|, run the
following steps:
</p>
<ol class="algorithm">
<li>If the [=current settings object=]'s [=environment settings
object/responsible document=] is not <a>allowed to use</a> the
"[=web-share-feature|web-share=]" permission, return false.
</li>
<li>Let |base| be [=this=]'s [=relevant settings object=]'s
[=environment settings object/API base URL=].
</li>
<li>Return the result of [=validate share data=] with |data| and
|base|.
</li>
</ol>
</section>
<section>
<h3>
`canShare(name, options)` method
</h3>
<aside class="note" title="When to use canShare(name, options)">
<p>
The `canShare(name, options)` method allows developer to check if
a particular member is [=supported=]. Further, through the
`options` method, it allows a developer check if, for example,
the user agent [=supported|supports=] sharing files of a
particular [=MIME type=].
</p>
<pre class="example">
// Check if files are supported
if (navigates.canShare("files")) {
// Enable file sharing...
}

// Alternatively, check if can share jpeg files
if (navigates.canShare("files", { type: "image/jpeg" })) {
// Enable jpeg image sharing...
}
</pre>
</aside>
<p>
When the <dfn data-lt="canShare!overload-1">canShare(name,
options)</dfn> method is called with arguments {{DOMString}}
|name:DOMString| and {{CanShareQuery}} |options:CanShareQuery|, run
the following steps:
</p>
<ol class="algorithm">
<li>If the [=current settings object=]'s [=environment settings
object/responsible document=] is not <a>allowed to use</a> the
"[=web-share-feature|web-share=]" permission, return false.
</li>
<li>If |name| is "url", return either `true` or `false` depending
on if sharing URLs is [=supported=].
</li>
<li>If |name| is "text", return either `true` or `false` depending
on if sharing text is [=supported=].
</li>
<li>If |name| is "files":
<ol>
<li>If sharing files is not [=supported=], return `false`.
</li>
<li>If |options| is doesn't have a {{CanShareQuery/type}}
member, return `true`.
</li>
<li>Let |type| be the result of [=parse a MIME type=] with
{{CanShareQuery/type}} as input.
</li>
<li>If |type| is failure, throw a {{TypeError}}.
</li>
<li>Return either `true` or `false` depending on if the user
agent supports sharing the [=MIME type/essence=] of |type|.
</li>
</ol>
</li>
<li>|name| is not a [=supported share member=], return `false`.
</li>
</ol>
</section>
</section>
<section data-dfn-for="ShareData">
<h3>
Expand All @@ -282,7 +384,7 @@ <h3>
USVString text;
USVString url;
};
</pre>
</pre>
<p>
The <dfn>ShareData</dfn> dictionary consists of several optional
members:
Expand Down Expand Up @@ -314,6 +416,10 @@ <h3>
A URL string referring to a resource being shared.
</dd>
</dl>
<p>
A member is <dfn>supported</dfn> if the use agent supports sharing
the value of a {{ShareData}} member with [=share targets=].
</p>
<div class="note">
These members are {{USVString}} (as opposed to {{DOMString}}) because
they are not allowed to contain surrogate code points. Among other
Expand All @@ -329,6 +435,114 @@ <h3>
an [^a^] element, before being given to the share target.
</div>
</section>
<section>
<h3>
Validate share data
</h3>
<p>
To <dfn>validate share data</dfn> with |data:ShareData| and
|base:URL|, run the following steps:
</p>
<ol class="algorithm">
<li>If none of |data|'s members {{ShareData/title}},
{{ShareData/text}}, or {{ShareData/url}} or {{ShareData/files}} are
present, return false.
</li>
<li>Let |titleTextOrUrl:boolean| be true if any of
{{ShareData/title}}, or {{ShareData/text}}, or {{ShareData/url}} is
present.
</li>
<li>If |data|'s {{ShareData/files}} member is present:
<ol>
<li>If |titleTextOrUrl| is false and |data|'s {{ShareData/files}}
member is empty, return false.
<p class="note">
This causes a `{ files: [] }` dictionary to be treated as an
empty dictionary. However, passing a dictionary like `{text:
"text", files: []}` is fine, as `files` is just ignored.
</p>
</li>
<li>If the implementation does not support file sharing, return
false.
</li>
<li>If the user agent believes sharing any of the files in
`files` would result in a potentially hostile share, return
false.
<div class="issue" data-number="127"></div>
</li>
</ol>
</li>
<li>If |data|'s url member is present:
<ol>
<li>Let |url:URL| be the result of running the [=URL parser=] on
|data|'s url, with |base|, and no encoding override.
</li>
<li>If |url| is failure, return false.
</li>
<li>If |url|'s [=URL/scheme=] is not "http" or "https", return
false.
</li>
</ol>
</li>
<li>Return true.
</li>
</ol>
</section>
<section data-dfn-for="CanShareQuery">
<h3>
`CanShareQuery` dictionary
</h3>
<pre class="idl">
dictionary CanShareQuery {
DOMString type;
};
</pre>
<section>
<h4>
`type` member
</h4>
<p>
The <dfn>type</dfn> member is a [=MIME Type=] that the developer
would like to check support for.
</p>
</section>
</section>
<section data-dfn-for="SupportedShareMember">
<h3>
Supported share members
</h3>
<p>
The <dfn>supported share members</dfn> provide a means for developers
to check if particular sharing capabilities are supported by the user
agent. They are represented by the following string literals:
</p>
<dl>
<dt>
"<dfn>files</dfn>"
</dt>
<dd>
Represents support for the {{ShareData/files}} member.
</dd>
<dt>
"<dfn>url</dfn>"
</dt>
<dd>
Represents support for the {{ShareData/url}} member.
</dd>
<dt>
"<dfn>text</dfn>"
</dt>
<dd>
Represents support for the {{ShareData/text}} member.
</dd>
</dl>
<p class="Note" title="Why not an enum?">
The choice to use string literals instead of an [=enumeration=] is
deliberate. Doing so allows implementers to gracefully support future
{{ShareData}} member values, while also giving the user agent the
ability to disable sharing those members if needed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this mention whatwg/webidl#893 or whatwg/webidl#491?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. If something changes in WebIDL, then it should be adopted. whatwg/webidl#491 should be closed anyway - it's no longer relevant.

</p>
</section>
</section>
<section>
<h2>
Expand Down