Bug 1375119 - Consider a page active if it has running AudioContexts. r?ehsan
MozReview-Commit-ID: 7Pf8LwvwvR8
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -4417,16 +4417,21 @@ void
nsPIDOMWindowInner::SyncStateFromParentWindow()
{
nsGlobalWindow::Cast(this)->SyncStateFromParentWindow();
}
bool
nsPIDOMWindowInner::IsPlayingAudio()
{
+ for (uint32_t i = 0; i < mAudioContexts.Length(); i++) {
+ if (mAudioContexts[i]->IsRunning()) {
+ return true;
+ }
+ }
RefPtr<AudioChannelService> acs = AudioChannelService::Get();
if (!acs) {
return false;
}
auto outer = GetOuterWindow();
if (!outer) {
// We've been unlinked and are about to die. Not a good time to pretend to
// be playing audio.
--- a/dom/base/test/file_webaudioLoop.html
+++ b/dom/base/test/file_webaudioLoop.html
@@ -3,29 +3,22 @@
var ac = new AudioContext();
var runningPromise = new Promise(resolve => {
ac.onstatechange = event => {
if (ac.state == "running") {
resolve();
}
};
});
-fetch("audio.ogg").then(response => {
- return response.arrayBuffer();
-}).then(ab => {
- return ac.decodeAudioData(ab);
-}).then(ab => {
- var src = ac.createBufferSource();
- src.buffer = ab;
- src.loop = true;
- src.loopStart = 0;
- src.loopEnd = ab.duration;
- src.start();
- src.connect(ac.destination);
-});
+
+var osc = ac.createOscillator();
+osc.connect(ac.destination);
+osc.start(0);
+osc.stop(ac.currentTime + 2);
+
var suspendPromise;
function suspendAC() {
runningPromise.then(() => {
suspendPromise = ac.suspend();
});
}
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -487,16 +487,22 @@ AudioListener*
AudioContext::Listener()
{
if (!mListener) {
mListener = new AudioListener(this);
}
return mListener;
}
+bool
+AudioContext::IsRunning() const
+{
+ return mAudioContextState == AudioContextState::Running;
+}
+
already_AddRefed<Promise>
AudioContext::DecodeAudioData(const ArrayBuffer& aBuffer,
const Optional<OwningNonNull<DecodeSuccessCallback> >& aSuccessCallback,
const Optional<OwningNonNull<DecodeErrorCallback> >& aFailureCallback,
ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> parentObject = do_QueryInterface(GetParentObject());
RefPtr<Promise> promise;
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -176,16 +176,17 @@ public:
bool ShouldSuspendNewStream() const { return mSuspendCalled; }
double CurrentTime() const;
AudioListener* Listener();
AudioContextState State() const { return mAudioContextState; }
+ bool IsRunning() const;
// Those three methods return a promise to content, that is resolved when an
// (possibly long) operation is completed on the MSG (and possibly other)
// thread(s). To avoid having to match the calls and asychronous result when
// the operation is completed, we keep a reference to the promises on the main
// thread, and then send the promises pointers down the MSG thread, as a void*
// (to make it very clear that the pointer is to merely be treated as an ID).
// When back on the main thread, we can resolve or reject the promise, by