You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Clarify interaction between user-invoked suspend and autoplay policy #1802
In Chrome, the autoplay policy automatically suspends the context when UA is not allowed to start it. This "automatic" suspension by autoplay policy will be lifted off when 1) context.resume() call or 2) source.start() call upon user activation.
However, the problem here is that there is no way to distinguish between automatic suspension and user-invoked suspension. Therefore, even if user explicitly suspended the context, the current design will unblock the context and resume the rendering.
Apparently violating user's explicit request for suspension is not acceptable. So A and B should both be suspended. But this still brings a couple of questions:
Then what happens to source.start()? Do we still have to stash it somewhere and play it when the suspension is lifted off? Or should it be disregarded completely?
How do we differentiate a suspension from autoplay policy and user code? If we have to introduce another flag for this, now we have to deal with 3 internal flags; was allowed to start, is allowed to start, and user suspension. I think this needs a careful review to figure out all the corner cases.
IMO, having the context blocked for autoplay reasons is orthogonal to suspending the context. It shares some of the mechanism underneath, but that's about it. It's important that the user intent is respected. We need multiple flags to know whether the source of the "suspend" is the autoplay policy, or the user.
When source.start(), the context can initiate a transition from "suspended" to "running". However, it's also "suspended" explicitly, no sound should occur. start() starts the audio at t=currentTime, which is 0 and is not moving. When the page "resume()"s manually, this node will output sound. No change here.
Agreed on both points. But also it means the current PR might have to be re-written with new flags.
I am doing some thought experiments about introducing a new context state autoplayblocked, which can be useful to developers and also it gets rid of the need of new internal flags.
The only case that my change doesn't take into account is when suspend() was called when the context wasn't running. It seems a bit odd to me that calling suspend() on a blocked audio context should have a side effect that it doesn't actually resume when start() is called and I would be very surprised that either Chrome or Safari implement the autoplay policy this way. In my opinion, it is cleaner to avoid the side effect and only avoid resuming from a start() call when the audio context actually ran at some point. This is what is spec'd and I think what is implemented by all.
The only case that my change doesn't take into account is when suspend() was called when the context wasn't running.
Calling suspend() on a suspended context technically will do nothing. This is specced.
It seems a bit odd to me that calling suspend() on a blocked audio context should have a side effect that it doesn't actually resume when start() is called
I am confused with this sentence. Calling suspend() on a blocked audio context will do nothing. What side effect are you referring here?
In my opinion, it is cleaner to avoid the side effect and only avoid resuming from a start() call when the audio context actually ran at some point.
What if you call suspend() when it is already blocked by autoplay policy? Then the context did not run at all since its construction, so start() call will resume the context.
Not elegant, but a simple solution would be having a flag in AudioContext, something like was suspended by user. source.start() call will check two flags,was suspended by user and is allowed to play, and then resume the context rendering when they are false and true respectively. Any suspend() call will make the former true and start() call will not be able to resume the context. Again, not elegant.
The side effect I'm referring to is that, if I understand correctly, the issue is that the following will resume the audio context and you believe it shouldn't:
My concern here is that suspend() happens when ac is already suspended and this should be a no-op. If I understand correctly, the change that is being suggested is to make this prevent auto-resume when start() is called. Is this correct?
My concern here is that suspend() happens when ac is already suspended and this should be a no-op.
These are two different types of suspension, and I think it needs to be handled differently.
If I understand correctly, the change that is being suggested is to make this prevent auto-resume when start() is called.
Yes. If user wants a context to be suspended explicitly, the intent should be honored. The question is how this behavior should be documented in the spec.
@mounirlamouri I still think we have some confusions. Consider this case on an AudioContext C:
C is suspended by UA at construction because is allowed to play flag is false.
source.start() is called from user interaction.
is allowed to play is true, and then C is automatically resumed because it was suspended by UA.
The step 3 is the current behavior of Chrome. The current PR does not cause any action (i.e. start rendering) other than setting two flags. I am not sure if that's right. The initial start() call upon an UA suspended context will automatically resume it, so the prose should contain something more than flipping flags.
#1802 (comment) is the correct way to implement this. It needs to be explicit.
In fact, this is more or less what we will be doing soon in Gecko for JavaScript breakpoints, for example: the audio stops when execution is paused, but the state is different than suspend/resume/close from the user.
At the end of the day, I don't feel very strongly either way but our implementation was trying to stay close to WebKit's and the spec was obviously following it. I'm not sure how safe it is to spec something that doesn't match current implementations but maybe @jernoble would be willing to change WebKit's?
I have not tried yet, but how does WebKit handle the example I posted above? If WebKit somehow does it "correctly" by not resuming a context that is explicitly suspended, we might have missed some internal details in the PR.
I've been informed by jer that this is a Safari bug, and is not intentional, so we'll proceed with the spec as proposed here: respecting the author's intent, and not resuming the context if it's been suspended.
I do not think so. This is simple a spec bug which UA could bypass a suspend() call with a user gesture. We agreed to fix the oversight. There's no issue in the regard of fingerprinting or general privacy.
I tend to agree. The problem is not that the user can't suspend or that there is no auto-suspend; it is that when auto-suspend is cancelled (by user interaction), that also accidentally resumes user-invoked suspension.
Activity
padenot commentedon Jan 11, 2019
Answering in reverse order:
source.start(), the context can initiate a transition from "suspended" to "running". However, it's also "suspended" explicitly, no sound should occur.start()starts the audio at t=currentTime, which is 0 and is not moving. When the page "resume()"s manually, this node will output sound. No change here.@alastor0325
hoch commentedon Jan 11, 2019
Agreed on both points. But also it means the current PR might have to be re-written with new flags.
I am doing some thought experiments about introducing a new context state
autoplayblocked, which can be useful to developers and also it gets rid of the need of new internal flags.mounirlamouri commentedon Jan 11, 2019
The only case that my change doesn't take into account is when
suspend()was called when the context wasn't running. It seems a bit odd to me that callingsuspend()on a blocked audio context should have a side effect that it doesn't actually resume whenstart()is called and I would be very surprised that either Chrome or Safari implement the autoplay policy this way. In my opinion, it is cleaner to avoid the side effect and only avoid resuming from astart()call when the audio context actually ran at some point. This is what is spec'd and I think what is implemented by all.hoch commentedon Jan 11, 2019
Okay, let's clarify a bit more.
Calling suspend() on a suspended context technically will do nothing. This is specced.
I am confused with this sentence. Calling suspend() on a blocked audio context will do nothing. What side effect are you referring here?
What if you call suspend() when it is already blocked by autoplay policy? Then the context did not run at all since its construction, so start() call will resume the context.
Not elegant, but a simple solution would be having a flag in AudioContext, something like
was suspended by user.source.start()call will check two flags,was suspended by userandis allowed to play, and then resume the context rendering when they arefalseandtruerespectively. Any suspend() call will make the formertrueand start() call will not be able to resume the context. Again, not elegant.mounirlamouri commentedon Jan 14, 2019
The side effect I'm referring to is that, if I understand correctly, the issue is that the following will resume the audio context and you believe it shouldn't:
My concern here is that suspend() happens when
acis already suspended and this should be a no-op. If I understand correctly, the change that is being suggested is to make this prevent auto-resume whenstart()is called. Is this correct?hoch commentedon Jan 14, 2019
These are two different types of suspension, and I think it needs to be handled differently.
Yes. If user wants a context to be suspended explicitly, the intent should be honored. The question is how this behavior should be documented in the spec.
hoch commentedon Jan 14, 2019
@mounirlamouri I still think we have some confusions. Consider this case on an AudioContext C:
is allowed to playflag is false.source.start()is called from user interaction.is allowed to playis true, and then C is automatically resumed because it was suspended by UA.The step 3 is the current behavior of Chrome. The current PR does not cause any action (i.e. start rendering) other than setting two flags. I am not sure if that's right. The initial start() call upon an UA suspended context will automatically resume it, so the prose should contain something more than flipping flags.
@mounirlamouri WDTY about this point?
The solution that I am thinking is:
is allowed to playflag is false.context.suspend()called from user code.2.1. This sets
is suspended by userflag to true.source.start()is called from user interaction.is allowed to playbecomes true, but C cannot be resumed becauseis suspended by useris true.context.resume()is called from non-user interaction code.5.1. This sets
is suspended by userto false.is allowed to playis true andis suspended by useris false, so the rendering will be resumed.In short, we need an additional flag of
is suspended by userand this flag only gets updated bycontext.suspend()andcontext.resume()method.hoch commentedon Jan 14, 2019
Two directions:
padenot commentedon Jan 15, 2019
#1802 (comment) is the correct way to implement this. It needs to be explicit.
In fact, this is more or less what we will be doing soon in Gecko for JavaScript breakpoints, for example: the audio stops when execution is paused, but the state is different than suspend/resume/close from the user.
mounirlamouri commentedon Jan 15, 2019
At the end of the day, I don't feel very strongly either way but our implementation was trying to stay close to WebKit's and the spec was obviously following it. I'm not sure how safe it is to spec something that doesn't match current implementations but maybe @jernoble would be willing to change WebKit's?
hoch commentedon Jan 15, 2019
I have not tried yet, but how does WebKit handle the example I posted above? If WebKit somehow does it "correctly" by not resuming a context that is explicitly suspended, we might have missed some internal details in the PR.
mounirlamouri commentedon Jan 15, 2019
My understanding is that WebKit does the same as Chrome which should be the same as the PR I sent.
3 remaining items
hoch commentedon Feb 25, 2019
@jernoble @mounirlamouri We would like to proceed with the PR #1823. PTAL.
padenot commentedon Mar 14, 2019
I've been informed by jer that this is a Safari bug, and is not intentional, so we'll proceed with the spec as proposed here: respecting the author's intent, and not resuming the context if it's been suspended.
mounirlamouri commentedon Mar 14, 2019
I've approved the CL. Thanks @hoch
svgeesus commentedon Jul 1, 2019
Reopening; the Director want to know whether there are privacy implications for this issue which would merit review.
hoch commentedon Jul 1, 2019
I do not think so. This is simple a spec bug which UA could bypass a suspend() call with a user gesture. We agreed to fix the oversight. There's no issue in the regard of fingerprinting or general privacy.
svgeesus commentedon Jul 2, 2019
I tend to agree. The problem is not that the user can't suspend or that there is no auto-suspend; it is that when auto-suspend is cancelled (by user interaction), that also accidentally resumes user-invoked suspension.
rtoy commentedon Sep 18, 2019
What needs to be done here?
padenot commentedon Sep 26, 2019
Probably just re-closing it.
rtoy commentedon Sep 26, 2019
Closing.
Re-open if needed.