зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
683790582e
Коммит
57261b2aa0
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче