summaryrefslogtreecommitdiffstats
path: root/dom/media/autoplay/test/browser/browser_autoplay_policy_web_audio_with_gum.js
blob: 45d5c71a52a4de1a63a976a52139812f71848b5b (plain)
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/* eslint-disable mozilla/no-arbitrary-setTimeout */

/**
 * This test is used to ensure web audio can be allowed to start when we have
 * GUM permission.
 */
add_task(async function startTestingWebAudioWithGUM() {
  info("- setup test preference -");
  await setupTestPreferences();

  info("- test web audio with gUM success -");
  await testWebAudioWithGUM({
    constraints: { audio: true },
    shouldAllowStartingContext: true,
  });
  await testWebAudioWithGUM({
    constraints: { video: true },
    shouldAllowStartingContext: true,
  });
  await testWebAudioWithGUM({
    constraints: { video: true, audio: true },
    shouldAllowStartingContext: true,
  });

  await SpecialPowers.pushPrefEnv({
    set: [["media.navigator.permission.force", true]],
  }).then(async function () {
    info("- test web audio with gUM denied -");
    await testWebAudioWithGUM({
      constraints: { video: true },
      shouldAllowStartingContext: false,
    });
    await testWebAudioWithGUM({
      constraints: { audio: true },
      shouldAllowStartingContext: false,
    });
    await testWebAudioWithGUM({
      constraints: { video: true, audio: true },
      shouldAllowStartingContext: false,
    });
  });
});

/**
 * testing helper functions
 */
function setupTestPreferences() {
  return SpecialPowers.pushPrefEnv({
    set: [
      ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
      ["media.autoplay.blocking_policy", 0],
      ["media.autoplay.block-event.enabled", true],
      ["media.autoplay.block-webaudio", true],
      ["media.navigator.permission.fake", true],
    ],
  });
}

function createAudioContext() {
  content.ac = new content.AudioContext();
  let ac = content.ac;
  ac.resumePromises = [];
  ac.stateChangePromise = new Promise(resolve => {
    ac.addEventListener(
      "statechange",
      function () {
        resolve();
      },
      { once: true }
    );
  });
  ac.notAllowedToStart = new Promise(resolve => {
    ac.addEventListener(
      "blocked",
      function () {
        resolve();
      },
      { once: true }
    );
  });
}

async function checkingAudioContextRunningState() {
  let ac = content.ac;
  await ac.notAllowedToStart;
  ok(ac.state === "suspended", `AudioContext is not started yet.`);
}

function resumeWithoutExpectedSuccess() {
  let ac = content.ac;
  let promise = ac.resume();
  ac.resumePromises.push(promise);
  return new Promise((resolve, reject) => {
    content.setTimeout(() => {
      if (ac.state == "suspended") {
        ok(true, "audio context is still suspended");
        resolve();
      } else {
        reject("audio context should not be allowed to start");
      }
    }, 2000);
  });
}

function resumeWithExpectedSuccess() {
  let ac = content.ac;
  ac.resumePromises.push(ac.resume());
  return Promise.all(ac.resumePromises).then(() => {
    ok(ac.state == "running", "audio context starts running");
  });
}

async function callGUM(testParameters) {
  info("- calling gum with " + JSON.stringify(testParameters.constraints));
  if (testParameters.shouldAllowStartingContext) {
    // Because of the prefs we've set and passed, this is going to allow the
    // window to start an AudioContext synchronously.
    testParameters.constraints.fake = true;
    await content.navigator.mediaDevices.getUserMedia(
      testParameters.constraints
    );
    return;
  }

  // Call gUM, without sucess: we've made it so that only fake requests
  // succeed without permission, and this is requesting non-fake-devices. Return
  // a resolved promise so that the test continues, but the getUserMedia Promise
  // will never be resolved.
  // We do this to check that it's not merely calling gUM that allows starting
  // an AudioContext, it's having the Promise it return resolved successfuly,
  // because of saved permissions for an origin or explicit user consent using
  // the prompt.
  content.navigator.mediaDevices.getUserMedia(testParameters.constraints);
}

async function testWebAudioWithGUM(testParameters) {
  info("- open new tab -");
  let tab = await BrowserTestUtils.openNewForegroundTab(
    window.gBrowser,
    "https://example.com"
  );
  info("- create audio context -");
  await SpecialPowers.spawn(tab.linkedBrowser, [], createAudioContext);

  info("- check whether audio context starts running -");
  try {
    await SpecialPowers.spawn(
      tab.linkedBrowser,
      [],
      checkingAudioContextRunningState
    );
  } catch (error) {
    ok(false, error.toString());
  }

  try {
    await SpecialPowers.spawn(tab.linkedBrowser, [testParameters], callGUM);
  } catch (error) {
    ok(false, error.toString());
  }

  info("- calling resume() again");
  try {
    let resumeFunc = testParameters.shouldAllowStartingContext
      ? resumeWithExpectedSuccess
      : resumeWithoutExpectedSuccess;
    await SpecialPowers.spawn(tab.linkedBrowser, [], resumeFunc);
  } catch (error) {
    ok(false, error.toString());
  }

  info("- remove tab -");
  await BrowserTestUtils.removeTab(tab);
}