Closed Bug 1402369 Opened 7 years ago Closed 5 years ago

Implement Web Share API on Fenix

Categories

(GeckoView :: General, enhancement, P1)

All
Android
enhancement

Tracking

(Webcompat Priority:?, firefox-esr68 wontfix, firefox68 wontfix, firefox69 wontfix, firefox70 wontfix, firefox71 wontfix, firefox72 fixed)

RESOLVED FIXED
mozilla72
Webcompat Priority ?
Tracking Status
firefox-esr68 --- wontfix
firefox68 --- wontfix
firefox69 --- wontfix
firefox70 --- wontfix
firefox71 --- wontfix
firefox72 --- fixed

People

(Reporter: divjot94, Assigned: droeh, Mentored)

References

Details

(Keywords: dev-doc-needed, Whiteboard: [webcompat] [geckoview:m1909] [geckoview:m1910])

Attachments

(3 files)

Web Share API landed on Chrome for Android in version 61. It's a neat way of providing native sharing hooks to a web app.


Example code: https://gist.github.com/bogas04/50eae983a9b478e172a22bb9fce9c77e
API Spec: https://wicg.github.io/web-share/
Explainer: https://github.com/WICG/web-share/blob/master/docs/explainer.md
I've started looking into scheduling this for an upcoming mobile release. 

Marcos, Anne - what's your take on this? Any comments on the API?
Flags: needinfo?(mcaceres)
Flags: needinfo?(annevk)
I'm moving the discussion here:
https://github.com/mozilla/standards-positions/issues/27
Flags: needinfo?(mcaceres)
Great that you are looking at this again :)

Note there is an existing discussion here: https://bugzilla.mozilla.org/show_bug.cgi?id=1312422

Will continue discussion on RFP.
I have a couple concerns:

* I don't think it's good to focus on Fennec exclusively. We need a plan and resources for desktop too. If we don't do a rigorous approach I suspect we'll have to eventually remove the code again. (This doesn't mean we can't experiment on Fennec, but it does mean we can't ship the API just there.)
* It's still unclear to me whether this API meets the requirements Apple might have. They have dedicated sharing UI on both desktop and mobile and this seems rather incompatible with that. https://wiki.whatwg.org/wiki/Sharing does not have this problem though also did not have explicit buy-in from Apple either. (The concern here is that with Apple not adopting the API or doing something else, we'll be forced to adapt or end up with something that only works well on Android and possibly Windows.)
Flags: needinfo?(annevk)
(In reply to Anne (:annevk) from comment #4)
> I have a couple concerns:
> 
> * I don't think it's good to focus on Fennec exclusively. We need a plan and
> resources for desktop too. If we don't do a rigorous approach I suspect
> we'll have to eventually remove the code again. (This doesn't mean we can't
> experiment on Fennec, but it does mean we can't ship the API just there.)

I agree, but still think it's worth while starting on Fennec.

> * It's still unclear to me whether this API meets the requirements Apple
> might have. They have dedicated sharing UI on both desktop and mobile and
> this seems rather incompatible with that.

Practically speaking, and IIUC, we need to basically show where it doesn't work with:

https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Share.html#//apple_ref/doc/uid/TP40014214-CH12-SW1

And: 
https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW8

Matt, have Google folks looked at adding support on Chrome for iOS? Even if not, we should could get someone like Garvan, cc'ed, who knows iOS/Mac really well, to evaluate the feasibility of supporting the API given the API descriptions above. 

If there are incompatibilities, as Anne states, then we should address those in the spec. 

Garvan, maybe something we can go over together in Texas over 30 mins? All the objective-c makes my eyes bleed :)  

> https://wiki.whatwg.org/wiki/Sharing does not have this problem though also
> did not have explicit buy-in from Apple either. 

I'm not sure they've communicated something for or against. As we know, getting any signal out of Apple can be challenging until they are actually implementing.  

> (The concern here is that
> with Apple not adopting the API or doing something else, we'll be forced to
> adapt or end up with something that only works well on Android and possibly
> Windows.)

Yeah, we should rope them in and make sure they are well aware of what is being proposed. That mostly involves emailing a bunch of Apple folks so later they can't claim they didn't know about it... but we should go in with something that works with what they have.
Flags: needinfo?(gkeeley)
Anne: Thanks for commenting (though it might be helpful to keep the discussion on the GitHub).

> * It's still unclear to me whether this API meets the requirements Apple might have.

I share that concern (of Apple adopting), though Dean Jackson from Apple has been in contact with us and positive about the API. What in particular about the API do you think is incompatible with Apple's sharing UI?

While I admit I'm not very familiar with Mac or iOS development, I did do some preliminary research awhile ago about how you might hook up the Web Share API to Apple's share systems:

https://github.com/WICG/web-share/blob/master/docs/native.md

It seems reasonably straightforward. Keep in mind we are talking about the Web Share API (navigator.share), and not share targets (which are mostly separate, documented at https://github.com/WICG/web-share-target).

Marcos:

> Practically speaking, and IIUC, we need to basically show where it doesn't work with:
>
> https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Share.html#//apple_ref/doc/uid/TP40014214-CH12-SW1
>
> And: 
> https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW8

Those both appear to be relevant to creating your own share target UI, something much more complex than what the Web Share spec attempts to accomplish. I think the links I mention in my doc above are the relevant here.

> Matt, have Google folks looked at adding support on Chrome for iOS? Even if not, we should could get someone like Garvan, cc'ed, who knows iOS/Mac really well, to evaluate the feasibility of supporting the API given the API descriptions above. 

We haven't considered it deeper than what I've written above, AFAIK.

We have been speaking with Dean Jackson at Apple. This is the WebKit bug tracking implementation of this feature: https://bugs.webkit.org/show_bug.cgi?id=171100 Again, this applies only to Web Share, not Web Share Target.
Marcos: lets meet in Austin and look at this. Stefan on the Firefox iOS team is also interested. I'll read over the docs here and get up-to-speed with this spec, I am not familiar with it.

This appears implementable by client-code (i.e. Firefox iOS, Focus iOS) on iOS (whereas in other WebAPI cases webkit would have to implement it).
Flags: needinfo?(gkeeley)
If you setup an austin meeting please invite me.
I've sent out an invite.
OS: Unspecified → Android
Hardware: Unspecified → All

In the conversation above there was uncertainty about Apple adopting. Web Share Level 1 is now implemented, Supported in Preview.

(Level 2 adds canShare() and the ability to share files.)

Apple is shipping Web Share in iOS 12.2, which is coming out in the coming months.

Whiteboard: [geckoview:fenix:p3]

Twitter is now using this in their mobile site, and users have noticed that Firefox isn't getting the same "Share" option that Chrome is getting: https://webcompat.com/issues/27191

Flags: webcompat?
Whiteboard: [geckoview:fenix:p3] → [geckoview:fenix:p3][webcompat]

Migrating Webcompat whiteboard priorities to project flags. See bug 1547409.

Webcompat Priority: --- → ?

See bug 1547409. Migrating whiteboard priority tags to program flags.

Also on Desktop Chrome v75. Even if https://caniuse.com/#feat=web-share does not list that yet.

Also opened https://github.com/mozilla/platform-status/issues/581 to get this on the status page.

Also on Desktop Chrome v75.

No, Chrome is only shipping this on Android for now.

I am proposing we do this via the PromptDelegate.

The sequence of events for DOM should be as follows:

share()

  • Web page calls navigator.share({...})
  • Navigator class handles call:
    -- - Returns promise.
    -- - Checks that at least one of title, url, text or files are present. Rejects promise with TypeError if not.
    -- - Checks that the url is valid and meets criteria defined in spec. Rejects promise with TypeError if not.
    -- - Checks that the call was triggered by user interaction, rejects promise with "NotAllowedError" DOMException if not.
    -- - Checks for file types that are blocked due to security considerations, rejects promise with "NotAllowedError" DOMException if any are found.
    -- - Creates a nsIPromptService instance and calls share with url, text, files and title as arguments.
    -- - waits for response from prompt. If prompt fails (aSuccess == false), reject promise with "AbortError" DOMException. If prompt succeeds (aSuccess == true), resolve promise with undefined.

canShare()

  • Web page calls navigator.canShare({...})
  • Navigator class handles call:
    -- - Checks that at least one of title, url, text or files are present. Returns false if not.
    -- - Checks that the url is valid and meets criteria defined in spec. Returns false if not.
    -- -returns true

A new function needs to be added to nsIPromptService called

boolean share(in mozIDOMWindowProxy aParent,
                 in wstring aTitle,
                 in wstring aText,
                 inout wstring aUrl,
                 in File[] aFiles,
                 out boolean aSuccess);

An equivalent method needs to be added to GeckoViewPrompt.js.

This method will need to send a GeckoView:Prompt event with the following message:

{
  type: "prompt",
  options: {
    title: aTitle,
    text: aText,
    url: aUrl,
    files:[file1,...]
  },
}

If the prompt response is null, an error has occured and aSuccess should be set to false. If prompt response is true, the share succeeded and aSuccess should be set to true.

In Java, a new class will be required to encapsulate share data:

ShareData

class ShareData {
    private String mTitle;
    private String mText;
    private String mUrl;
    private File[] mFiles; 

    public ShareData(GeckoBundle bundle);
}

A new callback interface is required to provide callback methods.
ShareCallback

interface ShareCallback extends AlertCallback {
    @UiThread
    default void abort() {}
    default void success() {}
}

PromptCallback should be extended to implement ShareCallback

@Override
public void abort() {
    mCallback.sendError("abort");
}

@Override
public void success() {
    mCallback.sendSuccess(true);
}

GeckoSession.PromptDelegate will need to handle the share prompt and callback

default void onSharePrompt(@NonNull GeckoSession session, @NonNull ShareData data, @NonNull ShareCallback callback)

GeckoSession#handlePromptEvent will need to be updated to listen to handle the new prompt case. When received, the data needs to be tranformed into a ShareData and sent, along with the callback, to the PromptDelegate set by the consumer.

case "share": {
   GeckoBundle data = message.getBundle("data");
   ShareData shareData = new ShareData(data);
   delegate.onSharePrompt(session, shareData, cb);
}

I am not sure what format the Files will be sent through from Navigator, and therefore am unsure what format they will need to be converted into once they hit JS/Java. Maybe :mcaceres can answer that one.

Feedback requested, :mcaceres, :snorp.

Flags: needinfo?(snorp)
Flags: needinfo?(mcaceres)

This seems broadly fine to me. Some comments below:

(In reply to Emily Toop (:fluffyemily) from comment #19)

  • Navigator class handles call:
    -- - Returns promise.
    -- - Checks that at least one of title, url, text or files are present. Rejects promise with TypeError if not.

I don't see files in the spec anywhere. Is this some pending change that we're expecting?

A new function needs to be added to nsIPromptService called

boolean share(in mozIDOMWindowProxy aParent,
                 in wstring aTitle,
                 in wstring aText,
                 inout wstring aUrl,
                 in File[] aFiles,
                 out boolean aSuccess);

Like the other methods in nsIPromptService, this is synchronous, but our web API and the way GV works is async. I think we should use a Promise here as well.

An equivalent method needs to be added to GeckoViewPrompt.js.

This method will need to send a GeckoView:Prompt event with the following message:

{
  type: "prompt",

"share" probably?

options: {
title: aTitle,
text: aText,
url: aUrl,
files:[file1,...]
},
}


If the prompt response is `null`, an error has occured and `aSuccess` should be set to false. If prompt response is `true`, the share succeeded and `aSuccess` should be set to `true`.

In Java, a new class will be required to encapsulate share data:

`ShareData`

class ShareData {
private String mTitle;
private String mText;
private String mUrl;
private File[] mFiles;

public ShareData(GeckoBundle bundle);

}


A new callback interface is required to provide callback methods.
`ShareCallback` 

interface ShareCallback extends AlertCallback {
@UiThread
default void abort() {}
default void success() {}
}

There has been a plan to convert all of these callbacks to just use GeckoResult (bug 1499394). We should try to do that here too.

PromptCallback should be extended to implement ShareCallback

@Override
public void abort() {
    mCallback.sendError("abort");
}

@Override
public void success() {
    mCallback.sendSuccess(true);
}

GeckoSession.PromptDelegate will need to handle the share prompt and callback

default void onSharePrompt(@NonNull GeckoSession session, @NonNull ShareData data, @NonNull ShareCallback callback)

GeckoSession#handlePromptEvent will need to be updated to listen to handle the new prompt case. When received, the data needs to be tranformed into a ShareData and sent, along with the callback, to the PromptDelegate set by the consumer.

case "share": {
   GeckoBundle data = message.getBundle("data");
   ShareData shareData = new ShareData(data);
   delegate.onSharePrompt(session, shareData, cb);
}

I am not sure what format the Files will be sent through from Navigator, and therefore am unsure what format they will need to be converted into once they hit JS/Java. Maybe :mcaceres can answer that one.

I'd guess it would be one of these: https://developer.mozilla.org/en-US/docs/Web/API/File

We'd probably need some special handling for these, since they don't necessarily map to files on the disk. We may need to write temp files or some other such nonsense.

What's the testing story like here? Do we have existing mochitests or wpt?

Flags: needinfo?(snorp)

I don't see files in the spec anywhere. Is this some pending change that we're expecting?

files (and canShare) arrived in Level 2: https://wicg.github.io/web-share-target/level-2/

Chrome for Android recently shipped Level 2. Both Chrome for Android and Safari had earlier shipped Level 1.

What's the testing story like here?

There are some WPTs in https://github.com/web-platform-tests/wpt/tree/master/web-share - most are either testing for expected failures, or are manual tests.

Assignee: nobody → mcaceres
Flags: needinfo?(mcaceres)

This seems great Emily. I've put in the DOM infrastructure based on your proposal, but as James pointed out, we will need to coordinate a bit more on the async aspects. I'm sending an "intent to experiment" to dev-platform for now, as we sort out how to implement this on GeckoView. Once we are happy with the architectural design, we can do a proper "intent to implement".

@Eric Willigers, let's pick up the level-2 stuff over on Github, but we should make those pull request on "level 1" (and hopefully get rid of the levels!).

I'll implement .canShare() as "chrome-only" for now as I need to review that part of the spec more closely. I have finger-printing concerns.

I am not sure what format the Files will be sent through from Navigator, and therefore am unsure what format they will need to be converted into once they hit JS/Java. Maybe :mcaceres can answer that one.

@baku, you might recall we talked briefly about this at the AllHands... you had some suggestions for what we could use to get the files over to GeckoView. Would you mind providing some guidance again? The IDL from [1]:

dictionary ShareData {
  USVString title;
  USVString text;
  USVString url;
  FrozenArray<File> files;
};

(i.e., so it's File as per FileAPI)

[1] https://wicg.github.io/web-share/level-2/#sharedata-dictionary

Flags: needinfo?(amarchesini)
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true

Un-assigning myself... we have an old DOM specific bug for this. I'll assign myself to that and block with this one on.

Intent to experiment:
https://groups.google.com/d/msg/mozilla.dev.platform/RHk3QJbV_7o/YCDCWRCCCgAJ

Depends on: 1312422
No longer depends on: 1312422
Depends on: 1312422
Assignee: mcaceres → nobody
Status: ASSIGNED → NEW
Alias: webshare

I'll implement .canShare() as "chrome-only" for now as I need to review that part of the spec more closely. I have finger-printing concerns.

It is intended to allow feature detection, so sites don't need to parse user agent strings.

If the browser does not support file sharing, then it detects the absence of title, text and url fields.
If the browser does support file sharing, then it detects the absence of title, text, url and files fields.
It also detects invalid URLs.

canShare reveals no information about which file contents/names/MIME types are accepted for sharing on a given device.

(In reply to Eric Willigers from comment #26)

canShare reveals no information about which file contents/names/MIME types are accepted for sharing on a given device.

Thanks, and that's great to hear! Let's pick this up over in the repo so we can have a more detailed discussion there about this.

FrozenArray<File> files;
};

(i.e., so it's `File` as per FileAPI)

A file can be a anything: a in-memory chunk of data, a fetch/xhr response, a real OS File, a remote Blob (IPCBlob). Each DOM File can be read using nsIInputStream. See Blob::CreateInputStream(). The real question is: in which format do we want the File to be for GeckoView? Is it OK to have an nsIInputStream, a length and a type?
In case we need a real file, we can probably use MutableBlobStorage which can create a temporary OS File.

Marcos, are you going to work on the geckoview part too? Do you know how geckoview consumes the File objects?

Flags: needinfo?(amarchesini) → needinfo?(mcaceres)

(In reply to Andrea Marchesini [:baku] from comment #28)

Marcos, are you going to work on the geckoview part too? Do you know how geckoview consumes the File objects?

I think just the DOM side. But can pass whatever GeckoView needs/consumes... I guess we will need to figure that out between Emily, Snorp, and I.

I seem to recall form the AllHands there was some precedence to write temporary files and then pass file paths to read on the Android side, but I can't recall which project had done that.

Flags: needinfo?(mcaceres)
Assignee: nobody → etoop

After chatting with :marcosc it seems that there are questions over the security of sharing files, so we're going to move to a level 1 implementation only to start with and we will move to a level 2 implementation once the issues have been resolved: https://github.com/WICG/web-share/issues/108.

This involves removing canShare from the spec and not supporting the sharing of files in share.

Assignee: etoop → nobody
Mentor: etoop

Was playing around a bit with nsIPromptService and trying to add share() to it.

I think we should use a Promise here as well.

Ol' funky .idl doesn't seem to know about Promise as a primitive type... and there is no nsIPromise type. I'm generally not inclined to implement a new Promise type. Anyone know of any precedence around making these things async? I see there is a asyncPromptAuth, but also returns a boolean.

(In reply to Marcos Caceres [:marcosc] from comment #32)

Was playing around a bit with nsIPromptService and trying to add share() to it.

I think we should use a Promise here as well.

Ol' funky .idl doesn't seem to know about Promise as a primitive type... and there is no nsIPromise type. I'm generally not inclined to implement a new Promise type. Anyone know of any precedence around making these things async? I see there is a asyncPromptAuth, but also returns a boolean.

I think .idl does support Promise as a primitive -- see https://searchfox.org/mozilla-central/source/docshell/base/nsIDocShell.idl#1194 for example.

(In reply to Dylan Roeh (:droeh) (he/him) from comment #33)

I think .idl does support Promise as a primitive -- see https://searchfox.org/mozilla-central/source/docshell/base/nsIDocShell.idl#1194 for example.

Yeah, the docs were wrong... promise was in lowercase, so I tried WebIDL style Promise<type>. Anyway, working now. Fixed docs.

Attached image Screenshot from Safari

I have a question about how we are implementing this.... with regards to:

Promise share(in mozIDOMWindowProxy aParent, ... other stuff);

If I'm understanding nsIPromptService correctly, mozIDOMWindowProxy aParent will be the outer window container, which assumes the share prompt will be bound to the outer window. That might be fine on Android, for instance - where we likely don't want "right-click" style popup, but rather a modal share selector.

However, on Desktop, we probably want the share() menu to appear where the user activated the share. For example, in Safari (screenshot):

<script>
async function share(){
   await navigator.share({ url: window.location.href, title: document.title })
}
</script>
<button onclick="share()">Share this article</button>

Will passing mozIDOMWindowProxy aParent allow us to get back to the child window? If yes, then 👍. Otherwise, we might want to just pass in the current window, and then either use the outer window to prompt on Android or the inner window on Desktop.

Assigning to Dylan because Emily said he will be working on Web Share for GeckoView.

Assignee: nobody → droeh
Whiteboard: [geckoview:fenix:p3][webcompat] → [geckoview:fenix:m8] [webcompat]

Moving from Fennec's Bugzilla component to GV's.

Component: Web Apps → General
Product: Firefox for Android → GeckoView
Priority: -- → P2
Whiteboard: [geckoview:fenix:m8] [webcompat] → [geckoview:m1910] [webcompat]

Changing whiteboard tag to [geckoview:m1909] because Emily expects WebShare will land in September.

firefox70=wontfix because we can't uplift WebShare GV APIs without the WebShare DOM APIs (bug 1312422) and we're unlikely to uplift new DOM APIs to Beta.

Whiteboard: [geckoview:m1910] [webcompat] → [geckoview:m1909] [webcompat]

Setting priority P1 since we're working on this bug for September.

Priority: P2 → P1

(In reply to Chris Peterson [:cpeterson] from comment #39)

WebShare is in Fenix's "want for Q4" list.

Chris, I haven't seen Web Share show up in any of the Q4 Product OKRs. Do you know who/what is driving this?

Flags: needinfo?(cpeterson)

(In reply to Mike Conca [:mconca] from comment #41)

(In reply to Chris Peterson [:cpeterson] from comment #39)

WebShare is in Fenix's "want for Q4" list.

Chris, I haven't seen Web Share show up in any of the Q4 Product OKRs. Do you know who/what is driving this?

Good question. WebShare is in the Fenix team's "Items needed for Fenix Q4" bug list, but I don't see it in Fenix's Q4 OKRs.

Leaving needinfo until I get clarification.

(In reply to Chris Peterson [:cpeterson] from comment #42)

Good question. WebShare is in the Fenix team's "Items needed for Fenix Q4" bug list, but I don't see it in Fenix's Q4 OKRs.

Leaving needinfo until I get clarification.

The Fenix team says they will have more information by the end of next week. They have a Q4 planning week next week, including a session about Fenix's plans for PWA support (which is an umbrella term covering many different APIs and features to be delivered in phases).

Flags: needinfo?(cpeterson)

WebShare is listed in the GeckoView Q3 OKRs. That said, it is probably more of a "should" than a "must".

Emily recommends we move Web Share out of our September sprint and back to the top of our October backlog. GV work is still blocked waiting for DOM Web Share bug 1312422.

Rank: 5
Priority: P1 → P2
Whiteboard: [geckoview:m1909] [webcompat] → [webcompat]

Tentatively adding to GV's October sprint.

Whiteboard: [webcompat] → [webcompat] [geckoview:m1910]

P1 because we want to work on this in October.

Priority: P2 → P1
Whiteboard: [webcompat] [geckoview:m1910] → [webcompat] [geckoview:m1909] [geckoview:m1910]
Depends on: 1588420
Pushed by droeh@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/da2be4bf7c5d
Add WebShare support to GeckoView. r=geckoview-reviewers,snorp,marcosc,esawin

firefox71=wontfix because Fenix doesn't need us to uplift Web Share to GV 71 Beta.

Status: NEW → RESOLVED
Closed: 5 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla72

I don't think there is any documentation to do right now, as GeckoView/Fenix is not available yet, right?

For any such items that we'll need to update when Fenix is available, we are storing them at the following issue: https://github.com/mdn/browser-compat-data/issues/4983

We will then action these when Fenix is available.

Summary: Implement Web Share API → Implement Web Share API on Fenix
Alias: webshare
Blocks: 1701841
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: