tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

browser_autoplay_policy_web_audio.js (6107B)


      1 /**
      2 * This test is used for testing whether WebAudio can be started correctly in
      3 * different scenarios, such as
      4 * 1) site has existing 'autoplay-media' permission for allowing autoplay
      5 * 2) site has existing 'autoplay-media' permission for blocking autoplay
      6 * 3) site doesn't have permission, start audio context by calling resume() or
      7 *    AudioScheduledNode.start() after granting user activation.
      8 */
      9 "use strict";
     10 
     11 const { PermissionTestUtils } = ChromeUtils.importESModule(
     12  "resource://testing-common/PermissionTestUtils.sys.mjs"
     13 );
     14 
     15 const PAGE = GetTestWebBasedURL("file_empty.html");
     16 
     17 function setup_test_preference() {
     18  return SpecialPowers.pushPrefEnv({
     19    set: [
     20      ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
     21      ["media.autoplay.blocking_policy", 0],
     22      ["media.autoplay.block-event.enabled", true],
     23    ],
     24  });
     25 }
     26 
     27 function createAudioContext() {
     28  content.ac = new content.AudioContext();
     29  const ac = content.ac;
     30 
     31  ac.allowedToStart = new Promise(resolve => {
     32    ac.addEventListener(
     33      "statechange",
     34      function () {
     35        if (ac.state === "running") {
     36          resolve();
     37        }
     38      },
     39      { once: true }
     40    );
     41  });
     42 
     43  ac.notAllowedToStart = new Promise(resolve => {
     44    ac.addEventListener(
     45      "blocked",
     46      function () {
     47        resolve();
     48      },
     49      { once: true }
     50    );
     51  });
     52 }
     53 
     54 async function checkIfAudioContextIsAllowedToStart(isAllowedToStart) {
     55  const ac = content.ac;
     56  if (isAllowedToStart) {
     57    await ac.allowedToStart;
     58    Assert.strictEqual(ac.state, "running", `AudioContext is running.`);
     59  } else {
     60    await ac.notAllowedToStart;
     61    Assert.strictEqual(
     62      ac.state,
     63      "suspended",
     64      `AudioContext is not started yet.`
     65    );
     66  }
     67 }
     68 
     69 async function resumeAudioContext(isAllowedToStart) {
     70  const ac = content.ac;
     71  const resumePromise = ac.resume();
     72  const blockedPromise = new Promise(resolve => {
     73    ac.addEventListener(
     74      "blocked",
     75      function () {
     76        resolve();
     77      },
     78      { once: true }
     79    );
     80  });
     81 
     82  if (isAllowedToStart) {
     83    await resumePromise;
     84    ok(true, `successfully resume AudioContext.`);
     85  } else {
     86    await blockedPromise;
     87    ok(true, `resume is blocked because AudioContext is not allowed to start.`);
     88  }
     89 }
     90 
     91 function startAudioContext(method) {
     92  const ac = content.ac;
     93  if (method == "AudioContext") {
     94    info(`using AudioContext.resume() to start AudioContext`);
     95    ac.resume();
     96    return;
     97  }
     98  info(`using ${method}.start() to start AudioContext`);
     99  let node;
    100  switch (method) {
    101    case "AudioBufferSourceNode":
    102      node = ac.createBufferSource();
    103      break;
    104    case "ConstantSourceNode":
    105      node = ac.createConstantSource();
    106      break;
    107    case "OscillatorNode":
    108      node = ac.createOscillator();
    109      break;
    110    default:
    111      ok(false, "undefined AudioScheduledSourceNode type");
    112      return;
    113  }
    114  node.connect(ac.destination);
    115  node.start();
    116 }
    117 
    118 async function testAutoplayExistingPermission({ name, permission }) {
    119  info(`- starting \"${name}\" -`);
    120  const tab = await BrowserTestUtils.openNewForegroundTab(
    121    window.gBrowser,
    122    PAGE
    123  );
    124  const browser = tab.linkedBrowser;
    125 
    126  info(`- set the 'autoplay-media' permission -`);
    127  const promptShow = () =>
    128    PopupNotifications.getNotification("autoplay-media", browser);
    129  PermissionTestUtils.add(browser.currentURI, "autoplay-media", permission);
    130  ok(!promptShow(), `should not be showing permission prompt yet`);
    131 
    132  info(`- create audio context -`);
    133  await SpecialPowers.spawn(browser, [], createAudioContext);
    134 
    135  info(`- check AudioContext status -`);
    136  const isAllowedToStart = permission === Services.perms.ALLOW_ACTION;
    137  await SpecialPowers.spawn(
    138    browser,
    139    [isAllowedToStart],
    140    checkIfAudioContextIsAllowedToStart
    141  );
    142  await SpecialPowers.spawn(browser, [isAllowedToStart], resumeAudioContext);
    143 
    144  info(`- remove tab -`);
    145  PermissionTestUtils.remove(browser.currentURI, "autoplay-media");
    146  await BrowserTestUtils.removeTab(tab);
    147 }
    148 
    149 async function testAutoplayUnknownPermission({ name, method }) {
    150  info(`- starting \"${name}\" -`);
    151  const tab = await BrowserTestUtils.openNewForegroundTab(
    152    window.gBrowser,
    153    PAGE
    154  );
    155  const browser = tab.linkedBrowser;
    156 
    157  info(`- set the 'autoplay-media' permission to UNKNOWN -`);
    158  const promptShow = () =>
    159    PopupNotifications.getNotification("autoplay-media", browser);
    160  PermissionTestUtils.add(
    161    browser.currentURI,
    162    "autoplay-media",
    163    Services.perms.UNKNOWN_ACTION
    164  );
    165  ok(!promptShow(), `should not be showing permission prompt yet`);
    166 
    167  info(`- create AudioContext which should not start -`);
    168  await SpecialPowers.spawn(browser, [], createAudioContext);
    169  await SpecialPowers.spawn(
    170    browser,
    171    [false],
    172    checkIfAudioContextIsAllowedToStart
    173  );
    174 
    175  info(`- simulate user activate the page -`);
    176  await SpecialPowers.spawn(browser, [], () => {
    177    content.document.notifyUserGestureActivation();
    178  });
    179 
    180  info(`- try to start AudioContext -`);
    181  await SpecialPowers.spawn(browser, [method], startAudioContext);
    182 
    183  info(`- check AudioContext status -`);
    184  await SpecialPowers.spawn(
    185    browser,
    186    [true],
    187    checkIfAudioContextIsAllowedToStart
    188  );
    189  await SpecialPowers.spawn(browser, [true], resumeAudioContext);
    190 
    191  info(`- remove tab -`);
    192  PermissionTestUtils.remove(browser.currentURI, "autoplay-media");
    193  await BrowserTestUtils.removeTab(tab);
    194 }
    195 
    196 add_task(async function start_tests() {
    197  info("- setup test preference -");
    198  await setup_test_preference();
    199 
    200  await testAutoplayExistingPermission({
    201    name: "Prexisting allow permission",
    202    permission: Services.perms.ALLOW_ACTION,
    203  });
    204  await testAutoplayExistingPermission({
    205    name: "Prexisting block permission",
    206    permission: Services.perms.DENY_ACTION,
    207  });
    208  const startMethods = [
    209    "AudioContext",
    210    "AudioBufferSourceNode",
    211    "ConstantSourceNode",
    212    "OscillatorNode",
    213  ];
    214  for (let method of startMethods) {
    215    await testAutoplayUnknownPermission({
    216      name: "Unknown permission and start AudioContext after granting user activation",
    217      method,
    218    });
    219  }
    220 });