[WebAudio] AudioContext does not remove user-gesture restriction during resume()
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 27 Apr 2015 21:35:03 +0000 (21:35 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 27 Apr 2015 21:35:03 +0000 (21:35 +0000)
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):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@183424 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
LayoutTests/ChangeLog
LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start-expected.txt [new file with mode: 0644]
LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start.html [new file with mode: 0644]
LayoutTests/webaudio/audiocontext-restriction-expected.txt [new file with mode: 0644]
LayoutTests/webaudio/audiocontext-restriction.html [new file with mode: 0644]
LayoutTests/webaudio/resources/audio-testing.js
Source/WebCore/ChangeLog
Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp
Source/WebCore/Modules/webaudio/AudioContext.cpp
Source/WebCore/Modules/webaudio/AudioContext.h
Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp
Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl

index f159b87..e1420e4 100644 (file)
@@ -1,3 +1,17 @@
+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):
+
 2015-04-27  Alexey Proskuryakov  <ap@apple.com>
 
         rdar://problem/16678392 Page visibility tests are broken in Yosemite
diff --git a/LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start-expected.txt b/LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start-expected.txt
new file mode 100644 (file)
index 0000000..bf60f66
--- /dev/null
@@ -0,0 +1,16 @@
+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
+
diff --git a/LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start.html b/LayoutTests/webaudio/audiocontext-restriction-audiobuffersourcenode-start.html
new file mode 100644 (file)
index 0000000..4b1dfcc
--- /dev/null
@@ -0,0 +1,69 @@
+<!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>
diff --git a/LayoutTests/webaudio/audiocontext-restriction-expected.txt b/LayoutTests/webaudio/audiocontext-restriction-expected.txt
new file mode 100644 (file)
index 0000000..1c4d6fd
--- /dev/null
@@ -0,0 +1,18 @@
+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
+
diff --git a/LayoutTests/webaudio/audiocontext-restriction.html b/LayoutTests/webaudio/audiocontext-restriction.html
new file mode 100644 (file)
index 0000000..7cf1cf2
--- /dev/null
@@ -0,0 +1,85 @@
+<!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>
index 9d3e0c6..3488279 100644 (file)
@@ -190,3 +190,23 @@ function shouldThrowTypeError(func, text) {
         testFailed(text + " should throw TypeError.");
     }
 }
+
+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();
+    }
+}
index cde4ba2..bd5cbf6 100644 (file)
@@ -1,3 +1,62 @@
+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:
+
 2015-04-27  Alexey Proskuryakov  <ap@apple.com>
 
         Build fix.
index 2f781bd..627b01d 100644 (file)
@@ -469,8 +469,7 @@ void AudioBufferSourceNode::startPlaying(BufferPlaybackMode playbackMode, double
 {
     ASSERT(isMainThread());
 
-    if (ScriptController::processingUserGesture())
-        context()->removeBehaviorRestriction(AudioContext::RequireUserGestureForAudioStartRestriction);
+    context()->nodeWillBeginPlayback();
 
     if (m_playbackState != UNSCHEDULED_STATE) {
         ec = INVALID_STATE_ERR;
index 7b3d80a..d23b510 100644 (file)
@@ -985,18 +985,64 @@ ScriptExecutionContext* AudioContext::scriptExecutionContext() const
     return m_isStopScheduled ? 0 : ActiveDOMObject::scriptExecutionContext();
 }
 
-void AudioContext::startRendering()
+void AudioContext::nodeWillBeginPlayback()
 {
-    if (ScriptController::processingUserGesture())
+    // 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;
         removeBehaviorRestriction(AudioContext::RequireUserGestureForAudioStartRestriction);
+    }
 
     if (pageConsentRequiredForAudioStart()) {
         Page* page = document()->page();
-        if (page && !page->canStartMedia())
+        if (page && !page->canStartMedia()) {
             document()->addMediaCanStartListener(this);
-        else
-            removeBehaviorRestriction(AudioContext::RequirePageConsentForAudioStartRestriction);
+            return false;
+        }
+        removeBehaviorRestriction(AudioContext::RequirePageConsentForAudioStartRestriction);
     }
+
+    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;
+
     destination()->startRendering();
     setState(State::Running);
 }
@@ -1077,9 +1123,11 @@ void AudioContext::suspendContext(std::function<void()> successCallback, std::fu
 
     addReaction(State::Suspended, successCallback);
 
-    if (!m_mediaSession->clientWillPausePlayback())
+    if (!willPausePlayback())
         return;
 
+    lazyInitialize();
+
     RefPtr<AudioContext> strongThis(this);
     m_destinationNode->suspend([strongThis] {
         strongThis->setState(State::Suspended);
@@ -1108,9 +1156,11 @@ void AudioContext::resumeContext(std::function<void()> successCallback, std::fun
 
     addReaction(State::Running, successCallback);
 
-    if (!m_mediaSession->clientWillBeginPlayback())
+    if (!willBeginPlayback())
         return;
 
+    lazyInitialize();
+
     RefPtr<AudioContext> strongThis(this);
     m_destinationNode->resume([strongThis] {
         strongThis->setState(State::Running);
@@ -1133,6 +1183,8 @@ void AudioContext::closeContext(std::function<void()> successCallback, std::func
 
     addReaction(State::Closed, successCallback);
 
+    lazyInitialize();
+
     RefPtr<AudioContext> strongThis(this);
     m_destinationNode->close([strongThis, successCallback] {
         strongThis->setState(State::Closed);
@@ -1152,6 +1204,8 @@ void AudioContext::suspendPlayback()
         return;
     }
 
+    lazyInitialize();
+
     RefPtr<AudioContext> strongThis(this);
     m_destinationNode->suspend([strongThis] {
         bool interrupted = strongThis->m_mediaSession->state() == MediaSession::Interrupted;
@@ -1169,6 +1223,11 @@ void AudioContext::mayResumePlayback(bool shouldResume)
         return;
     }
 
+    if (!willBeginPlayback())
+        return;
+
+    lazyInitialize();
+
     RefPtr<AudioContext> strongThis(this);
     m_destinationNode->resume([strongThis] {
         strongThis->setState(State::Running);
index 8940520..1fa05f3 100644 (file)
@@ -251,14 +251,14 @@ public:
     };
     typedef unsigned BehaviorRestrictions;
 
-    bool userGestureRequiredForAudioStart() const { return m_restrictions & RequireUserGestureForAudioStartRestriction; }
-    bool pageConsentRequiredForAudioStart() const { return m_restrictions & RequirePageConsentForAudioStartRestriction; }
-
+    BehaviorRestrictions behaviorRestrictions() const { return m_restrictions; }
     void addBehaviorRestriction(BehaviorRestrictions restriction) { m_restrictions |= restriction; }
     void removeBehaviorRestriction(BehaviorRestrictions restriction) { m_restrictions &= ~restriction; }
 
     void isPlayingAudioDidChange();
 
+    void nodeWillBeginPlayback();
+
 protected:
     explicit AudioContext(Document&);
     AudioContext(Document&, unsigned numberOfChannels, size_t numberOfFrames, float sampleRate);
@@ -271,6 +271,12 @@ private:
     void lazyInitialize();
     void uninitialize();
 
+    bool willBeginPlayback();
+    bool willPausePlayback();
+
+    bool userGestureRequiredForAudioStart() const { return m_restrictions & RequireUserGestureForAudioStartRestriction; }
+    bool pageConsentRequiredForAudioStart() const { return m_restrictions & RequirePageConsentForAudioStartRestriction; }
+
     enum class State { Suspended, Running, Interrupted, Closed };
     void setState(State);
 
index afea416..66486e1 100644 (file)
@@ -68,18 +68,6 @@ void AudioDestinationNode::render(AudioBus* sourceBus, AudioBus* destinationBus,
         return;
     }
 
-    if (context()->userGestureRequiredForAudioStart()) {
-        destinationBus->zero();
-        setIsSilent(true);
-        return;
-    }
-
-    if (context()->pageConsentRequiredForAudioStart()) {
-        destinationBus->zero();
-        setIsSilent(true);
-        return;
-    }
-
     // Let the context take care of any business at the start of each render quantum.
     context()->handlePreRenderTasks();
 
index fe6a34d..723b988 100644 (file)
@@ -146,8 +146,7 @@ void AudioScheduledSourceNode::start(double when, ExceptionCode& ec)
 {
     ASSERT(isMainThread());
 
-    if (ScriptController::processingUserGesture())
-        context()->removeBehaviorRestriction(AudioContext::RequireUserGestureForAudioStartRestriction);
+    context()->nodeWillBeginPlayback();
 
     if (m_playbackState != UNSCHEDULED_STATE) {
         ec = INVALID_STATE_ERR;
index c6b053e..e9bdfde 100644 (file)
 #include "MockContentFilter.h"
 #endif
 
+#if ENABLE(WEB_AUDIO)
+#include "AudioContext.h"
+#endif
+
 using JSC::CodeBlock;
 using JSC::FunctionExecutable;
 using JSC::JSFunction;
@@ -2587,6 +2591,33 @@ bool Internals::elementIsBlockingDisplaySleep(Element* element) const
 
 #endif // ENABLE(VIDEO)
 
+#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
+
 void Internals::simulateSystemSleep() const
 {
 #if ENABLE(VIDEO)
index a46692a..f99bce8 100644 (file)
@@ -41,6 +41,7 @@
 
 namespace WebCore {
 
+class AudioContext;
 class ClientRect;
 class ClientRectList;
 class DOMStringList;
@@ -372,6 +373,10 @@ public:
     bool elementIsBlockingDisplaySleep(Element*) const;
 #endif
 
+#if ENABLE(WEB_AUDIO)
+    void setAudioContextRestrictions(AudioContext*, const String& restrictions, ExceptionCode&);
+#endif
+
     void simulateSystemSleep() const;
     void simulateSystemWake() const;
 
index 2088d18..7fe97de 100644 (file)
@@ -336,6 +336,7 @@ enum ResourceLoadPriority {
     [Conditional=VIDEO] void applicationWillEnterForeground();
     [Conditional=VIDEO] void applicationWillEnterBackground();
     [Conditional=VIDEO, RaisesException] void setMediaSessionRestrictions(DOMString mediaType, DOMString restrictions);
+    [Conditional=WEB_AUDIO, RaisesException] void setAudioContextRestrictions(AudioContext context, DOMString restrictions);
     [Conditional=VIDEO, RaisesException] void postRemoteControlCommand(DOMString command);
     
     [Conditional=VIDEO] void simulateSystemSleep();