tor-browser

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

browser_autoplay_policy_user_gestures.js (7600B)


      1 /* eslint-disable mozilla/no-arbitrary-setTimeout */
      2 
      3 const VIDEO_PAGE = GetTestWebBasedURL("file_video.html");
      4 
      5 const UserGestures = {
      6  MOUSE_CLICK: "mouse-click",
      7  MOUSE_MOVE: "mouse-move",
      8  KEYBOARD_PRESS: "keyboard-press",
      9 };
     10 
     11 const UserGestureTests = [
     12  { type: UserGestures.MOUSE_CLICK, isActivationGesture: true },
     13  { type: UserGestures.MOUSE_MOVE, isActivationGesture: false },
     14  // test different keycode here. printable key, non-printable key and other
     15  // special keys.
     16  {
     17    type: UserGestures.KEYBOARD_PRESS,
     18    isActivationGesture: true,
     19    keyCode: "a",
     20  },
     21  {
     22    type: UserGestures.KEYBOARD_PRESS,
     23    isActivationGesture: false,
     24    keyCode: "VK_ESCAPE",
     25  },
     26  {
     27    type: UserGestures.KEYBOARD_PRESS,
     28    isActivationGesture: true,
     29    keyCode: "VK_RETURN",
     30  },
     31  {
     32    type: UserGestures.KEYBOARD_PRESS,
     33    isActivationGesture: true,
     34    keyCode: "VK_SPACE",
     35  },
     36 ];
     37 
     38 /**
     39 * This test is used to ensure we would stop blocking autoplay after document
     40 * has been activated by user gestures. We would treat mouse clicking, key board
     41 * pressing (printable keys or carriage return) as valid user gesture input.
     42 */
     43 add_task(async function startTestUserGestureInput() {
     44  info("- setup test preference -");
     45  await setupTestPreferences();
     46 
     47  info("- test play when page doesn't be activated -");
     48  await testPlayWithoutUserGesture();
     49 
     50  info("- test play after page got user gesture -");
     51  for (let idx = 0; idx < UserGestureTests.length; idx++) {
     52    info("- test play after page got user gesture -");
     53    await testPlayWithUserGesture(UserGestureTests[idx]);
     54 
     55    info("- test web audio with user gesture -");
     56    await testWebAudioWithUserGesture(UserGestureTests[idx]);
     57  }
     58 });
     59 
     60 /**
     61 * testing helper functions
     62 */
     63 function setupTestPreferences() {
     64  return SpecialPowers.pushPrefEnv({
     65    set: [
     66      ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
     67      ["media.autoplay.blocking_policy", 0],
     68      ["media.autoplay.block-event.enabled", true],
     69    ],
     70  });
     71 }
     72 
     73 function simulateUserGesture(gesture, targetBrowser) {
     74  info(`- simulate ${gesture.type} event -`);
     75  switch (gesture.type) {
     76    case UserGestures.MOUSE_CLICK:
     77      return BrowserTestUtils.synthesizeMouseAtCenter(
     78        "body",
     79        { button: 0 },
     80        targetBrowser
     81      );
     82    case UserGestures.MOUSE_MOVE:
     83      return BrowserTestUtils.synthesizeMouseAtCenter(
     84        "body",
     85        { type: "mousemove" },
     86        targetBrowser
     87      );
     88    case UserGestures.KEYBOARD_PRESS:
     89      info(`- keycode=${gesture.keyCode} -`);
     90      return BrowserTestUtils.synthesizeKey(gesture.keyCode, {}, targetBrowser);
     91    default:
     92      ok(false, "undefined user gesture");
     93      return false;
     94  }
     95 }
     96 
     97 async function testPlayWithoutUserGesture() {
     98  info("- open new tab -");
     99  let tab = await BrowserTestUtils.openNewForegroundTab(
    100    window.gBrowser,
    101    "about:blank"
    102  );
    103  BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, VIDEO_PAGE);
    104  await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
    105 
    106  async function checkAutoplayKeyword() {
    107    info("- create an new autoplay video -");
    108    let video = content.document.createElement("video");
    109    video.src = "gizmo.mp4";
    110    video.autoplay = true;
    111    let canplayPromise = new Promise(function (resolve) {
    112      video.addEventListener(
    113        "canplaythrough",
    114        function () {
    115          resolve();
    116        },
    117        { once: true }
    118      );
    119    });
    120    content.document.body.appendChild(video);
    121 
    122    info("- can't autoplay without user activation -");
    123    await canplayPromise;
    124    ok(video.paused, "video can't start without user input.");
    125  }
    126  await SpecialPowers.spawn(tab.linkedBrowser, [], checkAutoplayKeyword);
    127 
    128  async function playVideo() {
    129    let video = content.document.getElementById("v");
    130    info("- call play() without user activation -");
    131    await video.play().catch(function () {
    132      ok(video.paused, "video can't start play without user input.");
    133    });
    134  }
    135  await SpecialPowers.spawn(tab.linkedBrowser, [], playVideo);
    136 
    137  info("- remove tab -");
    138  BrowserTestUtils.removeTab(tab);
    139 }
    140 
    141 async function testPlayWithUserGesture(gesture) {
    142  info("- open new tab -");
    143  let tab = await BrowserTestUtils.openNewForegroundTab(
    144    window.gBrowser,
    145    "about:blank"
    146  );
    147  BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, VIDEO_PAGE);
    148  await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
    149 
    150  info("- simulate user gesture -");
    151  await simulateUserGesture(gesture, tab.linkedBrowser);
    152 
    153  info("- call play() -");
    154  async function playVideo(gesture) {
    155    let video = content.document.getElementById("v");
    156    try {
    157      await video.play();
    158      ok(gesture.isActivationGesture, "user gesture can activate the page");
    159      ok(!video.paused, "video starts playing.");
    160    } catch (e) {
    161      ok(
    162        !gesture.isActivationGesture,
    163        "user gesture can not activate the page"
    164      );
    165      ok(video.paused, "video can not start playing.");
    166    }
    167  }
    168 
    169  await SpecialPowers.spawn(tab.linkedBrowser, [gesture], playVideo);
    170 
    171  info("- remove tab -");
    172  BrowserTestUtils.removeTab(tab);
    173 }
    174 
    175 function createAudioContext() {
    176  content.ac = new content.AudioContext();
    177  let ac = content.ac;
    178  ac.resumePromises = [];
    179  ac.stateChangePromise = new Promise(resolve => {
    180    ac.addEventListener(
    181      "statechange",
    182      function () {
    183        resolve();
    184      },
    185      { once: true }
    186    );
    187  });
    188  ac.notAllowedToStart = new Promise(resolve => {
    189    ac.addEventListener(
    190      "blocked",
    191      function () {
    192        resolve();
    193      },
    194      { once: true }
    195    );
    196  });
    197 }
    198 
    199 function resumeWithoutExpectedSuccess() {
    200  let ac = content.ac;
    201  let promise = ac.resume();
    202  ac.resumePromises.push(promise);
    203  return new Promise((resolve, reject) => {
    204    content.setTimeout(() => {
    205      if (ac.state == "suspended") {
    206        ok(true, "audio context is still suspended");
    207        resolve();
    208      } else {
    209        reject("audio context should not be allowed to start");
    210      }
    211    }, 2000);
    212  });
    213 }
    214 
    215 function resumeWithExpectedSuccess() {
    216  let ac = content.ac;
    217  ac.resumePromises.push(ac.resume());
    218  return Promise.all(ac.resumePromises).then(() => {
    219    Assert.equal(ac.state, "running", "audio context starts running");
    220  });
    221 }
    222 
    223 async function testWebAudioWithUserGesture(gesture) {
    224  info("- open new tab -");
    225  let tab = await BrowserTestUtils.openNewForegroundTab(
    226    window.gBrowser,
    227    "about:blank"
    228  );
    229  info("- create audio context -");
    230  await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
    231    content.ac = new content.AudioContext();
    232    let ac = content.ac;
    233    ac.resumePromises = [];
    234    return new Promise(resolve => {
    235      ac.addEventListener(
    236        "blocked",
    237        function () {
    238          Assert.equal(
    239            ac.state,
    240            "suspended",
    241            `AudioContext is not started yet.`
    242          );
    243          resolve();
    244        },
    245        { once: true }
    246      );
    247    });
    248  });
    249 
    250  info("- calling resume() -");
    251  try {
    252    await SpecialPowers.spawn(
    253      tab.linkedBrowser,
    254      [],
    255      resumeWithoutExpectedSuccess
    256    );
    257  } catch (error) {
    258    ok(false, error.toString());
    259  }
    260 
    261  info("- simulate user gesture -");
    262  await simulateUserGesture(gesture, tab.linkedBrowser);
    263 
    264  info("- calling resume() again");
    265  try {
    266    let resumeFunc = gesture.isActivationGesture
    267      ? resumeWithExpectedSuccess
    268      : resumeWithoutExpectedSuccess;
    269    await SpecialPowers.spawn(tab.linkedBrowser, [], resumeFunc);
    270  } catch (error) {
    271    ok(false, error.toString());
    272  }
    273 
    274  info("- remove tab -");
    275  await BrowserTestUtils.removeTab(tab);
    276 }