tor-browser

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

browser_media_control_main_controller.js (12422B)


      1 // Import this in order to use `triggerPictureInPicture()`.
      2 Services.scriptloader.loadSubScript(
      3  "chrome://mochitests/content/browser/toolkit/components/pictureinpicture/tests/head.js",
      4  this
      5 );
      6 
      7 const PAGE_NON_AUTOPLAY =
      8  "https://example.com/browser/dom/media/mediacontrol/tests/browser/file_non_autoplay.html";
      9 
     10 const testVideoId = "video";
     11 
     12 add_task(async function setupTestingPref() {
     13  await SpecialPowers.pushPrefEnv({
     14    set: [["media.mediacontrol.testingevents.enabled", true]],
     15  });
     16 });
     17 
     18 /**
     19 * This test is used to check in different situaition if we can determine the
     20 * main controller correctly that is the controller which can receive media
     21 * control keys and show its metadata on the virtual control interface.
     22 *
     23 * We will assign different metadata for each tab and know which tab is the main
     24 * controller by checking main controller's metadata.
     25 *
     26 * We will always choose the last tab which plays media as the main controller,
     27 * and maintain a list by the order of playing media. If the top element in the
     28 * list has been removed, then we will use the last element in the list as the
     29 * main controller.
     30 *
     31 * Eg. tab1 plays first, then tab2 plays, then tab3 plays, the list would be
     32 * like [tab1, tab2, tab3] and the main controller would be tab3. If tab3 has
     33 * been closed, then the list would become [tab1, tab2] and the tab2 would be
     34 * the main controller.
     35 */
     36 add_task(async function testDeterminingMainController() {
     37  info(`open three different tabs`);
     38  const tab0 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
     39  const tab1 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
     40  const tab2 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
     41 
     42  /**
     43   * part1 : [] -> [tab0] -> [tab0, tab1] -> [tab0, tab1, tab2]
     44   */
     45  info(`# [] -> [tab0] -> [tab0, tab1] -> [tab0, tab1, tab2] #`);
     46  info(`set different metadata for each tab`);
     47  await setMediaMetadataForTabs([tab0, tab1, tab2]);
     48 
     49  info(`start media for tab0, main controller should become tab0`);
     50  await makeTabBecomeMainControllerAndWaitForMetadataChange(tab0);
     51 
     52  info(`currrent metadata should be equal to tab0's metadata`);
     53  await isCurrentMetadataEqualTo(tab0.metadata);
     54 
     55  info(`start media for tab1, main controller should become tab1`);
     56  await makeTabBecomeMainControllerAndWaitForMetadataChange(tab1);
     57 
     58  info(`currrent metadata should be equal to tab1's metadata`);
     59  await isCurrentMetadataEqualTo(tab1.metadata);
     60 
     61  info(`start media for tab2, main controller should become tab2`);
     62  await makeTabBecomeMainControllerAndWaitForMetadataChange(tab2);
     63 
     64  info(`currrent metadata should be equal to tab2's metadata`);
     65  await isCurrentMetadataEqualTo(tab2.metadata);
     66 
     67  /**
     68   * part2 : [tab0, tab1, tab2] -> [tab0, tab2, tab1] -> [tab2, tab1, tab0]
     69   */
     70  info(`# [tab0, tab1, tab2] -> [tab0, tab2, tab1] -> [tab2, tab1, tab0] #`);
     71  info(`start media for tab1, main controller should become tab1`);
     72  await makeTabBecomeMainController(tab1);
     73 
     74  info(`currrent metadata should be equal to tab1's metadata`);
     75  await isCurrentMetadataEqualTo(tab1.metadata);
     76 
     77  info(`start media for tab0, main controller should become tab0`);
     78  await makeTabBecomeMainController(tab0);
     79 
     80  info(`currrent metadata should be equal to tab0's metadata`);
     81  await isCurrentMetadataEqualTo(tab0.metadata);
     82 
     83  /**
     84   * part3 : [tab2, tab1, tab0] -> [tab2, tab1] -> [tab2] -> []
     85   */
     86  info(`# [tab2, tab1, tab0] -> [tab2, tab1] -> [tab2] -> [] #`);
     87  info(`remove tab0 and wait until main controller changes`);
     88  await Promise.all([waitUntilMainMediaControllerChanged(), tab0.close()]);
     89 
     90  info(`currrent metadata should be equal to tab1's metadata`);
     91  await isCurrentMetadataEqualTo(tab1.metadata);
     92 
     93  info(`remove tab1 and wait until main controller changes`);
     94  await Promise.all([waitUntilMainMediaControllerChanged(), tab1.close()]);
     95 
     96  info(`currrent metadata should be equal to tab2's metadata`);
     97  await isCurrentMetadataEqualTo(tab2.metadata);
     98 
     99  info(`remove tab2 and wait until main controller changes`);
    100  await Promise.all([waitUntilMainMediaControllerChanged(), tab2.close()]);
    101  isCurrentMetadataEmpty();
    102 });
    103 
    104 add_task(async function testPIPControllerWontBeReplacedByNormalController() {
    105  info(`open two different tabs`);
    106  const tab0 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
    107  const tab1 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
    108 
    109  info(`set different metadata for each tab`);
    110  await setMediaMetadataForTabs([tab0, tab1]);
    111 
    112  info(`start media for tab0, main controller should become tab0`);
    113  await makeTabBecomeMainControllerAndWaitForMetadataChange(tab0);
    114 
    115  info(`currrent metadata should be equal to tab0's metadata`);
    116  await isCurrentMetadataEqualTo(tab0.metadata);
    117 
    118  info(`trigger Picture-in-Picture mode for tab0`);
    119  const winPIP = await triggerPictureInPicture(tab0.linkedBrowser, testVideoId);
    120 
    121  info(`start media for tab1, main controller should still be tab0`);
    122  await playMediaAndWaitUntilRegisteringController(tab1, testVideoId);
    123 
    124  info(`currrent metadata should be equal to tab0's metadata`);
    125  await isCurrentMetadataEqualTo(tab0.metadata);
    126 
    127  info(`remove tab0 and wait until main controller changes`);
    128  await BrowserTestUtils.closeWindow(winPIP);
    129  await Promise.all([waitUntilMainMediaControllerChanged(), tab0.close()]);
    130 
    131  info(`currrent metadata should be equal to tab1's metadata`);
    132  await isCurrentMetadataEqualTo(tab1.metadata);
    133 
    134  info(`remove tab1 and wait until main controller changes`);
    135  await Promise.all([waitUntilMainMediaControllerChanged(), tab1.close()]);
    136  isCurrentMetadataEmpty();
    137 });
    138 
    139 add_task(
    140  async function testFullscreenControllerWontBeReplacedByNormalController() {
    141    info(`open two different tabs`);
    142    const tab0 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
    143    const tab1 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
    144 
    145    info(`set different metadata for each tab`);
    146    await setMediaMetadataForTabs([tab0, tab1]);
    147 
    148    info(`start media for tab0, main controller should become tab0`);
    149    await makeTabBecomeMainControllerAndWaitForMetadataChange(tab0);
    150 
    151    info(`current metadata should be equal to tab0's metadata`);
    152    await isCurrentMetadataEqualTo(tab0.metadata);
    153 
    154    info(`video in tab0 enters fullscreen`);
    155    await switchTabToForegroundAndEnableFullScreen(tab0, testVideoId);
    156 
    157    info(
    158      `normal controller won't become the main controller, ` +
    159        `which is still fullscreen controller`
    160    );
    161    await playMediaAndWaitUntilRegisteringController(tab1, testVideoId);
    162 
    163    info(`currrent metadata should be equal to tab0's metadata`);
    164    await isCurrentMetadataEqualTo(tab0.metadata);
    165 
    166    info(`remove tabs`);
    167    await Promise.all([tab0.close(), tab1.close()]);
    168  }
    169 );
    170 
    171 add_task(async function testFullscreenAndPIPControllers() {
    172  info(`open three different tabs`);
    173  const tab0 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
    174  const tab1 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
    175  const tab2 = await createLoadedTabWrapper(PAGE_NON_AUTOPLAY);
    176 
    177  info(`set different metadata for each tab`);
    178  await setMediaMetadataForTabs([tab0, tab1, tab2]);
    179 
    180  /**
    181   * Current controller list : [tab0 (fullscreen)]
    182   */
    183  info(`start media for tab0, main controller should become tab0`);
    184  await makeTabBecomeMainControllerAndWaitForMetadataChange(tab0);
    185 
    186  info(`currrent metadata should be equal to tab0's metadata`);
    187  await isCurrentMetadataEqualTo(tab0.metadata);
    188 
    189  info(`video in tab0 enters fullscreen`);
    190  await switchTabToForegroundAndEnableFullScreen(tab0, testVideoId);
    191 
    192  /**
    193   * Current controller list : [tab1, tab0 (fullscreen)]
    194   */
    195  info(`start media for tab1, main controller should still be tab0`);
    196  await playMediaAndWaitUntilRegisteringController(tab1, testVideoId);
    197 
    198  info(`currrent metadata should be equal to tab0's metadata`);
    199  await isCurrentMetadataEqualTo(tab0.metadata);
    200 
    201  /**
    202   * Current controller list : [tab0 (fullscreen), tab1 (PIP)]
    203   */
    204  info(`tab1 enters PIP so tab1 should become new main controller`);
    205  const mainControllerChange = waitUntilMainMediaControllerChanged();
    206  const winPIP = await triggerPictureInPicture(tab1.linkedBrowser, testVideoId);
    207  await mainControllerChange;
    208 
    209  info(`currrent metadata should be equal to tab1's metadata`);
    210  await isCurrentMetadataEqualTo(tab1.metadata);
    211 
    212  /**
    213   * Current controller list : [tab2, tab0 (fullscreen), tab1 (PIP)]
    214   */
    215  info(`play video from tab2 which shouldn't affect main controller`);
    216  await playMediaAndWaitUntilRegisteringController(tab2, testVideoId);
    217 
    218  /**
    219   * Current controller list : [tab2, tab0 (fullscreen)]
    220   */
    221  info(`remove tab1 and wait until main controller changes`);
    222  await BrowserTestUtils.closeWindow(winPIP);
    223  await Promise.all([waitUntilMainMediaControllerChanged(), tab1.close()]);
    224 
    225  info(`currrent metadata should be equal to tab0's metadata`);
    226  await isCurrentMetadataEqualTo(tab0.metadata);
    227 
    228  /**
    229   * Current controller list : [tab2]
    230   */
    231  info(`remove tab0 and wait until main controller changes`);
    232  await Promise.all([waitUntilMainMediaControllerChanged(), tab0.close()]);
    233 
    234  info(`currrent metadata should be equal to tab0's metadata`);
    235  await isCurrentMetadataEqualTo(tab2.metadata);
    236 
    237  /**
    238   * Current controller list : []
    239   */
    240  info(`remove tab2 and wait until main controller changes`);
    241  await Promise.all([waitUntilMainMediaControllerChanged(), tab2.close()]);
    242  isCurrentMetadataEmpty();
    243 });
    244 
    245 /**
    246 * The following are helper functions
    247 */
    248 async function setMediaMetadataForTabs(tabs) {
    249  for (let idx = 0; idx < tabs.length; idx++) {
    250    const tabName = "tab" + idx;
    251    info(`create metadata for ${tabName}`);
    252    tabs[idx].metadata = {
    253      title: tabName,
    254      artist: tabName,
    255      album: tabName,
    256      artwork: [{ src: tabName, sizes: "128x128", type: "image/jpeg" }],
    257    };
    258    const spawn = SpecialPowers.spawn(
    259      tabs[idx].linkedBrowser,
    260      [tabs[idx].metadata],
    261      data => {
    262        content.navigator.mediaSession.metadata = new content.MediaMetadata(
    263          data
    264        );
    265      }
    266    );
    267    // As those controller hasn't been activated yet, we can't listen to
    268    // `mediacontroll.onmetadatachange`, which would only be notified after a
    269    // controller becomes active.
    270    await Promise.all([spawn, waitUntilControllerMetadataChanged()]);
    271  }
    272 }
    273 
    274 function makeTabBecomeMainController(tab) {
    275  const playPromise = SpecialPowers.spawn(
    276    tab.linkedBrowser,
    277    [testVideoId],
    278    async Id => {
    279      const video = content.document.getElementById(Id);
    280      if (!video) {
    281        ok(false, `can't get the media element!`);
    282      }
    283      // If media has been started, we would stop media first and then start it
    284      // again, which would make controller's playback state change to `playing`
    285      // again and result in updating new main controller.
    286      if (!video.paused) {
    287        video.pause();
    288        info(`wait until media stops`);
    289        await new Promise(r => (video.onpause = r));
    290      }
    291      info(`start media`);
    292      return video.play();
    293    }
    294  );
    295  return Promise.all([playPromise, waitUntilMainMediaControllerChanged()]);
    296 }
    297 
    298 function makeTabBecomeMainControllerAndWaitForMetadataChange(tab) {
    299  return Promise.all([
    300    new Promise(r => (tab.controller.onmetadatachange = r)),
    301    makeTabBecomeMainController(tab),
    302  ]);
    303 }
    304 
    305 function playMediaAndWaitUntilRegisteringController(tab, elementId) {
    306  const playPromise = SpecialPowers.spawn(
    307    tab.linkedBrowser,
    308    [elementId],
    309    Id => {
    310      const video = content.document.getElementById(Id);
    311      if (!video) {
    312        ok(false, `can't get the media element!`);
    313      }
    314      return video.play();
    315    }
    316  );
    317  return Promise.all([waitUntilMediaControllerAmountChanged(), playPromise]);
    318 }
    319 
    320 async function switchTabToForegroundAndEnableFullScreen(tab, elementId) {
    321  // Fullscreen can only be allowed to enter from a focus tab.
    322  await BrowserTestUtils.switchTab(gBrowser, tab.tabElement);
    323  await SpecialPowers.spawn(tab.linkedBrowser, [elementId], elementId => {
    324    return new Promise(r => {
    325      const element = content.document.getElementById(elementId);
    326      element.requestFullscreen();
    327      element.onfullscreenchange = () => {
    328        element.onfullscreenchange = null;
    329        element.onfullscreenerror = null;
    330        r();
    331      };
    332      element.onfullscreenerror = () => {
    333        // Retry until the element successfully enters fullscreen.
    334        element.requestFullscreen();
    335      };
    336    });
    337  });
    338 }