html icon indicating copy to clipboard operation
html copied to clipboard

Add some mechanism to know that a message event was triggered by user activation

Open RByers opened this issue 9 years ago • 16 comments

The triggered by user activation logic allows for this state to flow between cross-origin iframes via postMessage. Unfortunately there's no good way for script to query whether it's currently in this state.

For example script in a trusted frame may want to respond to requests from an untrusted iframe (such as navigating to a new URL) only when the request from the iframe was initiated as a result of a user activation.

That is to say, there's no good reason the "triggered by user activation" logic should be available only to the user agent, script in other frames might want to depend on it for the exact same reasons. There's some discussion here, here and here about why this is particularly important for AMP. Today they're hacking around this in Chrome by attempting to write to the clipboard and seeing if that's successful to sniff that they're currently in the context of a user gesture. This is unfortunate because it potentially clobbers the clipboard and isn't necessarily interoperable. /cc @dvoytenko @cramforce.

Should we just add a boolean property like navigator.triggeredByUserActivation? Or should we perhaps define MessageEvent.isTrusted to be set to true when the script that invoked postMessage was itself triggered by user activation?

RByers avatar Oct 27 '16 00:10 RByers

I don't think we can use isTrusted on message events, because it is currently always true for all message events invoked via postMessage.

navigator.triggeredByUserActivation may leak the fact to other origins. A new property on MessageEvent might be better to delegate the knowledge of a user action by explicitly sending the message.

cramforce avatar Oct 27 '16 01:10 cramforce

MessageEvent.triggeredByUserActivation (or some such) SGTM.

But can you elaborate on your "leak" concern? Isn't it fine for any origin to know at any time whether it's currently inside the context of "triggered by user activation" algorithm? It can figure that out already (at least in blink) by trying to write to the clipboard.

RByers avatar Oct 27 '16 01:10 RByers

I don't see why we would make this MessageEvent-specific. After all it also flows through various other interesting things like setTimeouts and such.

domenic avatar Oct 27 '16 01:10 domenic

Yeah, that was my thinking too. Malte makes a good point that this is probably mainly useful cross-document (because within a single document there's no security boundary - just like Event.isTrusted).

But I can imagine some scenarios where it's useful to know even within a document. For example, you want to do a window.open either directly or (if you can tell it'll get blocked) via some additional "click here to open" UI.

RByers avatar Oct 27 '16 01:10 RByers

I have no preference in API really. If we can make it work for setTimeout and friends that is cool, of course.

Exposing this will unlock a lot of use cases. For example it is rarely possible to use sandbox on iframe today without allowing allow-popups and allow-top-navigation. If user gestures were exposed, one could mandate to iframes: "Let me know when you would like to navigate via a message" and then the container can, for example, check whether the destination is acceptable, before executing the navigation on behalf of the child iframe.

In AMP we'd like to allow iframes to request resize in response to a user gesture, which is very similar.

cramforce avatar Oct 27 '16 03:10 cramforce

If we expose it it should be exposed on the global I think since it's a task-annotation effectively. But I'm not sure we have described the underlying concept well enough to expose an API. E.g., integration with a theoretical interaction event specification is missing, setTimeout() forwarding is not defined. This new MessageEvent forwarding is not defined, etc.

annevk avatar Oct 27 '16 06:10 annevk

Global sounds pretty reasonable to me. It also won't leak to other origins since it's not on the whitelist of cross-origin-accessible global properties. I guess it'd be only on Window, not on WorkerGlobalScopes.

@annevk we have a separate bug to define this better; #1903. Do you think straightening that out is a prerequisite to speccing/shipping an API that reflects it?

We also need multi-vendor interest, of course. I guess we should let this thread settle a bit further before we send up the signal flares in that direction though.

domenic avatar Oct 27 '16 19:10 domenic

Whether it needs to be exposed in workers depends on the semantics of what OP is requesting. Fairly easy to postMessage() to a worker. But if that message goes to a MessageChannel you would not know where it ends up and when it might be handled, so you'll have to deliminate that somehow.

I think whether I'm comfortable with just adding an API without corresponding architecture depends largely on how interoperable implementations are today. Extending it to messaging as requested by OP would require some suitable definition though as there's a myriad of interpretations possible.

annevk avatar Oct 28 '16 07:10 annevk

I just want to pitch in and highlight the use case of using workers. With OffscreenCanvas it's now becoming feasible to run an entire game engine in a worker. However at the moment a game engine in a worker never has a user gesture. Considering some major features like starting audio playback are limited to user gestures, this issue amounts to blocking use of OffscreenCanvas for games.

This is complicated by the fact various APIs are unavailable in a worker. So playing audio when the user taps the screen would probably end up working like this:

"touchstart" event in main document -> postMessage() -> run game logic in worker -> postMessage() -> play audio in main document

For the audio playback to be allowed, the user gesture needs to be propagated through two separate postMessages(). Alternatively if the main document could "save" the user gesture to use it later, that could work too.

A related case is using user gestures with async code. For example if you want to prepare data to copy to the clipboard asynchronously, you can never copy it, because you lose the user gesture by the time the data is ready. I wrote more about this issue here: https://discourse.wicg.io/t/user-gesture-restrictions-and-async-code/1640

AshleyScirra avatar Jun 14 '17 17:06 AshleyScirra

Thanks for the scenario! In chromium @mustaqahmed is experimenting with a simpler model for gestures that should solve most of what you've described.

Still, probably worth thinking specifically about explicit ways of passing 'activation' to a worker - I filed this chromium bug.

Is the ability to play audio in a Worker tracked anywhere? There's @hoch's AudioWorklet proposal, but I don't think the use case is the same there (in that case you really want your audio work isolated from the rest of the application, where you want it running on the same thread as your game logic). Also AFAIK WebAudio doesn't currently require a user gesture in chromium (though may in the future). Maybe you're talking about other ways of playing audio? /cc @rtoy

RByers avatar Jun 15 '17 15:06 RByers

Now the AudioWorklet spec has become more than a proposal. Also we recently added the MessagePort between AudioWorkletNode and AudioWorkletProcessor. Perhaps this can be something to think about.

FWIW, there has been an attempt to support BaseAudioContext in worker scope, but it was pushed back to V2 spec. I believe this is the solution for @AshleyScirra's scenario. I am aware that it is really important for game engine developers, so I would love to support it sooner than later.

hoch avatar Jun 15 '17 16:06 hoch

The difficult thing about audio playback is that while AudioContext works for a lot of things, it is designed around fully-decoded, fully-decompressed audio buffers. This is unsuitable for long music tracks, where HTMLAudioElement has a nice streaming capability. However being a DOM element it's difficult to see how it would ever run in a worker. Perhaps one day MediaStreams will even be transferrable to a worker, but even then you still need a play() call on a DOM element that would have to obey user gesture restrictions.

AshleyScirra avatar Jun 15 '17 16:06 AshleyScirra

I've posted my proposal here: https://github.com/dtapuska/useractivation TLDR: add navigator.userActivation and message.userActivation

dtapuska avatar Jun 27 '18 21:06 dtapuska

Thanks! I'm pretty happy with the proposal myself. And I remain hopeful that we can add something here even before we fix the interop problems around user activation (#1903); the basic functionality, of knowing whether user activation is currently in progress, or has at some point happened, should survive any future modifications.

Looking back at earlier parts of the thread, it seems like some of @annevk's concerns were around how this fits with postMessage and message-passing. I think @dtapuska's proposal addresses that pretty elegantly, with the opt-in at message time capturing the state of the window posting the message. I'm curious to hear others' thoughts.

domenic avatar Jun 27 '18 21:06 domenic

I'm curious to hear others' thoughts.

As long as folks are convinced it's really best for this to be sender opt-in, then I'm happy with @dtapuska's proposal!

RByers avatar Jun 27 '18 21:06 RByers

The first part of this issue (adding a navigator interface) is covered in #4008, and we currently have PR #8254 under review for that part.

mustaqahmed avatar Sep 14 '22 16:09 mustaqahmed

Let's consider this resolved. If there's still a need for message-event API that can be discussed in a new issue as a lot of things changed since 2016.

annevk avatar Sep 30 '22 12:09 annevk