<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[183424] trunk</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/183424">183424</a></dd>
<dt>Author</dt> <dd>jer.noble@apple.com</dd>
<dt>Date</dt> <dd>2015-04-27 14:35:03 -0700 (Mon, 27 Apr 2015)</dd>
</dl>
<h3>Log Message</h3>
<pre>[WebAudio] AudioContext does not remove user-gesture restriction during resume()
https://bugs.webkit.org/show_bug.cgi?id=144211
Reviewed by Eric Carlson.
Source/WebCore:
Tests: webaudio/audiocontext-restriction-audiobuffersourcenode-start.html
webaudio/audiocontext-restriction.html
Before the introduction of resume(), suspend(), and stop(), AudioContexts which required
a user-gesture would start normally, but would effectively mute their outputs. Now that
the AudioContext's state property is exposed to JavaScript, the AudioContext should stay
in the "suspended" state until the user-gesture restriction is lifted.
Add a new method, willBeginPlayback() which checks and potentially clears the context's
behavior restrictions before checking with the MediaSession. Call this new willBeginPlayback()
method when the state would transition to "running".
Because they may be called before any nodes are created, make sure to call lazyInitialize()
from within the JS-exposed resumePlayback(), suspendPlayback(), and stopPlayback() methods.
Instead of clearing the behavior restrictions directly, scheduled AudioNodes should instead
call a new method nodeWillBeginPlayback(). Because existing sites will call AudioNode.start()
inside a user-gesture handler to clear the user-gesture restriction, call startRendering()
from nodeWillBeginPlayback(). But because we don't want AudioNode.start() to resume playback
unconditionally, only do so when the user-gesture restriction is set.
Now that an AudioContext will not transition to "running" state without a user-gesture (if
that restriction is set), there's no reason to check for that restriction from inside
AudioDestinationNode.
Add some internal methods to set and clear AudioContext BehaviorRestrictions for testing purposes.
* Modules/webaudio/AudioBufferSourceNode.cpp:
(WebCore::AudioBufferSourceNode::startPlaying):
* Modules/webaudio/AudioContext.cpp:
(WebCore::AudioContext::nodeWillBeginPlayback):
(WebCore::AudioContext::willBeginPlayback):
(WebCore::AudioContext::willPausePlayback):
(WebCore::AudioContext::startRendering):
(WebCore::AudioContext::suspendContext):
(WebCore::AudioContext::resumeContext):
(WebCore::AudioContext::closeContext):
(WebCore::AudioContext::suspendPlayback):
(WebCore::AudioContext::mayResumePlayback):
* Modules/webaudio/AudioContext.h:
(WebCore::AudioContext::behaviorRestrictions):
(WebCore::AudioContext::userGestureRequiredForAudioStart):
(WebCore::AudioContext::pageConsentRequiredForAudioStart):
* Modules/webaudio/AudioDestinationNode.cpp:
(WebCore::AudioDestinationNode::render):
* Modules/webaudio/AudioScheduledSourceNode.cpp:
(WebCore::AudioScheduledSourceNode::start):
* testing/Internals.cpp:
(WebCore::Internals::setAudioContextRestrictions):
* testing/Internals.h:
* testing/Internals.idl:
LayoutTests:
* webaudio/audiocontext-restriction-audiobuffersourcenode-start-expected.txt: Added.
* webaudio/audiocontext-restriction-audiobuffersourcenode-start.html: Added.
* webaudio/audiocontext-restriction-expected.txt: Added.
* webaudio/audiocontext-restriction.html: Added.
* webaudio/resources/audio-testing.js:
(runWithKeyDown):</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestswebaudioresourcesaudiotestingjs">trunk/LayoutTests/webaudio/resources/audio-testing.js</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModuleswebaudioAudioBufferSourceNodecpp">trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp</a></li>
<li><a href="#trunkSourceWebCoreModuleswebaudioAudioContextcpp">trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp</a></li>
<li><a href="#trunkSourceWebCoreModuleswebaudioAudioContexth">trunk/Source/WebCore/Modules/webaudio/AudioContext.h</a></li>
<li><a href="#trunkSourceWebCoreModuleswebaudioAudioDestinationNodecpp">trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp</a></li>
<li><a href="#trunkSourceWebCoreModuleswebaudioAudioScheduledSourceNodecpp">trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp</a></li>
<li><a href="#trunkSourceWebCoretestingInternalscpp">trunk/Source/WebCore/testing/Internals.cpp</a></li>
<li><a href="#trunkSourceWebCoretestingInternalsh">trunk/Source/WebCore/testing/Internals.h</a></li>
<li><a href="#trunkSourceWebCoretestingInternalsidl">trunk/Source/WebCore/testing/Internals.idl</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestswebaudioaudiocontextrestrictionaudiobuffersourcenodestartexpectedtxt">trunk/LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start-expected.txt</a></li>
<li><a href="#trunkLayoutTestswebaudioaudiocontextrestrictionaudiobuffersourcenodestarthtml">trunk/LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start.html</a></li>
<li><a href="#trunkLayoutTestswebaudioaudiocontextrestrictionexpectedtxt">trunk/LayoutTests/webaudio/audiocontext-restriction-expected.txt</a></li>
<li><a href="#trunkLayoutTestswebaudioaudiocontextrestrictionhtml">trunk/LayoutTests/webaudio/audiocontext-restriction.html</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (183423 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-04-27 21:30:58 UTC (rev 183423)
+++ trunk/LayoutTests/ChangeLog        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+2015-04-27 Jer Noble <jer.noble@apple.com>
+
+ [WebAudio] AudioContext does not remove user-gesture restriction during resume()
+ https://bugs.webkit.org/show_bug.cgi?id=144211
+
+ Reviewed by Eric Carlson.
+
+ * webaudio/audiocontext-restriction-audiobuffersourcenode-start-expected.txt: Added.
+ * webaudio/audiocontext-restriction-audiobuffersourcenode-start.html: Added.
+ * webaudio/audiocontext-restriction-expected.txt: Added.
+ * webaudio/audiocontext-restriction.html: Added.
+ * webaudio/resources/audio-testing.js:
+ (runWithKeyDown):
+
</ins><span class="cx"> 2015-04-27 Alexey Proskuryakov <ap@apple.com>
</span><span class="cx">
</span><span class="cx"> rdar://problem/16678392 Page visibility tests are broken in Yosemite
</span></span></pre></div>
<a id="trunkLayoutTestswebaudioaudiocontextrestrictionaudiobuffersourcenodestartexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start-expected.txt (0 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start-expected.txt         (rev 0)
+++ trunk/LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start-expected.txt        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+Basic tests for AudioNode API.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS context.state is "suspended"
+node.connect(context.destination)
+PASS context.state is "suspended"
+Calling context.resume() without a user gesture
+Wait for 100ms
+Calling node.start() with a user gesture
+PASS context.resume() (without a user gesture) promise resolved
+PASS context.state is "running"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestswebaudioaudiocontextrestrictionaudiobuffersourcenodestarthtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start.html (0 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start.html         (rev 0)
+++ trunk/LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start.html        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -0,0 +1,69 @@
</span><ins>+<!DOCTYPE html>
+
+<html>
+<head>
+<script src="../resources/js-test-pre.js"></script>
+<script type="text/javascript" src="resources/audio-testing.js"></script>
+</head>
+
+<body>
+<div id="description"></div>
+<div id="console"></div>
+
+<script>
+description('Basic tests for AudioNode API.');
+
+var context = null;
+var node = null;
+var calledResumeWithUserGesture = false;
+
+function runTest() {
+ if (window.testRunner) {
+ testRunner.dumpAsText();
+ testRunner.waitUntilDone();
+ }
+
+ window.jsTestIsAsync = true;
+
+ context = new webkitAudioContext();
+
+ if (window.internals)
+ internals.setAudioContextRestrictions(context, 'RequireUserGestureForAudioStart');
+
+ shouldBe('context.state', '"suspended"');
+
+ node = context.createBufferSource();
+ evalAndLog('node.connect(context.destination)');
+
+ shouldBe('context.state', '"suspended"');
+
+ debug('Calling context.resume() without a user gesture');
+ context.resume().then(noUserGestureResumeSucceeded, noUserGestureResumeFailed);
+
+ debug('Wait for 100ms');
+ window.setTimeout(function() {
+ runWithKeyDown(function() {
+ debug('Calling node.start() with a user gesture');
+ node.start();
+ });
+ }, 100);
+}
+
+function noUserGestureResumeFailed() {
+ testFailed('context.resume() (without a user gesture) promise rejected');
+ finishJSTest();
+}
+
+function noUserGestureResumeSucceeded() {
+ testPassed('context.resume() (without a user gesture) promise resolved');
+ shouldBe('context.state', '"running"');
+ finishJSTest();
+}
+
+runTest();
+
+</script>
+
+<script src="../resources/js-test-post.js"></script>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestswebaudioaudiocontextrestrictionexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/webaudio/audiocontext-restriction-expected.txt (0 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/webaudio/audiocontext-restriction-expected.txt         (rev 0)
+++ trunk/LayoutTests/webaudio/audiocontext-restriction-expected.txt        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -0,0 +1,18 @@
</span><ins>+Basic tests for AudioNode API.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS context.state is "suspended"
+node.connect(context.destination)
+PASS context.state is "suspended"
+Calling context.resume() without a user gesture
+Wait for 100ms
+Calling context.resume() with a user gesture
+PASS context.resume() (without a user gesture) promise resolved
+PASS context.state is "running"
+PASS context.resume() (with a user gesture) promise resolved
+PASS context.state is "running"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestswebaudioaudiocontextrestrictionhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/webaudio/audiocontext-restriction.html (0 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/webaudio/audiocontext-restriction.html         (rev 0)
+++ trunk/LayoutTests/webaudio/audiocontext-restriction.html        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -0,0 +1,85 @@
</span><ins>+<!DOCTYPE html>
+
+<html>
+<head>
+<script src="../resources/js-test-pre.js"></script>
+<script type="text/javascript" src="resources/audio-testing.js"></script>
+</head>
+
+<body>
+<div id="description"></div>
+<div id="console"></div>
+
+<script>
+description('Basic tests for AudioNode API.');
+
+var context = null;
+var node = null;
+var calledResumeWithUserGesture = false;
+
+function runTest() {
+ if (window.testRunner) {
+ testRunner.dumpAsText();
+ testRunner.waitUntilDone();
+ }
+
+ window.jsTestIsAsync = true;
+
+ context = new webkitAudioContext();
+
+ if (window.internals)
+ internals.setAudioContextRestrictions(context, 'RequireUserGestureForAudioStart');
+
+ shouldBe('context.state', '"suspended"');
+
+ node = context.createBufferSource();
+ evalAndLog('node.connect(context.destination)');
+
+ shouldBe('context.state', '"suspended"');
+
+ debug('Calling context.resume() without a user gesture');
+ context.resume().then(noUserGestureResumeSucceeded, noUserGestureResumeFailed);
+
+ debug('Wait for 100ms');
+ window.setTimeout(function() {
+ runWithKeyDown(function() {
+ debug('Calling context.resume() with a user gesture');
+ context.resume().then(resumeSucceeded, resumeFailed);
+ calledResumeWithUserGesture = true;
+ });
+ }, 100);
+}
+
+function noUserGestureResumeFailed() {
+ testFailed('context.resume() (without a user gesture) promise rejected');
+ finishJSTest();
+}
+
+function noUserGestureResumeSucceeded() {
+ if (!calledResumeWithUserGesture) {
+ testFailed('context.resume() (without a user gesture) suceeded incorrectly.');
+ finishJSTest();
+ return;
+ }
+ testPassed('context.resume() (without a user gesture) promise resolved');
+ shouldBe('context.state', '"running"');
+}
+
+function resumeFailed() {
+ testFailed('context.resume() (with a user gesture) promise rejected');
+ finishJSTest();
+}
+
+function resumeSucceeded() {
+ testPassed('context.resume() (with a user gesture) promise resolved');
+ shouldBe('context.state', '"running"');
+ finishJSTest();
+}
+
+runTest();
+
+</script>
+
+<script src="../resources/js-test-post.js"></script>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestswebaudioresourcesaudiotestingjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/webaudio/resources/audio-testing.js (183423 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/webaudio/resources/audio-testing.js        2015-04-27 21:30:58 UTC (rev 183423)
+++ trunk/LayoutTests/webaudio/resources/audio-testing.js        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -190,3 +190,23 @@
</span><span class="cx"> testFailed(text + " should throw TypeError.");
</span><span class="cx"> }
</span><span class="cx"> }
</span><ins>+
+function runWithKeyDown(fn)
+{
+ // FIXME: WKTR does not yet support the keyDown() message. Do a mouseDown here
+ // instead until keyDown support is added.
+ var eventName = !window.testRunner || eventSender.keyDown ? 'keypress' : 'mousedown'
+
+ function thunk() {
+ document.removeEventListener(eventName, thunk, false);
+ fn();
+ }
+ document.addEventListener(eventName, thunk, false);
+
+ if (window.testRunner) {
+ if (eventSender.keyDown)
+ eventSender.keyDown(" ", []);
+ else
+ eventSender.mouseDown();
+ }
+}
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (183423 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-04-27 21:30:58 UTC (rev 183423)
+++ trunk/Source/WebCore/ChangeLog        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -1,3 +1,62 @@
</span><ins>+2015-04-27 Jer Noble <jer.noble@apple.com>
+
+ [WebAudio] AudioContext does not remove user-gesture restriction during resume()
+ https://bugs.webkit.org/show_bug.cgi?id=144211
+
+ Reviewed by Eric Carlson.
+
+ Tests: webaudio/audiocontext-restriction-audiobuffersourcenode-start.html
+ webaudio/audiocontext-restriction.html
+
+ Before the introduction of resume(), suspend(), and stop(), AudioContexts which required
+ a user-gesture would start normally, but would effectively mute their outputs. Now that
+ the AudioContext's state property is exposed to JavaScript, the AudioContext should stay
+ in the "suspended" state until the user-gesture restriction is lifted.
+
+ Add a new method, willBeginPlayback() which checks and potentially clears the context's
+ behavior restrictions before checking with the MediaSession. Call this new willBeginPlayback()
+ method when the state would transition to "running".
+
+ Because they may be called before any nodes are created, make sure to call lazyInitialize()
+ from within the JS-exposed resumePlayback(), suspendPlayback(), and stopPlayback() methods.
+
+ Instead of clearing the behavior restrictions directly, scheduled AudioNodes should instead
+ call a new method nodeWillBeginPlayback(). Because existing sites will call AudioNode.start()
+ inside a user-gesture handler to clear the user-gesture restriction, call startRendering()
+ from nodeWillBeginPlayback(). But because we don't want AudioNode.start() to resume playback
+ unconditionally, only do so when the user-gesture restriction is set.
+
+ Now that an AudioContext will not transition to "running" state without a user-gesture (if
+ that restriction is set), there's no reason to check for that restriction from inside
+ AudioDestinationNode.
+
+ Add some internal methods to set and clear AudioContext BehaviorRestrictions for testing purposes.
+
+ * Modules/webaudio/AudioBufferSourceNode.cpp:
+ (WebCore::AudioBufferSourceNode::startPlaying):
+ * Modules/webaudio/AudioContext.cpp:
+ (WebCore::AudioContext::nodeWillBeginPlayback):
+ (WebCore::AudioContext::willBeginPlayback):
+ (WebCore::AudioContext::willPausePlayback):
+ (WebCore::AudioContext::startRendering):
+ (WebCore::AudioContext::suspendContext):
+ (WebCore::AudioContext::resumeContext):
+ (WebCore::AudioContext::closeContext):
+ (WebCore::AudioContext::suspendPlayback):
+ (WebCore::AudioContext::mayResumePlayback):
+ * Modules/webaudio/AudioContext.h:
+ (WebCore::AudioContext::behaviorRestrictions):
+ (WebCore::AudioContext::userGestureRequiredForAudioStart):
+ (WebCore::AudioContext::pageConsentRequiredForAudioStart):
+ * Modules/webaudio/AudioDestinationNode.cpp:
+ (WebCore::AudioDestinationNode::render):
+ * Modules/webaudio/AudioScheduledSourceNode.cpp:
+ (WebCore::AudioScheduledSourceNode::start):
+ * testing/Internals.cpp:
+ (WebCore::Internals::setAudioContextRestrictions):
+ * testing/Internals.h:
+ * testing/Internals.idl:
+
</ins><span class="cx"> 2015-04-27 Alexey Proskuryakov <ap@apple.com>
</span><span class="cx">
</span><span class="cx"> Build fix.
</span></span></pre></div>
<a id="trunkSourceWebCoreModuleswebaudioAudioBufferSourceNodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp (183423 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp        2015-04-27 21:30:58 UTC (rev 183423)
+++ trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -469,8 +469,7 @@
</span><span class="cx"> {
</span><span class="cx"> ASSERT(isMainThread());
</span><span class="cx">
</span><del>- if (ScriptController::processingUserGesture())
- context()->removeBehaviorRestriction(AudioContext::RequireUserGestureForAudioStartRestriction);
</del><ins>+ context()->nodeWillBeginPlayback();
</ins><span class="cx">
</span><span class="cx"> if (m_playbackState != UNSCHEDULED_STATE) {
</span><span class="cx"> ec = INVALID_STATE_ERR;
</span></span></pre></div>
<a id="trunkSourceWebCoreModuleswebaudioAudioContextcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp (183423 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp        2015-04-27 21:30:58 UTC (rev 183423)
+++ trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -985,18 +985,64 @@
</span><span class="cx"> return m_isStopScheduled ? 0 : ActiveDOMObject::scriptExecutionContext();
</span><span class="cx"> }
</span><span class="cx">
</span><del>-void AudioContext::startRendering()
</del><ins>+void AudioContext::nodeWillBeginPlayback()
</ins><span class="cx"> {
</span><del>- if (ScriptController::processingUserGesture())
</del><ins>+ // Called by scheduled AudioNodes when clients schedule their start times.
+ // Prior to the introduction of suspend(), resume(), and stop(), starting
+ // a scheduled AudioNode would remove the user-gesture restriction, if present,
+ // and would thus unmute the context. Now that AudioContext stays in the
+ // "suspended" state if a user-gesture restriction is present, starting a
+ // schedule AudioNode should set the state to "running", but only if the
+ // user-gesture restriction is set.
+ if (userGestureRequiredForAudioStart())
+ startRendering();
+}
+
+bool AudioContext::willBeginPlayback()
+{
+ if (userGestureRequiredForAudioStart()) {
+ if (!ScriptController::processingUserGesture())
+ return false;
</ins><span class="cx"> removeBehaviorRestriction(AudioContext::RequireUserGestureForAudioStartRestriction);
</span><ins>+ }
</ins><span class="cx">
</span><span class="cx"> if (pageConsentRequiredForAudioStart()) {
</span><span class="cx"> Page* page = document()->page();
</span><del>- if (page && !page->canStartMedia())
</del><ins>+ if (page && !page->canStartMedia()) {
</ins><span class="cx"> document()->addMediaCanStartListener(this);
</span><del>- else
- removeBehaviorRestriction(AudioContext::RequirePageConsentForAudioStartRestriction);
</del><ins>+ return false;
+ }
+ removeBehaviorRestriction(AudioContext::RequirePageConsentForAudioStartRestriction);
</ins><span class="cx"> }
</span><ins>+
+ return m_mediaSession->clientWillBeginPlayback();
+}
+
+bool AudioContext::willPausePlayback()
+{
+ if (userGestureRequiredForAudioStart()) {
+ if (!ScriptController::processingUserGesture())
+ return false;
+ removeBehaviorRestriction(AudioContext::RequireUserGestureForAudioStartRestriction);
+ }
+
+ if (pageConsentRequiredForAudioStart()) {
+ Page* page = document()->page();
+ if (page && !page->canStartMedia()) {
+ document()->addMediaCanStartListener(this);
+ return false;
+ }
+ removeBehaviorRestriction(AudioContext::RequirePageConsentForAudioStartRestriction);
+ }
+
+ return m_mediaSession->clientWillPausePlayback();
+}
+
+void AudioContext::startRendering()
+{
+ if (!willBeginPlayback())
+ return;
+
</ins><span class="cx"> destination()->startRendering();
</span><span class="cx"> setState(State::Running);
</span><span class="cx"> }
</span><span class="lines">@@ -1077,9 +1123,11 @@
</span><span class="cx">
</span><span class="cx"> addReaction(State::Suspended, successCallback);
</span><span class="cx">
</span><del>- if (!m_mediaSession->clientWillPausePlayback())
</del><ins>+ if (!willPausePlayback())
</ins><span class="cx"> return;
</span><span class="cx">
</span><ins>+ lazyInitialize();
+
</ins><span class="cx"> RefPtr<AudioContext> strongThis(this);
</span><span class="cx"> m_destinationNode->suspend([strongThis] {
</span><span class="cx"> strongThis->setState(State::Suspended);
</span><span class="lines">@@ -1108,9 +1156,11 @@
</span><span class="cx">
</span><span class="cx"> addReaction(State::Running, successCallback);
</span><span class="cx">
</span><del>- if (!m_mediaSession->clientWillBeginPlayback())
</del><ins>+ if (!willBeginPlayback())
</ins><span class="cx"> return;
</span><span class="cx">
</span><ins>+ lazyInitialize();
+
</ins><span class="cx"> RefPtr<AudioContext> strongThis(this);
</span><span class="cx"> m_destinationNode->resume([strongThis] {
</span><span class="cx"> strongThis->setState(State::Running);
</span><span class="lines">@@ -1133,6 +1183,8 @@
</span><span class="cx">
</span><span class="cx"> addReaction(State::Closed, successCallback);
</span><span class="cx">
</span><ins>+ lazyInitialize();
+
</ins><span class="cx"> RefPtr<AudioContext> strongThis(this);
</span><span class="cx"> m_destinationNode->close([strongThis, successCallback] {
</span><span class="cx"> strongThis->setState(State::Closed);
</span><span class="lines">@@ -1152,6 +1204,8 @@
</span><span class="cx"> return;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ lazyInitialize();
+
</ins><span class="cx"> RefPtr<AudioContext> strongThis(this);
</span><span class="cx"> m_destinationNode->suspend([strongThis] {
</span><span class="cx"> bool interrupted = strongThis->m_mediaSession->state() == MediaSession::Interrupted;
</span><span class="lines">@@ -1169,6 +1223,11 @@
</span><span class="cx"> return;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ if (!willBeginPlayback())
+ return;
+
+ lazyInitialize();
+
</ins><span class="cx"> RefPtr<AudioContext> strongThis(this);
</span><span class="cx"> m_destinationNode->resume([strongThis] {
</span><span class="cx"> strongThis->setState(State::Running);
</span></span></pre></div>
<a id="trunkSourceWebCoreModuleswebaudioAudioContexth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/webaudio/AudioContext.h (183423 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/webaudio/AudioContext.h        2015-04-27 21:30:58 UTC (rev 183423)
+++ trunk/Source/WebCore/Modules/webaudio/AudioContext.h        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -251,14 +251,14 @@
</span><span class="cx"> };
</span><span class="cx"> typedef unsigned BehaviorRestrictions;
</span><span class="cx">
</span><del>- bool userGestureRequiredForAudioStart() const { return m_restrictions & RequireUserGestureForAudioStartRestriction; }
- bool pageConsentRequiredForAudioStart() const { return m_restrictions & RequirePageConsentForAudioStartRestriction; }
-
</del><ins>+ BehaviorRestrictions behaviorRestrictions() const { return m_restrictions; }
</ins><span class="cx"> void addBehaviorRestriction(BehaviorRestrictions restriction) { m_restrictions |= restriction; }
</span><span class="cx"> void removeBehaviorRestriction(BehaviorRestrictions restriction) { m_restrictions &= ~restriction; }
</span><span class="cx">
</span><span class="cx"> void isPlayingAudioDidChange();
</span><span class="cx">
</span><ins>+ void nodeWillBeginPlayback();
+
</ins><span class="cx"> protected:
</span><span class="cx"> explicit AudioContext(Document&);
</span><span class="cx"> AudioContext(Document&, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate);
</span><span class="lines">@@ -271,6 +271,12 @@
</span><span class="cx"> void lazyInitialize();
</span><span class="cx"> void uninitialize();
</span><span class="cx">
</span><ins>+ bool willBeginPlayback();
+ bool willPausePlayback();
+
+ bool userGestureRequiredForAudioStart() const { return m_restrictions & RequireUserGestureForAudioStartRestriction; }
+ bool pageConsentRequiredForAudioStart() const { return m_restrictions & RequirePageConsentForAudioStartRestriction; }
+
</ins><span class="cx"> enum class State { Suspended, Running, Interrupted, Closed };
</span><span class="cx"> void setState(State);
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCoreModuleswebaudioAudioDestinationNodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp (183423 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp        2015-04-27 21:30:58 UTC (rev 183423)
+++ trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -68,18 +68,6 @@
</span><span class="cx"> return;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- if (context()->userGestureRequiredForAudioStart()) {
- destinationBus->zero();
- setIsSilent(true);
- return;
- }
-
- if (context()->pageConsentRequiredForAudioStart()) {
- destinationBus->zero();
- setIsSilent(true);
- return;
- }
-
</del><span class="cx"> // Let the context take care of any business at the start of each render quantum.
</span><span class="cx"> context()->handlePreRenderTasks();
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCoreModuleswebaudioAudioScheduledSourceNodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp (183423 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp        2015-04-27 21:30:58 UTC (rev 183423)
+++ trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -146,8 +146,7 @@
</span><span class="cx"> {
</span><span class="cx"> ASSERT(isMainThread());
</span><span class="cx">
</span><del>- if (ScriptController::processingUserGesture())
- context()->removeBehaviorRestriction(AudioContext::RequireUserGestureForAudioStartRestriction);
</del><ins>+ context()->nodeWillBeginPlayback();
</ins><span class="cx">
</span><span class="cx"> if (m_playbackState != UNSCHEDULED_STATE) {
</span><span class="cx"> ec = INVALID_STATE_ERR;
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.cpp (183423 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.cpp        2015-04-27 21:30:58 UTC (rev 183423)
+++ trunk/Source/WebCore/testing/Internals.cpp        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -181,6 +181,10 @@
</span><span class="cx"> #include "MockContentFilter.h"
</span><span class="cx"> #endif
</span><span class="cx">
</span><ins>+#if ENABLE(WEB_AUDIO)
+#include "AudioContext.h"
+#endif
+
</ins><span class="cx"> using JSC::CodeBlock;
</span><span class="cx"> using JSC::FunctionExecutable;
</span><span class="cx"> using JSC::JSFunction;
</span><span class="lines">@@ -2587,6 +2591,33 @@
</span><span class="cx">
</span><span class="cx"> #endif // ENABLE(VIDEO)
</span><span class="cx">
</span><ins>+#if ENABLE(WEB_AUDIO)
+void Internals::setAudioContextRestrictions(AudioContext* context, const String &restrictionsString, ExceptionCode &ec)
+{
+ if (!context) {
+ ec = INVALID_ACCESS_ERR;
+ return;
+ }
+
+ AudioContext::BehaviorRestrictions restrictions = context->behaviorRestrictions();
+ context->removeBehaviorRestriction(restrictions);
+
+ restrictions = HTMLMediaSession::NoRestrictions;
+
+ Vector<String> restrictionsArray;
+ restrictionsString.split(',', false, restrictionsArray);
+ for (auto& restrictionString : restrictionsArray) {
+ if (equalIgnoringCase(restrictionString, "NoRestrictions"))
+ restrictions |= AudioContext::NoRestrictions;
+ if (equalIgnoringCase(restrictionString, "RequireUserGestureForAudioStart"))
+ restrictions |= AudioContext::RequireUserGestureForAudioStartRestriction;
+ if (equalIgnoringCase(restrictionString, "RequirePageConsentForAudioStart"))
+ restrictions |= AudioContext::RequirePageConsentForAudioStartRestriction;
+ }
+ context->addBehaviorRestriction(restrictions);
+}
+#endif
+
</ins><span class="cx"> void Internals::simulateSystemSleep() const
</span><span class="cx"> {
</span><span class="cx"> #if ENABLE(VIDEO)
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.h (183423 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.h        2015-04-27 21:30:58 UTC (rev 183423)
+++ trunk/Source/WebCore/testing/Internals.h        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -41,6 +41,7 @@
</span><span class="cx">
</span><span class="cx"> namespace WebCore {
</span><span class="cx">
</span><ins>+class AudioContext;
</ins><span class="cx"> class ClientRect;
</span><span class="cx"> class ClientRectList;
</span><span class="cx"> class DOMStringList;
</span><span class="lines">@@ -372,6 +373,10 @@
</span><span class="cx"> bool elementIsBlockingDisplaySleep(Element*) const;
</span><span class="cx"> #endif
</span><span class="cx">
</span><ins>+#if ENABLE(WEB_AUDIO)
+ void setAudioContextRestrictions(AudioContext*, const String& restrictions, ExceptionCode&);
+#endif
+
</ins><span class="cx"> void simulateSystemSleep() const;
</span><span class="cx"> void simulateSystemWake() const;
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalsidl"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.idl (183423 => 183424)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.idl        2015-04-27 21:30:58 UTC (rev 183423)
+++ trunk/Source/WebCore/testing/Internals.idl        2015-04-27 21:35:03 UTC (rev 183424)
</span><span class="lines">@@ -336,6 +336,7 @@
</span><span class="cx"> [Conditional=VIDEO] void applicationWillEnterForeground();
</span><span class="cx"> [Conditional=VIDEO] void applicationWillEnterBackground();
</span><span class="cx"> [Conditional=VIDEO, RaisesException] void setMediaSessionRestrictions(DOMString mediaType, DOMString restrictions);
</span><ins>+ [Conditional=WEB_AUDIO, RaisesException] void setAudioContextRestrictions(AudioContext context, DOMString restrictions);
</ins><span class="cx"> [Conditional=VIDEO, RaisesException] void postRemoteControlCommand(DOMString command);
</span><span class="cx">
</span><span class="cx"> [Conditional=VIDEO] void simulateSystemSleep();
</span></span></pre>
</div>
</div>
</body>
</html>