Closed Bug 1312422 Opened 4 years ago Closed 8 months ago

DOM implementation of Web Share API

Categories

(Core :: DOM: Core & HTML, enhancement, P1)

enhancement

Tracking

()

RESOLVED FIXED
mozilla71
Tracking Status
firefox70 --- wontfix
firefox71 --- fixed

People

(Reporter: jkt, Assigned: marcosc)

References

()

Details

(4 keywords, Whiteboard: [geckoview:m1909] [geckoview:m1910], [wptsync upstream])

Attachments

(2 files, 2 obsolete files)

Google has implemented the Web Share API which more info can be found here:

https://developers.google.com/web/updates/2016/10/navigator-share

The API can be found here:

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

The advantages are clear:
- Less third party JS loaded on pages to add share buttons
  - Less tracking
- Better UX for users
  - Less need for 6000 integrations add this site buttons, users use native OS
- Simple share implementation for site owners
Component: API: DOM → DOM
Product: Developer Documentation → Core
Alias: webshare
Coordinated a bit with Google folks on this API. Let's see where it ends up in the DOM triage... maybe something for next year.
> Google has implemented the Web Share API

Note that we've only rolled it out in a limited trial which will expire [1], so we aren't committing to it yet.

Thanks for filing the bug. I will keep an eye on this.

[1] https://github.com/jpchase/OriginTrials/
I was going to suggest a extension pollyfill? It doesn't seem something that would fit for test pilot etc. However developers could install and mess about with. Low effort to implement and devs can have a play with it etc. Perhaps even try a different angle to Google implementation etc too for a different view.
Yeah, we can do a polyfill, but it wouldn't be able to share to native apps, nor to a user-customizable set of web apps (since we don't yet have any [registration mechanism](https://github.com/WICG/web-share-target)). The best we'd be able to do is provide a set of popular services (Twitter, Facebook, etc). Still, I think that'd be worth doing.
Priority: -- → P3
W3C draft has been published https://wicg.github.io/web-share/
I'm interested in implementing this. If it seems important enough, can somebody mentor me ?

Thanks a lot.
@bogas04, maybe after 57 we can find someone to mentor you (as this is low priority right now). However, in the mean time, it would be valuable to make sure the spec has adequate test coverage:

https://github.com/w3c/web-platform-tests/tree/master/web-share

Reviewing the spec in detail would also be super helpful (and double checking that all assertions are tested). That will make implementation a lot easier, as you'll basically have a complete test suite to code against.
I hope I've covered the spec in sufficient detail in those tests :) But another set of eyes would always be appreciated.

I am the author of the Web Share spec and while I can't mentor work on Firefox, I am happy to answer any questions about the spec, test suite and Chrome implementation. Ping me at mgiuca@chromium.org or file issues at https://github.com/WICG/web-share/issues.
(In reply to Matt Giuca from comment #8)
> I hope I've covered the spec in sufficient detail in those tests :) 

From what I saw, they were pretty good.  

> But another set of eyes would always be appreciated.

Agree. Particularly if an adversarial approach is taken (i.e., try to break it and hack the crap out of it). 

> I am the author of the Web Share spec and while I can't mentor work on
> Firefox, I am happy to answer any questions about the spec, test suite and
> Chrome implementation. Ping me at mgiuca@chromium.org or file issues at
> https://github.com/WICG/web-share/issues.

Web Share API - Level 2 https://wicg.github.io/web-share/level-2/

Allow sharing and receiving files
Blocks: 1528801
Component: DOM → DOM: Core & HTML
Type: defect → enhancement
Alias: webshare
Summary: Consider experimental support for Web Share API → DOM implementation of Web Share API
Blocks: webshare
No longer blocks: 1528801
Blocks: 1528801
No longer blocks: webshare
No longer blocks: payment-handler, 1528801
Blocks: 1528801, webshare
Assignee: nobody → mcaceres
Priority: P3 → P2
Status: NEW → ASSIGNED

Initial DOM implementation

@olli, @baku, in order to create something like in the screenshot above on Desktop, but also allow share to work on Android/Fenix as a modal dialog, I was thinking of replicating how context menus are implemented in Gecko (I don't actually know how Context Menus are implemented... hence me pinging you here for guidance - so loosely basing this off nsContextMenu.js [1] ).

Here a is a rough sketch of what I'm thinking architecturally... The IDL would be:

// https://wicg.github.io/web-share/#navigator-interface
partial interface Navigator {
  [SecureContext, Throws] Promise<void> share(optional ShareData data = {});
};

// https://wicg.github.io/web-share/#sharedata-dictionary
dictionary ShareData {
  USVString title;
  USVString text;
  USVString url;
};

// The following events are fired internally inside Navigator::Share()...
[ChromeOnly, Constructor(optional MozShareEventInit eventInit = {})]
interface MozCanShareEvent : Event { 
  void supportsSharing(ShareResponse canShare);
};

[ChromeOnly, Constructor(optional MozShareEventInit eventInit = {})]
interface MozWebShare : Event {
  readonly attribute USVString title;
  readonly attribute USVString text;
  readonly attribute USVString url;
  void shareResult(ShareResponse didShare); // Called second
};

dictionary MozShareEventInit : EventInit {
  USVString title;
  USVString text;
  USVString url;
};

enum ShareResponse { "success", "fail", "abort" };

So, effectively, when Navigator::Share(ShareData data) is called, we do various pre-checks (e.g., check the url member is a valid url, etc.), and a promise mSharePromise is returned to script. Then, in order to handle the async aspects:

  1. Fire a MozCanShareEvent: this would allow GeckoViewPrompt.js and the equivalent on desktop to listen for a "canshare" event (can we prevent this from going to the actual Document... I only want GeckoViewPrompt.js to pick it up?).
  2. If it has any registered "share targets" on Android or Desktop, it calls ev.supportsSharing("success"). Otherwise, "fail" - mSharePromise is rejected with some DOMException.
  3. If "yes", ::Share() then dispatches a "share" event.
  4. GeckoViewPrompt.js listens for the event and tries to do the sharing: In JS/Android, show the actual context menu (do something similar to nsContextMenu.j [1] for Desktop... at mouseX, mouseY, etc.).
  5. Depending on the outcome of doing the share on Android or Desktop, the script calls ev.shareResult(/*a ShareResponse value, like "abort" if the user aborted*/).
  6. Either ::Share() gets the above call back, or we have a PromiseNativeHandler pick this up somehow.
  7. mSharePromise is settled: either resolve, or rejected.

WDYT? Some better way to do this? I'm open to alternatives.

[1] https://searchfox.org/mozilla-central/source/browser/base/content/nsContextMenu.js#115

Flags: needinfo?(bugs)
Flags: needinfo?(amarchesini)

It seems a good plan. I would like to know from Gijs if we can reuse some existing code from nsContextMenu.

Flags: needinfo?(amarchesini) → needinfo?(gijskruitbosch+bugs)

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

It seems a good plan. I would like to know from Gijs if we can reuse some existing code from nsContextMenu.

I'm afraid I don't understand the question. We don't have a context menu share implementation. What kind of code do you want to reuse?

Flags: needinfo?(gijskruitbosch+bugs) → needinfo?(amarchesini)

I'm thinking about how to open the contextual menu in the right position. Is it something that can be shared between this new 'shared' context menu and the right-click one?

Flags: needinfo?(amarchesini)

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

I'm thinking about how to open the contextual menu in the right position. Is it something that can be shared between this new 'shared' context menu and the right-click one?

There's not really a lot to be shared. We just pass a screenX/screenY from the content process to the parent, see https://searchfox.org/mozilla-central/rev/da3f3eaaacb6fb344fd21ac29ace2da0e33f12d3/browser/base/content/nsContextMenu.js#122-123,135 (parent) and https://searchfox.org/mozilla-central/rev/da3f3eaaacb6fb344fd21ac29ace2da0e33f12d3/browser/actors/ContextMenuChild.jsm#749-750 (child)

So, effectively, when Navigator::Share(ShareData data) is called, we do various pre-checks (e.g., check the url member is a valid url, etc.), and a promise mSharePromise is returned to script. Then, in order to handle the async aspects:

  1. Fire a MozCanShareEvent: this would allow GeckoViewPrompt.js and the equivalent on desktop to listen for a "canshare" event (can we prevent this from going to the actual Document... I only want GeckoViewPrompt.js to pick it up?).
  2. If it has any registered "share targets" on Android or Desktop, it calls ev.supportsSharing("success"). Otherwise, "fail" - mSharePromise is rejected with some DOMException.
    event dispatch is synchronous, but sharing support needs to happen asynchronously in the parent process (right?).
    Couldn't you just add a new (async) message to PContent which returns the result?
  1. If "yes", ::Share() then dispatches a "share" event.
  2. GeckoViewPrompt.js listens for the event and tries to do the sharing: In JS/Android, show the actual context menu (do something similar to nsContextMenu.j [1] for Desktop... at mouseX, mouseY, etc.).
  3. Depending on the outcome of doing the share on Android or Desktop, the script calls ev.shareResult(/*a ShareResponse value, like "abort" if the user aborted*/).
    How does this work, given that DOM event dispatch is synchronous, and message passing from child to parent is asynchronous?
Flags: needinfo?(bugs)

Adding [geckoview:fenix:m8] whiteboard tag because we would like to implement Web Share for GeckoView and Fenix (bug 1402369) in Q3.

Whiteboard: [geckoview:fenix:m8]

(In reply to :Gijs (he/him) from comment #17)

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

I'm thinking about how to open the contextual menu in the right position. Is it something that can be shared between this new 'shared' context menu and the right-click one?

There's not really a lot to be shared. We just pass a screenX/screenY from the content process to the parent, see https://searchfox.org/mozilla-central/rev/da3f3eaaacb6fb344fd21ac29ace2da0e33f12d3/browser/base/content/nsContextMenu.js#122-123,135 (parent) and https://searchfox.org/mozilla-central/rev/da3f3eaaacb6fb344fd21ac29ace2da0e33f12d3/browser/actors/ContextMenuChild.jsm#749-750 (child)

Ok, so, it might be neat then to create a "ShareMenuChild.js", "ShareMenuParent.js". At least, conceptually, they should be mostly structurally the same as context menu, but we can build independent share menu. We can then build that up over time using the ContextMenu implementation as inspiration/guide.

(In reply to Olli Pettay [:smaug] from comment #18)

How does this work, given that DOM event dispatch is synchronous, and message passing from child to parent is asynchronous?

I was looking at how FullScreen API works... it does: nsContentUtils::DispatchEventOnlyToChrome(), which gets picked up by the child:
https://searchfox.org/mozilla-central/source/browser/actors/DOMFullscreenChild.jsm#46

I can then pick that up ShareMenuChild.js -> which can send the message up to the parent.

GeckoView could also use the same thing:
https://searchfox.org/mozilla-central/source/mobile/android/chrome/geckoview/GeckoViewContentChild.js#409

However, I need to get a callback from the parent to the child to let me know two things: is share supported, and did the share succeed.

So:

  1. Hey, child, ask the parent/OS if it actually supports sharing?
  2. Wait for response: If yes, continue. If no, reject mSharePromise, with AbortError.
  3. Hey child, tell parent to share this { data } and let me know how it goes.
  4. Wait for response: how did the share go? "success", "failure", (user) "aborted" - settle mSharePromise appropriately.

From c++, is there some way I can set up a callback to listen for messages as described above?

What's the current status of this issue :marcosc? This is the last remaining piece left for PWA support in Fenix.

Flags: needinfo?(mcaceres)

Coming along, but waiting on feedback. I’d guess another 2-4 weeks to stabilize it. However, we can do Geckoview side in parallel. We have a pretty good idea how it’s going to work.

Flags: needinfo?(mcaceres)
Blocks: 1573029

@baku, @saschanaz and I have been putting in the IPC infrastructure and I'm now able to successfully send messages back and forth over IPC (yay!). We are wondering if you can provide us some feedback on the basic design of the IPDL protocol and how it's been implemented?

See: https://phabricator.services.mozilla.com/D36714

However, we have no idea how to now send a message to get a message to GeckoViewPrompt.js ... Basically, we are stuck at:

WebShareParent::RecvHasShareTargets() -> {🤷‍♂️ no idea/magic 🤷‍♀️} -> GeckoViewPrompt.js

Any suggestions? Or anyone we could ping for help?

Flags: needinfo?(amarchesini)

I wrote some comments about the IPDL, but all good. About GeckoView, usually I ask for help to :esawin, who is on PTO.

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

Ok, I'm just going to copy ColorPicker for now. The architecture closely resembles what we want for Web Share IMO.

Flags: needinfo?(esawin) → needinfo?(snorp)

Working though it with folks from the DOM Team... hope to have something in a couple of weeks.

Flags: needinfo?(snorp)
Attached file Implement DOM WebShare IPC (obsolete) —

The IPC stuff, for discussion.

Whiteboard: [geckoview:fenix:m8] → [geckoview:m1910]

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

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

Web Share base implementation

Attachment #9090274 - Attachment description: WIP: Base implementation → WIP: Web Share Base/DOM implementation
Attachment #9090274 - Attachment description: WIP: Web Share Base/DOM implementation → Web Share Base/DOM implementation
Attachment #9075582 - Attachment is obsolete: true
Attachment #9088668 - Attachment is obsolete: true

Hi Nika,
I'm getting this odd build error on Try [1], but not on my local compile:

/builds/worker/workspace/build/src/dom/ipc/WindowGlobalParent.cpp:320:55: error: Type 'mozilla::dom::WindowGlobalParent::ShareResolver' (aka 'function<void (const nsresult &)>') must not be used as parameter

Which I assume has something to do with https://bugzilla.mozilla.org/show_bug.cgi?format=default&id=1339537 ?

Any suggestions on how to fix that?

[1] https://treeherder.mozilla.org/#/jobs?repo=try&revision=0fb38cb7043632f96e3e9fe4efc94627e7862ebc&selectedJob=265860649

Flags: needinfo?(nika)

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

Hi Nika,
I'm getting this odd build error on Try [1], but not on my local compile:

/builds/worker/workspace/build/src/dom/ipc/WindowGlobalParent.cpp:320:55: error: Type 'mozilla::dom::WindowGlobalParent::ShareResolver' (aka 'function<void (const nsresult &)>') must not be used as parameter

Which I assume has something to do with https://bugzilla.mozilla.org/show_bug.cgi?format=default&id=1339537 ?

Any suggestions on how to fix that?

[1] https://treeherder.mozilla.org/#/jobs?repo=try&revision=0fb38cb7043632f96e3e9fe4efc94627e7862ebc&selectedJob=265860649

You can't pass std::function directly as a parameter unfortunately. IIRC the Mac OS STL uses an alignas declaration somewhere in the implementation, which has ABI issues potentially leading to unaligned loads on x86, leading to the MOZ_NON_PARAM warning.

The easiest solution is to pass the function in by reference, as-in std::function<foo>&&. In this case you'd want to probably take ShareResolver&&. Ideally this won't be required forever, as this current restriction is unfortunate.

Flags: needinfo?(nika)

Emily recommends we move GeckoView Web Share (bug 1402369) out of GeckoView's September sprint and back to the top of our October backlog. GV work is still blocked waiting for this DOM Web Share bug.

Whiteboard: [geckoview:m1909] → [geckoview:m19109

Tentatively adding to GeckoView's October sprint.

Whiteboard: [geckoview:m19109 → [geckoview:m1910]
Duplicate of this bug: 1528801
Priority: P2 → P1

Matching Bug 1402369 with P1.

Setting firefox70=wontfix because we don't need to uplift the Web Share API to GeckoView 70 Beta.

Whiteboard: [geckoview:m1910] → [geckoview:m1909] [geckoview:m1910]
Pushed by mcaceres@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/87334fc42a06
Web Share Base/DOM implementation r=farre
Created web-platform-tests PR https://github.com/web-platform-tests/wpt/pull/19594 for changes under testing/web-platform/tests
Whiteboard: [geckoview:m1909] [geckoview:m1910] → [geckoview:m1909] [geckoview:m1910], [wptsync upstream]
Upstream web-platform-tests status checks passed, PR will merge once commit reaches central.
Status: ASSIGNED → RESOLVED
Closed: 8 months ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla71
Upstream PR merged by moz-wptsync-bot

I've done some docs updates on this; see https://github.com/mdn/sprints/issues/2271#issuecomment-554352178 for the full details. Let me know if you'd like to see anything else done at this current time. Thanks!

Regressions: 1613292
You need to log in before you can comment on or make changes to this bug.