Bug 1719183 - part3 : let running audio context to keep tab awake. r=padenot,nika

Let running AudioContext also prevents tab from being suspended by asking/revoking the page awake via the browsing context.

Differential Revision: https://phabricator.services.mozilla.com/D119839
This commit is contained in:
alwu 2021-08-03 21:32:27 +00:00
Родитель 683790582e
Коммит 57261b2aa0
6 изменённых файлов: 136 добавлений и 0 удалений

Просмотреть файл

@ -6555,6 +6555,20 @@ mozilla::ipc::IPCResult ContentParent::RecvNotifyPositionStateChanged(
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvAddOrRemovePageAwakeRequest(
const MaybeDiscarded<BrowsingContext>& aContext,
const bool& aShouldAddCount) {
if (aContext.IsNullOrDiscarded()) {
return IPC_OK();
}
if (aShouldAddCount) {
aContext.get_canonical()->AddPageAwakeRequest();
} else {
aContext.get_canonical()->RemovePageAwakeRequest();
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvGetModulesTrust(
ModulePaths&& aModPaths, bool aRunAtNormalPriority,
GetModulesTrustResolver&& aResolver) {

Просмотреть файл

@ -1319,6 +1319,10 @@ class ContentParent final
const MaybeDiscarded<BrowsingContext>& aContext,
const PositionState& aState);
mozilla::ipc::IPCResult RecvAddOrRemovePageAwakeRequest(
const MaybeDiscarded<BrowsingContext>& aContext,
const bool& aShouldAddCount);
mozilla::ipc::IPCResult RecvGetModulesTrust(
ModulePaths&& aModPaths, bool aRunAtNormalPriority,
GetModulesTrustResolver&& aResolver);

Просмотреть файл

@ -1657,6 +1657,14 @@ parent:
MaybeDiscardedBrowsingContext aContext,
PositionState aState);
/**
* This method will make canonical browsing context to update the count of
* callers which want to keep the page from being suspended even if the page
* is inactive.
*/
async AddOrRemovePageAwakeRequest(MaybeDiscardedBrowsingContext aContext,
bool aShouldAddCount);
/**
* Due to sandboxing, a child process's UntrustedModulesProcessor cannot
* obtain enough information about a DLL file to determine its

Просмотреть файл

@ -68,6 +68,26 @@ add_task(async function testInactiveTabEverStartPlayingWontBeSuspended() {
await Promise.all([tab1.close(), tab2.close()]);
});
add_task(
async function testInactiveTabWithRunningAudioContextWontBeSuspended() {
info(`open tab and start an audio context (AC)`);
const tab = await createTab("about:blank");
await startAudioContext(tab);
await assertIfWindowGetSuspended(tab, { shouldBeSuspended: false });
info(`tab with running AC won't be suspended when it becomes inactive`);
setTabActive(tab, false);
await assertIfWindowGetSuspended(tab, { shouldBeSuspended: false });
info(`if AC has been suspended, then inactive tab should be suspended`);
await suspendAudioContext(tab);
await assertIfWindowGetSuspended(tab, { shouldBeSuspended: true });
info(`remove tab`);
await tab.close();
}
);
/**
* The following are helper functions.
*/
@ -94,3 +114,18 @@ function assertIfWindowGetSuspended(tab, { shouldBeSuspended }) {
function setTabActive(tab, isActive) {
tab.linkedBrowser.docShellIsActive = isActive;
}
function startAudioContext(tab) {
return SpecialPowers.spawn(tab.linkedBrowser, [], async _ => {
content.ac = new content.AudioContext();
await new Promise(r => (content.ac.onstatechange = r));
ok(content.ac.state == "running", `Audio context started running`);
});
}
function suspendAudioContext(tab) {
return SpecialPowers.spawn(tab.linkedBrowser, [], async _ => {
await content.ac.suspend();
ok(content.ac.state == "suspended", `Audio context is suspended`);
});
}

Просмотреть файл

@ -21,8 +21,11 @@
#include "mozilla/dom/AudioContextBinding.h"
#include "mozilla/dom/BaseAudioContextBinding.h"
#include "mozilla/dom/BiquadFilterNodeBinding.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ChannelMergerNodeBinding.h"
#include "mozilla/dom/ChannelSplitterNodeBinding.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ConvolverNodeBinding.h"
#include "mozilla/dom/DelayNodeBinding.h"
#include "mozilla/dom/DynamicsCompressorNodeBinding.h"
@ -220,6 +223,7 @@ void AudioContext::StartBlockedAudioContextIfAllowed() {
}
void AudioContext::DisconnectFromWindow() {
MaybeClearPageAwakeRequest();
nsPIDOMWindowInner* window = GetOwner();
if (window) {
window->RemoveAudioContext(this);
@ -229,6 +233,7 @@ void AudioContext::DisconnectFromWindow() {
AudioContext::~AudioContext() {
DisconnectFromWindow();
UnregisterWeakMemoryReporter(this);
MOZ_ASSERT(!mSetPageAwakeRequest, "forgot to revoke for page awake?");
}
JSObject* AudioContext::WrapObject(JSContext* aCx,
@ -766,6 +771,7 @@ nsISerialEventTarget* AudioContext::GetMainThread() const {
void AudioContext::DisconnectFromOwner() {
mIsDisconnecting = true;
MaybeClearPageAwakeRequest();
OnWindowDestroy();
DOMEventTargetHelper::DisconnectFromOwner();
}
@ -894,6 +900,60 @@ void AudioContext::OnStateChanged(void* aPromise, AudioContextState aNewState) {
mAudioContextState = aNewState;
Destination()->NotifyAudioContextStateChanged();
MaybeUpdatePageAwakeRequest();
}
BrowsingContext* AudioContext::GetTopLevelBrowsingContext() {
nsCOMPtr<nsPIDOMWindowInner> window = GetParentObject();
if (!window) {
return nullptr;
}
BrowsingContext* bc = window->GetBrowsingContext();
if (!bc || bc->IsDiscarded()) {
return nullptr;
}
return bc->Top();
}
void AudioContext::MaybeUpdatePageAwakeRequest() {
// No need to keep page awake for offline context.
if (IsOffline()) {
return;
}
if (mIsShutDown) {
return;
}
if (IsRunning() && !mSetPageAwakeRequest) {
SetPageAwakeRequest(true);
} else if (!IsRunning() && mSetPageAwakeRequest) {
SetPageAwakeRequest(false);
}
}
void AudioContext::SetPageAwakeRequest(bool aShouldSet) {
mSetPageAwakeRequest = aShouldSet;
BrowsingContext* bc = GetTopLevelBrowsingContext();
if (!bc) {
return;
}
if (XRE_IsContentProcess()) {
ContentChild* contentChild = ContentChild::GetSingleton();
Unused << contentChild->SendAddOrRemovePageAwakeRequest(bc, aShouldSet);
return;
}
if (aShouldSet) {
bc->Canonical()->AddPageAwakeRequest();
} else {
bc->Canonical()->RemovePageAwakeRequest();
}
}
void AudioContext::MaybeClearPageAwakeRequest() {
if (mSetPageAwakeRequest) {
SetPageAwakeRequest(false);
}
}
nsTArray<RefPtr<mozilla::MediaTrack>> AudioContext::GetAllTracks() const {

Просмотреть файл

@ -51,6 +51,7 @@ class AudioDestinationNode;
class AudioListener;
class AudioNode;
class BiquadFilterNode;
class BrowsingContext;
class ChannelMergerNode;
class ChannelSplitterNode;
class ConstantSourceNode;
@ -376,6 +377,15 @@ class AudioContext final : public DOMEventTargetHelper,
void MaybeUpdateAutoplayTelemetry();
void MaybeUpdateAutoplayTelemetryWhenShutdown();
// If the pref `dom.suspend_inactive.enabled` is enabled, the dom window will
// be suspended when the window becomes inactive. In order to keep audio
// context running still, we will ask pages to keep awake in that situation.
void MaybeUpdatePageAwakeRequest();
void MaybeClearPageAwakeRequest();
void SetPageAwakeRequest(bool aShouldSet);
BrowsingContext* GetTopLevelBrowsingContext();
private:
// Each AudioContext has an id, that is passed down the MediaTracks that
// back the AudioNodes, so we can easily compute the set of all the
@ -434,6 +444,11 @@ class AudioContext final : public DOMEventTargetHelper,
bool mWasEverAllowedToStart;
bool mWasEverBlockedToStart;
bool mWouldBeAllowedToStart;
// Whether we have set the page awake reqeust when non-offline audio context
// is running. That will keep the audio context being able to continue running
// even if the window is inactive.
bool mSetPageAwakeRequest = false;
};
static const dom::AudioContext::AudioContextId NO_AUDIO_CONTEXT = 0;