Skip to content


Browse files Browse the repository at this point in the history
Only require a "transient" user activation for Web Audio rendering

Reviewed by Jer Noble.

Only require a "transient" user activation for Web Audio rendering instead of
having a strict synchronous user gesture requirement. Our behavior was so
strict it was confusing Web developers. It was also causing a lot of demo sites
to fail in Safari and work in other browsers.

For example:

Such demos require you to click a button but then they use an async function
and use `await` before actually starting the audio rendering.

* LayoutTests/webaudio/require-transient-activation-expected.txt: Added.
* LayoutTests/webaudio/require-transient-activation.html: Added.
* Source/WebCore/Modules/webaudio/AudioContext.cpp:

Canonical link:
  • Loading branch information
cdumez committed Nov 16, 2022
1 parent 4e7d68f commit 7cc4b42
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 2 deletions.
16 changes: 16 additions & 0 deletions LayoutTests/webaudio/require-transient-activation-expected.txt
@@ -0,0 +1,16 @@
Tests that starting Web Audio rendering requires a transient user activation.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".

PASS context.state is "suspended"
PASS context.state is "suspended"
PASS navigator.userActivation.isActive is true
PASS resume() promise was resolved
PASS context.state is "running"
PASS navigator.userActivation.isActive is true
PASS successfullyParsed is true


44 changes: 44 additions & 0 deletions LayoutTests/webaudio/require-transient-activation.html
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<script src="../resources/js-test.js"></script>
<script type="text/javascript" src="resources/audio-testing.js"></script>
description("Tests that starting Web Audio rendering requires a transient user activation.");
jsTestIsAsync = true;

onload = () => {
context = new AudioContext();

if (window.internals)
internals.setAudioContextRestrictions(context, 'RequireUserGestureForAudioStart');

shouldBeEqualToString('context.state', 'suspended');

node = context.createBufferSource();

shouldBeEqualToString('context.state', 'suspended');

runWithKeyDown(function() {
setTimeout(() => {
// We should have transient activation.
context.resume().then(() => {
testPassed("resume() promise was resolved");
shouldBeEqualToString('context.state', 'running');
// Transient activation should not have been consumed.
}, () => {
testFailed("resume() promise was rejected");
}, 10);
7 changes: 5 additions & 2 deletions Source/WebCore/Modules/webaudio/AudioContext.cpp
Expand Up @@ -77,9 +77,12 @@ static std::optional<float>& defaultSampleRateForTesting()

static bool shouldDocumentAllowWebAudioToAutoPlay(const Document& document)
if (document.processingUserGestureForMedia() || document.isCapturing())
if (document.isCapturing())
return true;
return document.quirks().shouldAutoplayWebAudioForArbitraryUserGesture() && document.topDocument().hasHadUserInteraction();
if (document.quirks().shouldAutoplayWebAudioForArbitraryUserGesture() && document.topDocument().hasHadUserInteraction())
return true;
auto* window = document.domWindow();
return window && window->hasTransientActivation();

void AudioContext::setDefaultSampleRateForTesting(std::optional<float> sampleRate)
Expand Down

0 comments on commit 7cc4b42

Please sign in to comment.