1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
/**
* Test if wakelock can be required correctly when we play web audio. The
* wakelock should only be required when web audio is audible.
*/
const AUDIO_WAKELOCK_NAME = "audio-playing";
const VIDEO_WAKELOCK_NAME = "video-playing";
add_task(async function testCheckAudioWakelockWhenChangeTabVisibility() {
await checkWakelockWhenChangeTabVisibility({
description: "playing audible web audio",
needLock: true,
});
await checkWakelockWhenChangeTabVisibility({
description: "suspended web audio",
additionalParams: {
suspend: true,
},
needLock: false,
});
});
add_task(
async function testBrieflyAudibleAudioContextReleasesAudioWakeLockWhenInaudible() {
const tab = await BrowserTestUtils.openNewForegroundTab(
window.gBrowser,
"about:blank"
);
info(`make a short noise on web audio`);
await Promise.all([
// As the sound would only happen for a really short period, calling
// checking wakelock first helps to ensure that we won't miss that moment.
waitForExpectedWakeLockState(AUDIO_WAKELOCK_NAME, {
needLock: true,
isForegroundLock: true,
}),
createWebAudioDocument(tab, { stopTimeOffset: 0.1 }),
]);
await ensureNeverAcquireVideoWakelock();
info(`audio wakelock should be released after web audio becomes silent`);
await waitForExpectedWakeLockState(AUDIO_WAKELOCK_NAME, false, {
needLock: false,
});
await ensureNeverAcquireVideoWakelock();
await BrowserTestUtils.removeTab(tab);
}
);
/**
* Following are helper functions.
*/
async function checkWakelockWhenChangeTabVisibility({
description,
additionalParams,
needLock,
}) {
const originalTab = gBrowser.selectedTab;
info(`start a new tab for '${description}'`);
const mediaTab = await BrowserTestUtils.openNewForegroundTab(
window.gBrowser,
"about:blank"
);
await createWebAudioDocument(mediaTab, additionalParams);
await waitForExpectedWakeLockState(AUDIO_WAKELOCK_NAME, {
needLock,
isForegroundLock: true,
});
await ensureNeverAcquireVideoWakelock();
info(`switch media tab to background`);
await BrowserTestUtils.switchTab(window.gBrowser, originalTab);
await waitForExpectedWakeLockState(AUDIO_WAKELOCK_NAME, {
needLock,
isForegroundLock: false,
});
await ensureNeverAcquireVideoWakelock();
info(`switch media tab to foreground again`);
await BrowserTestUtils.switchTab(window.gBrowser, mediaTab);
await waitForExpectedWakeLockState(AUDIO_WAKELOCK_NAME, {
needLock,
isForegroundLock: true,
});
await ensureNeverAcquireVideoWakelock();
info(`remove media tab`);
BrowserTestUtils.removeTab(mediaTab);
}
function createWebAudioDocument(tab, { stopTimeOffset, suspend } = {}) {
return SpecialPowers.spawn(
tab.linkedBrowser,
[suspend, stopTimeOffset],
async (suspend, stopTimeOffset) => {
// Create an oscillatorNode to produce sound.
content.ac = new content.AudioContext();
const ac = content.ac;
const dest = ac.destination;
const source = new content.OscillatorNode(ac);
source.start(ac.currentTime);
source.connect(dest);
if (stopTimeOffset) {
source.stop(ac.currentTime + 0.1);
}
if (suspend) {
await content.ac.suspend();
} else {
while (ac.state != "running") {
info(`wait until AudioContext starts running`);
await new Promise(r => (ac.onstatechange = r));
}
info("AudioContext is running");
}
}
);
}
function ensureNeverAcquireVideoWakelock() {
// Web audio won't play any video, we never need video wakelock.
return waitForExpectedWakeLockState(VIDEO_WAKELOCK_NAME, { needLock: false });
}
|